diff options
Diffstat (limited to 'src')
343 files changed, 13635 insertions, 32005 deletions
diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c index 1404854d..fc0c5b8d 100644 --- a/src/NetworkManagerUtils.c +++ b/src/NetworkManagerUtils.c @@ -34,6 +34,7 @@ #include "platform/nm-platform.h" #include "nm-auth-utils.h" +#include "systemd/nm-sd-utils-shared.h" /*****************************************************************************/ @@ -48,11 +49,11 @@ nm_utils_get_shared_wifi_permission (NMConnection *connection) { NMSettingWireless *s_wifi; NMSettingWirelessSecurity *s_wsec; - const char *method = NULL; + const char *method; - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); - if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED) != 0) - return NULL; /* Not shared */ + method = nm_utils_get_ip_config_method (connection, AF_INET); + if (!nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) + return NULL; s_wifi = nm_connection_get_setting_wireless (connection); if (s_wifi) { @@ -160,38 +161,39 @@ next: const char * nm_utils_get_ip_config_method (NMConnection *connection, - GType ip_setting_type) + int addr_family) { NMSettingConnection *s_con; - NMSettingIPConfig *s_ip4, *s_ip6; + NMSettingIPConfig *s_ip; const char *method; s_con = nm_connection_get_setting_connection (connection); - if (ip_setting_type == NM_TYPE_SETTING_IP4_CONFIG) { + if (addr_family == AF_INET) { g_return_val_if_fail (s_con != NULL, NM_SETTING_IP4_CONFIG_METHOD_AUTO); - s_ip4 = nm_connection_get_setting_ip4_config (connection); - if (!s_ip4) + s_ip = nm_connection_get_setting_ip4_config (connection); + if (!s_ip) return NM_SETTING_IP4_CONFIG_METHOD_DISABLED; - method = nm_setting_ip_config_get_method (s_ip4); - g_return_val_if_fail (method != NULL, NM_SETTING_IP4_CONFIG_METHOD_AUTO); + method = nm_setting_ip_config_get_method (s_ip); + g_return_val_if_fail (method != NULL, NM_SETTING_IP4_CONFIG_METHOD_AUTO); return method; + } - } else if (ip_setting_type == NM_TYPE_SETTING_IP6_CONFIG) { + if (addr_family == AF_INET6) { g_return_val_if_fail (s_con != NULL, NM_SETTING_IP6_CONFIG_METHOD_AUTO); - s_ip6 = nm_connection_get_setting_ip6_config (connection); - if (!s_ip6) + s_ip = nm_connection_get_setting_ip6_config (connection); + if (!s_ip) return NM_SETTING_IP6_CONFIG_METHOD_IGNORE; - method = nm_setting_ip_config_get_method (s_ip6); - g_return_val_if_fail (method != NULL, NM_SETTING_IP6_CONFIG_METHOD_AUTO); + method = nm_setting_ip_config_get_method (s_ip); + g_return_val_if_fail (method != NULL, NM_SETTING_IP6_CONFIG_METHOD_AUTO); return method; + } - } else - g_assert_not_reached (); + g_return_val_if_reached ("" /* bogus */); } gboolean @@ -221,16 +223,13 @@ nm_utils_connection_has_default_route (NMConnection *connection, goto out; } + method = nm_utils_get_ip_config_method (connection, addr_family); if (addr_family == AF_INET) { - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); - if (NM_IN_STRSET (method, NULL, - NM_SETTING_IP4_CONFIG_METHOD_DISABLED, + if (NM_IN_STRSET (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) goto out; } else { - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); - if (NM_IN_STRSET (method, NULL, - NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + if (NM_IN_STRSET (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) goto out; } @@ -341,29 +340,28 @@ check_ip6_method (NMConnection *orig, if (!props) return TRUE; - /* If the generated connection is 'link-local' and the candidate is both 'auto' - * and may-fail=TRUE, then the candidate is OK to use. may-fail is included - * in the decision because if the candidate is 'auto' but may-fail=FALSE, then - * the connection could not possibly have been previously activated on the - * device if the device has no non-link-local IPv6 address. - */ - orig_ip6_method = nm_utils_get_ip_config_method (orig, NM_TYPE_SETTING_IP6_CONFIG); - candidate_ip6_method = nm_utils_get_ip_config_method (candidate, NM_TYPE_SETTING_IP6_CONFIG); + orig_ip6_method = nm_utils_get_ip_config_method (orig, AF_INET6); + candidate_ip6_method = nm_utils_get_ip_config_method (candidate, AF_INET6); candidate_ip6 = nm_connection_get_setting_ip6_config (candidate); - if ( strcmp (orig_ip6_method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0 - && strcmp (candidate_ip6_method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0 - && (!candidate_ip6 || nm_setting_ip_config_get_may_fail (candidate_ip6))) { + if ( nm_streq (orig_ip6_method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) + && nm_streq (candidate_ip6_method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) + && ( !candidate_ip6 + || nm_setting_ip_config_get_may_fail (candidate_ip6))) { + /* If the generated connection is 'link-local' and the candidate is both 'auto' + * and may-fail=TRUE, then the candidate is OK to use. may-fail is included + * in the decision because if the candidate is 'auto' but may-fail=FALSE, then + * the connection could not possibly have been previously activated on the + * device if the device has no non-link-local IPv6 address. + */ allow = TRUE; - } - - /* If the generated connection method is 'link-local' or 'auto' and the candidate - * method is 'ignore' we can take the connection, because NM didn't simply take care - * of IPv6. - */ - if ( ( strcmp (orig_ip6_method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0 - || strcmp (orig_ip6_method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0) - && strcmp (candidate_ip6_method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0) { + } else if ( NM_IN_STRSET (orig_ip6_method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL, + NM_SETTING_IP6_CONFIG_METHOD_AUTO) + && nm_streq0 (candidate_ip6_method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) { + /* If the generated connection method is 'link-local' or 'auto' and the candidate + * method is 'ignore' we can take the connection, because NM didn't simply take care + * of IPv6. + */ allow = TRUE; } @@ -372,6 +370,7 @@ check_ip6_method (NMConnection *orig, NM_SETTING_IP6_CONFIG_SETTING_NAME, NM_SETTING_IP_CONFIG_METHOD); } + return allow; } @@ -519,19 +518,20 @@ check_ip4_method (NMConnection *orig, if (!props) return TRUE; - /* If the generated connection is 'disabled' (device had no IP addresses) - * but it has no carrier, that most likely means that IP addressing could - * not complete and thus no IP addresses were assigned. In that case, allow - * matching to the "auto" method. - */ - orig_ip4_method = nm_utils_get_ip_config_method (orig, NM_TYPE_SETTING_IP4_CONFIG); - candidate_ip4_method = nm_utils_get_ip_config_method (candidate, NM_TYPE_SETTING_IP4_CONFIG); + orig_ip4_method = nm_utils_get_ip_config_method (orig, AF_INET); + candidate_ip4_method = nm_utils_get_ip_config_method (candidate, AF_INET); candidate_ip4 = nm_connection_get_setting_ip4_config (candidate); - if ( strcmp (orig_ip4_method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0 - && strcmp (candidate_ip4_method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0 - && (!candidate_ip4 || nm_setting_ip_config_get_may_fail (candidate_ip4)) - && (device_has_carrier == FALSE)) { + if ( nm_streq (orig_ip4_method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) + && nm_streq (candidate_ip4_method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) + && ( !candidate_ip4 + || nm_setting_ip_config_get_may_fail (candidate_ip4)) + && !device_has_carrier) { + /* If the generated connection is 'disabled' (device had no IP addresses) + * but it has no carrier, that most likely means that IP addressing could + * not complete and thus no IP addresses were assigned. In that case, allow + * matching to the "auto" method. + */ remove_from_hash (settings, props, NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_SETTING_IP_CONFIG_METHOD); @@ -881,6 +881,7 @@ nm_utils_match_connection (NMConnection *const*connections, int nm_match_spec_device_by_pllink (const NMPlatformLink *pllink, const char *match_device_type, + const char *match_dhcp_plugin, const GSList *specs, int no_match_value) { @@ -897,7 +898,8 @@ nm_match_spec_device_by_pllink (const NMPlatformLink *pllink, pllink ? pllink->driver : NULL, NULL, NULL, - NULL); + NULL, + match_dhcp_plugin); switch (m) { case NM_MATCH_SPEC_MATCH: @@ -998,3 +1000,62 @@ nm_shutdown_wait_obj_unregister (NMShutdownWaitObjHandle *handle) g_object_weak_unref (handle->watched_obj, _shutdown_waitobj_cb, handle); _shutdown_waitobj_unregister (handle); } + +/*****************************************************************************/ + +/** + * nm_utils_file_is_in_path: + * @abs_filename: the absolute filename to test + * @abs_path: the absolute path, to check whether filename is in. + * + * This tests, whether @abs_filename is a file which lies inside @abs_path. + * Basically, this checks whether @abs_filename is the same as @abs_path + + * basename(@abs_filename). It allows simple normalizations, like coalescing + * multiple "//". + * + * However, beware that this function is purely filename based. That means, + * it will reject files that reference the same file (i.e. inode) via + * symlinks or bind mounts. Maybe one would like to check for file (inode) + * identity, but that is not really possible based on the file name alone. + * + * This means, that nm_utils_file_is_in_path("/var/run/some-file", "/var/run") + * will succeed, but nm_utils_file_is_in_path("/run/some-file", "/var/run") + * will not (although, it's well known that they reference the same path). + * + * Also, note that @abs_filename must not have trailing slashes itself. + * So, this will reject nm_utils_file_is_in_path("/usr/lib/", "/usr") as + * invalid, because the function searches for file names (and "lib/" is + * clearly a directory). + * + * Returns: if @abs_filename is a file inside @abs_path, returns the + * trailing part of @abs_filename which is the filename. Otherwise + * %NULL. + */ +const char * +nm_utils_file_is_in_path (const char *abs_filename, + const char *abs_path) +{ + const char *path; + + g_return_val_if_fail (abs_filename && abs_filename[0] == '/', NULL); + g_return_val_if_fail (abs_path && abs_path[0] == '/', NULL); + + path = nm_sd_utils_path_startswith (abs_filename, abs_path); + if (!path) + return NULL; + + nm_assert (path[0] != '/'); + nm_assert (path > abs_filename); + nm_assert (path <= &abs_filename[strlen (abs_filename)]); + + /* we require a non-empty remainder with no slashes. That is, only a filename. + * + * Note this will reject "/var/run/" as not being in "/var", + * while "/var/run" would pass. The function searches for files + * only, so a trailing slash (indicating a directory) is not allowed). + * This is despite that the function cannot determine whether "/var/run" + * is itself a file or a directory. "*/ + return path[0] && !strchr (path, '/') + ? path + : NULL; +} diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h index b26d08bd..5e701224 100644 --- a/src/NetworkManagerUtils.h +++ b/src/NetworkManagerUtils.h @@ -26,6 +26,9 @@ /*****************************************************************************/ +const char *nm_utils_get_ip_config_method (NMConnection *connection, + int addr_family); + const char *nm_utils_get_shared_wifi_permission (NMConnection *connection); void nm_utils_complete_generic (NMPlatform *platform, @@ -50,6 +53,7 @@ NMConnection *nm_utils_match_connection (NMConnection *const*connections, int nm_match_spec_device_by_pllink (const NMPlatformLink *pllink, const char *match_device_type, + const char *match_dhcp_plugin, const GSList *specs, int no_match_value); @@ -69,7 +73,7 @@ int nm_match_spec_device_by_pllink (const NMPlatformLink *pllink, * away. It iterates the mainloop for another NM_SHUTDOWN_TIMEOUT_MS_EXTRA. This * should give time to reap the child process (after SIGKILL). * - * So, the maxiumum time we should wait before sending SIGKILL should be at most + * So, the maximum time we should wait before sending SIGKILL should be at most * NM_SHUTDOWN_TIMEOUT_MS. */ #define NM_SHUTDOWN_TIMEOUT_MS 1500 @@ -86,4 +90,10 @@ void nm_shutdown_wait_obj_unregister (NMShutdownWaitObjHandle *handle); /*****************************************************************************/ +const char * +nm_utils_file_is_in_path (const char *abs_filename, + const char *abs_path); + +/*****************************************************************************/ + #endif /* __NETWORKMANAGER_UTILS_H__ */ diff --git a/src/devices/adsl/meson.build b/src/devices/adsl/meson.build index 7ac0e123..f92e809c 100644 --- a/src/devices/adsl/meson.build +++ b/src/devices/adsl/meson.build @@ -1,11 +1,11 @@ sources = files( 'nm-atm-manager.c', - 'nm-device-adsl.c' + 'nm-device-adsl.c', ) deps = [ libudev_dep, - nm_dep + nm_dep, ] libnm_device_plugin_adsl = shared_module( @@ -15,7 +15,7 @@ libnm_device_plugin_adsl = shared_module( link_args: ldflags_linker_script_devices, link_depends: linker_script_devices, install: true, - install_dir: nm_plugindir + install_dir: nm_plugindir, ) core_plugins += libnm_device_plugin_adsl diff --git a/src/devices/adsl/nm-device-adsl.c b/src/devices/adsl/nm-device-adsl.c index c9984f51..ddc03ce3 100644 --- a/src/devices/adsl/nm-device-adsl.c +++ b/src/devices/adsl/nm-device-adsl.c @@ -259,8 +259,9 @@ pppoe_vcc_config (NMDeviceAdsl *self) NMDevice *device = NM_DEVICE (self); NMSettingAdsl *s_adsl; - s_adsl = nm_connection_get_setting_adsl (nm_device_get_applied_connection (device)); - g_assert (s_adsl); + s_adsl = nm_device_get_applied_setting (device, NM_TYPE_SETTING_ADSL); + + g_return_val_if_fail (s_adsl, FALSE); /* Set up the VCC */ if (!br2684_assign_vcc (self, s_adsl)) @@ -389,7 +390,8 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) NMSettingAdsl *s_adsl; const char *protocol; - s_adsl = nm_connection_get_setting_adsl (nm_device_get_applied_connection (device)); + s_adsl = nm_device_get_applied_setting (device, NM_TYPE_SETTING_ADSL); + g_return_val_if_fail (s_adsl, NM_ACT_STAGE_RETURN_FAILURE); protocol = nm_setting_adsl_get_protocol (s_adsl); @@ -465,8 +467,11 @@ act_stage3_ip4_config_start (NMDevice *device, const char *ppp_iface; req = nm_device_get_act_request (device); + g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE); - s_adsl = (NMSettingAdsl *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_ADSL); + + s_adsl = nm_device_get_applied_setting (device, NM_TYPE_SETTING_ADSL); + g_return_val_if_fail (s_adsl, NM_ACT_STAGE_RETURN_FAILURE); /* PPPoE uses the NAS interface, not the ATM interface */ @@ -523,7 +528,7 @@ adsl_cleanup (NMDeviceAdsl *self) if (priv->ppp_manager) { g_signal_handlers_disconnect_by_func (priv->ppp_manager, G_CALLBACK (ppp_state_changed), self); g_signal_handlers_disconnect_by_func (priv->ppp_manager, G_CALLBACK (ppp_ip4_config), self); - nm_ppp_manager_stop (priv->ppp_manager, NULL, NULL); + nm_ppp_manager_stop (priv->ppp_manager, NULL, NULL, NULL); g_clear_object (&priv->ppp_manager); } diff --git a/src/devices/bluetooth/meson.build b/src/devices/bluetooth/meson.build index 628a3bc8..b2f67ceb 100644 --- a/src/devices/bluetooth/meson.build +++ b/src/devices/bluetooth/meson.build @@ -5,12 +5,12 @@ sources = files( 'nm-bluez4-manager.c', 'nm-bluez5-manager.c', 'nm-bt-error.c', - 'nm-device-bt.c' + 'nm-device-bt.c', ) deps = [ libnm_wwan_dep, - nm_dep + nm_dep, ] if enable_bluez5_dun @@ -27,7 +27,7 @@ libnm_device_plugin_bluetooth = shared_module( link_depends: linker_script_devices, install: true, install_dir: nm_plugindir, - install_rpath: nm_plugindir + install_rpath: nm_plugindir, ) core_plugins += libnm_device_plugin_bluetooth diff --git a/src/devices/bluetooth/nm-bluez-device.c b/src/devices/bluetooth/nm-bluez-device.c index b722f692..8e2a96b7 100644 --- a/src/devices/bluetooth/nm-bluez-device.c +++ b/src/devices/bluetooth/nm-bluez-device.c @@ -451,6 +451,9 @@ nm_bluez_device_disconnect (NMBluezDevice *self) g_return_if_fail (priv->dbus_connection); + /* FIXME: if we are in the process of connecting and cancel the + * connection attempt, we must complete the pending connect request. + * However, we must also ensure that we don't leave a connected device. */ if (priv->connection_bt_type == NM_BT_CAPABILITY_DUN) { if (priv->bluez_version == 4) { /* Can't pass a NULL interface name through dbus to bluez, so just @@ -496,76 +499,109 @@ out: } static void -bluez_connect_cb (GDBusConnection *dbus_connection, +_connect_complete (NMBluezDevice *self, + const char *device, + NMBluezDeviceConnectCallback callback, + gpointer callback_user_data, + GError *error) +{ + NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); + + nm_assert ((device || error) && !(device && error)); + + if ( device + && priv->bluez_version == 5) { + priv->connected = TRUE; + _notify (self, PROP_CONNECTED); + } + + if (callback) + callback (self, device, error, callback_user_data); +} + +static void +_connect_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { - GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (user_data); - GObject *result_object = g_async_result_get_source_object (G_ASYNC_RESULT (result)); - NMBluezDevice *self = NM_BLUEZ_DEVICE (result_object); - NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); - GError *error = NULL; - char *device; - GVariant *variant; + gs_unref_object NMBluezDevice *self = NULL; + NMBluezDevicePrivate *priv; + NMBluezDeviceConnectCallback callback; + gpointer callback_user_data; + gs_free_error GError *error = NULL; + char *device = NULL; + gs_unref_variant GVariant *variant = NULL; - variant = g_dbus_connection_call_finish (dbus_connection, res, &error); + nm_utils_user_data_unpack (user_data, &self, &callback, &callback_user_data); - if (!variant) { - g_simple_async_result_take_error (result, error); - } else { - g_variant_get (variant, "(s)", &device); + priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); - g_simple_async_result_set_op_res_gpointer (result, - g_strdup (device), - g_free); + variant = _nm_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), res, G_VARIANT_TYPE ("(s)"), &error); + if (variant) { + g_variant_get (variant, "(s)", &device); priv->b4_iface = device; - g_variant_unref (variant); } - g_simple_async_result_complete (result); - g_object_unref (result); - g_object_unref (result_object); + _connect_complete (self, device, callback, callback_user_data, error); } #if WITH_BLUEZ5_DUN static void -bluez5_dun_connect_cb (NMBluez5DunContext *context, - const char *device, - GError *error, - gpointer user_data) +_connect_cb_bluez5_dun (NMBluez5DunContext *context, + const char *device, + GError *error, + gpointer user_data) { - GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (user_data); + gs_unref_object NMBluezDevice *self = NULL; + gs_unref_object GCancellable *cancellable = NULL; + NMBluezDeviceConnectCallback callback; + gpointer callback_user_data; + gs_free_error GError *cancelled_error = NULL; - if (error) { - g_simple_async_result_take_error (result, error); - } else { - g_simple_async_result_set_op_res_gpointer (result, - g_strdup (device), - g_free); - } + nm_utils_user_data_unpack (user_data, &self, &cancellable, &callback, &callback_user_data); + + /* FIXME(shutdown): the async operation nm_bluez5_dun_connect() should be cancellable. + * Fake it here. */ + if (g_cancellable_set_error_if_cancelled (cancellable, &cancelled_error)) + error = cancelled_error; - g_simple_async_result_complete (result); - g_object_unref (result); + _connect_complete (self, device, callback, callback_user_data, error); } -#endif +#else /* WITH_BLUEZ5_DUN */ +static void +_connect_cb_bluez5_dun_idle_no_b5 (gpointer user_data, + GCancellable *cancellable) +{ + gs_unref_object NMBluezDevice *self = NULL; + NMBluezDeviceConnectCallback callback; + gpointer callback_user_data; + gs_free_error GError *error = NULL; + + nm_utils_user_data_unpack (user_data, &self, &callback, &callback_user_data); + + if (!g_cancellable_set_error_if_cancelled (cancellable, &error)) { + g_set_error (&error, + NM_BT_ERROR, + NM_BT_ERROR_DUN_CONNECT_FAILED, + "NetworkManager built without support for Bluez 5"); + } + callback (self, NULL, error, callback_user_data); +} +#endif /* WITH_BLUEZ5_DUN */ void nm_bluez_device_connect_async (NMBluezDevice *self, NMBluetoothCapabilities connection_bt_type, - GAsyncReadyCallback callback, - gpointer user_data) + GCancellable *cancellable, + NMBluezDeviceConnectCallback callback, + gpointer callback_user_data) { - GSimpleAsyncResult *simple; NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); const char *dbus_iface = NULL; const char *connect_type = NULL; g_return_if_fail (priv->capabilities & connection_bt_type & (NM_BT_CAPABILITY_DUN | NM_BT_CAPABILITY_NAP)); - simple = g_simple_async_result_new (G_OBJECT (self), - callback, - user_data, - nm_bluez_device_connect_async); priv->connection_bt_type = connection_bt_type; if (connection_bt_type == NM_BT_CAPABILITY_NAP) { @@ -582,19 +618,29 @@ nm_bluez_device_connect_async (NMBluezDevice *self, #if WITH_BLUEZ5_DUN if (priv->b5_dun_context == NULL) priv->b5_dun_context = nm_bluez5_dun_new (priv->adapter_address, priv->address); - nm_bluez5_dun_connect (priv->b5_dun_context, bluez5_dun_connect_cb, simple); + nm_bluez5_dun_connect (priv->b5_dun_context, + _connect_cb_bluez5_dun, + nm_utils_user_data_pack (g_object_ref (self), + nm_g_object_ref (cancellable), + callback, + callback_user_data)); #else - g_simple_async_result_set_error (simple, - NM_BT_ERROR, - NM_BT_ERROR_DUN_CONNECT_FAILED, - "NetworkManager built without support for Bluez 5"); - g_simple_async_result_complete (simple); + if (callback) { + nm_utils_invoke_on_idle (_connect_cb_bluez5_dun_idle_no_b5, + nm_utils_user_data_pack (g_object_ref (self), + callback, + callback_user_data), + cancellable); + } #endif return; } } else - g_assert_not_reached (); + g_return_if_reached (); + /* FIXME: we need to remember that a connect is in progress. + * So, if the request gets cancelled, that we disconnect the + * connection that was established in the meantime. */ g_dbus_connection_call (priv->dbus_connection, NM_BLUEZ_SERVICE, priv->path, @@ -604,37 +650,11 @@ nm_bluez_device_connect_async (NMBluezDevice *self, NULL, G_DBUS_CALL_FLAGS_NONE, 20000, - NULL, - (GAsyncReadyCallback) bluez_connect_cb, - simple); -} - -const char * -nm_bluez_device_connect_finish (NMBluezDevice *self, - GAsyncResult *result, - GError **error) -{ - NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); - GSimpleAsyncResult *simple; - const char *device; - - g_return_val_if_fail (g_simple_async_result_is_valid (result, - G_OBJECT (self), - nm_bluez_device_connect_async), - NULL); - - simple = (GSimpleAsyncResult *) result; - - if (g_simple_async_result_propagate_error (simple, error)) - return NULL; - - device = (const char *) g_simple_async_result_get_op_res_gpointer (simple); - if (device && priv->bluez_version == 5) { - priv->connected = TRUE; - _notify (self, PROP_CONNECTED); - } - - return device; + cancellable, + _connect_cb, + nm_utils_user_data_pack (g_object_ref (self), + callback, + callback_user_data)); } /*****************************************************************************/ diff --git a/src/devices/bluetooth/nm-bluez-device.h b/src/devices/bluetooth/nm-bluez-device.h index f8a1872f..d2d0beb0 100644 --- a/src/devices/bluetooth/nm-bluez-device.h +++ b/src/devices/bluetooth/nm-bluez-device.h @@ -66,16 +66,17 @@ guint32 nm_bluez_device_get_capabilities (NMBluezDevice *self); gboolean nm_bluez_device_get_connected (NMBluezDevice *self); +typedef void (*NMBluezDeviceConnectCallback) (NMBluezDevice *self, + const char *device, + GError *error, + gpointer user_data); + void nm_bluez_device_connect_async (NMBluezDevice *self, NMBluetoothCapabilities connection_bt_type, - GAsyncReadyCallback callback, - gpointer user_data); - -const char * -nm_bluez_device_connect_finish (NMBluezDevice *self, - GAsyncResult *result, - GError **error); + GCancellable *cancellable, + NMBluezDeviceConnectCallback callback, + gpointer callback_user_data); void nm_bluez_device_disconnect (NMBluezDevice *self); diff --git a/src/devices/bluetooth/nm-bluez5-dun.c b/src/devices/bluetooth/nm-bluez5-dun.c index ca09b276..32f8da65 100644 --- a/src/devices/bluetooth/nm-bluez5-dun.c +++ b/src/devices/bluetooth/nm-bluez5-dun.c @@ -342,13 +342,14 @@ nm_bluez5_dun_connect (NMBluez5DunContext *context, if (context->rfcomm_channel != -1) { nm_log_dbg (LOGD_BT, "(%s): channel number on device %s cached: %d", - context->src_str, context->dst_str, context->rfcomm_channel); + context->src_str, context->dst_str, context->rfcomm_channel); + /* FIXME: don't invoke the callback synchronously. */ dun_connect (context); return; } nm_log_dbg (LOGD_BT, "(%s): starting channel number discovery for device %s", - context->src_str, context->dst_str); + context->src_str, context->dst_str); context->sdp_session = sdp_connect (&context->src, &context->dst, SDP_NON_BLOCKING); if (!context->sdp_session) { @@ -358,10 +359,12 @@ nm_bluez5_dun_connect (NMBluez5DunContext *context, error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED, "Failed to connect to the SDP server: (%d) %s", err, strerror (err)); + /* FIXME: don't invoke the callback synchronously. */ context->callback (context, NULL, error, context->user_data); return; } + /* FIXME(shutdown): make connect cancellable. */ channel = g_io_channel_unix_new (sdp_get_socket (context->sdp_session)); context->sdp_watch_id = g_io_add_watch (channel, G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL, diff --git a/src/devices/bluetooth/nm-bluez5-dun.h b/src/devices/bluetooth/nm-bluez5-dun.h index 124c1a05..b75e4399 100644 --- a/src/devices/bluetooth/nm-bluez5-dun.h +++ b/src/devices/bluetooth/nm-bluez5-dun.h @@ -32,7 +32,8 @@ NMBluez5DunContext *nm_bluez5_dun_new (const char *adapter, const char *remote); void nm_bluez5_dun_connect (NMBluez5DunContext *context, - NMBluez5DunFunc callback, gpointer user_data); + NMBluez5DunFunc callback, + gpointer user_data); /* Clean up connection resources */ void nm_bluez5_dun_cleanup (NMBluez5DunContext *context); diff --git a/src/devices/bluetooth/nm-device-bt.c b/src/devices/bluetooth/nm-device-bt.c index 1f650d33..68209d8a 100644 --- a/src/devices/bluetooth/nm-device-bt.c +++ b/src/devices/bluetooth/nm-device-bt.c @@ -78,7 +78,9 @@ typedef struct { char *rfcomm_iface; NMModem *modem; - guint32 timeout_id; + guint timeout_id; + + GCancellable *cancellable; guint32 bt_type; /* BT type of the current connection */ } NMDeviceBtPrivate; @@ -318,12 +320,10 @@ complete_connection (NMDevice *device, if (s_gsm) { fallback_prefix = _("GSM connection"); - if (!nm_setting_gsm_get_number (s_gsm)) - g_object_set (G_OBJECT (s_gsm), NM_SETTING_GSM_NUMBER, "*99#", NULL); } else { fallback_prefix = _("CDMA connection"); if (!nm_setting_cdma_get_number (s_cdma)) - g_object_set (G_OBJECT (s_cdma), NM_SETTING_GSM_NUMBER, "#777", NULL); + g_object_set (G_OBJECT (s_cdma), NM_SETTING_CDMA_NUMBER, "#777", NULL); } } else { g_set_error_literal (error, @@ -672,6 +672,7 @@ component_added (NMDevice *device, GObject *component) /* Got the modem */ nm_clear_g_source (&priv->timeout_id); + nm_clear_g_cancellable (&priv->cancellable); /* Can only accept the modem in stage2, but since the interface matched * what we were expecting, don't let anything else claim the modem either. @@ -715,8 +716,11 @@ static gboolean modem_find_timeout (gpointer user_data) { NMDeviceBt *self = NM_DEVICE_BT (user_data); + NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self); + + priv->timeout_id = 0; + nm_clear_g_cancellable (&priv->cancellable); - NM_DEVICE_BT_GET_PRIVATE (self)->timeout_id = 0; nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_MODEM_NOT_FOUND); @@ -738,8 +742,8 @@ check_connect_continue (NMDeviceBt *self) "Activation: (bluetooth) Stage 2 of 5 (Device Configure) successful. Will connect via %s.", dun ? "DUN" : (pan ? "PAN" : "unknown")); - /* Kill the connect timeout since we're connected now */ nm_clear_g_source (&priv->timeout_id); + nm_clear_g_cancellable (&priv->cancellable); if (pan) { /* Bluez says we're connected now. Start IP config. */ @@ -755,25 +759,25 @@ check_connect_continue (NMDeviceBt *self) } static void -bluez_connect_cb (GObject *object, - GAsyncResult *res, - void *user_data) +bluez_connect_cb (NMBluezDevice *bt_device, + const char *device_name, + GError *error, + gpointer user_data) { - gs_unref_object NMDeviceBt *self = NM_DEVICE_BT (user_data); + gs_unref_object NMDeviceBt *self = user_data; NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self); - GError *error = NULL; - const char *device; - device = nm_bluez_device_connect_finish (NM_BLUEZ_DEVICE (object), - res, &error); + if (nm_utils_error_is_cancelled (error, FALSE)) + return; + + nm_clear_g_source (&priv->timeout_id); + g_clear_object (&priv->cancellable); if (!nm_device_is_activating (NM_DEVICE (self))) return; - if (!device) { + if (!device_name) { _LOGW (LOGD_BT, "Error connecting with bluez: %s", error->message); - g_clear_error (&error); - nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_BT_FAILED); @@ -782,10 +786,10 @@ bluez_connect_cb (GObject *object, if (priv->bt_type == NM_BT_CAPABILITY_DUN) { g_free (priv->rfcomm_iface); - priv->rfcomm_iface = g_strdup (device); + priv->rfcomm_iface = g_strdup (device_name); } else if (priv->bt_type == NM_BT_CAPABILITY_NAP) { - if (!nm_device_set_ip_iface (NM_DEVICE (self), device)) { - _LOGW (LOGD_BT, "Error connecting with bluez: cannot find device %s", device); + if (!nm_device_set_ip_iface (NM_DEVICE (self), device_name)) { + _LOGW (LOGD_BT, "Error connecting with bluez: cannot find device %s", device_name); nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_BT_FAILED); @@ -843,10 +847,13 @@ static gboolean bt_connect_timeout (gpointer user_data) { NMDeviceBt *self = NM_DEVICE_BT (user_data); + NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self); _LOGD (LOGD_BT, "initial connection timed out"); - NM_DEVICE_BT_GET_PRIVATE (self)->timeout_id = 0; + priv->timeout_id = 0; + nm_clear_g_cancellable (&priv->cancellable); + nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_BT_FAILED); @@ -876,13 +883,17 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) _LOGD (LOGD_BT, "requesting connection to the device"); - /* Connect to the BT device */ - nm_bluez_device_connect_async (priv->bt_device, - priv->bt_type & (NM_BT_CAPABILITY_DUN | NM_BT_CAPABILITY_NAP), - bluez_connect_cb, g_object_ref (device)); - nm_clear_g_source (&priv->timeout_id); + nm_clear_g_cancellable (&priv->cancellable); + priv->timeout_id = g_timeout_add_seconds (30, bt_connect_timeout, device); + priv->cancellable = g_cancellable_new (); + + nm_bluez_device_connect_async (priv->bt_device, + priv->bt_type & (NM_BT_CAPABILITY_DUN | NM_BT_CAPABILITY_NAP), + priv->cancellable, + bluez_connect_cb, + g_object_ref (self)); return NM_ACT_STAGE_RETURN_POSTPONE; } @@ -925,6 +936,9 @@ deactivate (NMDevice *device) priv->have_iface = FALSE; priv->connected = FALSE; + nm_clear_g_source (&priv->timeout_id); + nm_clear_g_cancellable (&priv->cancellable); + if (priv->bt_type == NM_BT_CAPABILITY_DUN) { if (priv->modem) { nm_modem_deactivate (priv->modem, device); @@ -942,8 +956,6 @@ deactivate (NMDevice *device) if (priv->bt_type != NM_BT_CAPABILITY_NONE) nm_bluez_device_disconnect (priv->bt_device); - nm_clear_g_source (&priv->timeout_id); - priv->bt_type = NM_BT_CAPABILITY_NONE; g_free (priv->rfcomm_iface); @@ -1128,6 +1140,7 @@ dispose (GObject *object) NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) object); nm_clear_g_source (&priv->timeout_id); + nm_clear_g_cancellable (&priv->cancellable); g_signal_handlers_disconnect_matched (priv->bt_device, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, object); diff --git a/src/devices/nm-acd-manager.c b/src/devices/nm-acd-manager.c index 035487a3..a0a175be 100644 --- a/src/devices/nm-acd-manager.c +++ b/src/devices/nm-acd-manager.c @@ -39,40 +39,23 @@ typedef enum { typedef struct { in_addr_t address; gboolean duplicate; - NMAcdManager *manager; - NAcd *acd; - GIOChannel *channel; - guint event_id; + NAcdProbe *probe; } AddressInfo; -enum { - PROBE_TERMINATED, - LAST_SIGNAL, -}; - -static guint signals[LAST_SIGNAL] = { 0 }; - -typedef struct { +struct _NMAcdManager { int ifindex; guint8 hwaddr[ETH_ALEN]; State state; GHashTable *addresses; guint completed; -} NMAcdManagerPrivate; + NAcd *acd; + GIOChannel *channel; + guint event_id; -struct _NMAcdManager { - GObject parent; - NMAcdManagerPrivate _priv; + NMAcdCallbacks callbacks; + gpointer user_data; }; -struct _NMAcdManagerClass { - GObjectClass parent; -}; - -G_DEFINE_TYPE (NMAcdManager, nm_acd_manager, G_TYPE_OBJECT) - -#define NM_ACD_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMAcdManager, NM_IS_ACD_MANAGER) - /*****************************************************************************/ #define _NMLOG_DOMAIN LOGD_IP4 @@ -80,14 +63,13 @@ G_DEFINE_TYPE (NMAcdManager, nm_acd_manager, G_TYPE_OBJECT) #define _NMLOG(level, ...) \ G_STMT_START { \ char _sbuf[64]; \ - int _ifindex = (self) ? NM_ACD_MANAGER_GET_PRIVATE (self)->ifindex : 0; \ \ nm_log ((level), _NMLOG_DOMAIN, \ - nm_platform_link_get_name (NM_PLATFORM_GET, _ifindex), \ + self && self->ifindex > 0 ? nm_platform_link_get_name (NM_PLATFORM_GET, self->ifindex) : NULL, \ NULL, \ "%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ _NMLOG_PREFIX_NAME, \ - self ? nm_sprintf_buf (_sbuf, "[%p,%d]", self, _ifindex) : "" \ + self ? nm_sprintf_buf (_sbuf, "[%p,%d]", self, self->ifindex) : "" \ _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ } G_STMT_END @@ -111,32 +93,25 @@ _acd_event_to_string (unsigned int event) return NULL; } -#define acd_event_to_string(event) NM_UTILS_LOOKUP_STR (_acd_event_to_string, event) +#define acd_event_to_string_a(event) NM_UTILS_LOOKUP_STR_A (_acd_event_to_string, event) static const char * -_acd_error_to_string (int error) +acd_error_to_string (int error) { if (error < 0) - return strerror(-error); + return g_strerror (-error); switch (error) { case _N_ACD_E_SUCCESS: return "success"; - case N_ACD_E_DONE: - return "no more events (engine running)"; - case N_ACD_E_STOPPED: - return "no more events (engine stopped)"; case N_ACD_E_PREEMPTED: return "preempted"; case N_ACD_E_INVALID_ARGUMENT: return "invalid argument"; - case N_ACD_E_BUSY: - return "busy"; } - return NULL; -} -#define acd_error_to_string(error) NM_UTILS_LOOKUP_STR (_acd_error_to_string, error) + g_return_val_if_reached (NULL); +} /*****************************************************************************/ @@ -152,21 +127,18 @@ _acd_error_to_string (int error) gboolean nm_acd_manager_add_address (NMAcdManager *self, in_addr_t address) { - NMAcdManagerPrivate *priv; AddressInfo *info; - g_return_val_if_fail (NM_IS_ACD_MANAGER (self), FALSE); - priv = NM_ACD_MANAGER_GET_PRIVATE (self); - g_return_val_if_fail (priv->state == STATE_INIT, FALSE); + g_return_val_if_fail (self, FALSE); + g_return_val_if_fail (self->state == STATE_INIT, FALSE); - if (g_hash_table_lookup (priv->addresses, GUINT_TO_POINTER (address))) + if (g_hash_table_lookup (self->addresses, GUINT_TO_POINTER (address))) return FALSE; info = g_slice_new0 (AddressInfo); info->address = address; - info->manager = self; - g_hash_table_insert (priv->addresses, GUINT_TO_POINTER (address), info); + g_hash_table_insert (self->addresses, GUINT_TO_POINTER (address), info); return TRUE; } @@ -174,115 +146,142 @@ nm_acd_manager_add_address (NMAcdManager *self, in_addr_t address) static gboolean acd_event (GIOChannel *source, GIOCondition condition, gpointer data) { - AddressInfo *info = data; - NMAcdManager *self = info->manager; - NMAcdManagerPrivate *priv = NM_ACD_MANAGER_GET_PRIVATE (self); + NMAcdManager *self = data; NAcdEvent *event; + AddressInfo *info; + gboolean emit_probe_terminated = FALSE; char address_str[INET_ADDRSTRLEN]; gs_free char *hwaddr_str = NULL; int r; - if ( n_acd_dispatch (info->acd) - || n_acd_pop_event (info->acd, &event)) + if (n_acd_dispatch (self->acd)) return G_SOURCE_CONTINUE; - switch (event->event) { - case N_ACD_EVENT_READY: - info->duplicate = FALSE; - if (priv->state == STATE_ANNOUNCING) { - r = n_acd_announce (info->acd, N_ACD_DEFEND_ONCE); - if (r) { - _LOGW ("couldn't announce address %s on interface '%s': %s", - nm_utils_inet4_ntop (info->address, address_str), - nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex), - acd_error_to_string (r)); - } else { - _LOGD ("announcing address %s", - nm_utils_inet4_ntop (info->address, address_str)); + while ( !n_acd_pop_event (self->acd, &event) + && event) { + gboolean check_probing_done = FALSE; + + switch (event->event) { + case N_ACD_EVENT_READY: + n_acd_probe_get_userdata (event->ready.probe, (void **) &info); + info->duplicate = FALSE; + if (self->state == STATE_ANNOUNCING) { + /* fake probe ended, start announcing */ + r = n_acd_probe_announce (info->probe, N_ACD_DEFEND_ONCE); + if (r) { + _LOGW ("couldn't announce address %s on interface '%s': %s", + nm_utils_inet4_ntop (info->address, address_str), + nm_platform_link_get_name (NM_PLATFORM_GET, self->ifindex), + acd_error_to_string (r)); + } else { + _LOGD ("announcing address %s", + nm_utils_inet4_ntop (info->address, address_str)); + } } + check_probing_done = TRUE; + break; + case N_ACD_EVENT_USED: + n_acd_probe_get_userdata (event->used.probe, (void **) &info); + info->duplicate = TRUE; + check_probing_done = TRUE; + break; + case N_ACD_EVENT_DEFENDED: + n_acd_probe_get_userdata (event->defended.probe, (void **) &info); + _LOGD ("defended address %s from host %s", + nm_utils_inet4_ntop (info->address, address_str), + (hwaddr_str = nm_utils_hwaddr_ntoa (event->defended.sender, + event->defended.n_sender))); + break; + case N_ACD_EVENT_CONFLICT: + n_acd_probe_get_userdata (event->conflict.probe, (void **) &info); + _LOGW ("conflict for address %s detected with host %s on interface '%s'", + nm_utils_inet4_ntop (info->address, address_str), + (hwaddr_str = nm_utils_hwaddr_ntoa (event->defended.sender, + event->defended.n_sender)), + nm_platform_link_get_name (NM_PLATFORM_GET, self->ifindex)); + break; + default: + _LOGD ("unhandled event '%s'", acd_event_to_string_a (event->event)); + break; + } + + if ( check_probing_done + && self->state == STATE_PROBING + && ++self->completed == g_hash_table_size (self->addresses)) { + self->state = STATE_PROBE_DONE; + emit_probe_terminated = TRUE; } - break; - case N_ACD_EVENT_USED: - info->duplicate = TRUE; - break; - case N_ACD_EVENT_DEFENDED: - _LOGD ("defended address %s from host %s", - nm_utils_inet4_ntop (info->address, address_str), - (hwaddr_str = nm_utils_hwaddr_ntoa (event->defended.sender, - event->defended.n_sender))); - break; - case N_ACD_EVENT_CONFLICT: - _LOGW ("conflict for address %s detected with host %s on interface '%s'", - nm_utils_inet4_ntop (info->address, address_str), - (hwaddr_str = nm_utils_hwaddr_ntoa (event->defended.sender, - event->defended.n_sender)), - nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex)); - break; - default: - _LOGD ("event '%s' for address %s", - acd_event_to_string (event->event), - nm_utils_inet4_ntop (info->address, address_str)); - return G_SOURCE_CONTINUE; } - if ( priv->state == STATE_PROBING - && ++priv->completed == g_hash_table_size (priv->addresses)) { - priv->state = STATE_PROBE_DONE; - g_signal_emit (self, signals[PROBE_TERMINATED], 0); + if (emit_probe_terminated) { + if (self->callbacks.probe_terminated_callback) { + self->callbacks.probe_terminated_callback (self, + self->user_data); + } } return G_SOURCE_CONTINUE; } static gboolean -acd_probe_start (NMAcdManager *self, - AddressInfo *info, - guint64 timeout) +acd_probe_add (NMAcdManager *self, + AddressInfo *info, + guint64 timeout) { - NMAcdManagerPrivate *priv = NM_ACD_MANAGER_GET_PRIVATE (self); - NAcdConfig *config; - int r, fd; + NAcdProbeConfig *probe_config; + int r; + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; - r = n_acd_new (&info->acd); + r = n_acd_probe_config_new (&probe_config); if (r) { - _LOGW ("could not create ACD for %s on interface '%s': %s", - nm_utils_inet4_ntop (info->address, NULL), - nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex), + _LOGW ("could not create probe config for %s on interface '%s': %s", + nm_utils_inet4_ntop (info->address, sbuf), + nm_platform_link_get_name (NM_PLATFORM_GET, self->ifindex), acd_error_to_string (r)); return FALSE; } - n_acd_get_fd (info->acd, &fd); - info->channel = g_io_channel_unix_new (fd); - info->event_id = g_io_add_watch (info->channel, G_IO_IN, acd_event, info); - - config = &(NAcdConfig) { - .ifindex = priv->ifindex, - .mac = priv->hwaddr, - .n_mac = ETH_ALEN, - .ip = info->address, - .timeout_msec = timeout, - .transport = N_ACD_TRANSPORT_ETHERNET, - }; + n_acd_probe_config_set_ip (probe_config, (struct in_addr) { info->address }); + n_acd_probe_config_set_timeout (probe_config, timeout); - r = n_acd_start (info->acd, config); + r = n_acd_probe (self->acd, &info->probe, probe_config); if (r) { _LOGW ("could not start probe for %s on interface '%s': %s", - nm_utils_inet4_ntop (info->address, NULL), - nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex), + nm_utils_inet4_ntop (info->address, sbuf), + nm_platform_link_get_name (NM_PLATFORM_GET, self->ifindex), acd_error_to_string (r)); + n_acd_probe_config_free (probe_config); return FALSE; } - if (timeout) { - _LOGD ("started probe for %s with timeout %llu", - nm_utils_inet4_ntop (info->address, NULL), - (unsigned long long) timeout); - } + n_acd_probe_set_userdata (info->probe, info); + n_acd_probe_config_free (probe_config); return TRUE; } +static int +acd_init (NMAcdManager *self) +{ + NAcdConfig *config; + int r; + + if (self->acd) + return 0; + + r = n_acd_config_new (&config); + if (r) + return r; + + n_acd_config_set_ifindex (config, self->ifindex); + n_acd_config_set_transport (config, N_ACD_TRANSPORT_ETHERNET); + n_acd_config_set_mac (config, self->hwaddr, ETH_ALEN); + + r = n_acd_new (&self->acd, config); + n_acd_config_free (config); + return r; +} + /** * nm_acd_manager_start_probe: * @self: a #NMAcdManager @@ -297,59 +296,36 @@ acd_probe_start (NMAcdManager *self, gboolean nm_acd_manager_start_probe (NMAcdManager *self, guint timeout) { - NMAcdManagerPrivate *priv; GHashTableIter iter; AddressInfo *info; gboolean success = FALSE; + int fd, r; - g_return_val_if_fail (NM_IS_ACD_MANAGER (self), FALSE); - priv = NM_ACD_MANAGER_GET_PRIVATE (self); - g_return_val_if_fail (priv->state == STATE_INIT, FALSE); + g_return_val_if_fail (self, FALSE); + g_return_val_if_fail (self->state == STATE_INIT, FALSE); + + r = acd_init (self); + if (r) { + _LOGW ("couldn't init ACD for probing on interface '%s': %s", + nm_platform_link_get_name (NM_PLATFORM_GET, self->ifindex), + acd_error_to_string (r)); + return FALSE; + } - priv->completed = 0; + self->completed = 0; - g_hash_table_iter_init (&iter, priv->addresses); + g_hash_table_iter_init (&iter, self->addresses); while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info)) - success |= acd_probe_start (self, info, timeout); + success |= acd_probe_add (self, info, timeout); if (success) - priv->state = STATE_PROBING; + self->state = STATE_PROBING; - return success; -} - -/** - * nm_acd_manager_reset: - * @self: a #NMAcdManager - * - * Stop any operation in progress and reset @self to the initial state. - */ -void -nm_acd_manager_reset (NMAcdManager *self) -{ - NMAcdManagerPrivate *priv; - - g_return_if_fail (NM_IS_ACD_MANAGER (self)); - priv = NM_ACD_MANAGER_GET_PRIVATE (self); - - g_hash_table_remove_all (priv->addresses); + n_acd_get_fd (self->acd, &fd); + self->channel = g_io_channel_unix_new (fd); + self->event_id = g_io_add_watch (self->channel, G_IO_IN, acd_event, self); - priv->state = STATE_INIT; -} - -/** - * nm_acd_manager_destroy: - * @self: the #NMAcdManager - * - * Calls nm_acd_manager_reset() and unrefs @self. - */ -void -nm_acd_manager_destroy (NMAcdManager *self) -{ - g_return_if_fail (NM_IS_ACD_MANAGER (self)); - - nm_acd_manager_reset (self); - g_object_unref (self); + return success; } /** @@ -365,15 +341,12 @@ nm_acd_manager_destroy (NMAcdManager *self) gboolean nm_acd_manager_check_address (NMAcdManager *self, in_addr_t address) { - NMAcdManagerPrivate *priv; AddressInfo *info; - g_return_val_if_fail (NM_IS_ACD_MANAGER (self), FALSE); - priv = NM_ACD_MANAGER_GET_PRIVATE (self); - g_return_val_if_fail ( priv->state == STATE_INIT - || priv->state == STATE_PROBE_DONE, FALSE); + g_return_val_if_fail (self, FALSE); + g_return_val_if_fail (NM_IN_SET (self->state, STATE_INIT, STATE_PROBE_DONE), FALSE); - info = g_hash_table_lookup (priv->addresses, GUINT_TO_POINTER (address)); + info = g_hash_table_lookup (self->addresses, GUINT_TO_POINTER (address)); g_return_val_if_fail (info, FALSE); return !info->duplicate; @@ -388,41 +361,43 @@ nm_acd_manager_check_address (NMAcdManager *self, in_addr_t address) void nm_acd_manager_announce_addresses (NMAcdManager *self) { - NMAcdManagerPrivate *priv = NM_ACD_MANAGER_GET_PRIVATE (self); GHashTableIter iter; AddressInfo *info; int r; - if (priv->state == STATE_INIT) { + r = acd_init (self); + if (r) { + _LOGW ("couldn't init ACD for announcing addresses on interface '%s': %s", + nm_platform_link_get_name (NM_PLATFORM_GET, self->ifindex), + acd_error_to_string (r)); + return; + } + + if (self->state == STATE_INIT) { /* n-acd can't announce without probing, therefore let's * start a fake probe with zero timeout and then perform - * the announce. */ - priv->state = STATE_ANNOUNCING; - g_hash_table_iter_init (&iter, priv->addresses); - while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info)) { - if (!acd_probe_start (self, info, 0)) { - _LOGW ("couldn't announce address %s on interface '%s'", - nm_utils_inet4_ntop (info->address, NULL), - nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex)); - } - } - } else if (priv->state == STATE_PROBE_DONE) { - priv->state = STATE_ANNOUNCING; - g_hash_table_iter_init (&iter, priv->addresses); + * the announcement. */ + g_hash_table_iter_init (&iter, self->addresses); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info)) + acd_probe_add (self, info, 0); + self->state = STATE_ANNOUNCING; + } else if (self->state == STATE_ANNOUNCING) { + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + + g_hash_table_iter_init (&iter, self->addresses); while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info)) { if (info->duplicate) continue; - r = n_acd_announce (info->acd, N_ACD_DEFEND_ONCE); + r = n_acd_probe_announce (info->probe, N_ACD_DEFEND_ONCE); if (r) { _LOGW ("couldn't announce address %s on interface '%s': %s", - nm_utils_inet4_ntop (info->address, NULL), - nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex), + nm_utils_inet4_ntop (info->address, sbuf), + nm_platform_link_get_name (NM_PLATFORM_GET, self->ifindex), acd_error_to_string (r)); } else - _LOGD ("announcing address %s", nm_utils_inet4_ntop (info->address, NULL)); + _LOGD ("announcing address %s", nm_utils_inet4_ntop (info->address, sbuf)); } - } else - nm_assert_not_reached (); + } } static void @@ -430,64 +405,52 @@ destroy_address_info (gpointer data) { AddressInfo *info = (AddressInfo *) data; - g_clear_pointer (&info->channel, g_io_channel_unref); - g_clear_pointer (&info->acd, n_acd_free); - nm_clear_g_source (&info->event_id); + n_acd_probe_free (info->probe); g_slice_free (AddressInfo, info); } /*****************************************************************************/ -static void -nm_acd_manager_init (NMAcdManager *self) -{ - NMAcdManagerPrivate *priv = NM_ACD_MANAGER_GET_PRIVATE (self); - - priv->addresses = g_hash_table_new_full (nm_direct_hash, NULL, - NULL, destroy_address_info); - priv->state = STATE_INIT; -} - NMAcdManager * -nm_acd_manager_new (int ifindex, const guint8 *hwaddr, size_t hwaddr_len) +nm_acd_manager_new (int ifindex, + const guint8 *hwaddr, + guint hwaddr_len, + const NMAcdCallbacks *callbacks, + gpointer user_data) { NMAcdManager *self; - NMAcdManagerPrivate *priv; + g_return_val_if_fail (ifindex > 0, NULL); g_return_val_if_fail (hwaddr, NULL); g_return_val_if_fail (hwaddr_len == ETH_ALEN, NULL); - self = g_object_new (NM_TYPE_ACD_MANAGER, NULL); - priv = NM_ACD_MANAGER_GET_PRIVATE (self); - priv->ifindex = ifindex; - memcpy (priv->hwaddr, hwaddr, ETH_ALEN); + self = g_slice_new0 (NMAcdManager); + + if (callbacks) + self->callbacks = *callbacks; + self->user_data = user_data; + self->addresses = g_hash_table_new_full (nm_direct_hash, NULL, + NULL, destroy_address_info); + self->state = STATE_INIT; + self->ifindex = ifindex; + memcpy (self->hwaddr, hwaddr, ETH_ALEN); return self; } -static void -dispose (GObject *object) +void +nm_acd_manager_free (NMAcdManager *self) { - NMAcdManager *self = NM_ACD_MANAGER (object); - NMAcdManagerPrivate *priv = NM_ACD_MANAGER_GET_PRIVATE (self); + g_return_if_fail (self); - g_clear_pointer (&priv->addresses, g_hash_table_destroy); - - G_OBJECT_CLASS (nm_acd_manager_parent_class)->dispose (object); -} - -static void -nm_acd_manager_class_init (NMAcdManagerClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); + if (self->callbacks.user_data_destroy) + self->callbacks.user_data_destroy (self->user_data); - object_class->dispose = dispose; + nm_clear_pointer (&self->addresses, g_hash_table_destroy); + nm_clear_pointer (&self->channel, g_io_channel_unref); + nm_clear_g_source (&self->event_id); + nm_clear_pointer (&self->acd, n_acd_unref); - signals[PROBE_TERMINATED] = - g_signal_new (NM_ACD_MANAGER_PROBE_TERMINATED, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, NULL, NULL, NULL, - G_TYPE_NONE, 0); + g_slice_free (NMAcdManager, self); } diff --git a/src/devices/nm-acd-manager.h b/src/devices/nm-acd-manager.h index eeede5da..75884846 100644 --- a/src/devices/nm-acd-manager.h +++ b/src/devices/nm-acd-manager.h @@ -19,25 +19,25 @@ #include <netinet/in.h> -#define NM_TYPE_ACD_MANAGER (nm_acd_manager_get_type ()) -#define NM_ACD_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_ACD_MANAGER, NMAcdManager)) -#define NM_ACD_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_ACD_MANAGER, NMAcdManagerClass)) -#define NM_IS_ACD_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_ACD_MANAGER)) -#define NM_IS_ACD_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_ACD_MANAGER)) -#define NM_ACD_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_ACD_MANAGER, NMAcdManagerClass)) +typedef struct _NMAcdManager NMAcdManager; -#define NM_ACD_MANAGER_PROBE_TERMINATED "probe-terminated" +typedef struct { + void (*probe_terminated_callback) (NMAcdManager *self, + gpointer user_data); + GDestroyNotify user_data_destroy; +} NMAcdCallbacks; -typedef struct _NMAcdManagerClass NMAcdManagerClass; +NMAcdManager *nm_acd_manager_new (int ifindex, + const guint8 *hwaddr, + guint hwaddr_len, + const NMAcdCallbacks *callbacks, + gpointer user_data); -GType nm_acd_manager_get_type (void); +void nm_acd_manager_free (NMAcdManager *self); -NMAcdManager *nm_acd_manager_new (int ifindex, const guint8 *hwaddr, size_t hwaddr_len); -void nm_acd_manager_destroy (NMAcdManager *self); gboolean nm_acd_manager_add_address (NMAcdManager *self, in_addr_t address); gboolean nm_acd_manager_start_probe (NMAcdManager *self, guint timeout); gboolean nm_acd_manager_check_address (NMAcdManager *self, in_addr_t address); void nm_acd_manager_announce_addresses (NMAcdManager *self); -void nm_acd_manager_reset (NMAcdManager *self); #endif /* __NM_ACD_MANAGER__ */ diff --git a/src/devices/nm-device-6lowpan.c b/src/devices/nm-device-6lowpan.c index b6b9157c..40103747 100644 --- a/src/devices/nm-device-6lowpan.c +++ b/src/devices/nm-device-6lowpan.c @@ -110,9 +110,9 @@ create_and_realize (NMDevice *device, GError **error) { const char *iface = nm_device_get_iface (device); - NMPlatformError plerr; NMSetting6Lowpan *s_6lowpan; int parent_ifindex; + int r; s_6lowpan = NM_SETTING_6LOWPAN (nm_connection_get_setting (connection, NM_TYPE_SETTING_6LOWPAN)); g_return_val_if_fail (s_6lowpan, FALSE); @@ -126,13 +126,13 @@ create_and_realize (NMDevice *device, return FALSE; } - plerr = nm_platform_link_6lowpan_add (nm_device_get_platform (device), iface, parent_ifindex, out_plink); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) { + r = nm_platform_link_6lowpan_add (nm_device_get_platform (device), iface, parent_ifindex, out_plink); + if (r < 0) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to create 6lowpan interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string_a (plerr)); + nm_strerror (r)); return FALSE; } diff --git a/src/devices/nm-device-bond.c b/src/devices/nm-device-bond.c index 6e7e6ffc..78cba9bb 100644 --- a/src/devices/nm-device-bond.c +++ b/src/devices/nm-device-bond.c @@ -220,7 +220,6 @@ static NMActStageReturn apply_bonding_config (NMDevice *device) { NMDeviceBond *self = NM_DEVICE_BOND (device); - NMConnection *connection; NMSettingBond *s_bond; int ifindex = nm_device_get_ifindex (device); const char *mode_str, *value; @@ -241,10 +240,9 @@ apply_bonding_config (NMDevice *device) * arp_interval doesn't require miimon to be 0 */ - connection = nm_device_get_applied_connection (device); - g_assert (connection); - s_bond = nm_connection_get_setting_bond (connection); - g_assert (s_bond); + s_bond = nm_device_get_applied_setting (device, NM_TYPE_SETTING_BOND); + + g_return_val_if_fail (s_bond, NM_ACT_STAGE_RETURN_FAILURE); mode_str = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_MODE); if (!mode_str) @@ -461,17 +459,17 @@ create_and_realize (NMDevice *device, GError **error) { const char *iface = nm_device_get_iface (device); - NMPlatformError plerr; + int r; g_assert (iface); - plerr = nm_platform_link_bond_add (nm_device_get_platform (device), iface, out_plink); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) { + r = nm_platform_link_bond_add (nm_device_get_platform (device), iface, out_plink); + if (r < 0) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to create bond interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string_a (plerr)); + nm_strerror (r)); return FALSE; } return TRUE; diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c index e79de95c..4c8921c0 100644 --- a/src/devices/nm-device-bridge.c +++ b/src/devices/nm-device-bridge.c @@ -168,26 +168,52 @@ complete_connection (NMDevice *device, typedef struct { const char *name; const char *sysname; - gboolean default_if_zero; - gboolean user_hz_compensate; + uint nm_min; + uint nm_max; + uint nm_default; + bool default_if_zero; + bool user_hz_compensate; + bool only_with_stp; } Option; static const Option master_options[] = { - { NM_SETTING_BRIDGE_STP, "stp_state", FALSE, FALSE }, - { NM_SETTING_BRIDGE_PRIORITY, "priority", TRUE, FALSE }, - { NM_SETTING_BRIDGE_FORWARD_DELAY, "forward_delay", TRUE, TRUE }, - { NM_SETTING_BRIDGE_HELLO_TIME, "hello_time", TRUE, TRUE }, - { NM_SETTING_BRIDGE_MAX_AGE, "max_age", TRUE, TRUE }, - { NM_SETTING_BRIDGE_AGEING_TIME, "ageing_time", TRUE, TRUE }, - { NM_SETTING_BRIDGE_GROUP_FORWARD_MASK, "group_fwd_mask", TRUE, FALSE }, - { NM_SETTING_BRIDGE_MULTICAST_SNOOPING, "multicast_snooping", FALSE, FALSE }, + { NM_SETTING_BRIDGE_STP, "stp_state", /* this must stay as the first item */ + 0, 1, 1, + FALSE, FALSE, FALSE }, + { NM_SETTING_BRIDGE_PRIORITY, "priority", + 0, G_MAXUINT16, 0x8000, + TRUE, FALSE, TRUE }, + { NM_SETTING_BRIDGE_FORWARD_DELAY, "forward_delay", + 0, NM_BR_MAX_FORWARD_DELAY, 15, + TRUE, TRUE, TRUE}, + { NM_SETTING_BRIDGE_HELLO_TIME, "hello_time", + 0, NM_BR_MAX_HELLO_TIME, 2, + TRUE, TRUE, TRUE }, + { NM_SETTING_BRIDGE_MAX_AGE, "max_age", + 0, NM_BR_MAX_MAX_AGE, 20, + TRUE, TRUE, TRUE }, + { NM_SETTING_BRIDGE_AGEING_TIME, "ageing_time", + NM_BR_MIN_AGEING_TIME, NM_BR_MAX_AGEING_TIME, 300, + TRUE, TRUE, FALSE }, + { NM_SETTING_BRIDGE_GROUP_FORWARD_MASK, "group_fwd_mask", + 0, 0xFFFF, 0, + TRUE, FALSE, FALSE }, + { NM_SETTING_BRIDGE_MULTICAST_SNOOPING, "multicast_snooping", + 0, 1, 1, + FALSE, FALSE, FALSE }, { NULL, NULL } }; static const Option slave_options[] = { - { NM_SETTING_BRIDGE_PORT_PRIORITY, "priority", TRUE, FALSE }, - { NM_SETTING_BRIDGE_PORT_PATH_COST, "path_cost", TRUE, FALSE }, - { NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE, "hairpin_mode", FALSE, FALSE }, + { NM_SETTING_BRIDGE_PORT_PRIORITY, "priority", + 0, NM_BR_PORT_MAX_PRIORITY, NM_BR_PORT_DEF_PRIORITY, + TRUE, FALSE }, + { NM_SETTING_BRIDGE_PORT_PATH_COST, "path_cost", + 0, NM_BR_PORT_MAX_PATH_COST, 100, + TRUE, FALSE }, + { NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE, "hairpin_mode", + 0, 1, 0, + FALSE, FALSE }, { NULL, NULL } }; @@ -275,23 +301,43 @@ update_connection (NMDevice *device, NMConnection *connection) NMSettingBridge *s_bridge = nm_connection_get_setting_bridge (connection); int ifindex = nm_device_get_ifindex (device); const Option *option; + gs_free char *stp = NULL; + int stp_value; if (!s_bridge) { s_bridge = (NMSettingBridge *) nm_setting_bridge_new (); nm_connection_add_setting (connection, (NMSetting *) s_bridge); } - for (option = master_options; option->name; option++) { + option = master_options; + nm_assert (nm_streq (option->sysname, "stp_state")); + + stp = nm_platform_sysctl_master_get_option (nm_device_get_platform (device), ifindex, option->sysname); + stp_value = _nm_utils_ascii_str_to_int64 (stp, 10, option->nm_min, option->nm_max, option->nm_default); + g_object_set (s_bridge, option->name, stp_value, NULL); + option++; + + for (; option->name; option++) { gs_free char *str = nm_platform_sysctl_master_get_option (nm_device_get_platform (device), ifindex, option->sysname); - int value; + uint value; - if (str) { - value = strtol (str, NULL, 10); + if (!stp_value && option->only_with_stp) + continue; + if (str) { /* See comments in set_sysfs_uint() about centiseconds. */ - if (option->user_hz_compensate) + if (option->user_hz_compensate) { + value = _nm_utils_ascii_str_to_int64 (str, 10, + option->nm_min * 100, + option->nm_max * 100, + option->nm_default * 100); value /= 100; - + } else { + value = _nm_utils_ascii_str_to_int64 (str, 10, + option->nm_min, + option->nm_max, + option->nm_default); + } g_object_set (s_bridge, option->name, value, NULL); } else _LOGW (LOGD_BRIDGE, "failed to read bridge setting '%s'", option->sysname); @@ -322,15 +368,22 @@ master_update_slave_connection (NMDevice *device, for (option = slave_options; option->name; option++) { gs_free char *str = nm_platform_sysctl_slave_get_option (nm_device_get_platform (device), ifindex_slave, option->sysname); - int value; + uint value; if (str) { - value = strtol (str, NULL, 10); - /* See comments in set_sysfs_uint() about centiseconds. */ - if (option->user_hz_compensate) + if (option->user_hz_compensate) { + value = _nm_utils_ascii_str_to_int64 (str, 10, + option->nm_min * 100, + option->nm_max * 100, + option->nm_default * 100); value /= 100; - + } else { + value = _nm_utils_ascii_str_to_int64 (str, 10, + option->nm_min, + option->nm_max, + option->nm_default); + } g_object_set (s_port, option->name, value, NULL); } else _LOGW (LOGD_BRIDGE, "failed to read bridge port setting '%s'", option->sysname); @@ -459,7 +512,7 @@ create_and_realize (NMDevice *device, const char *hwaddr; gs_free char *hwaddr_cloned = NULL; guint8 mac_address[NM_UTILS_HWADDR_LEN_MAX]; - NMPlatformError plerr; + int r; nm_assert (iface); @@ -486,17 +539,17 @@ create_and_realize (NMDevice *device, } } - plerr = nm_platform_link_bridge_add (nm_device_get_platform (device), - iface, - hwaddr ? mac_address : NULL, - hwaddr ? ETH_ALEN : 0, - out_plink); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) { + r = nm_platform_link_bridge_add (nm_device_get_platform (device), + iface, + hwaddr ? mac_address : NULL, + hwaddr ? ETH_ALEN : 0, + out_plink); + if (r < 0) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to create bridge interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string_a (plerr)); + nm_strerror (r)); return FALSE; } diff --git a/src/devices/nm-device-dummy.c b/src/devices/nm-device-dummy.c index a9059383..eb90456a 100644 --- a/src/devices/nm-device-dummy.c +++ b/src/devices/nm-device-dummy.c @@ -98,19 +98,19 @@ create_and_realize (NMDevice *device, GError **error) { const char *iface = nm_device_get_iface (device); - NMPlatformError plerr; NMSettingDummy *s_dummy; + int r; s_dummy = nm_connection_get_setting_dummy (connection); g_assert (s_dummy); - plerr = nm_platform_link_dummy_add (nm_device_get_platform (device), iface, out_plink); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) { + r = nm_platform_link_dummy_add (nm_device_get_platform (device), iface, out_plink); + if (r < 0) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to create dummy interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string_a (plerr)); + nm_strerror (r)); return FALSE; } diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c index 0fb8fea4..3b6fb35b 100644 --- a/src/devices/nm-device-ethernet.c +++ b/src/devices/nm-device-ethernet.c @@ -556,7 +556,9 @@ build_supplicant_config (NMDeviceEthernet *self, guint32 mtu; connection = nm_device_get_applied_connection (NM_DEVICE (self)); - g_assert (connection); + + g_return_val_if_fail (connection, NULL); + con_uuid = nm_connection_get_uuid (connection); mtu = nm_platform_link_get_mtu (nm_device_get_platform (NM_DEVICE (self)), nm_device_get_ifindex (NM_DEVICE (self))); @@ -790,7 +792,7 @@ link_negotiation_set (NMDevice *device) guint32 speed = 0; guint32 link_speed; - s_wired = (NMSettingWired *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRED); + s_wired = nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRED); if (s_wired) { autoneg = nm_setting_wired_get_auto_negotiate (s_wired); speed = nm_setting_wired_get_speed (s_wired); @@ -880,8 +882,8 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *out_failure_reason) delay); g_assert (!priv->pppoe_wait_id); priv->pppoe_wait_id = g_timeout_add_seconds (delay, - pppoe_reconnect_delay, - self); + pppoe_reconnect_delay, + self); return NM_ACT_STAGE_RETURN_POSTPONE; } priv->last_pppoe_time = 0; @@ -900,6 +902,7 @@ nm_8021x_stage2_config (NMDeviceEthernet *self, NMDeviceStateReason *out_failure NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; connection = nm_device_get_applied_connection (NM_DEVICE (self)); + g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); security = nm_connection_get_setting_802_1x (connection); @@ -995,10 +998,12 @@ pppoe_stage3_ip4_config_start (NMDeviceEthernet *self, NMDeviceStateReason *out_ NMActRequest *req; GError *err = NULL; - req = nm_device_get_act_request (NM_DEVICE (self)); + req = nm_device_get_act_request (device); + g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE); - s_pppoe = (NMSettingPppoe *) nm_device_get_applied_setting ((NMDevice *) self, NM_TYPE_SETTING_PPPOE); + s_pppoe = nm_device_get_applied_setting (device, NM_TYPE_SETTING_PPPOE); + g_return_val_if_fail (s_pppoe, NM_ACT_STAGE_RETURN_FAILURE); priv->ppp_manager = nm_ppp_manager_create (nm_device_get_iface (device), @@ -1069,8 +1074,10 @@ dcb_configure (NMDevice *device) nm_clear_g_source (&priv->dcb_timeout_id); - s_dcb = (NMSettingDcb *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_DCB); - g_assert (s_dcb); + s_dcb = nm_device_get_applied_setting (device, NM_TYPE_SETTING_DCB); + + g_return_val_if_fail (s_dcb, FALSE); + if (!nm_dcb_setup (nm_device_get_iface (device), s_dcb, &error)) { _LOGW (LOGD_DCB, "Activation: (ethernet) failed to enable DCB/FCoE: %s", error->message); @@ -1199,7 +1206,8 @@ wake_on_lan_enable (NMDevice *device) NMSettingWired *s_wired; const char *password = NULL; - s_wired = (NMSettingWired *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRED); + s_wired = nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRED); + if (s_wired) { wol = nm_setting_wired_get_wake_on_lan (s_wired); password = nm_setting_wired_get_wake_on_lan_password (s_wired); @@ -1208,7 +1216,7 @@ wake_on_lan_enable (NMDevice *device) } wol = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA, - "ethernet.wake-on-lan", + NM_CON_DEFAULT ("ethernet.wake-on-lan"), device, NM_SETTING_WIRED_WAKE_ON_LAN_NONE, G_MAXINT32, @@ -1240,8 +1248,8 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS; NMSettingDcb *s_dcb; - s_con = NM_SETTING_CONNECTION (nm_device_get_applied_setting (device, - NM_TYPE_SETTING_CONNECTION)); + s_con = nm_device_get_applied_setting (device, NM_TYPE_SETTING_CONNECTION); + g_return_val_if_fail (s_con, NM_ACT_STAGE_RETURN_FAILURE); nm_clear_g_source (&priv->dcb_timeout_id); @@ -1254,8 +1262,8 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) if (!strcmp (connection_type, NM_SETTING_WIRED_SETTING_NAME)) { NMSetting8021x *security; - security = (NMSetting8021x *) nm_device_get_applied_setting (device, - NM_TYPE_SETTING_802_1X); + security = nm_device_get_applied_setting (device, NM_TYPE_SETTING_802_1X); + if (security) { /* FIXME: for now 802.1x is mutually exclusive with DCB */ return nm_8021x_stage2_config (self, out_failure_reason); @@ -1265,7 +1273,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) wake_on_lan_enable (device); /* DCB and FCoE setup */ - s_dcb = (NMSettingDcb *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_DCB); + s_dcb = nm_device_get_applied_setting (device, NM_TYPE_SETTING_DCB); if (s_dcb) { /* lldpad really really wants the carrier to be up */ if (nm_platform_link_is_connected (nm_device_get_platform (device), nm_device_get_ifindex (device))) { @@ -1288,7 +1296,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) NM_SETTING_PPPOE_SETTING_NAME)) { NMSettingPpp *s_ppp; - s_ppp = (NMSettingPpp *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_PPP); + s_ppp = nm_device_get_applied_setting (device, NM_TYPE_SETTING_PPP); if (s_ppp) { guint32 mtu = 0, mru = 0, mxu; @@ -1316,7 +1324,8 @@ act_stage3_ip4_config_start (NMDevice *device, NMSettingConnection *s_con; const char *connection_type; - s_con = NM_SETTING_CONNECTION (nm_device_get_applied_setting (device, NM_TYPE_SETTING_CONNECTION)); + s_con = nm_device_get_applied_setting (device, NM_TYPE_SETTING_CONNECTION); + g_return_val_if_fail (s_con, NM_ACT_STAGE_RETURN_FAILURE); connection_type = nm_setting_connection_get_connection_type (s_con); @@ -1347,7 +1356,7 @@ deactivate (NMDevice *device) nm_clear_g_source (&priv->pppoe_wait_id); if (priv->ppp_manager) { - nm_ppp_manager_stop (priv->ppp_manager, NULL, NULL); + nm_ppp_manager_stop (priv->ppp_manager, NULL, NULL, NULL); g_clear_object (&priv->ppp_manager); } @@ -1358,7 +1367,7 @@ deactivate (NMDevice *device) priv->dcb_handle_carrier_changes = FALSE; /* Tear down DCB/FCoE if it was enabled */ - s_dcb = (NMSettingDcb *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_DCB); + s_dcb = nm_device_get_applied_setting (device, NM_TYPE_SETTING_DCB); if (s_dcb) { if (!nm_dcb_cleanup (nm_device_get_iface (device), &error)) { _LOGW (LOGD_DEVICE | LOGD_PLATFORM, "failed to disable DCB/FCoE: %s", @@ -1446,7 +1455,6 @@ new_default_connection (NMDevice *self) const char *uprop = "0"; gs_free char *defname = NULL; gs_free char *uuid = NULL; - gs_free char *machine_id = NULL; guint i, n_connections; if (nm_config_get_no_auto_default_for_device (nm_config_get (), self)) @@ -1470,12 +1478,10 @@ new_default_connection (NMDevice *self) if (!defname) return NULL; - machine_id = nm_utils_machine_id_read (); - /* Create a stable UUID. The UUID is also the Network_ID for stable-privacy addr-gen-mode, * thus when it changes we will also generate different IPv6 addresses. */ uuid = _nm_utils_uuid_generate_from_strings ("default-wired", - machine_id ?: "", + nm_utils_machine_id_str (), defname, perm_hw_addr, NULL); diff --git a/src/devices/nm-device-infiniband.c b/src/devices/nm-device-infiniband.c index 41fac157..4db7d8a7 100644 --- a/src/devices/nm-device-infiniband.c +++ b/src/devices/nm-device-infiniband.c @@ -22,6 +22,7 @@ #include "nm-device-infiniband.h" +#include <linux/if.h> #include <linux/if_infiniband.h> #include "NetworkManagerUtils.h" @@ -86,7 +87,8 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) if (ret != NM_ACT_STAGE_RETURN_SUCCESS) return ret; - s_infiniband = (NMSettingInfiniband *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_INFINIBAND); + s_infiniband = nm_device_get_applied_setting (device, NM_TYPE_SETTING_INFINIBAND); + g_return_val_if_fail (s_infiniband, NM_ACT_STAGE_RETURN_FAILURE); transport_mode = nm_setting_infiniband_get_transport_mode (s_infiniband); @@ -233,7 +235,7 @@ create_and_realize (NMDevice *device, { NMDeviceInfinibandPrivate *priv = NM_DEVICE_INFINIBAND_GET_PRIVATE ((NMDeviceInfiniband *) device); NMSettingInfiniband *s_infiniband; - NMPlatformError plerr; + int r; s_infiniband = nm_connection_get_setting_infiniband (connection); g_assert (s_infiniband); @@ -267,13 +269,13 @@ create_and_realize (NMDevice *device, return FALSE; } - plerr = nm_platform_link_infiniband_add (nm_device_get_platform (device), priv->parent_ifindex, priv->p_key, out_plink); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) { + r = nm_platform_link_infiniband_add (nm_device_get_platform (device), priv->parent_ifindex, priv->p_key, out_plink); + if (r < 0) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to create InfiniBand P_Key interface '%s' for '%s': %s", nm_device_get_iface (device), nm_connection_get_id (connection), - nm_platform_error_to_string_a (plerr)); + nm_strerror (r)); return FALSE; } @@ -285,7 +287,7 @@ static gboolean unrealize (NMDevice *device, GError **error) { NMDeviceInfinibandPrivate *priv; - NMPlatformError plerr; + int r; g_return_val_if_fail (NM_IS_DEVICE_INFINIBAND (device), FALSE); @@ -297,12 +299,12 @@ unrealize (NMDevice *device, GError **error) return FALSE; } - plerr = nm_platform_link_infiniband_delete (nm_device_get_platform (device), priv->parent_ifindex, priv->p_key); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) { + r = nm_platform_link_infiniband_delete (nm_device_get_platform (device), priv->parent_ifindex, priv->p_key); + if (r < 0) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to remove InfiniBand P_Key interface '%s': %s", nm_device_get_iface (device), - nm_platform_error_to_string_a (plerr)); + nm_strerror (r)); return FALSE; } diff --git a/src/devices/nm-device-ip-tunnel.c b/src/devices/nm-device-ip-tunnel.c index 568403c6..ca5f9c3f 100644 --- a/src/devices/nm-device-ip-tunnel.c +++ b/src/devices/nm-device-ip-tunnel.c @@ -27,6 +27,7 @@ #include <linux/if.h> #include <linux/ip.h> #include <linux/if_tunnel.h> +#include <linux/ip6_tunnel.h> #include "nm-device-private.h" #include "nm-manager.h" @@ -328,28 +329,28 @@ clear: if (!address_equal_pn (AF_INET, priv->local, &local4)) { g_clear_pointer (&priv->local, g_free); if (local4) - priv->local = g_strdup (nm_utils_inet4_ntop (local4, NULL)); + priv->local = nm_utils_inet4_ntop_dup (local4); _notify (self, PROP_LOCAL); } if (!address_equal_pn (AF_INET, priv->remote, &remote4)) { g_clear_pointer (&priv->remote, g_free); if (remote4) - priv->remote = g_strdup (nm_utils_inet4_ntop (remote4, NULL)); + priv->remote = nm_utils_inet4_ntop_dup (remote4); _notify (self, PROP_REMOTE); } } else { if (!address_equal_pn (AF_INET6, priv->local, &local6)) { g_clear_pointer (&priv->local, g_free); if (memcmp (&local6, &in6addr_any, sizeof (in6addr_any))) - priv->local = g_strdup (nm_utils_inet6_ntop (&local6, NULL)); + priv->local = nm_utils_inet6_ntop_dup (&local6); _notify (self, PROP_LOCAL); } if (!address_equal_pn (AF_INET6, priv->remote, &remote6)) { g_clear_pointer (&priv->remote, g_free); if (memcmp (&remote6, &in6addr_any, sizeof (in6addr_any))) - priv->remote = g_strdup (nm_utils_inet6_ntop (&remote6, NULL)); + priv->remote = nm_utils_inet6_ntop_dup (&remote6); _notify (self, PROP_REMOTE); } } @@ -659,7 +660,6 @@ create_and_realize (NMDevice *device, { const char *iface = nm_device_get_iface (device); NMSettingIPTunnel *s_ip_tunnel; - NMPlatformError plerr; NMPlatformLnkGre lnk_gre = { }; NMPlatformLnkSit lnk_sit = { }; NMPlatformLnkIpIp lnk_ipip = { }; @@ -667,6 +667,7 @@ create_and_realize (NMDevice *device, const char *str; gint64 val; NMIPTunnelMode mode; + int r; s_ip_tunnel = nm_connection_get_setting_ip_tunnel (connection); g_assert (s_ip_tunnel); @@ -712,13 +713,13 @@ create_and_realize (NMDevice *device, lnk_gre.output_flags = NM_GRE_KEY; } - plerr = nm_platform_link_gre_add (nm_device_get_platform (device), iface, &lnk_gre, out_plink); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) { + r = nm_platform_link_gre_add (nm_device_get_platform (device), iface, &lnk_gre, out_plink); + if (r < 0) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to create GRE interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string_a (plerr)); + nm_strerror (r)); return FALSE; } break; @@ -738,13 +739,13 @@ create_and_realize (NMDevice *device, lnk_sit.tos = nm_setting_ip_tunnel_get_tos (s_ip_tunnel); lnk_sit.path_mtu_discovery = nm_setting_ip_tunnel_get_path_mtu_discovery (s_ip_tunnel); - plerr = nm_platform_link_sit_add (nm_device_get_platform (device), iface, &lnk_sit, out_plink); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) { + r = nm_platform_link_sit_add (nm_device_get_platform (device), iface, &lnk_sit, out_plink); + if (r < 0) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to create SIT interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string_a (plerr)); + nm_strerror (r)); return FALSE; } break; @@ -764,13 +765,13 @@ create_and_realize (NMDevice *device, lnk_ipip.tos = nm_setting_ip_tunnel_get_tos (s_ip_tunnel); lnk_ipip.path_mtu_discovery = nm_setting_ip_tunnel_get_path_mtu_discovery (s_ip_tunnel); - plerr = nm_platform_link_ipip_add (nm_device_get_platform (device), iface, &lnk_ipip, out_plink); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) { + r = nm_platform_link_ipip_add (nm_device_get_platform (device), iface, &lnk_ipip, out_plink); + if (r < 0) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to create IPIP interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string_a (plerr)); + nm_strerror (r)); return FALSE; } break; @@ -819,21 +820,21 @@ create_and_realize (NMDevice *device, lnk_ip6tnl.is_gre = TRUE; lnk_ip6tnl.is_tap = (mode == NM_IP_TUNNEL_MODE_IP6GRETAP); - plerr = nm_platform_link_ip6gre_add (nm_device_get_platform (device), - iface, &lnk_ip6tnl, out_plink); + r = nm_platform_link_ip6gre_add (nm_device_get_platform (device), + iface, &lnk_ip6tnl, out_plink); } else { lnk_ip6tnl.proto = nm_setting_ip_tunnel_get_mode (s_ip_tunnel) == NM_IP_TUNNEL_MODE_IPIP6 ? IPPROTO_IPIP : IPPROTO_IPV6; - plerr = nm_platform_link_ip6tnl_add (nm_device_get_platform (device), - iface, &lnk_ip6tnl, out_plink); + r = nm_platform_link_ip6tnl_add (nm_device_get_platform (device), + iface, &lnk_ip6tnl, out_plink); } - if (plerr != NM_PLATFORM_ERROR_SUCCESS) { + if (r < 0) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to create IPv6 tunnel interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string_a (plerr)); + nm_strerror (r)); return FALSE; } break; diff --git a/src/devices/nm-device-macsec.c b/src/devices/nm-device-macsec.c index 8ea4c8b5..1a6b64a4 100644 --- a/src/devices/nm-device-macsec.c +++ b/src/devices/nm-device-macsec.c @@ -210,7 +210,7 @@ update_properties (NMDevice *device) static NMSupplicantConfig * build_supplicant_config (NMDeviceMacsec *self, GError **error) { - NMSupplicantConfig *config = NULL; + gs_unref_object NMSupplicantConfig *config = NULL; NMSettingMacsec *s_macsec; NMSetting8021x *s_8021x; NMConnection *connection; @@ -218,19 +218,21 @@ build_supplicant_config (NMDeviceMacsec *self, GError **error) guint32 mtu; connection = nm_device_get_applied_connection (NM_DEVICE (self)); - g_assert (connection); + + g_return_val_if_fail (connection, NULL); + con_uuid = nm_connection_get_uuid (connection); mtu = nm_platform_link_get_mtu (nm_device_get_platform (NM_DEVICE (self)), nm_device_get_ifindex (NM_DEVICE (self))); config = nm_supplicant_config_new (FALSE, FALSE); - s_macsec = (NMSettingMacsec *) - nm_device_get_applied_setting (NM_DEVICE (self), NM_TYPE_SETTING_MACSEC); + s_macsec = nm_device_get_applied_setting (NM_DEVICE (self), NM_TYPE_SETTING_MACSEC); + + g_return_val_if_fail (s_macsec, NULL); if (!nm_supplicant_config_add_setting_macsec (config, s_macsec, error)) { g_prefix_error (error, "macsec-setting: "); - g_object_unref (config); return NULL; } @@ -238,11 +240,11 @@ build_supplicant_config (NMDeviceMacsec *self, GError **error) s_8021x = nm_connection_get_setting_802_1x (connection); if (!nm_supplicant_config_add_setting_8021x (config, s_8021x, con_uuid, mtu, TRUE, error)) { g_prefix_error (error, "802-1x-setting: "); - g_clear_object (&config); + return NULL; } } - return config; + return g_steal_pointer (&config); } static void @@ -588,6 +590,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) const char *setting_name; connection = nm_device_get_applied_connection (NM_DEVICE (self)); + g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); if (!priv->supplicant.mgr) @@ -654,7 +657,6 @@ create_and_realize (NMDevice *device, GError **error) { const char *iface = nm_device_get_iface (device); - NMPlatformError plerr; NMSettingMacsec *s_macsec; NMPlatformLnkMacsec lnk = { }; int parent_ifindex; @@ -666,6 +668,7 @@ create_and_realize (NMDevice *device, } s; guint64 u; } sci; + int r; s_macsec = nm_connection_get_setting_macsec (connection); g_assert (s_macsec); @@ -694,13 +697,13 @@ create_and_realize (NMDevice *device, parent_ifindex = nm_device_get_ifindex (parent); g_warn_if_fail (parent_ifindex > 0); - plerr = nm_platform_link_macsec_add (nm_device_get_platform (device), iface, parent_ifindex, &lnk, out_plink); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) { + r = nm_platform_link_macsec_add (nm_device_get_platform (device), iface, parent_ifindex, &lnk, out_plink); + if (r < 0) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to create macsec interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string_a (plerr)); + nm_strerror (r)); return FALSE; } diff --git a/src/devices/nm-device-macvlan.c b/src/devices/nm-device-macvlan.c index ff386c82..bb629713 100644 --- a/src/devices/nm-device-macvlan.c +++ b/src/devices/nm-device-macvlan.c @@ -23,6 +23,7 @@ #include "nm-device-macvlan.h" #include <string.h> +#include <linux/if_link.h> #include "nm-device-private.h" #include "settings/nm-settings.h" @@ -226,10 +227,10 @@ create_and_realize (NMDevice *device, GError **error) { const char *iface = nm_device_get_iface (device); - NMPlatformError plerr; NMSettingMacvlan *s_macvlan; NMPlatformLnkMacvlan lnk = { }; int parent_ifindex; + int r; s_macvlan = nm_connection_get_setting_macvlan (connection); g_return_val_if_fail (s_macvlan, FALSE); @@ -254,14 +255,14 @@ create_and_realize (NMDevice *device, lnk.no_promisc = !nm_setting_macvlan_get_promiscuous (s_macvlan); lnk.tap = nm_setting_macvlan_get_tap (s_macvlan); - plerr = nm_platform_link_macvlan_add (nm_device_get_platform (device), iface, parent_ifindex, &lnk, out_plink); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) { + r = nm_platform_link_macvlan_add (nm_device_get_platform (device), iface, parent_ifindex, &lnk, out_plink); + if (r < 0) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to create %s interface '%s' for '%s': %s", lnk.tap ? "macvtap" : "macvlan", iface, nm_connection_get_id (connection), - nm_platform_error_to_string_a (plerr)); + nm_strerror (r)); return FALSE; } diff --git a/src/devices/nm-device-ppp.c b/src/devices/nm-device-ppp.c index 74b4d710..2f565d4b 100644 --- a/src/devices/nm-device-ppp.c +++ b/src/devices/nm-device-ppp.c @@ -125,10 +125,12 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) NMActRequest *req; GError *error = NULL; - req = nm_device_get_act_request (NM_DEVICE (self)); + req = nm_device_get_act_request (device); + g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE); - s_pppoe = (NMSettingPppoe *) nm_device_get_applied_setting ((NMDevice *) self, NM_TYPE_SETTING_PPPOE); + s_pppoe = nm_device_get_applied_setting (device, NM_TYPE_SETTING_PPPOE); + g_return_val_if_fail (s_pppoe, NM_ACT_STAGE_RETURN_FAILURE); g_clear_object (&priv->ip4_config); @@ -221,7 +223,7 @@ deactivate (NMDevice *device) NMDevicePppPrivate *priv = NM_DEVICE_PPP_GET_PRIVATE (self); if (priv->ppp_manager) { - nm_ppp_manager_stop (priv->ppp_manager, NULL, NULL); + nm_ppp_manager_stop (priv->ppp_manager, NULL, NULL, NULL); g_clear_object (&priv->ppp_manager); } } diff --git a/src/devices/nm-device-private.h b/src/devices/nm-device-private.h index 66c715de..6c0f473d 100644 --- a/src/devices/nm-device-private.h +++ b/src/devices/nm-device-private.h @@ -34,7 +34,7 @@ enum NMActStageReturn { NM_ACT_STAGE_RETURN_IP_DONE, /* IP config stage is done (state IP_DONE), For the ip-config stage, this is similar to NM_ACT_STAGE_RETURN_SUCCESS, except that no - IP config should be commited. */ + IP config should be committed. */ NM_ACT_STAGE_RETURN_IP_FAIL, /* IP config stage failed (state IP_FAIL), activation may proceed */ }; @@ -111,9 +111,12 @@ void nm_device_set_wwan_ip6_config (NMDevice *device, NMIP6Config *config); gboolean nm_device_hw_addr_is_explict (NMDevice *device); -void nm_device_ip_method_failed (NMDevice *self, int family, NMDeviceStateReason reason); +void nm_device_ip_method_failed (NMDevice *self, int addr_family, NMDeviceStateReason reason); -gboolean nm_device_ipv6_sysctl_set (NMDevice *self, const char *property, const char *value); +gboolean nm_device_sysctl_ip_conf_set (NMDevice *self, + int addr_family, + const char *property, + const char *value); /*****************************************************************************/ @@ -147,9 +150,9 @@ void nm_device_commit_mtu (NMDevice *self); ) gboolean _nm_device_hash_check_invalid_keys (GHashTable *hash, const char *setting_name, - GError **error, const char **whitelist); + GError **error, const char *const*whitelist); #define nm_device_hash_check_invalid_keys(hash, setting_name, error, ...) \ - _nm_device_hash_check_invalid_keys (hash, setting_name, error, ((const char *[]) { __VA_ARGS__, NULL })) + _nm_device_hash_check_invalid_keys (hash, setting_name, error, NM_MAKE_STRV (__VA_ARGS__)) gboolean nm_device_match_parent (NMDevice *device, const char *parent); gboolean nm_device_match_parent_hwaddr (NMDevice *device, diff --git a/src/devices/nm-device-tun.c b/src/devices/nm-device-tun.c index 0f76b23a..3fe3dfd4 100644 --- a/src/devices/nm-device-tun.c +++ b/src/devices/nm-device-tun.c @@ -231,9 +231,10 @@ create_and_realize (NMDevice *device, { const char *iface = nm_device_get_iface (device); NMPlatformLnkTun props = { }; - NMPlatformError plerr; NMSettingTun *s_tun; - gint64 owner, group; + gint64 owner; + gint64 group; + int r; s_tun = nm_connection_get_setting_tun (connection); g_return_val_if_fail (s_tun, FALSE); @@ -261,17 +262,17 @@ create_and_realize (NMDevice *device, props.multi_queue = nm_setting_tun_get_multi_queue (s_tun); props.persist = TRUE; - plerr = nm_platform_link_tun_add (nm_device_get_platform (device), - iface, - &props, - out_plink, - NULL); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) { + r = nm_platform_link_tun_add (nm_device_get_platform (device), + iface, + &props, + out_plink, + NULL); + if (r < 0) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to create TUN/TAP interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string_a (plerr)); + nm_strerror (r)); return FALSE; } diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c index b7f0c4e7..ace6a24b 100644 --- a/src/devices/nm-device-vlan.c +++ b/src/devices/nm-device-vlan.c @@ -241,7 +241,7 @@ create_and_realize (NMDevice *device, NMSettingVlan *s_vlan; int parent_ifindex; guint vlan_id; - NMPlatformError plerr; + int r; s_vlan = nm_connection_get_setting_vlan (connection); g_assert (s_vlan); @@ -271,18 +271,18 @@ create_and_realize (NMDevice *device, vlan_id = nm_setting_vlan_get_id (s_vlan); - plerr = nm_platform_link_vlan_add (nm_device_get_platform (device), - iface, - parent_ifindex, - vlan_id, - nm_setting_vlan_get_flags (s_vlan), - out_plink); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) { + r = nm_platform_link_vlan_add (nm_device_get_platform (device), + iface, + parent_ifindex, + vlan_id, + nm_setting_vlan_get_flags (s_vlan), + out_plink); + if (r < 0) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to create VLAN interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string_a (plerr)); + nm_strerror (r)); return FALSE; } @@ -493,7 +493,7 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) parent_mtu_maybe_changed (parent_device, NULL, device); } - s_vlan = (NMSettingVlan *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_VLAN); + s_vlan = nm_device_get_applied_setting (device, NM_TYPE_SETTING_VLAN); if (s_vlan) { gs_free NMVlanQosMapping *ingress_map = NULL; gs_free NMVlanQosMapping *egress_map = NULL; diff --git a/src/devices/nm-device-vxlan.c b/src/devices/nm-device-vxlan.c index c34f4142..50730320 100644 --- a/src/devices/nm-device-vxlan.c +++ b/src/devices/nm-device-vxlan.c @@ -34,6 +34,7 @@ #include "settings/nm-settings.h" #include "nm-act-request.h" #include "nm-ip4-config.h" +#include "nm-core-internal.h" #include "nm-device-logging.h" _LOG_DECLARE_SELF(NMDeviceVxlan); @@ -170,11 +171,11 @@ create_and_realize (NMDevice *device, GError **error) { const char *iface = nm_device_get_iface (device); - NMPlatformError plerr; NMPlatformLnkVxlan props = { }; NMSettingVxlan *s_vxlan; const char *str; int ret; + int r; s_vxlan = nm_connection_get_setting_vxlan (connection); g_assert (s_vxlan); @@ -213,13 +214,13 @@ create_and_realize (NMDevice *device, props.l2miss = nm_setting_vxlan_get_l2_miss (s_vxlan); props.l3miss = nm_setting_vxlan_get_l3_miss (s_vxlan); - plerr = nm_platform_link_vxlan_add (nm_device_get_platform (device), iface, &props, out_plink); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) { + r = nm_platform_link_vxlan_add (nm_device_get_platform (device), iface, &props, out_plink); + if (r < 0) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to create VXLAN interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string_a (plerr)); + nm_strerror (r)); return FALSE; } @@ -386,6 +387,7 @@ update_connection (NMDevice *device, NMConnection *connection) { NMDeviceVxlanPrivate *priv = NM_DEVICE_VXLAN_GET_PRIVATE ((NMDeviceVxlan *) device); NMSettingVxlan *s_vxlan = nm_connection_get_setting_vxlan (connection); + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; if (!s_vxlan) { s_vxlan = (NMSettingVxlan *) nm_setting_vxlan_new (); @@ -404,11 +406,11 @@ update_connection (NMDevice *device, NMConnection *connection) if (!address_matches (nm_setting_vxlan_get_remote (s_vxlan), priv->props.group, &priv->props.group6)) { if (priv->props.group) { g_object_set (s_vxlan, NM_SETTING_VXLAN_REMOTE, - nm_utils_inet4_ntop (priv->props.group, NULL), + nm_utils_inet4_ntop (priv->props.group, sbuf), NULL); } else { g_object_set (s_vxlan, NM_SETTING_VXLAN_REMOTE, - nm_utils_inet6_ntop (&priv->props.group6, NULL), + nm_utils_inet6_ntop (&priv->props.group6, sbuf), NULL); } } @@ -416,11 +418,11 @@ update_connection (NMDevice *device, NMConnection *connection) if (!address_matches (nm_setting_vxlan_get_local (s_vxlan), priv->props.local, &priv->props.local6)) { if (priv->props.local) { g_object_set (s_vxlan, NM_SETTING_VXLAN_LOCAL, - nm_utils_inet4_ntop (priv->props.local, NULL), + nm_utils_inet4_ntop (priv->props.local, sbuf), NULL); } else if (memcmp (&priv->props.local6, &in6addr_any, sizeof (in6addr_any))) { g_object_set (s_vxlan, NM_SETTING_VXLAN_LOCAL, - nm_utils_inet6_ntop (&priv->props.local6, NULL), + nm_utils_inet6_ntop (&priv->props.local6, sbuf), NULL); } } @@ -510,15 +512,15 @@ get_property (GObject *object, guint prop_id, break; case PROP_GROUP: if (priv->props.group) - g_value_set_string (value, nm_utils_inet4_ntop (priv->props.group, NULL)); + g_value_take_string (value, nm_utils_inet4_ntop_dup (priv->props.group)); else if (!IN6_IS_ADDR_UNSPECIFIED (&priv->props.group6)) - g_value_set_string (value, nm_utils_inet6_ntop (&priv->props.group6, NULL)); + g_value_take_string (value, nm_utils_inet6_ntop_dup (&priv->props.group6)); break; case PROP_LOCAL: if (priv->props.local) - g_value_set_string (value, nm_utils_inet4_ntop (priv->props.local, NULL)); + g_value_take_string (value, nm_utils_inet4_ntop_dup (priv->props.local)); else if (!IN6_IS_ADDR_UNSPECIFIED (&priv->props.local6)) - g_value_set_string (value, nm_utils_inet6_ntop (&priv->props.local6, NULL)); + g_value_take_string (value, nm_utils_inet6_ntop_dup (&priv->props.local6)); break; case PROP_TOS: g_value_set_uchar (value, priv->props.tos); diff --git a/src/devices/nm-device-wpan.c b/src/devices/nm-device-wpan.c index f910b0b8..c2fa7f4e 100644 --- a/src/devices/nm-device-wpan.c +++ b/src/devices/nm-device-wpan.c @@ -25,6 +25,7 @@ #include <stdlib.h> #include <string.h> #include <sys/types.h> +#include <linux/if.h> #include "nm-act-request.h" #include "nm-device-private.h" @@ -118,11 +119,11 @@ static NMActStageReturn act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) { NMDeviceWpan *self = NM_DEVICE_WPAN (device); - NMConnection *connection; NMSettingWpan *s_wpan; NMPlatform *platform; guint16 pan_id; guint16 short_address; + gint16 page, channel; int ifindex; const guint8 *hwaddr; gsize hwaddr_len = 0; @@ -138,12 +139,11 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) g_return_val_if_fail (platform, NM_ACT_STAGE_RETURN_FAILURE); ifindex = nm_device_get_ifindex (device); + g_return_val_if_fail (ifindex > 0, NM_ACT_STAGE_RETURN_FAILURE); - connection = nm_device_get_applied_connection (device); - g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); + s_wpan = nm_device_get_applied_setting (device, NM_TYPE_SETTING_WPAN); - s_wpan = NM_SETTING_WPAN (nm_connection_get_setting (connection, NM_TYPE_SETTING_WPAN)); g_return_val_if_fail (s_wpan, NM_ACT_STAGE_RETURN_FAILURE); hwaddr = nm_platform_link_get_address (platform, ifindex, &hwaddr_len); @@ -182,9 +182,18 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) } } + channel = nm_setting_wpan_get_channel (s_wpan); + if (channel != NM_SETTING_WPAN_CHANNEL_DEFAULT) { + page = nm_setting_wpan_get_page (s_wpan); + if (!nm_platform_wpan_set_channel (platform, ifindex, page, channel)) { + _LOGW (LOGD_DEVICE, "unable to set the channel"); + goto out; + } + } + ret = NM_ACT_STAGE_RETURN_SUCCESS; out: - nm_device_bring_up (device, TRUE, NULL); + nm_device_bring_up (device, TRUE, NULL); if (lowpan_device) nm_device_bring_up (lowpan_device, TRUE, NULL); diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index c5a152d8..9ded911c 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -37,7 +37,6 @@ #include <linux/if_arp.h> #include <linux/rtnetlink.h> #include <linux/pkt_sched.h> -#include <uuid/uuid.h> #include "nm-utils/nm-dedup-multi.h" #include "nm-utils/nm-random-utils.h" @@ -68,6 +67,7 @@ #include "settings/nm-settings.h" #include "nm-setting-ethtool.h" #include "nm-auth-utils.h" +#include "nm-keep-alive.h" #include "nm-netns.h" #include "nm-dispatcher.h" #include "nm-config.h" @@ -175,6 +175,7 @@ struct _NMDeviceConnectivityHandle { bool is_periodic:1; bool is_periodic_bump:1; bool is_periodic_bump_on_complete:1; + int addr_family; }; typedef struct { @@ -196,7 +197,6 @@ enum { REMOVED, RECHECK_AUTO_ACTIVATE, RECHECK_ASSUME, - CONNECTIVITY_CHANGED, LAST_SIGNAL, }; static guint signals[LAST_SIGNAL] = { 0 }; @@ -242,7 +242,8 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDevice, PROP_REFRESH_RATE_MS, PROP_TX_BYTES, PROP_RX_BYTES, - PROP_CONNECTIVITY, + PROP_IP4_CONNECTIVITY, + PROP_IP6_CONNECTIVITY, ); typedef struct _NMDevicePrivate { @@ -386,13 +387,15 @@ typedef struct _NMDevicePrivate { bool v4_route_table_initialized:1; bool v6_route_table_initialized:1; - NMDeviceAutoconnectBlockedFlags autoconnect_blocked_flags:4; + NMDeviceAutoconnectBlockedFlags autoconnect_blocked_flags:5; bool is_enslaved:1; bool master_ready_handled:1; bool ipv6ll_handle:1; /* TRUE if NM handles the device's IPv6LL address */ bool ipv6ll_has:1; + bool ndisc_started:1; + bool device_link_changed_down:1; /* Generic DHCP stuff */ char * dhcp_anycast_address; @@ -455,9 +458,6 @@ typedef struct _NMDevicePrivate { AppliedConfig wwan_ip_config_x[2]; }; - bool v4_has_shadowed_routes; - const char *ip4_rp_filter; - /* DHCPv4 tracking */ struct { NMDhcpClient * client; @@ -555,24 +555,24 @@ typedef struct _NMDevicePrivate { NMLldpListener *lldp_listener; NMConnectivity *concheck_mgr; + CList concheck_lst_head; + struct { + /* if periodic checks are enabled, this is the source id for the next check. */ + guint p_cur_id; - /* if periodic checks are enabled, this is the source id for the next check. */ - guint concheck_p_cur_id; - - /* the currently configured max periodic interval. */ - guint concheck_p_max_interval; - - /* the current interval. If we are probing, the interval might be lower - * then the configured max interval. */ - guint concheck_p_cur_interval; + /* the currently configured max periodic interval. */ + guint p_max_interval; - /* the timestamp, when we last scheduled the timer concheck_p_cur_id with current interval - * concheck_p_cur_interval. */ - gint64 concheck_p_cur_basetime_ns; + /* the current interval. If we are probing, the interval might be lower + * then the configured max interval. */ + guint p_cur_interval; - NMConnectivityState connectivity_state; + /* the timestamp, when we last scheduled the timer p_cur_id with current interval + * p_cur_interval. */ + gint64 p_cur_basetime_ns; - CList concheck_lst_head; + NMConnectivityState state; + } concheck_x[2]; guint check_delete_unrealized_id; @@ -643,7 +643,10 @@ static void _set_mtu (NMDevice *self, guint32 mtu); static void _commit_mtu (NMDevice *self, const NMIP4Config *config); static void _cancel_activation (NMDevice *self); -static void concheck_update_state (NMDevice *self, NMConnectivityState state, gboolean is_periodic); +static void concheck_update_state (NMDevice *self, + int addr_family, + NMConnectivityState state, + gboolean is_periodic); /*****************************************************************************/ @@ -741,8 +744,7 @@ NM_UTILS_LOOKUP_STR_DEFINE (nm_device_state_reason_to_str, NMDeviceStateReason, NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED, "sriov-configuration-failed"), ); -#define reason_to_string(reason) \ - NM_UTILS_LOOKUP_STR (nm_device_state_reason_to_str, reason) +#define reason_to_string_a(reason) NM_UTILS_LOOKUP_STR_A (nm_device_state_reason_to_str, reason) NM_UTILS_LOOKUP_STR_DEFINE_STATIC (mtu_source_to_str, NMDeviceMtuSource, NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT ("unknown"), @@ -1116,8 +1118,8 @@ init_ip_config_dns_priority (NMDevice *self, NMIPConfig *config) int priority; property = (nm_ip_config_get_addr_family (config) == AF_INET) - ? "ipv4.dns-priority" - : "ipv6.dns-priority"; + ? NM_CON_DEFAULT ("ipv4.dns-priority") + : NM_CON_DEFAULT ("ipv6.dns-priority"); priority = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA, property, @@ -1130,96 +1132,85 @@ init_ip_config_dns_priority (NMDevice *self, NMIPConfig *config) /*****************************************************************************/ -static gboolean -nm_device_ipv4_sysctl_set (NMDevice *self, const char *property, const char *value) +static char * +nm_device_sysctl_ip_conf_get (NMDevice *self, + int addr_family, + const char *property) { - NMPlatform *platform = nm_device_get_platform (self); - gs_free char *value_to_free = NULL; - const char *value_to_set; - char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; - - if (!nm_device_get_ip_ifindex (self)) - return FALSE; + const char *ifname; - if (value) { - value_to_set = value; - } else { - /* Set to a default value when we've got a NULL @value. */ - value_to_free = nm_platform_sysctl_get (platform, - NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET, buf, "default", property))); - value_to_set = value_to_free; - } + nm_assert_addr_family (addr_family); - return nm_platform_sysctl_set (platform, - NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET, buf, nm_device_get_ip_iface (self), property)), - value_to_set); + ifname = nm_device_get_ip_iface_from_platform (self); + if (!ifname) + return NULL; + return nm_platform_sysctl_ip_conf_get (nm_device_get_platform (self), addr_family, ifname, property); } -static guint32 -nm_device_ipv4_sysctl_get_effective_uint32 (NMDevice *self, const char *property, guint32 fallback) +static gint64 +nm_device_sysctl_ip_conf_get_int_checked (NMDevice *self, + int addr_family, + const char *property, + guint base, + gint64 min, + gint64 max, + gint64 fallback) { - char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; - gint64 v, v_all; + const char *ifname; - if (!nm_device_get_ip_ifindex (self)) - return fallback; + nm_assert_addr_family (addr_family); - /* for this kind of sysctl (e.g. "rp_filter"), kernel effectively uses the - * MAX of the per-device value and the "all" value. - * - * Also do that, by reading both sysctls and return the maximum. */ - - v = nm_platform_sysctl_get_int_checked (nm_device_get_platform (self), - NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET, - buf, - nm_device_get_ip_iface (self), - property)), - 10, - 0, - G_MAXUINT32, - -1); - - v_all = nm_platform_sysctl_get_int_checked (nm_device_get_platform (self), - NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET, - buf, - "all", - property)), - 10, - 0, - G_MAXUINT32, - -1); - - v = NM_MAX (v, v_all); - return v > -1 ? (guint32) v : fallback; + ifname = nm_device_get_ip_iface_from_platform (self); + if (!ifname) { + errno = EINVAL; + return fallback; + } + return nm_platform_sysctl_ip_conf_get_int_checked (nm_device_get_platform (self), + addr_family, + ifname, + property, + base, + min, + max, + fallback); } gboolean -nm_device_ipv6_sysctl_set (NMDevice *self, const char *property, const char *value) +nm_device_sysctl_ip_conf_set (NMDevice *self, + int addr_family, + const char *property, + const char *value) { - char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; - - if (!nm_device_get_ip_ifindex (self)) - return FALSE; + NMPlatform *platform = nm_device_get_platform (self); + gs_free char *value_to_free = NULL; + const char *ifname; - return nm_platform_sysctl_set (nm_device_get_platform (self), NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET6, buf, nm_device_get_ip_iface (self), property)), value); -} + nm_assert_addr_family (addr_family); -static guint32 -nm_device_ipv6_sysctl_get_uint32 (NMDevice *self, const char *property, guint32 fallback) -{ - char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; + ifname = nm_device_get_ip_iface_from_platform (self); + if (!ifname) + return FALSE; - if (!nm_device_get_ip_ifindex (self)) - return fallback; + if (!value) { + /* Set to a default value when we've got a NULL @value. */ + value_to_free = nm_platform_sysctl_ip_conf_get (platform, + addr_family, + "default", + property); + value = value_to_free; + if (!value) + return FALSE; + } - return nm_platform_sysctl_get_int_checked (nm_device_get_platform (self), - NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET6, buf, nm_device_get_ip_iface (self), property)), - 10, - 0, - G_MAXUINT32, - fallback); + return nm_platform_sysctl_ip_conf_set (platform, + addr_family, + ifname, + property, + value); } +/*****************************************************************************/ + gboolean nm_device_has_capability (NMDevice *self, NMDeviceCapabilities caps) { @@ -1264,6 +1255,8 @@ _get_stable_id (NMDevice *self, gs_free char *generated = NULL; NMUtilsStableType stable_type; NMSettingConnection *s_con; + gboolean hwaddr_is_fake; + const char *hwaddr; const char *stable_id; const char *uuid; @@ -1273,16 +1266,22 @@ _get_stable_id (NMDevice *self, if (!stable_id) { default_id = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, - "connection.stable-id", + NM_CON_DEFAULT ("connection.stable-id"), self); stable_id = default_id; } uuid = nm_connection_get_uuid (connection); + /* the cloned-mac-address may be generated based on the stable-id. + * Thus, at this point, we can only use the permanant MAC address + * as seed. */ + hwaddr = nm_device_get_permanent_hw_address_full (self, TRUE, &hwaddr_is_fake); + stable_type = nm_utils_stable_id_parse (stable_id, nm_device_get_ip_iface (self), - NULL, + !hwaddr_is_fake ? hwaddr : NULL, + nm_utils_boot_id_str (), uuid, &generated); @@ -1465,6 +1464,18 @@ nm_device_get_ip_iface (NMDevice *self) return priv->ip_iface ?: priv->iface; } +const char * +nm_device_get_ip_iface_from_platform (NMDevice *self) +{ + int ifindex; + + ifindex = nm_device_get_ip_ifindex (self); + if (ifindex <= 0) + return NULL; + + return nm_platform_link_get_name (nm_device_get_platform (self), ifindex); +} + int nm_device_get_ip_ifindex (const NMDevice *self) { @@ -1949,9 +1960,10 @@ nm_device_get_ip_iface_identifier (NMDevice *self, NMUtilsIPv6IfaceId *iid, gboo g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); if (!ignore_token) { - s_ip6 = (NMSettingIP6Config *) - nm_device_get_applied_setting (self, NM_TYPE_SETTING_IP6_CONFIG); + s_ip6 = nm_device_get_applied_setting (self, NM_TYPE_SETTING_IP6_CONFIG); + g_return_val_if_fail (s_ip6, FALSE); + token = nm_setting_ip6_config_get_token (s_ip6); } if (token) @@ -2092,13 +2104,14 @@ nm_device_get_route_metric_default (NMDeviceType device_type) } static gboolean -default_route_metric_penalty_detect (NMDevice *self) +default_route_metric_penalty_detect (NMDevice *self, int addr_family) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + const gboolean IS_IPv4 = (addr_family == AF_INET); /* currently we don't differentiate between IPv4 and IPv6 when detecting * connectivity. */ - if ( priv->connectivity_state != NM_CONNECTIVITY_FULL + if ( priv->concheck_x[IS_IPv4].state != NM_CONNECTIVITY_FULL && nm_connectivity_check_enabled (concheck_get_mgr (self))) return TRUE; @@ -2151,7 +2164,9 @@ nm_device_get_route_metric (NMDevice *self, /* use the current NMConfigData, which makes this configuration reloadable. * Note that that means that the route-metric might change between SIGHUP. * You must cache the returned value if that is a problem. */ - property = addr_family == AF_INET ? "ipv4.route-metric" : "ipv6.route-metric"; + property = addr_family == AF_INET + ? NM_CON_DEFAULT ("ipv4.route-metric") + : NM_CON_DEFAULT ("ipv6.route-metric"); route_metric = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA, property, self, @@ -2181,7 +2196,7 @@ _get_mdns (NMDevice *self) return mdns; return nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA, - "connection.mdns", + NM_CON_DEFAULT ("connection.mdns"), self, NM_SETTING_CONNECTION_MDNS_NO, NM_SETTING_CONNECTION_MDNS_YES, @@ -2203,7 +2218,7 @@ _get_llmnr (NMDevice *self) return llmnr; return nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA, - "connection.llmnr", + NM_CON_DEFAULT ("connection.llmnr"), self, NM_SETTING_CONNECTION_LLMNR_NO, NM_SETTING_CONNECTION_LLMNR_YES, @@ -2252,7 +2267,9 @@ nm_device_get_route_table (NMDevice *self, if (route_table == 0) { const char *property; - property = addr_family == AF_INET ? "ipv4.route-table" : "ipv6.route-table"; + property = addr_family == AF_INET + ? NM_CON_DEFAULT ("ipv4.route-table") + : NM_CON_DEFAULT ("ipv6.route-table"); route_table = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA, property, self, @@ -2365,10 +2382,27 @@ nm_device_get_act_request (NMDevice *self) return NM_DEVICE_GET_PRIVATE (self)->act_request.obj; } +NMActivationStateFlags +nm_device_get_activation_state_flags (NMDevice *self) +{ + NMActRequest *ac; + + g_return_val_if_fail (NM_IS_DEVICE (self), NM_ACTIVATION_STATE_FLAG_NONE); + + ac = NM_DEVICE_GET_PRIVATE (self)->act_request.obj; + if (!ac) + return NM_ACTIVATION_STATE_FLAG_NONE; + return nm_active_connection_get_state_flags (NM_ACTIVE_CONNECTION (ac)); +} + NMSettingsConnection * nm_device_get_settings_connection (NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMDevicePrivate *priv; + + g_return_val_if_fail (NM_IS_DEVICE (self), NULL); + + priv = NM_DEVICE_GET_PRIVATE (self); return priv->act_request.obj ? nm_act_request_get_settings_connection (priv->act_request.obj) : NULL; } @@ -2412,7 +2446,7 @@ nm_device_has_unmodified_applied_connection (NMDevice *self, NMSettingCompareFla return nm_active_connection_has_unmodified_applied_connection ((NMActiveConnection *) priv->act_request.obj, compare_flags); } -NMSetting * +gpointer nm_device_get_applied_setting (NMDevice *self, GType setting_type) { NMConnection *connection; @@ -2448,24 +2482,37 @@ typedef enum { } ConcheckScheduleMode; static NMDeviceConnectivityHandle *concheck_start (NMDevice *self, + int addr_family, NMDeviceConnectivityCallback callback, gpointer user_data, gboolean is_periodic); static void concheck_periodic_schedule_set (NMDevice *self, + int addr_family, ConcheckScheduleMode mode); static gboolean -concheck_periodic_timeout_cb (gpointer user_data) +_concheck_periodic_timeout_cb (NMDevice *self, int addr_family) { - NMDevice *self = user_data; - - _LOGt (LOGD_CONCHECK, "connectivity: periodic timeout"); - concheck_periodic_schedule_set (self, CONCHECK_SCHEDULE_CHECK_PERIODIC); + _LOGt (LOGD_CONCHECK, "connectivity: [IPv%c] periodic timeout", + nm_utils_addr_family_to_char (addr_family)); + concheck_periodic_schedule_set (self, addr_family, CONCHECK_SCHEDULE_CHECK_PERIODIC); return G_SOURCE_REMOVE; } static gboolean +concheck_ip4_periodic_timeout_cb (gpointer user_data) +{ + return _concheck_periodic_timeout_cb (user_data, AF_INET); +} + +static gboolean +concheck_ip6_periodic_timeout_cb (gpointer user_data) +{ + return _concheck_periodic_timeout_cb (user_data, AF_INET6); +} + +static gboolean concheck_is_possible (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); @@ -2483,17 +2530,18 @@ concheck_is_possible (NMDevice *self) } static gboolean -concheck_periodic_schedule_do (NMDevice *self, gint64 now_ns) +concheck_periodic_schedule_do (NMDevice *self, int addr_family, gint64 now_ns) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); gboolean periodic_check_disabled = FALSE; gint64 expiry, tdiff; + const gboolean IS_IPv4 = (addr_family == AF_INET); /* we always cancel whatever was pending. */ - if (nm_clear_g_source (&priv->concheck_p_cur_id)) + if (nm_clear_g_source (&priv->concheck_x[IS_IPv4].p_cur_id)) periodic_check_disabled = TRUE; - if (priv->concheck_p_max_interval == 0) { + if (priv->concheck_x[IS_IPv4].p_max_interval == 0) { /* periodic checks are disabled */ goto out; } @@ -2502,46 +2550,50 @@ concheck_periodic_schedule_do (NMDevice *self, gint64 now_ns) goto out; nm_assert (now_ns > 0); - nm_assert (priv->concheck_p_cur_interval > 0); + nm_assert (priv->concheck_x[IS_IPv4].p_cur_interval > 0); /* we schedule the timeout based on our current settings cur-interval and cur-basetime. * Before calling concheck_periodic_schedule_do(), make sure that these properties are * correct. */ - expiry = priv->concheck_p_cur_basetime_ns + (priv->concheck_p_cur_interval * NM_UTILS_NS_PER_SECOND); + expiry = priv->concheck_x[IS_IPv4].p_cur_basetime_ns + (priv->concheck_x[IS_IPv4].p_cur_interval * NM_UTILS_NS_PER_SECOND); tdiff = expiry - now_ns; - _LOGT (LOGD_CONCHECK, "connectivity: periodic-check: %sscheduled in %lld milliseconds (%u seconds interval)", + _LOGT (LOGD_CONCHECK, "connectivity: [IPv%c] periodic-check: %sscheduled in %lld milliseconds (%u seconds interval)", + nm_utils_addr_family_to_char (addr_family), periodic_check_disabled ? "re-" : "", (long long) (tdiff / NM_UTILS_NS_PER_MSEC), - priv->concheck_p_cur_interval); + priv->concheck_x[IS_IPv4].p_cur_interval); - priv->concheck_p_cur_id = g_timeout_add (NM_MAX ((gint64) 0, tdiff) / NM_UTILS_NS_PER_MSEC, - concheck_periodic_timeout_cb, - self); + priv->concheck_x[IS_IPv4].p_cur_id = + g_timeout_add (NM_MAX ((gint64) 0, tdiff) / NM_UTILS_NS_PER_MSEC, + IS_IPv4 ? concheck_ip4_periodic_timeout_cb : concheck_ip6_periodic_timeout_cb, + self); return TRUE; out: - if (periodic_check_disabled) - _LOGT (LOGD_CONCHECK, "connectivity: periodic-check: unscheduled"); + if (periodic_check_disabled) { + _LOGT (LOGD_CONCHECK, "connectivity: [IPv%c] periodic-check: unscheduled", + nm_utils_addr_family_to_char (addr_family)); + } return FALSE; } #define CONCHECK_P_PROBE_INTERVAL 1 static void -concheck_periodic_schedule_set (NMDevice *self, - ConcheckScheduleMode mode) +concheck_periodic_schedule_set (NMDevice *self, int addr_family, ConcheckScheduleMode mode) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); gint64 new_expiry, exp_expiry, cur_expiry, tdiff; gint64 now_ns = 0; + const gboolean IS_IPv4 = (addr_family == AF_INET); - if (priv->concheck_p_max_interval == 0) { + if (priv->concheck_x[IS_IPv4].p_max_interval == 0) { /* periodic check is disabled. Nothing to do. */ return; } - if (!priv->concheck_p_cur_id) { + if (!priv->concheck_x[IS_IPv4].p_cur_id) { /* we currently don't have a timeout scheduled. No need to reschedule * another one... */ if (NM_IN_SET (mode, CONCHECK_SCHEDULE_UPDATE_INTERVAL, @@ -2555,19 +2607,19 @@ concheck_periodic_schedule_set (NMDevice *self, switch (mode) { case CONCHECK_SCHEDULE_UPDATE_INTERVAL_RESTART: - priv->concheck_p_cur_interval = NM_MIN (priv->concheck_p_max_interval, CONCHECK_P_PROBE_INTERVAL); - priv->concheck_p_cur_basetime_ns = nm_utils_get_monotonic_timestamp_ns_cached (&now_ns); - if (concheck_periodic_schedule_do (self, now_ns)) - concheck_start (self, NULL, NULL, TRUE); + priv->concheck_x[IS_IPv4].p_cur_interval = NM_MIN (priv->concheck_x[IS_IPv4].p_max_interval, CONCHECK_P_PROBE_INTERVAL); + priv->concheck_x[IS_IPv4].p_cur_basetime_ns = nm_utils_get_monotonic_timestamp_ns_cached (&now_ns); + if (concheck_periodic_schedule_do (self, addr_family, now_ns)) + concheck_start (self, addr_family, NULL, NULL, TRUE); return; case CONCHECK_SCHEDULE_UPDATE_INTERVAL: - /* called with "UPDATE_INTERVAL" and already have a concheck_p_cur_id scheduled. */ + /* called with "UPDATE_INTERVAL" and already have a p_cur_id scheduled. */ - nm_assert (priv->concheck_p_max_interval > 0); - nm_assert (priv->concheck_p_cur_interval > 0); + nm_assert (priv->concheck_x[IS_IPv4].p_max_interval > 0); + nm_assert (priv->concheck_x[IS_IPv4].p_cur_interval > 0); - if (priv->concheck_p_cur_interval <= priv->concheck_p_max_interval) { + if (priv->concheck_x[IS_IPv4].p_cur_interval <= priv->concheck_x[IS_IPv4].p_max_interval) { /* we currently have a shorter interval set, than what we now have. Either, * because we are probing, or because the previous max interval was shorter. * @@ -2576,17 +2628,17 @@ concheck_periodic_schedule_set (NMDevice *self, return; } - cur_expiry = priv->concheck_p_cur_basetime_ns + (priv->concheck_p_max_interval * NM_UTILS_NS_PER_SECOND); + cur_expiry = priv->concheck_x[IS_IPv4].p_cur_basetime_ns + (priv->concheck_x[IS_IPv4].p_max_interval * NM_UTILS_NS_PER_SECOND); nm_utils_get_monotonic_timestamp_ns_cached (&now_ns); - priv->concheck_p_cur_interval = priv->concheck_p_max_interval; + priv->concheck_x[IS_IPv4].p_cur_interval = priv->concheck_x[IS_IPv4].p_max_interval; if (cur_expiry <= now_ns) { /* Since the last time we scheduled a periodic check, already more than the * new max_interval passed. We need to start a check right away (and * schedule a timeout in cur-interval in the future). */ - priv->concheck_p_cur_basetime_ns = now_ns; - if (concheck_periodic_schedule_do (self, now_ns)) - concheck_start (self, NULL, NULL, TRUE); + priv->concheck_x[IS_IPv4].p_cur_basetime_ns = now_ns; + if (concheck_periodic_schedule_do (self, addr_family, now_ns)) + concheck_start (self, addr_family, NULL, NULL, TRUE); } else { /* we are reducing the max-interval to a shorter interval that we have currently * scheduled (with cur_interval). @@ -2594,24 +2646,26 @@ concheck_periodic_schedule_set (NMDevice *self, * However, since the last time we scheduled the check, not even the new max-interval * expired. All we need to do, is reschedule the timer to expire sooner. The cur_basetime * is unchanged. */ - concheck_periodic_schedule_do (self, now_ns); + concheck_periodic_schedule_do (self, addr_family, now_ns); } return; case CONCHECK_SCHEDULE_CHECK_EXTERNAL: /* a external connectivity check delays our periodic check. We reset the counter. */ - priv->concheck_p_cur_basetime_ns = nm_utils_get_monotonic_timestamp_ns_cached (&now_ns); - concheck_periodic_schedule_do (self, now_ns); + priv->concheck_x[IS_IPv4].p_cur_basetime_ns = nm_utils_get_monotonic_timestamp_ns_cached (&now_ns); + concheck_periodic_schedule_do (self, addr_family, now_ns); return; case CONCHECK_SCHEDULE_CHECK_PERIODIC: { gboolean any_periodic_pending; NMDeviceConnectivityHandle *handle; - guint old_interval = priv->concheck_p_cur_interval; + guint old_interval = priv->concheck_x[IS_IPv4].p_cur_interval; any_periodic_pending = FALSE; c_list_for_each_entry (handle, &priv->concheck_lst_head, concheck_lst) { + if (handle->addr_family != addr_family) + continue; if (handle->is_periodic_bump) { handle->is_periodic_bump = FALSE; handle->is_periodic_bump_on_complete = FALSE; @@ -2622,7 +2676,7 @@ concheck_periodic_schedule_set (NMDevice *self, /* we reached a timeout to schedule a new periodic request, however we still * have period requests pending that didn't complete yet. We need to bump the * interval already. */ - priv->concheck_p_cur_interval = NM_MIN (old_interval * 2, priv->concheck_p_max_interval); + priv->concheck_x[IS_IPv4].p_cur_interval = NM_MIN (old_interval * 2, priv->concheck_x[IS_IPv4].p_max_interval); } /* we just reached a timeout. The expected expiry (exp_expiry) should be @@ -2630,13 +2684,13 @@ concheck_periodic_schedule_set (NMDevice *self, * * We want to reschedule the timeout at exp_expiry (aka now) + cur_interval. */ nm_utils_get_monotonic_timestamp_ns_cached (&now_ns); - exp_expiry = priv->concheck_p_cur_basetime_ns + (old_interval * NM_UTILS_NS_PER_SECOND); - new_expiry = exp_expiry + (priv->concheck_p_cur_interval * NM_UTILS_NS_PER_SECOND); + exp_expiry = priv->concheck_x[IS_IPv4].p_cur_basetime_ns + (old_interval * NM_UTILS_NS_PER_SECOND); + new_expiry = exp_expiry + (priv->concheck_x[IS_IPv4].p_cur_interval * NM_UTILS_NS_PER_SECOND); tdiff = NM_MAX (new_expiry - now_ns, 0); - priv->concheck_p_cur_basetime_ns = (now_ns + tdiff) - (priv->concheck_p_cur_interval * NM_UTILS_NS_PER_SECOND); - if (concheck_periodic_schedule_do (self, now_ns)) { - handle = concheck_start (self, NULL, NULL, TRUE); - if (old_interval != priv->concheck_p_cur_interval) { + priv->concheck_x[IS_IPv4].p_cur_basetime_ns = (now_ns + tdiff) - (priv->concheck_x[IS_IPv4].p_cur_interval * NM_UTILS_NS_PER_SECOND); + if (concheck_periodic_schedule_do (self, addr_family, now_ns)) { + handle = concheck_start (self, addr_family, NULL, NULL, TRUE); + if (old_interval != priv->concheck_x[IS_IPv4].p_cur_interval) { /* we just bumped the interval already when scheduling this check. * When the handle returns, don't bump a second time. * @@ -2651,54 +2705,57 @@ concheck_periodic_schedule_set (NMDevice *self, /* we just got an event that we lost connectivity (that is, concheck returned). We reset * the interval to min/max or increase the probe interval (bump). */ case CONCHECK_SCHEDULE_RETURNED_MIN: - priv->concheck_p_cur_interval = NM_MIN (priv->concheck_p_max_interval, CONCHECK_P_PROBE_INTERVAL); + priv->concheck_x[IS_IPv4].p_cur_interval = NM_MIN (priv->concheck_x[IS_IPv4].p_max_interval, CONCHECK_P_PROBE_INTERVAL); break; case CONCHECK_SCHEDULE_RETURNED_MAX: - priv->concheck_p_cur_interval = priv->concheck_p_max_interval; + priv->concheck_x[IS_IPv4].p_cur_interval = priv->concheck_x[IS_IPv4].p_max_interval; break; case CONCHECK_SCHEDULE_RETURNED_BUMP: - priv->concheck_p_cur_interval = NM_MIN (priv->concheck_p_cur_interval * 2, priv->concheck_p_max_interval); + priv->concheck_x[IS_IPv4].p_cur_interval = NM_MIN (priv->concheck_x[IS_IPv4].p_cur_interval * 2, priv->concheck_x[IS_IPv4].p_max_interval); break; } /* we are here, because we returned from a connectivity check and adjust the current interval. * * But note that we calculate the new timeout based on the time when we scheduled the - * last check, instead of counting from now. The reaons is, that we want that the times + * last check, instead of counting from now. The reason is that we want that the times * when we schedule checks be at precise intervals, without including the time it took for * the connectivity check. */ - new_expiry = priv->concheck_p_cur_basetime_ns + (priv->concheck_p_cur_interval * NM_UTILS_NS_PER_SECOND); + new_expiry = priv->concheck_x[IS_IPv4].p_cur_basetime_ns + (priv->concheck_x[IS_IPv4].p_cur_interval * NM_UTILS_NS_PER_SECOND); tdiff = NM_MAX (new_expiry - nm_utils_get_monotonic_timestamp_ns_cached (&now_ns), 0); - priv->concheck_p_cur_basetime_ns = now_ns + tdiff - (priv->concheck_p_cur_interval * NM_UTILS_NS_PER_SECOND); - concheck_periodic_schedule_do (self, now_ns); + priv->concheck_x[IS_IPv4].p_cur_basetime_ns = now_ns + tdiff - (priv->concheck_x[IS_IPv4].p_cur_interval * NM_UTILS_NS_PER_SECOND); + concheck_periodic_schedule_do (self, addr_family, now_ns); } static void -concheck_update_interval (NMDevice *self, gboolean check_now) +concheck_update_interval (NMDevice *self, int addr_family, gboolean check_now) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); guint new_interval; + const gboolean IS_IPv4 = (addr_family == AF_INET); new_interval = nm_connectivity_get_interval (concheck_get_mgr (self)); new_interval = NM_MIN (new_interval, 7 *24 * 3600); - if (new_interval != priv->concheck_p_max_interval) { - _LOGT (LOGD_CONCHECK, "connectivity: periodic-check: set interval to %u seconds", new_interval); - priv->concheck_p_max_interval = new_interval; + if (new_interval != priv->concheck_x[IS_IPv4].p_max_interval) { + _LOGT (LOGD_CONCHECK, "connectivity: [IPv%c] periodic-check: set interval to %u seconds", + nm_utils_addr_family_to_char (addr_family), new_interval); + priv->concheck_x[IS_IPv4].p_max_interval = new_interval; } if (!new_interval) { /* this will cancel any potentially pending timeout because max-interval is zero. * But it logs a nice message... */ - concheck_periodic_schedule_do (self, 0); + concheck_periodic_schedule_do (self, addr_family, 0); /* also update the fake connectivity state. */ - concheck_update_state (self, NM_CONNECTIVITY_FAKE, TRUE); + concheck_update_state (self, addr_family, NM_CONNECTIVITY_FAKE, TRUE); return; } concheck_periodic_schedule_set (self, + addr_family, check_now ? CONCHECK_SCHEDULE_UPDATE_INTERVAL_RESTART : CONCHECK_SCHEDULE_UPDATE_INTERVAL); @@ -2707,13 +2764,16 @@ concheck_update_interval (NMDevice *self, gboolean check_now) void nm_device_check_connectivity_update_interval (NMDevice *self) { - concheck_update_interval (self, FALSE); + concheck_update_interval (self, AF_INET, TRUE); + concheck_update_interval (self, AF_INET6, TRUE); } static void -concheck_update_state (NMDevice *self, NMConnectivityState state, gboolean allow_periodic_bump) +concheck_update_state (NMDevice *self, int addr_family, + NMConnectivityState state, gboolean allow_periodic_bump) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + const gboolean IS_IPv4 = (addr_family == AF_INET); /* @state is a result of the connectivity check. We only expect a precise * number of possible values. */ @@ -2726,7 +2786,7 @@ concheck_update_state (NMDevice *self, NMConnectivityState state, gboolean allow if (state == NM_CONNECTIVITY_ERROR) { /* on error, we don't change the current connectivity state, * except making UNKNOWN to NONE. */ - state = priv->connectivity_state; + state = priv->concheck_x[IS_IPv4].state; if (state == NM_CONNECTIVITY_UNKNOWN) state = NM_CONNECTIVITY_NONE; } else if (state == NM_CONNECTIVITY_FAKE) { @@ -2745,11 +2805,11 @@ concheck_update_state (NMDevice *self, NMConnectivityState state, gboolean allow state = NM_CONNECTIVITY_NONE; } - if (priv->connectivity_state == state) { + if (priv->concheck_x[IS_IPv4].state == state) { /* we got a connectivty update, but the state didn't change. If we were probing, * we bump the probe frequency. */ if (allow_periodic_bump) - concheck_periodic_schedule_set (self, CONCHECK_SCHEDULE_RETURNED_BUMP); + concheck_periodic_schedule_set (self, addr_family, CONCHECK_SCHEDULE_RETURNED_BUMP); return; } /* we need to update the probe interval before emitting signals. Emitting @@ -2758,23 +2818,22 @@ concheck_update_state (NMDevice *self, NMConnectivityState state, gboolean allow if (state == NM_CONNECTIVITY_FULL) { /* we reached full connectivity state. Stop probing by setting the * interval to the max. */ - concheck_periodic_schedule_set (self, CONCHECK_SCHEDULE_RETURNED_MAX); - } else if (priv->connectivity_state == NM_CONNECTIVITY_FULL) { + concheck_periodic_schedule_set (self, addr_family, CONCHECK_SCHEDULE_RETURNED_MAX); + } else if (priv->concheck_x[IS_IPv4].state == NM_CONNECTIVITY_FULL) { /* we are about to loose connectivity. (re)start probing by setting * the timeout interval to the min. */ - concheck_periodic_schedule_set (self, CONCHECK_SCHEDULE_RETURNED_MIN); + concheck_periodic_schedule_set (self, addr_family, CONCHECK_SCHEDULE_RETURNED_MIN); } else { if (allow_periodic_bump) - concheck_periodic_schedule_set (self, CONCHECK_SCHEDULE_RETURNED_BUMP); + concheck_periodic_schedule_set (self, addr_family, CONCHECK_SCHEDULE_RETURNED_BUMP); } _LOGD (LOGD_CONCHECK, "connectivity state changed from %s to %s", - nm_connectivity_state_to_string (priv->connectivity_state), + nm_connectivity_state_to_string (priv->concheck_x[IS_IPv4].state), nm_connectivity_state_to_string (state)); - priv->connectivity_state = state; + priv->concheck_x[IS_IPv4].state = state; - _notify (self, PROP_CONNECTIVITY); - g_signal_emit (self, signals[CONNECTIVITY_CHANGED], 0); + _notify (self, IS_IPv4 ? PROP_IP4_CONNECTIVITY : PROP_IP6_CONNECTIVITY); if ( priv->state == NM_DEVICE_STATE_ACTIVATED && !nm_device_sys_iface_state_is_external (self)) { @@ -2787,10 +2846,39 @@ concheck_update_state (NMDevice *self, NMConnectivityState state, gboolean allow } } +static const char * +nm_device_get_effective_ip_config_method (NMDevice *self, + int addr_family) +{ + NMDeviceClass *klass; + NMConnection *connection = nm_device_get_applied_connection (self); + const char *method; + + g_return_val_if_fail (NM_IS_CONNECTION (connection), "" /* bogus */); + nm_assert_addr_family (addr_family); + + method = nm_utils_get_ip_config_method (connection, addr_family); + + if ( (addr_family == AF_INET && nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) + || (addr_family == AF_INET6 && nm_streq (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO))) { + klass = NM_DEVICE_GET_CLASS (self); + if (klass->get_auto_ip_config_method) { + const char *auto_method; + + auto_method = klass->get_auto_ip_config_method (self, addr_family); + if (auto_method) + return auto_method; + } + } + + return method; +} + static void -concheck_handle_complete (NMDeviceConnectivityHandle *handle, - GError *error) +concheck_handle_complete (NMDeviceConnectivityHandle *handle, GError *error) { + const gboolean IS_IPv4 = (handle->addr_family == AF_INET); + /* The moment we invoke the callback, we unlink it. It signals * that @handle is handled -- as far as the callee of callback * is concerned. */ @@ -2802,7 +2890,7 @@ concheck_handle_complete (NMDeviceConnectivityHandle *handle, if (handle->callback) { handle->callback (handle->self, handle, - NM_DEVICE_GET_PRIVATE (handle->self)->connectivity_state, + NM_DEVICE_GET_PRIVATE (handle->self)->concheck_x[IS_IPv4].state, error, handle->user_data); } @@ -2838,7 +2926,8 @@ concheck_cb (NMConnectivity *connectivity, /* the only place where we nm_connectivity_check_cancel(@c_handle), is * from inside concheck_handle_complete(). This is a recursive call, * nothing to do. */ - _LOGT (LOGD_CONCHECK, "connectivity: complete check (seq:%llu, cancelled)", + _LOGT (LOGD_CONCHECK, "connectivity: [IPv%c] complete check (seq:%llu, cancelled)", + nm_utils_addr_family_to_char (handle->addr_family), (long long unsigned) handle->seq); return; } @@ -2848,7 +2937,8 @@ concheck_cb (NMConnectivity *connectivity, self_keep_alive = g_object_ref (self); - _LOGT (LOGD_CONCHECK, "connectivity: complete check (seq:%llu, state:%s)", + _LOGT (LOGD_CONCHECK, "connectivity: [Ipv%c] complete check (seq:%llu, state:%s)", + nm_utils_addr_family_to_char (handle->addr_family), (long long unsigned) handle->seq, nm_connectivity_state_to_string (state)); @@ -2864,6 +2954,8 @@ concheck_cb (NMConnectivity *connectivity, any_periodic_before = FALSE; any_periodic_after = FALSE; c_list_for_each_entry (other_handle, &priv->concheck_lst_head, concheck_lst) { + if (other_handle->addr_family != handle->addr_family) + continue; if (other_handle->is_periodic_bump_on_complete) { if (other_handle->seq < seq) any_periodic_before = TRUE; @@ -2890,7 +2982,7 @@ concheck_cb (NMConnectivity *connectivity, } /* first update the new state, and emit signals. */ - concheck_update_state (self, state, allow_periodic_bump); + concheck_update_state (self, handle->addr_family, state, allow_periodic_bump); handle_is_alive = FALSE; @@ -2902,6 +2994,8 @@ concheck_cb (NMConnectivity *connectivity, * @handle, as they are automatically obsoleted. */ check_handles: c_list_for_each_entry (other_handle, &priv->concheck_lst_head, concheck_lst) { + if (other_handle->addr_family != handle->addr_family) + continue; if (other_handle->seq >= seq) { /* it's not guaranteed that @handle is still in the list. It might already * be canceled while invoking callbacks for a previous other_handle. @@ -2939,6 +3033,7 @@ check_handles: static NMDeviceConnectivityHandle * concheck_start (NMDevice *self, + int addr_family, NMDeviceConnectivityCallback callback, gpointer user_data, gboolean is_periodic) @@ -2959,14 +3054,18 @@ concheck_start (NMDevice *self, handle->is_periodic = is_periodic; handle->is_periodic_bump = is_periodic; handle->is_periodic_bump_on_complete = is_periodic; + handle->addr_family = addr_family; c_list_link_tail (&priv->concheck_lst_head, &handle->concheck_lst); - _LOGT (LOGD_CONCHECK, "connectivity: start check (seq:%llu%s)", + _LOGT (LOGD_CONCHECK, "connectivity: [IPv%c] start check (seq:%llu%s)", + nm_utils_addr_family_to_char (addr_family), (long long unsigned) handle->seq, is_periodic ? ", periodic-check" : ""); handle->c_handle = nm_connectivity_check_start (concheck_get_mgr (self), + handle->addr_family, + nm_device_get_ip_ifindex (self), nm_device_get_ip_iface (self), concheck_cb, handle); @@ -2975,6 +3074,7 @@ concheck_start (NMDevice *self, NMDeviceConnectivityHandle * nm_device_check_connectivity (NMDevice *self, + int addr_family, NMDeviceConnectivityCallback callback, gpointer user_data) { @@ -2983,8 +3083,8 @@ nm_device_check_connectivity (NMDevice *self, if (!concheck_is_possible (self)) return NULL; - concheck_periodic_schedule_set (self, CONCHECK_SCHEDULE_CHECK_EXTERNAL); - handle = concheck_start (self, callback, user_data, FALSE); + concheck_periodic_schedule_set (self, addr_family, CONCHECK_SCHEDULE_CHECK_EXTERNAL); + handle = concheck_start (self, addr_family, callback, user_data, FALSE); return handle; } @@ -3006,11 +3106,26 @@ nm_device_check_connectivity_cancel (NMDeviceConnectivityHandle *handle) } NMConnectivityState -nm_device_get_connectivity_state (NMDevice *self) +nm_device_get_connectivity_state (NMDevice *self, int addr_family) { + NMDevicePrivate *priv; + const gboolean IS_IPv4 = (addr_family == AF_INET); + g_return_val_if_fail (NM_IS_DEVICE (self), NM_CONNECTIVITY_UNKNOWN); + nm_assert_addr_family (addr_family); - return NM_DEVICE_GET_PRIVATE (self)->connectivity_state; + priv = NM_DEVICE_GET_PRIVATE (self); + + switch (addr_family) { + case AF_INET: + case AF_INET6: + return priv->concheck_x[IS_IPv4].state; + default: + nm_assert (addr_family == AF_UNSPEC); + return NM_MAX_WITH_CMP (nm_connectivity_state_cmp, + priv->concheck_x[0].state, + priv->concheck_x[1].state); + } } /*****************************************************************************/ @@ -3413,11 +3528,14 @@ nm_device_set_carrier (NMDevice *self, gboolean carrier) static void nm_device_set_carrier_from_platform (NMDevice *self) { + int ifindex; + if (nm_device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT)) { - if (!nm_device_has_capability (self, NM_DEVICE_CAP_NONSTANDARD_CARRIER)) { + if ( !nm_device_has_capability (self, NM_DEVICE_CAP_NONSTANDARD_CARRIER) + && (ifindex = nm_device_get_ip_ifindex (self)) > 0) { nm_device_set_carrier (self, nm_platform_link_is_connected (nm_device_get_platform (self), - nm_device_get_ip_ifindex (self))); + ifindex)); } } else { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); @@ -3585,10 +3703,14 @@ device_link_changed (NMDevice *self) gboolean was_up; gboolean update_unmanaged_specs = FALSE; gboolean got_hw_addr = FALSE, had_hw_addr; + gboolean seen_down = priv->device_link_changed_down; priv->device_link_changed_id = 0; + priv->device_link_changed_down = FALSE; ifindex = nm_device_get_ifindex (self); + if (ifindex <= 0) + return G_SOURCE_REMOVE; pllink = nm_platform_link_get (nm_device_get_platform (self), ifindex); if (!pllink) return G_SOURCE_REMOVE; @@ -3696,7 +3818,7 @@ device_link_changed (NMDevice *self) device_recheck_slave_status (self, pllink); - if (priv->up && !was_up) { + if (priv->up && (!was_up || seen_down)) { /* the link was down and just came up. That happens for example, while changing MTU. * We must restore IP configuration. */ if (priv->ip4_state == IP_DONE) { @@ -3772,6 +3894,8 @@ link_changed_cb (NMPlatform *platform, priv = NM_DEVICE_GET_PRIVATE (self); if (ifindex == nm_device_get_ifindex (self)) { + if (!(info->n_ifi_flags & IFF_UP)) + priv->device_link_changed_down = TRUE; if (!priv->device_link_changed_id) { priv->device_link_changed_id = g_idle_add ((GSourceFunc) device_link_changed, self); _LOGD (LOGD_DEVICE, "queued link change for ifindex %d", ifindex); @@ -3786,126 +3910,6 @@ link_changed_cb (NMPlatform *platform, /*****************************************************************************/ -typedef struct { - in_addr_t network; - guint8 plen; -} IP4RPFilterData; - -static guint -_v4_has_shadowed_routes_detect_hash (const IP4RPFilterData *d) -{ - NMHashState h; - - nm_hash_init (&h, 1105201169u); - nm_hash_update_vals (&h, - d->network, - d->plen); - return nm_hash_complete (&h); -} - -static gboolean -_v4_has_shadowed_routes_detect_equal (const IP4RPFilterData *d1, const IP4RPFilterData *d2) -{ - return d1->network == d2->network && d1->plen == d2->plen; -} - -static gboolean -_v4_has_shadowed_routes_detect (NMDevice *self) -{ - NMPlatform *platform; - int ifindex; - NMPLookup lookup; - const NMDedupMultiHeadEntry *head_entry; - NMDedupMultiIter iter; - const NMPObject *o; - guint data_len; - gs_unref_hashtable GHashTable *data_hash = NULL; - gs_free IP4RPFilterData *data_arr = NULL; - - ifindex = nm_device_get_ip_ifindex (self); - if (ifindex <= 0) - return FALSE; - - platform = nm_device_get_platform (self); - - head_entry = nm_platform_lookup (platform, - nmp_lookup_init_object (&lookup, - NMP_OBJECT_TYPE_IP4_ROUTE, - ifindex)); - if (!head_entry) - return FALSE; - - /* first, create a lookup index @data_hash for all network/plen pairs. */ - data_len = 0; - data_arr = g_new (IP4RPFilterData, head_entry->len); - data_hash = g_hash_table_new ((GHashFunc) _v4_has_shadowed_routes_detect_hash, - (GEqualFunc) _v4_has_shadowed_routes_detect_equal); - - nmp_cache_iter_for_each (&iter, head_entry, &o) { - const NMPlatformIP4Route *r = NMP_OBJECT_CAST_IP4_ROUTE (o); - IP4RPFilterData *d; - - nm_assert (r->ifindex == ifindex); - - if ( NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r) - || r->table_coerced) - continue; - - d = &data_arr[data_len++]; - d->network = nm_utils_ip4_address_clear_host_address (r->network, r->plen); - d->plen = r->plen; - g_hash_table_add (data_hash, d); - } - - /* then, search if there is any route on another interface with the same - * network/plen destination. If yes, we consider this a multihoming - * setup. */ - head_entry = nm_platform_lookup (platform, - nmp_lookup_init_obj_type (&lookup, - NMP_OBJECT_TYPE_IP4_ROUTE)); - nmp_cache_iter_for_each (&iter, head_entry, &o) { - const NMPlatformIP4Route *r = NMP_OBJECT_CAST_IP4_ROUTE (o); - IP4RPFilterData d; - - if ( r->ifindex == ifindex - || NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r) - || r->table_coerced) - continue; - - d.network = nm_utils_ip4_address_clear_host_address (r->network, r->plen); - d.plen = r->plen; - if (g_hash_table_contains (data_hash, &d)) - return TRUE; - } - - return FALSE; -} - -static void -ip4_rp_filter_update (NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - const char *ip4_rp_filter; - - if ( priv->v4_has_shadowed_routes - || nm_device_get_best_default_route (self, AF_INET)) { - if (nm_device_ipv4_sysctl_get_effective_uint32 (self, "rp_filter", 0) != 1) { - /* Don't touch the rp_filter if it's not strict. */ - return; - } - /* Loose rp_filter */ - ip4_rp_filter = "2"; - } else { - /* Default rp_filter */ - ip4_rp_filter = NULL; - } - - if (ip4_rp_filter != priv->ip4_rp_filter) { - nm_device_ipv4_sysctl_set (self, "rp_filter", ip4_rp_filter); - priv->ip4_rp_filter = ip4_rp_filter; - } -} - static void link_changed (NMDevice *self, const NMPlatformLink *pllink) { @@ -4133,7 +4137,7 @@ device_init_static_sriov_num_vfs (NMDevice *self) num_vfs = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXINT32, -1); if (num_vfs >= 0) { nm_platform_link_set_sriov_params (nm_device_get_platform (self), - priv->ifindex, num_vfs, -1); + priv->ifindex, num_vfs, NM_TERNARY_DEFAULT); } } } @@ -4296,8 +4300,6 @@ realize_start_setup (NMDevice *self, nm_device_set_carrier_from_platform (self); - device_init_static_sriov_num_vfs (self); - nm_assert (!priv->stats.timeout_id); real_rate = _stats_refresh_rate_real (priv->stats.refresh_rate_ms); if (real_rate) @@ -4794,7 +4796,8 @@ nm_device_master_release_slaves (NMDevice *self) if (priv->state == NM_DEVICE_STATE_FAILED) reason = NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED; - if (!nm_platform_link_get (nm_device_get_platform (self), priv->ifindex)) + if ( priv->ifindex <= 0 + || !nm_platform_link_get (nm_device_get_platform (self), priv->ifindex)) configure = FALSE; c_list_for_each_safe (iter, safe, &priv->slaves) { @@ -4893,12 +4896,12 @@ check_ip_state (NMDevice *self, gboolean may_fail, gboolean full_state_update) && !priv->is_enslaved) return; - s_ip4 = (NMSettingIPConfig *) nm_device_get_applied_setting (self, NM_TYPE_SETTING_IP4_CONFIG); + s_ip4 = nm_device_get_applied_setting (self, NM_TYPE_SETTING_IP4_CONFIG); if (s_ip4 && nm_streq0 (nm_setting_ip_config_get_method (s_ip4), NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) ip4_disabled = TRUE; - s_ip6 = (NMSettingIPConfig *) nm_device_get_applied_setting (self, NM_TYPE_SETTING_IP6_CONFIG); + s_ip6 = nm_device_get_applied_setting (self, NM_TYPE_SETTING_IP6_CONFIG); if (s_ip6 && nm_streq0 (nm_setting_ip_config_get_method (s_ip6), NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) ip6_ignore = TRUE; @@ -5257,7 +5260,7 @@ nm_device_autoconnect_allowed (NMDevice *self) /* The 'autoconnect-allowed' signal is emitted on a device to allow * other listeners to block autoconnect on the device if they wish. * This is mainly used by the OLPC Mesh devices to block autoconnect - * on their companion WiFi device as they share radio resources and + * on their companion Wi-Fi device as they share radio resources and * cannot be connected at the same time. */ @@ -5476,10 +5479,13 @@ nm_device_generate_connection (NMDevice *self, nm_connection_add_setting (connection, nm_setting_proxy_new ()); pllink = nm_platform_link_get (nm_device_get_platform (self), priv->ifindex); - if (pllink && pllink->inet6_token.id) { + if ( pllink + && pllink->inet6_token.id) { + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE, NM_IN6_ADDR_GEN_MODE_EUI64, - NM_SETTING_IP6_CONFIG_TOKEN, nm_utils_inet6_interface_identifier_to_token (pllink->inet6_token, NULL), + NM_SETTING_IP6_CONFIG_TOKEN, nm_utils_inet6_interface_identifier_to_token (pllink->inet6_token, sbuf), NULL); } } @@ -5497,8 +5503,8 @@ nm_device_generate_connection (NMDevice *self, /* Ignore the connection if it has no IP configuration, * no slave configuration, and is not a master interface. */ - ip4_method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); - ip6_method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); + ip4_method = nm_utils_get_ip_config_method (connection, AF_INET); + ip6_method = nm_utils_get_ip_config_method (connection, AF_INET6); if ( g_strcmp0 (ip4_method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0 && g_strcmp0 (ip6_method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0 && !nm_setting_connection_get_master (NM_SETTING_CONNECTION (s_con)) @@ -5778,7 +5784,7 @@ unmanaged_on_quit (NMDevice *self) /* the only exception are IPv4 shared connections. We unmanage them on quit. */ connection = nm_device_get_applied_connection (self); if (connection) { - if (NM_IN_STRSET (nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG), + if (NM_IN_STRSET (nm_utils_get_ip_config_method (connection, AF_INET), NM_SETTING_IP4_CONFIG_METHOD_SHARED)) { /* shared connections are to be unmangaed. */ return TRUE; @@ -6102,7 +6108,7 @@ lldp_rx_enabled (NMDevice *self) lldp = nm_setting_connection_get_lldp (s_con); if (lldp == NM_SETTING_CONNECTION_LLDP_DEFAULT) { lldp = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA, - "connection.lldp", + NM_CON_DEFAULT ("connection.lldp"), self, NM_SETTING_CONNECTION_LLDP_DEFAULT, NM_SETTING_CONNECTION_LLDP_ENABLE_RX, @@ -6197,16 +6203,16 @@ act_stage1_prepare (NMDevice *self, NMDeviceStateReason *out_failure_reason) if ( priv->ifindex > 0 && nm_device_has_capability (self, NM_DEVICE_CAP_SRIOV) - && (s_sriov = (NMSettingSriov *) nm_device_get_applied_setting (self, NM_TYPE_SETTING_SRIOV))) { + && (s_sriov = nm_device_get_applied_setting (self, NM_TYPE_SETTING_SRIOV))) { nm_auto_freev NMPlatformVF **plat_vfs = NULL; gs_free_error GError *error = NULL; NMSriovVF *vf; - int autoprobe; + NMTernary autoprobe; autoprobe = nm_setting_sriov_get_autoprobe_drivers (s_sriov); if (autoprobe == NM_TERNARY_DEFAULT) { autoprobe = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA, - "sriov.autoprobe-drivers", + NM_CON_DEFAULT ("sriov.autoprobe-drivers"), self, NM_TERNARY_FALSE, NM_TERNARY_TRUE, @@ -6576,7 +6582,7 @@ get_ipv4_dad_timeout (NMDevice *self) return timeout; return nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA, - "ipv4.dad-timeout", + NM_CON_DEFAULT ("ipv4.dad-timeout"), self, 0, NM_SETTING_IP_CONFIG_DAD_TIMEOUT_MAX, @@ -6584,17 +6590,15 @@ get_ipv4_dad_timeout (NMDevice *self) } static void -acd_data_destroy (gpointer ptr, GClosure *closure) +acd_data_destroy (gpointer ptr) { AcdData *data = ptr; int i; - if (data) { - for (i = 0; data->configs && data->configs[i]; i++) - g_object_unref (data->configs[i]); - g_free (data->configs); - g_slice_free (AcdData, data); - } + for (i = 0; data->configs && data->configs[i]; i++) + g_object_unref (data->configs[i]); + g_free (data->configs); + g_slice_free (AcdData, data); } static void @@ -6606,7 +6610,7 @@ ipv4_manual_method_apply (NMDevice *self, NMIP4Config **configs, gboolean succes connection = nm_device_get_applied_connection (self); nm_assert (connection); - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); + method = nm_utils_get_ip_config_method (connection, AF_INET); nm_assert (NM_IN_STRSET (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, NM_SETTING_IP4_CONFIG_METHOD_AUTO)); @@ -6628,8 +6632,9 @@ ipv4_manual_method_apply (NMDevice *self, NMIP4Config **configs, gboolean succes } static void -acd_manager_probe_terminated (NMAcdManager *acd_manager, AcdData *data) +acd_manager_probe_terminated (NMAcdManager *acd_manager, gpointer user_data) { + AcdData *data = user_data; NMDevice *self; NMDevicePrivate *priv; NMDedupMultiIter ipconf_iter; @@ -6643,13 +6648,15 @@ acd_manager_probe_terminated (NMAcdManager *acd_manager, AcdData *data) for (i = 0; data->configs && data->configs[i]; i++) { nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, data->configs[i], &address) { + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + result = nm_acd_manager_check_address (acd_manager, address->address); success &= result; _NMLOG (result ? LOGL_DEBUG : LOGL_WARN, LOGD_DEVICE, "IPv4 DAD result: address %s is %s", - nm_utils_inet4_ntop (address->address, NULL), + nm_utils_inet4_ntop (address->address, sbuf), result ? "unique" : "duplicate"); } } @@ -6657,7 +6664,7 @@ acd_manager_probe_terminated (NMAcdManager *acd_manager, AcdData *data) data->callback (self, data->configs, success); priv->acd.dad_list = g_slist_remove (priv->acd.dad_list, acd_manager); - nm_acd_manager_destroy (acd_manager); + nm_acd_manager_free (acd_manager); } /** @@ -6673,6 +6680,10 @@ acd_manager_probe_terminated (NMAcdManager *acd_manager, AcdData *data) static void ipv4_dad_start (NMDevice *self, NMIP4Config **configs, AcdCallback cb) { + static const NMAcdCallbacks acd_callbacks = { + .probe_terminated_callback = acd_manager_probe_terminated, + .user_data_destroy = acd_data_destroy, + }; NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMAcdManager *acd_manager; const NMPlatformIP4Address *address; @@ -6716,26 +6727,23 @@ ipv4_dad_start (NMDevice *self, NMIP4Config **configs, AcdCallback cb) return; } - /* don't take additional references of @acd_manager that outlive @self. - * Otherwise, the callback can be invoked on a dangling pointer as we don't - * disconnect the handler. */ - acd_manager = nm_acd_manager_new (nm_device_get_ip_ifindex (self), hwaddr_arr, length); - priv->acd.dad_list = g_slist_append (priv->acd.dad_list, acd_manager); - data = g_slice_new0 (AcdData); data->configs = configs; data->callback = cb; data->device = self; + acd_manager = nm_acd_manager_new (nm_device_get_ip_ifindex (self), + hwaddr_arr, + length, + &acd_callbacks, + data); + priv->acd.dad_list = g_slist_append (priv->acd.dad_list, acd_manager); + for (i = 0; configs[i]; i++) { nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, configs[i], &address) nm_acd_manager_add_address (acd_manager, address->address); } - g_signal_connect_data (acd_manager, NM_ACD_MANAGER_PROBE_TERMINATED, - G_CALLBACK (acd_manager_probe_terminated), data, - acd_data_destroy, 0); - ret = nm_acd_manager_start_probe (acd_manager, timeout); if (!ret) { @@ -6745,7 +6753,7 @@ ipv4_dad_start (NMDevice *self, NMIP4Config **configs, AcdCallback cb) cb (self, configs, TRUE); priv->acd.dad_list = g_slist_remove (priv->acd.dad_list, acd_manager); - nm_acd_manager_destroy (acd_manager); + nm_acd_manager_free (acd_manager); } } @@ -6801,8 +6809,6 @@ nm_device_handle_ipv4ll_event (sd_ipv4ll *ll, int event, void *data) { NMDevice *self = data; NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMConnection *connection = NULL; - const char *method; struct in_addr address; NMIP4Config *config; int r; @@ -6810,13 +6816,8 @@ nm_device_handle_ipv4ll_event (sd_ipv4ll *ll, int event, void *data) if (priv->act_request.obj == NULL) return; - connection = nm_act_request_get_applied_connection (priv->act_request.obj); - g_assert (connection); - - /* Ignore if the connection isn't an AutoIP connection */ - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); - if (g_strcmp0 (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL) != 0) - return; + nm_assert (nm_streq (nm_device_get_effective_ip_config_method (self, AF_INET), + NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)); switch (event) { case SD_IPV4LL_EVENT_BIND: @@ -6988,6 +6989,7 @@ dhcp4_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + priv->dhcp4.was_active = FALSE; nm_clear_g_source (&priv->dhcp4.grace_id); g_clear_pointer (&priv->dhcp4.pac_url, g_free); g_clear_pointer (&priv->dhcp4.root_path, g_free); @@ -7100,7 +7102,7 @@ ip_config_merge_and_apply (NMDevice *self, if (commit) { gboolean v; - v = default_route_metric_penalty_detect (self); + v = default_route_metric_penalty_detect (self, addr_family); if (IS_IPv4) priv->default_route_metric_penalty_ip4_has = v; else @@ -7202,15 +7204,15 @@ ip_config_merge_and_apply (NMDevice *self, } if (!IS_IPv4) { - if (commit) { - NMUtilsIPv6IfaceId iid; + NMUtilsIPv6IfaceId iid; - if ( ip6_addr_gen_token - && nm_utils_ipv6_interface_identifier_get_from_token (&iid, ip6_addr_gen_token)) { - nm_platform_link_set_ipv6_token (nm_device_get_platform (self), - nm_device_get_ip_ifindex (self), - iid); - } + if ( commit + && priv->ndisc_started + && ip6_addr_gen_token + && nm_utils_ipv6_interface_identifier_get_from_token (&iid, ip6_addr_gen_token)) { + nm_platform_link_set_ipv6_token (nm_device_get_platform (self), + nm_device_get_ip_ifindex (self), + iid); } } @@ -7266,12 +7268,13 @@ dhcp4_grace_period_expired (gpointer user_data) } static void -dhcp4_fail (NMDevice *self) +dhcp4_fail (NMDevice *self, NMDhcpState dhcp_state) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - _LOGD (LOGD_DHCP4, "DHCPv4 failed (ip_state %s)", - _ip_state_to_string (priv->ip4_state)); + _LOGD (LOGD_DHCP4, "DHCPv4 failed (ip_state %s, was_active %d)", + _ip_state_to_string (priv->ip4_state), + priv->dhcp4.was_active); /* Keep client running if there are static addresses configured * on the interface. @@ -7281,11 +7284,14 @@ dhcp4_fail (NMDevice *self) && nm_ip4_config_get_num_addresses (priv->con_ip_config_4) > 0) goto clear_config; - /* Fail the method in case of timeout or failure during initial - * configuration. + /* Fail the method when one of the following is true: + * 1) the DHCP client terminated: it does not make sense to start a grace + * period without a client running; + * 2) we failed to get an initial lease AND the client was + * not active before. */ - if ( !priv->dhcp4.was_active - && priv->ip4_state == IP_CONF) { + if ( dhcp_state == NM_DHCP_STATE_TERMINATED + || (!priv->dhcp4.was_active && priv->ip4_state == IP_CONF)) { dhcp4_cleanup (self, CLEANUP_TYPE_DECONFIGURE, FALSE); nm_device_activate_schedule_ip4_config_timeout (self); return; @@ -7348,7 +7354,7 @@ dhcp4_state_changed (NMDhcpClient *client, case NM_DHCP_STATE_BOUND: if (!ip4_config) { _LOGW (LOGD_DHCP4, "failed to get IPv4 config in response to DHCP event."); - dhcp4_fail (self); + dhcp4_fail (self, state); break; } @@ -7391,11 +7397,11 @@ dhcp4_state_changed (NMDhcpClient *client, if (dhcp4_lease_change (self, ip4_config)) nm_device_update_metered (self); else - dhcp4_fail (self); + dhcp4_fail (self, state); } break; case NM_DHCP_STATE_TIMEOUT: - dhcp4_fail (self); + dhcp4_fail (self, state); break; case NM_DHCP_STATE_EXPIRE: /* Ignore expiry before we even have a lease (NAK, old lease, etc) */ @@ -7404,7 +7410,8 @@ dhcp4_state_changed (NMDhcpClient *client, /* fall through */ case NM_DHCP_STATE_DONE: case NM_DHCP_STATE_FAIL: - dhcp4_fail (self); + case NM_DHCP_STATE_TERMINATED: + dhcp4_fail (self, state); break; default: break; @@ -7435,8 +7442,8 @@ get_dhcp_timeout (NMDevice *self, int addr_family) timeout = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA, addr_family == AF_INET - ? "ipv4.dhcp-timeout" - : "ipv6.dhcp-timeout", + ? NM_CON_DEFAULT ("ipv4.dhcp-timeout") + : NM_CON_DEFAULT ("ipv6.dhcp-timeout"), self, 0, G_MAXINT32, 0); if (timeout) @@ -7450,18 +7457,6 @@ get_dhcp_timeout (NMDevice *self, int addr_family) } static GBytes * -dhcp4_get_client_id_mac (const guint8 *hwaddr /* ETH_ALEN bytes */) -{ - guint8 *client_id_buf; - guint8 hwaddr_type = ARPHRD_ETHER; - - client_id_buf = g_malloc (ETH_ALEN + 1); - client_id_buf[0] = hwaddr_type; - memcpy (&client_id_buf[1], hwaddr, ETH_ALEN); - return g_bytes_new_take (client_id_buf, ETH_ALEN + 1); -} - -static GBytes * dhcp4_get_client_id (NMDevice *self, NMConnection *connection, GBytes *hwaddr) @@ -7473,6 +7468,7 @@ dhcp4_get_client_id (NMDevice *self, const char *fail_reason; guint8 hwaddr_bin_buf[NM_UTILS_HWADDR_LEN_MAX]; const guint8 *hwaddr_bin; + int arp_type; gsize hwaddr_len; GBytes *result; gs_free char *logstr1 = NULL; @@ -7482,7 +7478,8 @@ dhcp4_get_client_id (NMDevice *self, if (!client_id) { client_id_default = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, - "ipv4.dhcp-client-id", self); + NM_CON_DEFAULT ("ipv4.dhcp-client-id"), + self); if (client_id_default && client_id_default[0]) { /* a non-empty client-id is always valid, see nm_dhcp_utils_client_id_string_to_bytes(). */ client_id = client_id_default; @@ -7491,23 +7488,24 @@ dhcp4_get_client_id (NMDevice *self, if (!client_id) { _LOGD (LOGD_DEVICE | LOGD_DHCP4 | LOGD_IP4, - "ipv4.dhcp-client-id: no explicity client-id configured"); + "ipv4.dhcp-client-id: no explicit client-id configured"); return NULL; } if (nm_streq (client_id, "mac")) { if (!hwaddr) { - fail_reason = "failed to get current MAC address"; + fail_reason = "missing link-layer address"; goto out_fail; } hwaddr_bin = g_bytes_get_data (hwaddr, &hwaddr_len); - if (hwaddr_len != ETH_ALEN) { - fail_reason = "MAC address is not ethernet"; + arp_type = nm_utils_arp_type_detect_from_hwaddrlen (hwaddr_len); + if (arp_type < 0) { + fail_reason = "unsupported link-layer address"; goto out_fail; } - result = dhcp4_get_client_id_mac (hwaddr_bin); + result = nm_utils_dhcp_client_id_mac (arp_type, hwaddr_bin, hwaddr_len); goto out_good; } @@ -7516,32 +7514,37 @@ dhcp4_get_client_id (NMDevice *self, hwaddr_str = nm_device_get_permanent_hw_address (self); if (!hwaddr_str) { - fail_reason = "failed to get permanent MAC address"; + fail_reason = "missing permanent link-layer address"; goto out_fail; } if (!_nm_utils_hwaddr_aton (hwaddr_str, hwaddr_bin_buf, sizeof (hwaddr_bin_buf), &hwaddr_len)) g_return_val_if_reached (NULL); - if (hwaddr_len != ETH_ALEN) { - /* unsupported type. */ - fail_reason = "MAC address is not ethernet"; + arp_type = nm_utils_arp_type_detect_from_hwaddrlen (hwaddr_len); + if (arp_type < 0) { + fail_reason = "unsupported permanent link-layer address"; goto out_fail; } - result = dhcp4_get_client_id_mac (hwaddr_bin_buf); + result = nm_utils_dhcp_client_id_mac (arp_type, hwaddr_bin_buf, hwaddr_len); + goto out_good; + } + + if (nm_streq (client_id, "duid")) { + result = nm_utils_dhcp_client_id_systemd_node_specific (TRUE, + nm_device_get_ip_iface (self)); goto out_good; } if (nm_streq (client_id, "stable")) { + nm_auto_free_checksum GChecksum *sum = NULL; + guint8 digest[NM_UTILS_CHECKSUM_LENGTH_SHA1]; NMUtilsStableType stable_type; const char *stable_id; - GChecksum *sum; - guint8 buf[20]; - gsize buf_size; guint32 salted_header; - const guint8 *secret_key; - gsize secret_key_len; + const guint8 *host_id; + gsize host_id_len; stable_id = _get_stable_id (self, connection, &stable_type); if (!stable_id) @@ -7549,23 +7552,17 @@ dhcp4_get_client_id (NMDevice *self, salted_header = htonl (2011610591 + stable_type); - nm_utils_secret_key_get (&secret_key, &secret_key_len); + nm_utils_host_id_get (&host_id, &host_id_len); sum = g_checksum_new (G_CHECKSUM_SHA1); - g_checksum_update (sum, (const guchar *) &salted_header, sizeof (salted_header)); g_checksum_update (sum, (const guchar *) stable_id, strlen (stable_id) + 1); - g_checksum_update (sum, (const guchar *) secret_key, secret_key_len); - - buf_size = sizeof (buf); - g_checksum_get_digest (sum, buf, &buf_size); - nm_assert (buf_size == sizeof (buf)); - - g_checksum_free (sum); + g_checksum_update (sum, (const guchar *) host_id, host_id_len); + nm_utils_checksum_get_digest (sum, digest); client_id_buf = g_malloc (1 + 15); client_id_buf[0] = 0; - memcpy (&client_id_buf[1], buf, 15); + memcpy (&client_id_buf[1], digest, 15); result = g_bytes_new_take (client_id_buf, 1 + 15); goto out_good; } @@ -7741,34 +7738,26 @@ static gboolean connection_ip4_method_requires_carrier (NMConnection *connection, gboolean *out_ip4_enabled) { - const char *method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); - static const char *ip4_carrier_methods[] = { - NM_SETTING_IP4_CONFIG_METHOD_AUTO, - NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL, - NULL - }; + const char *method; - if (out_ip4_enabled) - *out_ip4_enabled = !!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED); - return g_strv_contains (ip4_carrier_methods, method); + method = nm_utils_get_ip_config_method (connection, AF_INET); + NM_SET_OUT (out_ip4_enabled, !nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)); + return NM_IN_STRSET (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL); } static gboolean connection_ip6_method_requires_carrier (NMConnection *connection, gboolean *out_ip6_enabled) { - const char *method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); - static const char *ip6_carrier_methods[] = { - NM_SETTING_IP6_CONFIG_METHOD_AUTO, - NM_SETTING_IP6_CONFIG_METHOD_DHCP, - NM_SETTING_IP6_CONFIG_METHOD_SHARED, - NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL, - NULL - }; + const char *method; - if (out_ip6_enabled) - *out_ip6_enabled = !!strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE); - return g_strv_contains (ip6_carrier_methods, method); + method = nm_utils_get_ip_config_method (connection, AF_INET6); + NM_SET_OUT (out_ip6_enabled, !nm_streq (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)); + return NM_IN_STRSET (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO, + NM_SETTING_IP6_CONFIG_METHOD_DHCP, + NM_SETTING_IP6_CONFIG_METHOD_SHARED, + NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL); } static gboolean @@ -7837,12 +7826,12 @@ have_any_ready_slaves (NMDevice *self) } static gboolean -ip4_requires_slaves (NMConnection *connection) +ip4_requires_slaves (NMDevice *self) { const char *method; - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); - return strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0; + method = nm_device_get_effective_ip_config_method (self, AF_INET); + return nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO); } static NMActStageReturn @@ -7866,7 +7855,7 @@ act_stage3_ip4_config_start (NMDevice *self, return NM_ACT_STAGE_RETURN_IP_WAIT; } - if (nm_device_is_master (self) && ip4_requires_slaves (connection)) { + if (nm_device_is_master (self) && ip4_requires_slaves (self)) { /* If the master has no ready slaves, and depends on slaves for * a successful IPv4 attempt, then postpone IPv4 addressing. */ @@ -7877,7 +7866,8 @@ act_stage3_ip4_config_start (NMDevice *self, } } - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); + method = nm_device_get_effective_ip_config_method (self, AF_INET); + _LOGD (LOGD_IP4 | LOGD_DEVICE, "IPv4 config method is %s", method); if (NM_IN_STRSET (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO, @@ -7945,6 +7935,7 @@ dhcp6_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + priv->dhcp6.was_active = FALSE; priv->dhcp6.mode = NM_NDISC_DHCP_LEVEL_NONE; applied_config_clear (&priv->dhcp6.ip6_config); g_clear_pointer (&priv->dhcp6.event_id, g_free); @@ -8018,12 +8009,14 @@ dhcp6_grace_period_expired (gpointer user_data) } static void -dhcp6_fail (NMDevice *self, gboolean timeout) +dhcp6_fail (NMDevice *self, NMDhcpState dhcp_state) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); gboolean is_dhcp_managed; - _LOGD (LOGD_DHCP6, "DHCPv6 failed%s", timeout ? " (timeout)" : ""); + _LOGD (LOGD_DHCP6, "DHCPv6 failed (ip_state %s, was_active %d)", + _ip_state_to_string (priv->ip6_state), + priv->dhcp6.was_active); is_dhcp_managed = (priv->dhcp6.mode == NM_NDISC_DHCP_LEVEL_MANAGED); @@ -8036,11 +8029,14 @@ dhcp6_fail (NMDevice *self, gboolean timeout) && nm_ip6_config_get_num_addresses (priv->con_ip_config_6)) goto clear_config; - /* Fail the method in case of timeout or failure during initial - * configuration. + /* Fail the method when one of the following is true: + * 1) the DHCP client terminated: it does not make sense to start a grace + * period without a client running; + * 2) we failed to get an initial lease AND the client was + * not active before. */ - if ( !priv->dhcp6.was_active - && (timeout || priv->ip6_state == IP_CONF)) { + if ( dhcp_state == NM_DHCP_STATE_TERMINATED + || (!priv->dhcp6.was_active && priv->ip6_state == IP_CONF)) { dhcp6_cleanup (self, CLEANUP_TYPE_DECONFIGURE, FALSE); nm_device_activate_schedule_ip6_config_timeout (self); return; @@ -8077,21 +8073,6 @@ clear_config: } static void -dhcp6_timeout (NMDevice *self, NMDhcpClient *client) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - - if (priv->dhcp6.mode == NM_NDISC_DHCP_LEVEL_MANAGED) - dhcp6_fail (self, TRUE); - else { - /* not a hard failure; just live with the RA info */ - dhcp6_cleanup (self, CLEANUP_TYPE_DECONFIGURE, FALSE); - if (priv->ip6_state == IP_CONF) - nm_device_activate_schedule_ip6_config_result (self); - } -} - -static void dhcp6_state_changed (NMDhcpClient *client, NMDhcpState state, NMIP6Config *ip6_config, @@ -8148,17 +8129,24 @@ dhcp6_state_changed (NMDhcpClient *client, nm_device_activate_schedule_ip6_config_result (self); } else if (priv->ip6_state == IP_DONE) if (!dhcp6_lease_change (self)) - dhcp6_fail (self, FALSE); + dhcp6_fail (self, state); break; case NM_DHCP_STATE_TIMEOUT: - dhcp6_timeout (self, client); + if (priv->dhcp6.mode == NM_NDISC_DHCP_LEVEL_MANAGED) + dhcp6_fail (self, state); + else { + /* not a hard failure; just live with the RA info */ + dhcp6_cleanup (self, CLEANUP_TYPE_DECONFIGURE, FALSE); + if (priv->ip6_state == IP_CONF) + nm_device_activate_schedule_ip6_config_result (self); + } break; case NM_DHCP_STATE_EXPIRE: /* Ignore expiry before we even have a lease (NAK, old lease, etc) */ if (priv->ip6_state != IP_CONF) - dhcp6_fail (self, FALSE); + dhcp6_fail (self, state); break; - case NM_DHCP_STATE_DONE: + case NM_DHCP_STATE_TERMINATED: /* In IPv6 info-only mode, the client doesn't handle leases so it * may exit right after getting a response from the server. That's * normal. In that case we just ignore the exit. @@ -8166,8 +8154,9 @@ dhcp6_state_changed (NMDhcpClient *client, if (priv->dhcp6.mode == NM_NDISC_DHCP_LEVEL_OTHERCONF) break; /* fall through */ + case NM_DHCP_STATE_DONE: case NM_DHCP_STATE_FAIL: - dhcp6_fail (self, FALSE); + dhcp6_fail (self, state); break; default: break; @@ -8187,53 +8176,63 @@ dhcp6_prefix_delegated (NMDhcpClient *client, g_signal_emit (self, signals[IP6_PREFIX_DELEGATED], 0, prefix); } +/*****************************************************************************/ + /* RFC 3315 defines the epoch for the DUID-LLT time field on Jan 1st 2000. */ #define EPOCH_DATETIME_200001010000 946684800 static GBytes * -generate_duid_llt (const guint8 *hwaddr /* ETH_ALEN bytes */, +generate_duid_llt (int arp_type, + const guint8 *hwaddr, + gsize hwaddr_len, gint64 time) { guint8 *arr; const guint16 duid_type = htons (1); - const guint16 hw_type = htons (ARPHRD_ETHER); + const guint16 hw_type = htons (arp_type); const guint32 duid_time = htonl (NM_MAX (0, time - EPOCH_DATETIME_200001010000)); - arr = g_new (guint8, 2 + 2 + 4 + ETH_ALEN); + if (!nm_utils_arp_type_get_hwaddr_relevant_part (arp_type, &hwaddr, &hwaddr_len)) + nm_assert_not_reached (); + + arr = g_new (guint8, 2 + 2 + 4 + hwaddr_len); memcpy (&arr[0], &duid_type, 2); memcpy (&arr[2], &hw_type, 2); memcpy (&arr[4], &duid_time, 4); - memcpy (&arr[8], hwaddr, ETH_ALEN); + memcpy (&arr[8], hwaddr, hwaddr_len); - return g_bytes_new_take (arr, 2 + 2 + 4 + ETH_ALEN); + return g_bytes_new_take (arr, 2 + 2 + 4 + hwaddr_len); } static GBytes * -generate_duid_ll (const guint8 *hwaddr /* ETH_ALEN bytes */) +generate_duid_ll (int arp_type, + const guint8 *hwaddr, + gsize hwaddr_len) { guint8 *arr; const guint16 duid_type = htons (3); - const guint16 hw_type = htons (ARPHRD_ETHER); + const guint16 hw_type = htons (arp_type); + + if (!nm_utils_arp_type_get_hwaddr_relevant_part (arp_type, &hwaddr, &hwaddr_len)) + nm_assert_not_reached (); - arr = g_new (guint8, 2 + 2 + ETH_ALEN); + arr = g_new (guint8, 2 + 2 + hwaddr_len); memcpy (&arr[0], &duid_type, 2); memcpy (&arr[2], &hw_type, 2); - memcpy (&arr[4], hwaddr, ETH_ALEN); + memcpy (&arr[4], hwaddr, hwaddr_len); - return g_bytes_new_take (arr, 2 + 2 + ETH_ALEN); + return g_bytes_new_take (arr, 2 + 2 + hwaddr_len); } static GBytes * -generate_duid_uuid (guint8 *data, gsize data_len) +generate_duid_uuid (const NMUuid *uuid) { - const guint16 duid_type = g_htons (4); - const int DUID_SIZE = 18; + const guint16 duid_type = htons (4); guint8 *duid_buffer; - nm_assert (data); - nm_assert (data_len >= 16); + nm_assert (uuid); /* Generate a DHCP Unique Identifier for DHCPv6 using the * DUID-UUID method (see RFC 6355 section 4). Format is: @@ -8241,44 +8240,47 @@ generate_duid_uuid (guint8 *data, gsize data_len) * u16: type (DUID-UUID = 4) * u8[16]: UUID bytes */ - duid_buffer = g_malloc (DUID_SIZE); - G_STATIC_ASSERT_EXPR (sizeof (duid_type) == 2); + G_STATIC_ASSERT_EXPR (sizeof (*uuid) == 16); + duid_buffer = g_malloc (18); memcpy (&duid_buffer[0], &duid_type, 2); - - /* UUID is 128 bits, we just take the first 128 bits - * (regardless of data size) as the DUID-UUID. - */ - memcpy (&duid_buffer[2], data, 16); - - return g_bytes_new_take (duid_buffer, DUID_SIZE); + memcpy (&duid_buffer[2], uuid, 16); + return g_bytes_new_take (duid_buffer, 18); } static GBytes * generate_duid_from_machine_id (void) { - gs_free const char *machine_id_s = NULL; - uuid_t uuid; - GChecksum *sum; - guint8 sha256_digest[32]; - gsize len = sizeof (sha256_digest); - static GBytes *global_duid = NULL; + static GBytes *volatile global_duid = NULL; + GBytes *p; - if (global_duid) - return g_bytes_ref (global_duid); +again: + p = g_atomic_pointer_get (&global_duid); + if (G_UNLIKELY (!p)) { + nm_auto_free_checksum GChecksum *sum = NULL; + const NMUuid *machine_id; + union { + guint8 sha256[NM_UTILS_CHECKSUM_LENGTH_SHA256]; + NMUuid uuid; + } digest; + + machine_id = nm_utils_machine_id_bin (); + + /* Hash the machine ID so it's not leaked to the network */ + sum = g_checksum_new (G_CHECKSUM_SHA256); + g_checksum_update (sum, (const guchar *) machine_id, sizeof (*machine_id)); + nm_utils_checksum_get_digest (sum, digest.sha256); - machine_id_s = nm_utils_machine_id_read (); - if (!nm_utils_machine_id_parse (machine_id_s, uuid)) - return NULL; + G_STATIC_ASSERT_EXPR (sizeof (digest.sha256) > sizeof (digest.uuid)); + p = generate_duid_uuid (&digest.uuid); - /* Hash the machine ID so it's not leaked to the network */ - sum = g_checksum_new (G_CHECKSUM_SHA256); - g_checksum_update (sum, (const guchar *) &uuid, sizeof (uuid)); - g_checksum_get_digest (sum, sha256_digest, &len); - g_checksum_free (sum); + if (!g_atomic_pointer_compare_and_exchange (&global_duid, NULL, p)) { + g_bytes_unref (p); + goto again; + } + } - global_duid = generate_duid_uuid (sha256_digest, len); - return g_bytes_ref (global_duid); + return g_bytes_ref (p); } static GBytes * @@ -8289,17 +8291,19 @@ dhcp6_get_duid (NMDevice *self, NMConnection *connection, GBytes *hwaddr, gboole gs_free char *duid_default = NULL; const char *duid_error; GBytes *duid_out; - guint8 sha256_digest[32]; - gsize len = sizeof (sha256_digest); gboolean duid_enforce = TRUE; gs_free char *logstr1 = NULL; + const guint8 *hwaddr_bin; + gsize hwaddr_len; + int arp_type; s_ip6 = nm_connection_get_setting_ip6_config (connection); duid = nm_setting_ip6_config_get_dhcp_duid (NM_SETTING_IP6_CONFIG (s_ip6)); if (!duid) { duid_default = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, - "ipv6.dhcp-duid", self); + NM_CON_DEFAULT ("ipv6.dhcp-duid"), + self); duid = duid_default; if (!duid) duid = "lease"; @@ -8308,10 +8312,6 @@ dhcp6_get_duid (NMDevice *self, NMConnection *connection, GBytes *hwaddr, gboole if (nm_streq (duid, "lease")) { duid_enforce = FALSE; duid_out = generate_duid_from_machine_id (); - if (!duid_out) { - duid_error = "failure to read machine-id"; - goto out_fail; - } goto out_good; } @@ -8328,78 +8328,138 @@ dhcp6_get_duid (NMDevice *self, NMConnection *connection, GBytes *hwaddr, gboole duid_error = "missing link-layer address"; goto out_fail; } - if (g_bytes_get_size (hwaddr) != ETH_ALEN) { + + hwaddr_bin = g_bytes_get_data (hwaddr, &hwaddr_len); + arp_type = nm_utils_arp_type_detect_from_hwaddrlen (hwaddr_len); + if (arp_type < 0) { duid_error = "unsupported link-layer address"; goto out_fail; } - if (nm_streq (duid, "ll")) { - duid_out = generate_duid_ll (g_bytes_get_data (hwaddr, NULL)); - } else { - gint64 time; - - time = nm_utils_secret_key_get_timestamp (); - if (!time) { - duid_error = "cannot retrieve the secret key timestamp"; - goto out_fail; - } - - duid_out = generate_duid_llt (g_bytes_get_data (hwaddr, NULL), time); + if (nm_streq (duid, "ll")) + duid_out = generate_duid_ll (arp_type, hwaddr_bin, hwaddr_len); + else { + duid_out = generate_duid_llt (arp_type, hwaddr_bin, hwaddr_len, + nm_utils_host_id_get_timestamp_ns () / NM_UTILS_NS_PER_SECOND); } goto out_good; } if (NM_IN_STRSET (duid, "stable-ll", "stable-llt", "stable-uuid")) { + /* preferably, we would salt the checksum differently for each @duid type. We missed + * to do that initially, so most types use the DEFAULT_SALT. + * + * Implemenations that are added later, should use a distinct salt instead, + * like "stable-ll"/"stable-llt" with ARPHRD_INFINIBAND below. */ + const guint32 DEFAULT_SALT = 670531087u; + nm_auto_free_checksum GChecksum *sum = NULL; NMUtilsStableType stable_type; const char *stable_id = NULL; guint32 salted_header; - GChecksum *sum; - const guint8 *secret_key; - gsize secret_key_len; + const guint8 *host_id; + gsize host_id_len; + union { + guint8 sha256[NM_UTILS_CHECKSUM_LENGTH_SHA256]; + guint8 hwaddr_eth[ETH_ALEN]; + guint8 hwaddr_infiniband[INFINIBAND_ALEN]; + NMUuid uuid; + struct _nm_packed { + guint8 hwaddr[ETH_ALEN]; + guint32 timestamp; + } llt_eth; + struct _nm_packed { + guint8 hwaddr[INFINIBAND_ALEN]; + guint32 timestamp; + } llt_infiniband; + } digest; stable_id = _get_stable_id (self, connection, &stable_type); if (!stable_id) g_return_val_if_reached (NULL); - salted_header = htonl (670531087 + stable_type); + if (NM_IN_STRSET (duid, "stable-ll", "stable-llt")) { + /* for stable LL/LLT DUIDs, we still need a hardware address to detect + * the arp-type. Alternatively, we would be able to detect it based on + * other means (e.g. NMDevice type), but instead require the hardware + * address to be present. This is at least consistent with the "ll"/"llt" + * modes above. */ + if (!hwaddr) { + duid_error = "missing link-layer address"; + goto out_fail; + } + if ((arp_type = nm_utils_arp_type_detect_from_hwaddrlen (g_bytes_get_size (hwaddr))) < 0) { + duid_error = "unsupported link-layer address"; + goto out_fail; + } - nm_utils_secret_key_get (&secret_key, &secret_key_len); + if (arp_type == ARPHRD_ETHER) + salted_header = DEFAULT_SALT; + else { + nm_assert (arp_type == ARPHRD_INFINIBAND); + salted_header = 0x42492CEFu + ((guint32) arp_type); + } + } else { + salted_header = DEFAULT_SALT; + arp_type = -1; + } - sum = g_checksum_new (G_CHECKSUM_SHA256); + salted_header = htonl (salted_header + ((guint32) stable_type)); + + nm_utils_host_id_get (&host_id, &host_id_len); + sum = g_checksum_new (G_CHECKSUM_SHA256); g_checksum_update (sum, (const guchar *) &salted_header, sizeof (salted_header)); g_checksum_update (sum, (const guchar *) stable_id, -1); - g_checksum_update (sum, (const guchar *) secret_key, secret_key_len); + g_checksum_update (sum, (const guchar *) host_id, host_id_len); + nm_utils_checksum_get_digest (sum, digest.sha256); - g_checksum_get_digest (sum, sha256_digest, &len); - g_checksum_free (sum); + G_STATIC_ASSERT_EXPR (sizeof (digest) == sizeof (digest.sha256)); if (nm_streq (duid, "stable-ll")) { - duid_out = generate_duid_ll (sha256_digest); + switch (arp_type) { + case ARPHRD_ETHER: + duid_out = generate_duid_ll (arp_type, digest.hwaddr_eth, sizeof (digest.hwaddr_eth)); + break; + case ARPHRD_INFINIBAND: + duid_out = generate_duid_ll (arp_type, digest.hwaddr_infiniband, sizeof (digest.hwaddr_infiniband)); + break; + default: + g_return_val_if_reached (NULL); + } } else if (nm_streq (duid, "stable-llt")) { gint64 time; + guint32 timestamp; #define EPOCH_DATETIME_THREE_YEARS (356 * 24 * 3600 * 3) - /* We want a variable time between the secret_key timestamp and three years + /* We want a variable time between the host_id timestamp and three years * before. Let's compute the time (in seconds) from 0 to 3 years; then we'll - * subtract it from the secret_key timestamp. + * subtract it from the host_id timestamp. */ - time = nm_utils_secret_key_get_timestamp (); - if (!time) { - duid_error = "cannot retrieve the secret key timestamp"; - goto out_fail; - } + time = nm_utils_host_id_get_timestamp_ns () / NM_UTILS_NS_PER_SECOND; + /* don't use too old timestamps. They cannot be expressed in DUID-LLT and * would all be truncated to zero. */ time = NM_MAX (time, EPOCH_DATETIME_200001010000 + EPOCH_DATETIME_THREE_YEARS); - time -= (unaligned_read_be32 (&sha256_digest[ETH_ALEN]) % EPOCH_DATETIME_THREE_YEARS); - duid_out = generate_duid_llt (sha256_digest, time); + switch (arp_type) { + case ARPHRD_ETHER: + timestamp = unaligned_read_be32 (&digest.llt_eth.timestamp); + time -= timestamp % EPOCH_DATETIME_THREE_YEARS; + duid_out = generate_duid_llt (arp_type, digest.llt_eth.hwaddr, sizeof (digest.llt_eth.hwaddr), time); + break; + case ARPHRD_INFINIBAND: + timestamp = unaligned_read_be32 (&digest.llt_infiniband.timestamp); + time -= timestamp % EPOCH_DATETIME_THREE_YEARS; + duid_out = generate_duid_llt (arp_type, digest.llt_infiniband.hwaddr, sizeof (digest.llt_infiniband.hwaddr), time); + break; + default: + g_return_val_if_reached (NULL); + } } else { nm_assert (nm_streq (duid, "stable-uuid")); - duid_out = generate_duid_uuid (sha256_digest, len); + duid_out = generate_duid_uuid (&digest.uuid); } goto out_good; @@ -8410,14 +8470,14 @@ dhcp6_get_duid (NMDevice *self, NMConnection *connection, GBytes *hwaddr, gboole out_fail: nm_assert (!duid_out && duid_error); { - guint8 uuid[16]; + NMUuid uuid; _LOGW (LOGD_IP6 | LOGD_DHCP6, "ipv6.dhcp-duid: failure to generate %s DUID: %s. Fallback to random DUID-UUID.", duid, duid_error); - nm_utils_random_bytes (uuid, sizeof (uuid)); - duid_out = generate_duid_uuid (uuid, sizeof (uuid)); + nm_utils_random_bytes (&uuid, sizeof (uuid)); + duid_out = generate_duid_uuid (&uuid); } out_good: @@ -8432,6 +8492,8 @@ out_good: return duid_out; } +/*****************************************************************************/ + static gboolean dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection) { @@ -8593,6 +8655,7 @@ nm_device_use_ip6_subnet (NMDevice *self, const NMPlatformIP6Address *subnet) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMPlatformIP6Address address = *subnet; + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; if (!applied_config_get_current (&priv->ac_ip6_config)) applied_config_init_new (&priv->ac_ip6_config, self, AF_INET6); @@ -8602,7 +8665,7 @@ nm_device_use_ip6_subnet (NMDevice *self, const NMPlatformIP6Address *subnet) applied_config_add_address (&priv->ac_ip6_config, NM_PLATFORM_IP_ADDRESS_CAST (&address)); _LOGD (LOGD_IP6, "ipv6-pd: using %s address (preferred for %u seconds)", - nm_utils_inet6_ntop (&address.address, NULL), + nm_utils_inet6_ntop (&address.address, sbuf), subnet->preferred); /* This also updates the ndisc if there are actual changes. */ @@ -8694,19 +8757,19 @@ linklocal6_check_complete (NMDevice *self) connection = nm_device_get_applied_connection (self); g_assert (connection); - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); + method = nm_device_get_effective_ip_config_method (self, AF_INET6); _LOGD (LOGD_DEVICE, "linklocal6: waiting for link-local addresses successful, continue with method %s", method); - if ( strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0 - || strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_SHARED) == 0) + if (NM_IN_STRSET (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO, + NM_SETTING_IP6_CONFIG_METHOD_SHARED)) addrconf6_start_with_link_ready (self); - else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) == 0) { + else if (nm_streq (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP)) { if (!dhcp6_start_with_link_ready (self, connection)) { /* Time out IPv6 instead of failing the entire activation */ nm_device_activate_schedule_ip6_config_timeout (self); } - } else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0) + } else if (nm_streq (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) nm_device_activate_schedule_ip6_config_result (self); else g_return_if_fail (FALSE); @@ -8721,6 +8784,7 @@ check_and_add_ipv6ll_addr (NMDevice *self) NMSettingIP6Config *s_ip6 = NULL; GError *error = NULL; const char *addr_type; + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; if (!priv->ipv6ll_handle) return; @@ -8781,7 +8845,8 @@ check_and_add_ipv6ll_addr (NMDevice *self) addr_type = "EUI-64"; } - _LOGD (LOGD_IP6, "linklocal6: generated %s IPv6LL address %s", addr_type, nm_utils_inet6_ntop (&lladdr, NULL)); + _LOGD (LOGD_IP6, "linklocal6: generated %s IPv6LL address %s", + addr_type, nm_utils_inet6_ntop (&lladdr, sbuf)); priv->ipv6ll_has = TRUE; priv->ipv6ll_addr = lladdr; ip_config_merge_and_apply (self, AF_INET6, TRUE); @@ -8791,8 +8856,6 @@ static gboolean linklocal6_start (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMConnection *connection; - const char *method; nm_clear_g_source (&priv->linklocal6_timeout_id); @@ -8802,11 +8865,8 @@ linklocal6_start (NMDevice *self) | NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL)) return TRUE; - connection = nm_device_get_applied_connection (self); - g_assert (connection); - - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); - _LOGD (LOGD_DEVICE, "linklocal6: starting IPv6 with method '%s', but the device has no link-local addresses configured. Wait.", method); + _LOGD (LOGD_DEVICE, "linklocal6: starting IPv6 with method '%s', but the device has no link-local addresses configured. Wait.", + nm_device_get_effective_ip_config_method (self, AF_INET6)); check_and_add_ipv6ll_addr (self); @@ -8854,19 +8914,19 @@ nm_device_get_configured_mtu_from_connection (NMDevice *self, if (setting_type == NM_TYPE_SETTING_WIRED) { if (setting) mtu = nm_setting_wired_get_mtu (NM_SETTING_WIRED (setting)); - global_property_name = "ethernet.mtu"; + global_property_name = NM_CON_DEFAULT ("ethernet.mtu"); } else if (setting_type == NM_TYPE_SETTING_WIRELESS) { if (setting) mtu = nm_setting_wireless_get_mtu (NM_SETTING_WIRELESS (setting)); - global_property_name = "wifi.mtu"; + global_property_name = NM_CON_DEFAULT ("wifi.mtu"); } else if (setting_type == NM_TYPE_SETTING_INFINIBAND) { if (setting) mtu = nm_setting_infiniband_get_mtu (NM_SETTING_INFINIBAND (setting)); - global_property_name = "infiniband.mtu"; + global_property_name = NM_CON_DEFAULT ("infiniband.mtu"); } else if (setting_type == NM_TYPE_SETTING_IP_TUNNEL) { if (setting) mtu = nm_setting_ip_tunnel_get_mtu (NM_SETTING_IP_TUNNEL (setting)); - global_property_name = "ip-tunnel.mtu"; + global_property_name = NM_CON_DEFAULT ("ip-tunnel.mtu"); } else g_return_val_if_reached (0); @@ -8946,7 +9006,7 @@ _commit_mtu (NMDevice *self, const NMIP4Config *config) { guint32 mtu = 0; - /* preferably, get the MTU from explict user-configuration. + /* preferably, get the MTU from explicit user-configuration. * Only if that fails, look at the current @config (which contains * MTUs from DHCP/PPP) or maybe fallback to a device-specific MTU. */ @@ -8981,7 +9041,7 @@ _commit_mtu (NMDevice *self, const NMIP4Config *config) if (mtu_desired && mtu_desired < 1280) { NMSettingIPConfig *s_ip6; - s_ip6 = (NMSettingIPConfig *) nm_device_get_applied_setting (self, NM_TYPE_SETTING_IP6_CONFIG); + s_ip6 = nm_device_get_applied_setting (self, NM_TYPE_SETTING_IP6_CONFIG); if ( s_ip6 && !NM_IN_STRSET (nm_setting_ip_config_get_method (s_ip6), NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) { @@ -9036,7 +9096,7 @@ _commit_mtu (NMDevice *self, const NMIP4Config *config) #define _IP6_MTU_SYS() \ ({ \ if (!ip6_mtu_sysctl.initialized) { \ - ip6_mtu_sysctl.value = nm_device_ipv6_sysctl_get_uint32 (self, "mtu", 0); \ + ip6_mtu_sysctl.value = nm_device_sysctl_ip_conf_get_int_checked (self, AF_INET6, "mtu", 10, 0, G_MAXUINT32, 0); \ ip6_mtu_sysctl.initialized = TRUE; \ } \ ip6_mtu_sysctl.value; \ @@ -9053,7 +9113,10 @@ _commit_mtu (NMDevice *self, const NMIP4Config *config) } if (mtu_desired && mtu_desired != mtu_plat) { - if (nm_platform_link_set_mtu (nm_device_get_platform (self), ifindex, mtu_desired) == NM_PLATFORM_ERROR_CANT_SET_MTU) { + int r; + + r = nm_platform_link_set_mtu (nm_device_get_platform (self), ifindex, mtu_desired); + if (r == -NME_PL_CANT_SET_MTU) { anticipated_failure = TRUE; success = FALSE; _LOGW (LOGD_DEVICE, "mtu: failure to set MTU. %s", @@ -9067,8 +9130,8 @@ _commit_mtu (NMDevice *self, const NMIP4Config *config) } if (ip6_mtu && ip6_mtu != _IP6_MTU_SYS ()) { - if (!nm_device_ipv6_sysctl_set (self, "mtu", - nm_sprintf_buf (sbuf, "%u", (unsigned) ip6_mtu))) { + if (!nm_device_sysctl_ip_conf_set (self, AF_INET6, "mtu", + nm_sprintf_buf (sbuf, "%u", (unsigned) ip6_mtu))) { int errsv = errno; _NMLOG (anticipated_failure && errsv == EINVAL ? LOGL_DEBUG : LOGL_WARN, @@ -9210,7 +9273,7 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in } if (changed & NM_NDISC_CONFIG_HOP_LIMIT) - nm_platform_sysctl_set_ip6_hop_limit_safe (nm_device_get_platform (self), nm_device_get_ip_iface (self), rdata->hop_limit); + nm_platform_sysctl_ip_conf_set_ipv6_hop_limit_safe (nm_device_get_platform (self), nm_device_get_ip_iface (self), rdata->hop_limit); if (changed & NM_NDISC_CONFIG_MTU) { if (priv->ip6_mtu != rdata->mtu) { @@ -9266,7 +9329,7 @@ addrconf6_start_with_link_ready (NMDevice *self) } else { /* Don't abort the addrconf at this point -- if ndisc needs the iid * it will notice this itself. */ - _LOGI (LOGD_IP6, "addrconf6: no interface identifier; IPv6 adddress creation may fail"); + _LOGI (LOGD_IP6, "addrconf6: no interface identifier; IPv6 address creation may fail"); } /* Apply any manual configuration before starting RA */ @@ -9277,14 +9340,14 @@ addrconf6_start_with_link_ready (NMDevice *self) switch (nm_ndisc_get_node_type (priv->ndisc)) { case NM_NDISC_NODE_TYPE_HOST: /* Accepting prefixes from discovered routers. */ - nm_device_ipv6_sysctl_set (self, "accept_ra", "1"); - nm_device_ipv6_sysctl_set (self, "accept_ra_defrtr", "0"); - nm_device_ipv6_sysctl_set (self, "accept_ra_pinfo", "0"); - nm_device_ipv6_sysctl_set (self, "accept_ra_rtr_pref", "0"); + nm_device_sysctl_ip_conf_set (self, AF_INET6, "accept_ra", "1"); + nm_device_sysctl_ip_conf_set (self, AF_INET6, "accept_ra_defrtr", "0"); + nm_device_sysctl_ip_conf_set (self, AF_INET6, "accept_ra_pinfo", "0"); + nm_device_sysctl_ip_conf_set (self, AF_INET6, "accept_ra_rtr_pref", "0"); break; case NM_NDISC_NODE_TYPE_ROUTER: /* We're the router. */ - nm_device_ipv6_sysctl_set (self, "forwarding", "1"); + nm_device_sysctl_ip_conf_set (self, AF_INET6, "forwarding", "1"); nm_device_activate_schedule_ip6_config_result (self); priv->needs_ip6_subnet = TRUE; g_signal_emit (self, signals[IP6_SUBNET_NEEDED], 0); @@ -9304,22 +9367,17 @@ addrconf6_start_with_link_ready (NMDevice *self) ndisc_set_router_config (priv->ndisc, self); nm_ndisc_start (priv->ndisc); + priv->ndisc_started = TRUE; return; } static NMNDiscNodeType ndisc_node_type (NMDevice *self) { - NMConnection *connection; - - connection = nm_device_get_applied_connection (self); - g_assert (connection); - - if (strcmp (nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG), - NM_SETTING_IP4_CONFIG_METHOD_SHARED) == 0) + if (nm_streq (nm_device_get_effective_ip_config_method (self, AF_INET6), + NM_SETTING_IP4_CONFIG_METHOD_SHARED)) return NM_NDISC_NODE_TYPE_ROUTER; - else - return NM_NDISC_NODE_TYPE_HOST; + return NM_NDISC_NODE_TYPE_HOST; } static gboolean @@ -9388,6 +9446,7 @@ addrconf6_cleanup (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + priv->ndisc_started = FALSE; nm_clear_g_signal_handler (priv->ndisc, &priv->ndisc_changed_id); nm_clear_g_signal_handler (priv->ndisc, &priv->ndisc_timeout_id); @@ -9401,34 +9460,36 @@ addrconf6_cleanup (NMDevice *self) /*****************************************************************************/ -static const char *ip6_properties_to_save[] = { - "accept_ra", - "accept_ra_defrtr", - "accept_ra_pinfo", - "accept_ra_rtr_pref", - "forwarding", - "disable_ipv6", - "hop_limit", - "use_tempaddr", -}; - static void save_ip6_properties (NMDevice *self) { + static const char *const ip6_properties_to_save[] = { + "accept_ra", + "accept_ra_defrtr", + "accept_ra_pinfo", + "accept_ra_rtr_pref", + "forwarding", + "disable_ipv6", + "hop_limit", + "use_tempaddr", + }; NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - const char *ifname = nm_device_get_ip_iface (self); + NMPlatform *platform = nm_device_get_platform (self); + const char *ifname; char *value; int i; g_hash_table_remove_all (priv->ip6_saved_properties); - if (!nm_device_get_ip_ifindex (self)) + ifname = nm_device_get_ip_iface_from_platform (self); + if (!ifname) return; for (i = 0; i < G_N_ELEMENTS (ip6_properties_to_save); i++) { - char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; - - value = nm_platform_sysctl_get (nm_device_get_platform (self), NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET6, buf, ifname, ip6_properties_to_save[i]))); + value = nm_platform_sysctl_ip_conf_get (platform, + AF_INET6, + ifname, + ip6_properties_to_save[i]); if (value) { g_hash_table_insert (priv->ip6_saved_properties, (char *) ip6_properties_to_save[i], @@ -9450,7 +9511,7 @@ restore_ip6_properties (NMDevice *self) if ( priv->ipv6ll_handle && nm_streq (key, "disable_ipv6")) continue; - nm_device_ipv6_sysctl_set (self, key, value); + nm_device_sysctl_ip_conf_set (self, AF_INET6, key, value); } } @@ -9459,7 +9520,7 @@ set_disable_ipv6 (NMDevice *self, const char *value) { /* We only touch disable_ipv6 when NM is not managing the IPv6LL address */ if (!NM_DEVICE_GET_PRIVATE (self)->ipv6ll_handle) - nm_device_ipv6_sysctl_set (self, "disable_ipv6", value); + nm_device_sysctl_ip_conf_set (self, AF_INET6, "disable_ipv6", value); } static inline void @@ -9467,7 +9528,6 @@ set_nm_ipv6ll (NMDevice *self, gboolean enable) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); int ifindex = nm_device_get_ip_ifindex (self); - char *value; if (!nm_platform_check_kernel_support (nm_device_get_platform (self), NM_PLATFORM_KERNEL_SUPPORT_USER_IPV6LL)) @@ -9475,32 +9535,34 @@ set_nm_ipv6ll (NMDevice *self, gboolean enable) priv->ipv6ll_handle = enable; if (ifindex > 0) { - NMPlatformError plerr; const char *detail = enable ? "enable" : "disable"; + int r; _LOGD (LOGD_IP6, "will %s userland IPv6LL", detail); - plerr = nm_platform_link_set_user_ipv6ll_enabled (nm_device_get_platform (self), ifindex, enable); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) { - _NMLOG (( plerr == NM_PLATFORM_ERROR_NOT_FOUND - || plerr == NM_PLATFORM_ERROR_OPNOTSUPP) ? LOGL_DEBUG : LOGL_WARN, + r = nm_platform_link_set_user_ipv6ll_enabled (nm_device_get_platform (self), ifindex, enable); + if (r < 0) { + _NMLOG ( NM_IN_SET (r, -NME_PL_NOT_FOUND + -NME_PL_OPNOTSUPP) + ? LOGL_DEBUG + : LOGL_WARN, LOGD_IP6, "failed to %s userspace IPv6LL address handling (%s)", detail, - nm_platform_error_to_string_a (plerr)); + nm_strerror (r)); } if (enable) { - char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; + gs_free char *value = NULL; /* Bounce IPv6 to ensure the kernel stops IPv6LL address generation */ - value = nm_platform_sysctl_get (nm_device_get_platform (self), - NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET6, buf, nm_device_get_ip_iface (self), "disable_ipv6"))); - if (g_strcmp0 (value, "0") == 0) - nm_device_ipv6_sysctl_set (self, "disable_ipv6", "1"); - g_free (value); + value = nm_device_sysctl_ip_conf_get (self, + AF_INET6, + "disable_ipv6"); + if (nm_streq0 (value, "0")) + nm_device_sysctl_ip_conf_set (self, AF_INET6, "disable_ipv6", "1"); /* Ensure IPv6 is enabled */ - nm_device_ipv6_sysctl_set (self, "disable_ipv6", "0"); + nm_device_sysctl_ip_conf_set (self, AF_INET6, "disable_ipv6", "0"); } } @@ -9545,7 +9607,7 @@ _ip6_privacy_get (NMDevice *self) /* 2.) use the default value from the configuration. */ ip6_privacy = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA, - "ipv6.ip6-privacy", + NM_CON_DEFAULT ("ipv6.ip6-privacy"), self, NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR, @@ -9561,24 +9623,26 @@ _ip6_privacy_get (NMDevice *self) * Instead of reading static config files in /etc, just read the current sysctl value. * This works as NM only writes to "/proc/sys/net/ipv6/conf/IFNAME/use_tempaddr", but leaves * the "default" entry untouched. */ - ip6_privacy = nm_platform_sysctl_get_int32 (nm_device_get_platform (self), NMP_SYSCTL_PATHID_ABSOLUTE ("/proc/sys/net/ipv6/conf/default/use_tempaddr"), NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); + ip6_privacy = nm_platform_sysctl_get_int32 (nm_device_get_platform (self), + NMP_SYSCTL_PATHID_ABSOLUTE ("/proc/sys/net/ipv6/conf/default/use_tempaddr"), + NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); return _ip6_privacy_clamp (ip6_privacy); } /*****************************************************************************/ static gboolean -ip6_requires_slaves (NMConnection *connection) +ip6_requires_slaves (NMDevice *self) { const char *method; - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); + method = nm_device_get_effective_ip_config_method (self, AF_INET6); /* SLAAC, DHCP, and Link-Local depend on connectivity (and thus slaves) * to complete addressing. SLAAC and DHCP need a peer to provide a prefix. */ - return strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0 - || strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) == 0; + return NM_IN_STRSET (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO, + NM_SETTING_IP6_CONFIG_METHOD_DHCP); } static NMActStageReturn @@ -9604,7 +9668,7 @@ act_stage3_ip6_config_start (NMDevice *self, return NM_ACT_STAGE_RETURN_IP_WAIT; } - if (nm_device_is_master (self) && ip6_requires_slaves (connection)) { + if (nm_device_is_master (self) && ip6_requires_slaves (self)) { /* If the master has no ready slaves, and depends on slaves for * a successful IPv6 attempt, then postpone IPv6 addressing. */ @@ -9616,9 +9680,8 @@ act_stage3_ip6_config_start (NMDevice *self, } priv->dhcp6.mode = NM_NDISC_DHCP_LEVEL_NONE; - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); - - if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0) { + method = nm_device_get_effective_ip_config_method (self, AF_INET6); + if (nm_streq (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) { if ( !priv->master && !nm_device_sys_iface_state_is_external (self)) { gboolean ipv6ll_handle_old = priv->ipv6ll_handle; @@ -9629,7 +9692,7 @@ act_stage3_ip6_config_start (NMDevice *self, */ set_nm_ipv6ll (self, FALSE); if (ipv6ll_handle_old) - nm_device_ipv6_sysctl_set (self, "disable_ipv6", "1"); + nm_device_sysctl_ip_conf_set (self, AF_INET6, "disable_ipv6", "1"); restore_ip6_properties (self); } return NM_ACT_STAGE_RETURN_IP_DONE; @@ -9664,27 +9727,27 @@ act_stage3_ip6_config_start (NMDevice *self, ip6_privacy = _ip6_privacy_get (self); - if ( strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0 - || strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_SHARED) == 0) { + if (NM_IN_STRSET (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO, + NM_SETTING_IP6_CONFIG_METHOD_SHARED)) { if (!addrconf6_start (self, ip6_privacy)) { /* IPv6 might be disabled; allow IPv4 to proceed */ ret = NM_ACT_STAGE_RETURN_IP_FAIL; } else ret = NM_ACT_STAGE_RETURN_POSTPONE; - } else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0) { + } else if (nm_streq (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) { ret = linklocal6_start (self) ? NM_ACT_STAGE_RETURN_SUCCESS : NM_ACT_STAGE_RETURN_POSTPONE; - } else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) == 0) { + } else if (nm_streq (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP)) { priv->dhcp6.mode = NM_NDISC_DHCP_LEVEL_MANAGED; if (!dhcp6_start (self, TRUE)) { /* IPv6 might be disabled; allow IPv4 to proceed */ ret = NM_ACT_STAGE_RETURN_IP_FAIL; } else ret = NM_ACT_STAGE_RETURN_POSTPONE; - } else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL) == 0) { + } else if (nm_streq (method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) ret = NM_ACT_STAGE_RETURN_SUCCESS; - } else + else _LOGW (LOGD_IP6, "unhandled IPv6 config method '%s'; will fail", method); if ( ret != NM_ACT_STAGE_RETURN_FAILURE @@ -9701,7 +9764,7 @@ act_stage3_ip6_config_start (NMDevice *self, ip6_privacy_str = "2"; break; } - nm_device_ipv6_sysctl_set (self, "use_tempaddr", ip6_privacy_str); + nm_device_sysctl_ip_conf_set (self, AF_INET6, "use_tempaddr", ip6_privacy_str); } return ret; @@ -9814,6 +9877,8 @@ nm_device_activate_stage3_ip6_start (NMDevice *self) static void activate_stage3_ip_config_start (NMDevice *self) { + int ifindex; + _set_ip_state (self, AF_INET, IP_WAIT); _set_ip_state (self, AF_INET6, IP_WAIT); @@ -9823,7 +9888,8 @@ activate_stage3_ip_config_start (NMDevice *self) nm_device_state_changed (self, NM_DEVICE_STATE_IP_CONFIG, NM_DEVICE_STATE_REASON_NONE); /* Device should be up before we can do anything with it */ - if (!nm_platform_link_is_up (nm_device_get_platform (self), nm_device_get_ip_ifindex (self))) + if ( (ifindex = nm_device_get_ip_ifindex (self)) > 0 + && !nm_platform_link_is_up (nm_device_get_platform (self), ifindex)) _LOGW (LOGD_DEVICE, "interface %s not up for IP configuration", nm_device_get_ip_iface (self)); /* IPv4 */ @@ -10108,6 +10174,9 @@ start_sharing (NMDevice *self, NMIP4Config *config, GError **error) const NMPlatformIP4Address *ip4_addr = NULL; const char *ip_iface; GError *local = NULL; + NMConnection *conn; + NMSettingConnection *s_con; + gboolean announce_android_metered; g_return_val_if_fail (config, FALSE); @@ -10129,7 +10198,7 @@ start_sharing (NMDevice *self, NMIP4Config *config, GError **error) return FALSE; req = nm_device_get_act_request (self); - g_assert (req); + g_return_val_if_fail (req, FALSE); netmask = _nm_utils_ip4_prefix_to_netmask (ip4_addr->plen); nm_utils_inet4_ntop (netmask, str_mask); @@ -10150,7 +10219,35 @@ start_sharing (NMDevice *self, NMIP4Config *config, GError **error) nm_act_request_set_shared (req, TRUE); - if (!nm_dnsmasq_manager_start (priv->dnsmasq_manager, config, &local)) { + conn = nm_act_request_get_applied_connection (req); + s_con = nm_connection_get_setting_connection (conn); + + switch (nm_setting_connection_get_metered (s_con)) { + case NM_METERED_YES: + /* honor the metered flag. Note that reapply on the device does not affect + * the metered setting. This is different from other profiles, where the + * metered flag of an activated profile can be changed (reapplied). */ + announce_android_metered = TRUE; + break; + case NM_METERED_UNKNOWN: + /* we pick up the current value and announce it. But again, we cannot update + * the announced setting without restarting dnsmasq. That means, if the default + * route changes w.r.t. being metered, then the shared connection does not get + * updated before reactivating. */ + announce_android_metered = NM_IN_SET (nm_manager_get_metered (nm_manager_get ()), + NM_METERED_YES, + NM_METERED_GUESS_YES); + break; + default: + announce_android_metered = FALSE; + break; + } + + + if (!nm_dnsmasq_manager_start (priv->dnsmasq_manager, + config, + announce_android_metered, + &local)) { g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, "could not start dnsmasq due to %s", local->message); g_error_free (local); @@ -10169,10 +10266,7 @@ arp_cleanup (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (priv->acd.announcing) { - nm_acd_manager_destroy (priv->acd.announcing); - priv->acd.announcing = NULL; - } + nm_clear_pointer (&priv->acd.announcing, nm_acd_manager_free); } void @@ -10207,7 +10301,11 @@ nm_device_arp_announce (NMDevice *self) if (num == 0) return; - priv->acd.announcing = nm_acd_manager_new (nm_device_get_ip_ifindex (self), hw_addr, hw_addr_len); + priv->acd.announcing = nm_acd_manager_new (nm_device_get_ip_ifindex (self), + hw_addr, + hw_addr_len, + NULL, + NULL); for (i = 0; i < num; i++) { NMIPAddress *ip = nm_setting_ip_config_get_address (s_ip4, i); @@ -10228,13 +10326,10 @@ activate_stage5_ip4_config_result (NMDevice *self) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMActRequest *req; const char *method; - NMConnection *connection; int ip_ifindex; req = nm_device_get_act_request (self); g_assert (req); - connection = nm_act_request_get_applied_connection (req); - g_assert (connection); /* Interface must be IFF_UP before IP config can be applied */ ip_ifindex = nm_device_get_ip_ifindex (self); @@ -10251,9 +10346,8 @@ activate_stage5_ip4_config_result (NMDevice *self) } /* Start IPv4 sharing if we need it */ - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); - - if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED) == 0) { + method = nm_device_get_effective_ip_config_method (self, AF_INET); + if (nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) { gs_free_error GError *error = NULL; if (!start_sharing (self, priv->ip_config_4, &error)) { @@ -10399,14 +10493,11 @@ activate_stage5_ip6_config_commit (NMDevice *self) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMActRequest *req; const char *method; - NMConnection *connection; int ip_ifindex; int errsv; req = nm_device_get_act_request (self); g_assert (req); - connection = nm_act_request_get_applied_connection (req); - g_assert (connection); /* Interface must be IFF_UP before IP config can be applied */ ip_ifindex = nm_device_get_ip_ifindex (self); @@ -10438,9 +10529,8 @@ activate_stage5_ip6_config_commit (NMDevice *self) nm_device_remove_pending_action (self, NM_PENDING_ACTION_AUTOCONF6, FALSE); /* Start IPv6 forwarding if we need it */ - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); - - if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_SHARED) == 0) { + method = nm_device_get_effective_ip_config_method (self, AF_INET6); + if (nm_streq (method, NM_SETTING_IP6_CONFIG_METHOD_SHARED)) { if (!nm_platform_sysctl_set (nm_device_get_platform (self), NMP_SYSCTL_PATHID_ABSOLUTE ("/proc/sys/net/ipv6/conf/all/forwarding"), "1")) { errsv = errno; _LOGE (LOGD_SHARING, "share: error enabling IPv6 forwarding: (%d) %s", errsv, strerror (errsv)); @@ -10682,7 +10772,7 @@ _cleanup_ip_pre (NMDevice *self, int addr_family, CleanupType cleanup_type) arp_cleanup (self); dnsmasq_cleanup (self); ipv4ll_cleanup (self); - g_slist_free_full (priv->acd.dad_list, (GDestroyNotify) nm_acd_manager_destroy); + g_slist_free_full (priv->acd.dad_list, (GDestroyNotify) nm_acd_manager_free); priv->acd.dad_list = NULL; } else { g_slist_free_full (priv->dad6_failed_addrs, (GDestroyNotify) nmp_object_unref); @@ -10695,8 +10785,10 @@ _cleanup_ip_pre (NMDevice *self, int addr_family, CleanupType cleanup_type) } gboolean -_nm_device_hash_check_invalid_keys (GHashTable *hash, const char *setting_name, - GError **error, const char **whitelist) +_nm_device_hash_check_invalid_keys (GHashTable *hash, + const char *setting_name, + GError **error, + const char *const*whitelist) { guint found_whitelisted_keys = 0; guint i; @@ -10875,7 +10967,7 @@ nm_device_reactivate_ip6_config (NMDevice *self, if (s_ip6_old && s_ip6_new) { gint64 metric_old, metric_new; - /* See comment in nm_device_reactivate_ip6_config() */ + /* See comment in nm_device_reactivate_ip4_config() */ metric_old = nm_setting_ip_config_get_route_metric (s_ip6_old); metric_new = nm_setting_ip_config_get_route_metric (s_ip6_new); @@ -11512,14 +11604,15 @@ disconnect_cb (NMDevice *self, } static void -_clear_queued_act_request (NMDevicePrivate *priv) +_clear_queued_act_request (NMDevicePrivate *priv, + NMActiveConnectionStateReason active_reason) { if (priv->queued_act_request) { gs_unref_object NMActRequest *ac = NULL; ac = g_steal_pointer (&priv->queued_act_request); nm_active_connection_set_state_fail ((NMActiveConnection *) ac, - NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED, + active_reason, NULL); } } @@ -11661,7 +11754,8 @@ _carrier_wait_check_queued_act_request (NMDevice *self) priv->queued_act_request_is_waiting_for_carrier = FALSE; if (!priv->carrier) { _LOGD (LOGD_DEVICE, "Cancel queued activation request as we have no carrier after timeout"); - _clear_queued_act_request (priv); + _clear_queued_act_request (priv, + NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED); } else { gs_unref_object NMActRequest *queued_req = NULL; @@ -11716,7 +11810,9 @@ _carrier_wait_check_act_request_must_queue (NMDevice *self, NMActRequest *req) } void -nm_device_disconnect_active_connection (NMActiveConnection *active) +nm_device_disconnect_active_connection (NMActiveConnection *active, + NMDeviceStateReason device_reason, + NMActiveConnectionStateReason active_reason) { NMDevice *self; NMDevicePrivate *priv; @@ -11724,38 +11820,59 @@ nm_device_disconnect_active_connection (NMActiveConnection *active) g_return_if_fail (NM_IS_ACTIVE_CONNECTION (active)); self = nm_active_connection_get_device (active); - if (!self) { /* hm, no device? Just fail the active connection. */ - nm_active_connection_set_state_fail (active, - NM_ACTIVE_CONNECTION_STATE_REASON_UNKNOWN, - NULL); - return; + goto do_fail; } priv = NM_DEVICE_GET_PRIVATE (self); if (NM_ACTIVE_CONNECTION (priv->queued_act_request) == active) { - _clear_queued_act_request (priv); + _clear_queued_act_request (priv, active_reason); return; } + if (NM_ACTIVE_CONNECTION (priv->act_request.obj) == active) { if (priv->state < NM_DEVICE_STATE_DEACTIVATING) { nm_device_state_changed (self, NM_DEVICE_STATE_DEACTIVATING, - NM_DEVICE_STATE_REASON_NEW_ACTIVATION); + device_reason); } else { - /* it's going down already... */ + /* @active is the current ac of @self, but it's going down already. + * Nothing to do. */ } + return; } + + /* the active connection references this device, but it's neither the + * queued_act_request nor the current act_request. Just set it to fail... */ +do_fail: + nm_active_connection_set_state_fail (active, + active_reason, + NULL); } void nm_device_queue_activation (NMDevice *self, NMActRequest *req) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMDevicePrivate *priv; gboolean must_queue; + g_return_if_fail (NM_IS_DEVICE (self)); + g_return_if_fail (NM_IS_ACT_REQUEST (req)); + + nm_keep_alive_arm (nm_active_connection_get_keep_alive (NM_ACTIVE_CONNECTION (req))); + + if (nm_active_connection_get_state (NM_ACTIVE_CONNECTION (req)) >= NM_ACTIVE_CONNECTION_STATE_DEACTIVATING) { + /* it's already deactivating. Nothing to do. */ + nm_assert (NM_IN_SET (nm_active_connection_get_device (NM_ACTIVE_CONNECTION (req)), NULL, self)); + return; + } + + nm_assert (self == nm_active_connection_get_device (NM_ACTIVE_CONNECTION (req))); + + priv = NM_DEVICE_GET_PRIVATE (self); + must_queue = _carrier_wait_check_act_request_must_queue (self, req); if ( !priv->act_request.obj @@ -11765,8 +11882,9 @@ nm_device_queue_activation (NMDevice *self, NMActRequest *req) return; } - /* supercede any already-queued request */ - _clear_queued_act_request (priv); + /* supersede any already-queued request */ + _clear_queued_act_request (priv, + NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED); priv->queued_act_request = g_object_ref (req); priv->queued_act_request_is_waiting_for_carrier = must_queue; @@ -11961,11 +12079,6 @@ nm_device_set_ip_config (NMDevice *self, priv->needs_ip6_subnet = FALSE; } - if (IS_IPv4) { - if (!nm_device_sys_iface_state_is_external_or_assume (self)) - ip4_rp_filter_update (self); - } - if (has_changes) { if (IS_IPv4) @@ -12798,6 +12911,7 @@ queued_ip_config_change (NMDevice *self, int addr_family) if ( priv->state > NM_DEVICE_STATE_DISCONNECTED && priv->state < NM_DEVICE_STATE_DEACTIVATING + && priv->ifindex > 0 && !nm_device_sys_iface_state_is_external (self) && (platform = nm_device_get_platform (self)) && nm_platform_link_get (platform, priv->ifindex)) { @@ -12859,13 +12973,6 @@ queued_ip_config_change (NMDevice *self, int addr_family) set_unmanaged_external_down (self, TRUE); - if (IS_IPv4) { - if (!nm_device_sys_iface_state_is_external_or_assume (self)) { - priv->v4_has_shadowed_routes = _v4_has_shadowed_routes_detect (self);; - ip4_rp_filter_update (self); - } - } - return G_SOURCE_REMOVE; } @@ -13018,10 +13125,10 @@ _get_managed_by_flags(NMUnmanagedFlags flags, NMUnmanagedFlags mask, gboolean fo /* @for_user_request can make the result only ~more~ managed. * If the flags already indicate a managed state for a non-user-request, - * then it is also managed for an explict user-request. + * then it is also managed for an explicit user-request. * * Effectively, this check is redundant, as the code below already - * already ensures that. Still, express this invariant explictly here. */ + * already ensures that. Still, express this invariant explicitly here. */ if (_get_managed_by_flags (flags, mask, FALSE)) return TRUE; @@ -13077,7 +13184,7 @@ _get_managed_by_flags(NMUnmanagedFlags flags, NMUnmanagedFlags mask, gboolean fo /** * nm_device_get_managed: * @self: the #NMDevice - * @for_user_request: whether to check the flags for an explict user-request + * @for_user_request: whether to check the flags for an explicit user-request * * Whether the device is unmanaged according to the unmanaged flags. * @@ -13255,7 +13362,7 @@ _set_unmanaged_flags (NMDevice *self, flags, NM_PRINT_FMT_QUOTED (allow_state_transition, ", reason ", - reason_to_string (reason), + reason_to_string_a (reason), transition_state ? ", transition-state" : "", "")); @@ -14030,22 +14137,32 @@ nm_device_remove_pending_action (NMDevice *self, const char *action, gboolean as return FALSE; } -gboolean -nm_device_has_pending_action (NMDevice *self) +const char * +nm_device_has_pending_action_reason (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (priv->pending_actions) - return TRUE; + if (priv->pending_actions) { + + if ( !priv->pending_actions->next + && nm_device_get_state (self) == NM_DEVICE_STATE_ACTIVATED + && nm_streq (priv->pending_actions->data, NM_PENDING_ACTION_CARRIER_WAIT)) { + /* if the device is already in activated state, and the only reason + * why it appears still busy is "carrier-wait", then we are already complete. */ + return NULL; + } + + return priv->pending_actions->data; + } if ( nm_device_is_real (self) && nm_device_get_unmanaged_flags (self, NM_UNMANAGED_PLATFORM_INIT)) { /* as long as the platform link is not yet initialized, we have a pending * action. */ - return TRUE; + return NM_PENDING_ACTION_LINK_INIT; } - return FALSE; + return NULL; } /*****************************************************************************/ @@ -14171,7 +14288,7 @@ _cleanup_generic_post (NMDevice *self, CleanupType cleanup_type) delete_on_deactivate_check_and_schedule (self, nm_device_get_ip_ifindex (self)); } - /* ip_iface should be cleared after flushing all routes and addreses, since + /* ip_iface should be cleared after flushing all routes and addresses, since * those are identified by ip_iface, not by iface (which might be a tty * or ATM device). */ @@ -14195,7 +14312,7 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType clean if (reason == NM_DEVICE_STATE_REASON_NOW_MANAGED) _LOGD (LOGD_DEVICE, "preparing device"); else - _LOGD (LOGD_DEVICE, "deactivating device (reason '%s') [%d]", reason_to_string (reason), reason); + _LOGD (LOGD_DEVICE, "deactivating device (reason '%s') [%d]", reason_to_string_a (reason), reason); /* Save whether or not we tried IPv6 for later */ priv = NM_DEVICE_GET_PRIVATE (self); @@ -14205,8 +14322,8 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType clean /* Turn off kernel IPv6 */ if (cleanup_type == CLEANUP_TYPE_DECONFIGURE) { set_disable_ipv6 (self, "1"); - nm_device_ipv6_sysctl_set (self, "accept_ra", "0"); - nm_device_ipv6_sysctl_set (self, "use_tempaddr", "0"); + nm_device_sysctl_ip_conf_set (self, AF_INET6, "accept_ra", "0"); + nm_device_sysctl_ip_conf_set (self, AF_INET6, "use_tempaddr", "0"); } /* Call device type-specific deactivation */ @@ -14235,6 +14352,7 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType clean /* slave: mark no longer enslaved */ if ( priv->master + && priv->ifindex > 0 && nm_platform_link_get_master (nm_device_get_platform (self), priv->ifindex) <= 0) nm_device_master_release_one_slave (priv->master, self, FALSE, NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); @@ -14271,8 +14389,8 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType clean if (priv->ip6_mtu_initial) { char sbuf[64]; - nm_device_ipv6_sysctl_set (self, "mtu", - nm_sprintf_buf (sbuf, "%u", (unsigned) priv->ip6_mtu_initial)); + nm_device_sysctl_ip_conf_set (self, AF_INET6, "mtu", + nm_sprintf_buf (sbuf, "%u", (unsigned) priv->ip6_mtu_initial)); } } priv->mtu_initial = 0; @@ -14302,7 +14420,7 @@ find_dhcp4_address (NMDevice *self) nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, priv->ip_config_4, &a) { if (a->addr_source == NM_IP_CONFIG_SOURCE_DHCP) - return g_strdup (nm_utils_inet4_ntop (a->address, NULL)); + return nm_utils_inet4_ntop_dup (a->address); } return NULL; } @@ -14327,7 +14445,8 @@ nm_device_spawn_iface_helper (NMDevice *self) return; connection = nm_device_get_applied_connection (self); - g_assert (connection); + + g_return_if_fail (connection); argv = g_ptr_array_sized_new (10); g_ptr_array_set_free_func (argv, g_free); @@ -14361,8 +14480,8 @@ nm_device_spawn_iface_helper (NMDevice *self) dhcp4_address = find_dhcp4_address (self); - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); - if (g_strcmp0 (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0) { + method = nm_device_get_effective_ip_config_method (self, AF_INET); + if (nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) { NMSettingIPConfig *s_ip4; s_ip4 = nm_connection_get_setting_ip4_config (connection); @@ -14384,9 +14503,11 @@ nm_device_spawn_iface_helper (NMDevice *self) if (client_id) { g_ptr_array_add (argv, g_strdup ("--dhcp4-clientid")); g_ptr_array_add (argv, - _nm_utils_bin2str (g_bytes_get_data (client_id, NULL), - g_bytes_get_size (client_id), - FALSE)); + _nm_utils_bin2hexstr_full (g_bytes_get_data (client_id, NULL), + g_bytes_get_size (client_id), + ':', + FALSE, + NULL)); } hostname = nm_dhcp_client_get_hostname (priv->dhcp4.client); @@ -14402,8 +14523,8 @@ nm_device_spawn_iface_helper (NMDevice *self) configured = TRUE; } - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); - if (g_strcmp0 (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0) { + method = nm_utils_get_ip_config_method (connection, AF_INET6); + if (nm_streq (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) { NMSettingIPConfig *s_ip6; NMUtilsIPv6IfaceId iid = NM_UTILS_IPV6_IFACE_ID_INIT; @@ -14424,9 +14545,11 @@ nm_device_spawn_iface_helper (NMDevice *self) if (nm_device_get_ip_iface_identifier (self, &iid, FALSE)) { g_ptr_array_add (argv, g_strdup ("--iid")); g_ptr_array_add (argv, - _nm_utils_bin2str (iid.id_u8, - sizeof (NMUtilsIPv6IfaceId), - FALSE)); + _nm_utils_bin2hexstr_full (iid.id_u8, + sizeof (NMUtilsIPv6IfaceId), + ':', + FALSE, + NULL)); } g_ptr_array_add (argv, g_strdup ("--addr-gen-mode")); @@ -14485,39 +14608,34 @@ ip6_managed_setup (NMDevice *self) { set_nm_ipv6ll (self, TRUE); set_disable_ipv6 (self, "1"); - nm_device_ipv6_sysctl_set (self, "accept_ra_defrtr", "0"); - nm_device_ipv6_sysctl_set (self, "accept_ra_pinfo", "0"); - nm_device_ipv6_sysctl_set (self, "accept_ra_rtr_pref", "0"); - nm_device_ipv6_sysctl_set (self, "use_tempaddr", "0"); - nm_device_ipv6_sysctl_set (self, "forwarding", "0"); + nm_device_sysctl_ip_conf_set (self, AF_INET6, "accept_ra_defrtr", "0"); + nm_device_sysctl_ip_conf_set (self, AF_INET6, "accept_ra_pinfo", "0"); + nm_device_sysctl_ip_conf_set (self, AF_INET6, "accept_ra_rtr_pref", "0"); + nm_device_sysctl_ip_conf_set (self, AF_INET6, "use_tempaddr", "0"); + nm_device_sysctl_ip_conf_set (self, AF_INET6, "forwarding", "0"); } static void deactivate_async_ready (NMDevice *self, - GAsyncResult *res, + GError *error, gpointer user_data) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMDeviceStateReason reason = GPOINTER_TO_UINT (user_data); - GError *error = NULL; - - NM_DEVICE_GET_CLASS (self)->deactivate_async_finish (self, res, &error); - /* If operation cancelled, just return */ - if ( g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) - || (priv->deactivating_cancellable && g_cancellable_is_cancelled (priv->deactivating_cancellable))) { - _LOGW (LOGD_DEVICE, "Deactivation cancelled"); - } else { - /* In every other case, transition to the DISCONNECTED state */ - if (error) { - _LOGW (LOGD_DEVICE, "Deactivation failed: %s", - error->message); - } - nm_device_queue_state (self, NM_DEVICE_STATE_DISCONNECTED, reason); + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + _LOGD (LOGD_DEVICE, "Deactivation cancelled"); + return; } g_clear_object (&priv->deactivating_cancellable); - g_clear_error (&error); + + /* In every other case, transition to the DISCONNECTED state */ + if (error) { + _LOGW (LOGD_DEVICE, "Deactivation failed: %s", + error->message); + } + nm_device_queue_state (self, NM_DEVICE_STATE_DISCONNECTED, reason); } static void @@ -14537,14 +14655,13 @@ deactivate_dispatcher_complete (guint call_id, gpointer user_data) priv->dispatcher.post_state_reason = NM_DEVICE_STATE_REASON_NONE; if (nm_clear_g_cancellable (&priv->deactivating_cancellable)) - g_warn_if_reached (); + nm_assert_not_reached (); - if ( NM_DEVICE_GET_CLASS (self)->deactivate_async - && NM_DEVICE_GET_CLASS (self)->deactivate_async_finish) { + if (NM_DEVICE_GET_CLASS (self)->deactivate_async) { priv->deactivating_cancellable = g_cancellable_new (); NM_DEVICE_GET_CLASS (self)->deactivate_async (self, priv->deactivating_cancellable, - (GAsyncReadyCallback) deactivate_async_ready, + deactivate_async_ready, GUINT_TO_POINTER (reason)); } else nm_device_queue_state (self, NM_DEVICE_STATE_DISCONNECTED, reason); @@ -14561,6 +14678,7 @@ _set_state_full (NMDevice *self, NMActRequest *req; gboolean no_firmware = FALSE; NMSettingsConnection *sett_conn; + NMSettingSriov *s_sriov; g_return_if_fail (NM_IS_DEVICE (self)); @@ -14581,7 +14699,7 @@ _set_state_full (NMDevice *self, _LOGD (LOGD_DEVICE, "state change: %s -> %s (reason '%s', sys-iface-state: '%s'%s)", nm_device_state_to_str (old_state), nm_device_state_to_str (state), - reason_to_string (reason), + reason_to_string_a (reason), _sys_iface_state_to_str (priv->sys_iface_state), priv->firmware_missing ? ", missing firmware" : ""); return; @@ -14590,7 +14708,7 @@ _set_state_full (NMDevice *self, _LOGI (LOGD_DEVICE, "state change: %s -> %s (reason '%s', sys-iface-state: '%s')", nm_device_state_to_str (old_state), nm_device_state_to_str (state), - reason_to_string (reason), + reason_to_string_a (reason), _sys_iface_state_to_str (priv->sys_iface_state)); priv->in_state_changed = TRUE; @@ -14601,8 +14719,8 @@ _set_state_full (NMDevice *self, queued_state_clear (self); dispatcher_cleanup (self); - if (priv->deactivating_cancellable) - g_cancellable_cancel (priv->deactivating_cancellable); + + nm_clear_g_cancellable (&priv->deactivating_cancellable); /* Cache the activation request for the dispatcher */ req = nm_g_object_ref (priv->act_request.obj); @@ -14626,8 +14744,10 @@ _set_state_full (NMDevice *self, if (state <= NM_DEVICE_STATE_UNAVAILABLE) { if (available_connections_del_all (self)) _notify (self, PROP_AVAILABLE_CONNECTIONS); - if (old_state > NM_DEVICE_STATE_UNAVAILABLE) - _clear_queued_act_request (priv); + if (old_state > NM_DEVICE_STATE_UNAVAILABLE) { + _clear_queued_act_request (priv, + NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED); + } } /* Update the available connections list when a device first becomes available */ @@ -14668,6 +14788,7 @@ _set_state_full (NMDevice *self, save_ip6_properties (self); if (priv->sys_iface_state == NM_DEVICE_SYS_IFACE_STATE_MANAGED) ip6_managed_setup (self); + device_init_static_sriov_num_vfs (self); } if (priv->sys_iface_state == NM_DEVICE_SYS_IFACE_STATE_MANAGED) { @@ -14756,6 +14877,12 @@ _set_state_full (NMDevice *self, } break; case NM_DEVICE_STATE_DEACTIVATING: + if ( (s_sriov = nm_device_get_applied_setting (self, NM_TYPE_SETTING_SRIOV)) + && priv->ifindex > 0) { + nm_platform_link_set_sriov_params (nm_device_get_platform (self), + priv->ifindex, 0, NM_TERNARY_TRUE); + } + _cancel_activation (self); /* We cache the ignore_carrier state to not react on config-reloads while the connection @@ -14885,8 +15012,8 @@ _set_state_full (NMDevice *self, if (ip_config_valid (old_state) && !ip_config_valid (state)) notify_ip_properties (self); - concheck_update_interval (self, - state == NM_DEVICE_STATE_ACTIVATED); + concheck_update_interval (self, AF_INET, state == NM_DEVICE_STATE_ACTIVATED); + concheck_update_interval (self, AF_INET6, state == NM_DEVICE_STATE_ACTIVATED); /* Dispose of the cached activation request */ if (req) @@ -14918,7 +15045,7 @@ queued_state_set (gpointer user_data) _LOGD (LOGD_DEVICE, "queue-state[%s, reason:%s, id:%u]: %s", nm_device_state_to_str (priv->queued_state.state), - reason_to_string (priv->queued_state.reason), + reason_to_string_a (priv->queued_state.reason), priv->queued_state.id, "change state"); @@ -14949,23 +15076,23 @@ nm_device_queue_state (NMDevice *self, if (priv->queued_state.id && priv->queued_state.state == state) { _LOGD (LOGD_DEVICE, "queue-state[%s, reason:%s, id:%u]: %s%s%s%s", nm_device_state_to_str (priv->queued_state.state), - reason_to_string (priv->queued_state.reason), + reason_to_string_a (priv->queued_state.reason), priv->queued_state.id, "ignore queuing same state change", NM_PRINT_FMT_QUOTED (priv->queued_state.reason != reason, - " (reason differs: ", reason_to_string (reason), ")", "")); + " (reason differs: ", reason_to_string_a (reason), ")", "")); return; } /* Add pending action for the new state before clearing the queued states, so - * that we don't accidently pop all pending states and reach 'startup complete' */ + * that we don't accidentally pop all pending states and reach 'startup complete' */ nm_device_add_pending_action (self, queued_state_to_string (state), TRUE); /* We should only ever have one delayed state transition at a time */ if (priv->queued_state.id) { _LOGW (LOGD_DEVICE, "queue-state[%s, reason:%s, id:%u]: %s", nm_device_state_to_str (priv->queued_state.state), - reason_to_string (priv->queued_state.reason), + reason_to_string_a (priv->queued_state.reason), priv->queued_state.id, "replace previously queued state change"); nm_clear_g_source (&priv->queued_state.id); @@ -14978,7 +15105,7 @@ nm_device_queue_state (NMDevice *self, _LOGD (LOGD_DEVICE, "queue-state[%s, reason:%s, id:%u]: %s", nm_device_state_to_str (state), - reason_to_string (reason), + reason_to_string_a (reason), priv->queued_state.id, "queue state change"); } @@ -14993,7 +15120,7 @@ queued_state_clear (NMDevice *self) _LOGD (LOGD_DEVICE, "queue-state[%s, reason:%s, id:%u]: %s", nm_device_state_to_str (priv->queued_state.state), - reason_to_string (priv->queued_state.reason), + reason_to_string_a (priv->queued_state.reason), priv->queued_state.id, "clear queued state change"); nm_clear_g_source (&priv->queued_state.id); @@ -15083,7 +15210,7 @@ nm_device_update_hw_address (NMDevice *self) && priv->state < NM_DEVICE_STATE_PREPARE && !nm_device_is_activating (self))) { /* when we get a hw_addr the first time or while the device - * is not activated (with no explict hw address set), always + * is not activated (with no explicit hw address set), always * update our initial hw-address as well. */ nm_device_update_initial_hw_address (self); } @@ -15100,7 +15227,7 @@ nm_device_update_initial_hw_address (NMDevice *self) if ( priv->hw_addr_initial && priv->hw_addr_type != HW_ADDR_TYPE_UNSET) { /* once we have the initial hw address set, we only allow - * update if the currenty type is "unset". */ + * update if the currently type is "unset". */ return; } g_free (priv->hw_addr_initial); @@ -15225,7 +15352,9 @@ _get_cloned_mac_address_setting (NMDevice *self, NMConnection *connection, gbool gs_free char *a = NULL; a = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, - is_wifi ? "wifi.cloned-mac-address" : "ethernet.cloned-mac-address", + is_wifi + ? NM_CON_DEFAULT ("wifi.cloned-mac-address") + : NM_CON_DEFAULT ("ethernet.cloned-mac-address"), self); addr = NM_CLONED_MAC_PRESERVE; @@ -15236,7 +15365,7 @@ _get_cloned_mac_address_setting (NMDevice *self, NMConnection *connection, gbool /* for backward compatibility, read the deprecated wifi.mac-address-randomization setting. */ a = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, - "wifi." NM_SETTING_WIRELESS_MAC_ADDRESS_RANDOMIZATION, + NM_CON_DEFAULT ("wifi.mac-address-randomization"), self); v = _nm_utils_ascii_str_to_int64 (a, 10, NM_SETTING_MAC_RANDOMIZATION_DEFAULT, @@ -15273,7 +15402,9 @@ _get_generate_mac_address_mask_setting (NMDevice *self, NMConnection *connection } a = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, - is_wifi ? "wifi.generate-mac-address-mask" : "ethernet.generate-mac-mac-address-mask", + is_wifi + ? NM_CON_DEFAULT ("wifi.generate-mac-address-mask") + : NM_CON_DEFAULT ("ethernet.generate-mac-address-mask"), self); if (!a) return NULL; @@ -15311,10 +15442,10 @@ _hw_addr_set (NMDevice *self, { NMDevicePrivate *priv; gboolean success = FALSE; - NMPlatformError plerr; + int r; guint8 addr_bytes[NM_UTILS_HWADDR_LEN_MAX]; gsize addr_len; - gboolean was_taken_down; + gboolean was_taken_down = FALSE; gboolean retry_down; nm_assert (NM_IS_DEVICE (self)); @@ -15338,24 +15469,31 @@ _hw_addr_set (NMDevice *self, _LOGT (LOGD_DEVICE, "set-hw-addr: setting MAC address to '%s' (%s, %s)...", addr, operation, detail); - was_taken_down = FALSE; + if (nm_device_get_device_type (self) == NM_DEVICE_TYPE_WIFI) { + /* Always take the device down for Wi-Fi because + * wpa_supplicant needs it to properly detect the MAC + * change. */ + retry_down = FALSE; + was_taken_down = TRUE; + nm_device_take_down (self, FALSE); + } again: - plerr = nm_platform_link_set_address (nm_device_get_platform (self), nm_device_get_ip_ifindex (self), addr_bytes, addr_len); - success = (plerr == NM_PLATFORM_ERROR_SUCCESS); + r = nm_platform_link_set_address (nm_device_get_platform (self), nm_device_get_ip_ifindex (self), addr_bytes, addr_len); + success = (r >= 0); if (!success) { retry_down = !was_taken_down - && plerr != NM_PLATFORM_ERROR_NOT_FOUND + && r != -NME_PL_NOT_FOUND && nm_platform_link_is_up (nm_device_get_platform (self), nm_device_get_ip_ifindex (self)); - _NMLOG ( retry_down - || plerr == NM_PLATFORM_ERROR_NOT_FOUND + _NMLOG ( ( retry_down + || r == -NME_PL_NOT_FOUND) ? LOGL_DEBUG : LOGL_WARN, LOGD_DEVICE, "set-hw-addr: failed to %s MAC address to %s (%s) (%s)%s", operation, addr, detail, - nm_platform_error_to_string_a (plerr), + nm_strerror (r), retry_down ? " (retry with taking down)" : ""); } else { /* MAC address successfully changed; update the current MAC to match */ @@ -15378,7 +15516,7 @@ again: * that is rather complicated and it is not expected that this case * happens for regular drivers. * Note that brcmfmac can block NetworkManager for 500 msec while - * taking down the device. Let's add annother 100 msec to that. + * taking down the device. Let's add another 100 msec to that. * * wait/poll up to 100 msec until it changes. */ @@ -15754,7 +15892,8 @@ nm_device_spec_match_list_full (NMDevice *self, const GSList *specs, int no_matc nm_device_get_driver (self), nm_device_get_driver_version (self), nm_device_get_permanent_hw_address (self), - klass->get_s390_subchannels ? klass->get_s390_subchannels (self) : NULL); + klass->get_s390_subchannels ? klass->get_s390_subchannels (self) : NULL, + nm_dhcp_manager_get_config (nm_dhcp_manager_get ())); switch (m) { case NM_MATCH_SPEC_MATCH: @@ -15779,7 +15918,9 @@ nm_device_get_supplicant_timeout (NMDevice *self) g_return_val_if_fail (NM_IS_DEVICE (self), SUPPLICANT_DEFAULT_TIMEOUT); connection = nm_device_get_applied_connection (self); + g_return_val_if_fail (connection, SUPPLICANT_DEFAULT_TIMEOUT); + s_8021x = nm_connection_get_setting_802_1x (connection); if (s_8021x) { timeout = nm_setting_802_1x_get_auth_timeout (s_8021x); @@ -15788,7 +15929,7 @@ nm_device_get_supplicant_timeout (NMDevice *self) } return nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA, - "802-1x.auth-timeout", + NM_CON_DEFAULT ("802-1x.auth-timeout"), self, 1, G_MAXINT32, @@ -15810,13 +15951,13 @@ nm_device_auth_retries_try_next (NMDevice *self) if (G_UNLIKELY (auth_retries == NM_DEVICE_AUTH_RETRIES_UNSET)) { auth_retries = -1; - s_con = NM_SETTING_CONNECTION (nm_device_get_applied_setting (self, NM_TYPE_SETTING_CONNECTION)); + s_con = nm_device_get_applied_setting (self, NM_TYPE_SETTING_CONNECTION); if (s_con) auth_retries = nm_setting_connection_get_auth_retries (s_con); if (auth_retries == -1) { auth_retries = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA, - "connection.auth-retries", + NM_CON_DEFAULT ("connection.auth-retries"), self, -1, G_MAXINT32, -1); } @@ -15876,7 +16017,8 @@ nm_device_init (NMDevice *self) c_list_init (&self->devices_lst); c_list_init (&priv->slaves); - priv->connectivity_state = NM_CONNECTIVITY_UNKNOWN; + priv->concheck_x[0].state = NM_CONNECTIVITY_UNKNOWN; + priv->concheck_x[1].state = NM_CONNECTIVITY_UNKNOWN; nm_dbus_track_obj_path_init (&priv->parent_device, G_OBJECT (self), obj_properties[PROP_PARENT]); nm_dbus_track_obj_path_init (&priv->act_request, G_OBJECT (self), obj_properties[PROP_ACTIVE_CONNECTION]); @@ -16064,7 +16206,8 @@ dispose (GObject *object) if (nm_clear_g_source (&priv->carrier_wait_id)) nm_device_remove_pending_action (self, NM_PENDING_ACTION_CARRIER_WAIT, FALSE); - _clear_queued_act_request (priv); + _clear_queued_act_request (priv, + NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED); nm_clear_g_source (&priv->device_link_changed_id); nm_clear_g_source (&priv->device_ip_link_changed_id); @@ -16077,12 +16220,13 @@ dispose (GObject *object) g_clear_object (&priv->lldp_listener); } - nm_clear_g_source (&priv->concheck_p_cur_id); + nm_clear_g_source (&priv->concheck_x[0].p_cur_id); + nm_clear_g_source (&priv->concheck_x[1].p_cur_id); G_OBJECT_CLASS (nm_device_parent_class)->dispose (object); if (nm_clear_g_source (&priv->queued_state.id)) { - /* FIXME: we'd expect the queud_state to be alredy cleared and this statement + /* FIXME: we'd expect the queud_state to be already cleared and this statement * not being necessary. Add this check here to hopefully investigate crash * rh#1270247. */ g_return_if_reached (); @@ -16414,8 +16558,11 @@ get_property (GObject *object, guint prop_id, case PROP_RX_BYTES: g_value_set_uint64 (value, priv->stats.rx_bytes); break; - case PROP_CONNECTIVITY: - g_value_set_uint (value, priv->connectivity_state); + case PROP_IP4_CONNECTIVITY: + g_value_set_uint (value, priv->concheck_x[1].state); + break; + case PROP_IP6_CONNECTIVITY: + g_value_set_uint (value, priv->concheck_x[0].state); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -16503,6 +16650,8 @@ static const NMDBusInterfaceInfoExtended interface_info_device = { NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Metered", "u", NM_DEVICE_METERED), NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("LldpNeighbors", "aa{sv}", NM_DEVICE_LLDP_NEIGHBORS), NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Real", "b", NM_DEVICE_REAL), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("Ip4Connectivity", "u", NM_DEVICE_IP4_CONNECTIVITY), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("Ip6Connectivity", "u", NM_DEVICE_IP6_CONNECTIVITY), ), ), }; @@ -16779,8 +16928,13 @@ nm_device_class_init (NMDeviceClass *klass) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_CONNECTIVITY] = - g_param_spec_uint (NM_DEVICE_CONNECTIVITY, "", "", + obj_properties[PROP_IP4_CONNECTIVITY] = + g_param_spec_uint (NM_DEVICE_IP4_CONNECTIVITY, "", "", + NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_UNKNOWN, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + obj_properties[PROP_IP6_CONNECTIVITY] = + g_param_spec_uint (NM_DEVICE_IP6_CONNECTIVITY, "", "", NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_UNKNOWN, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); @@ -16860,12 +17014,12 @@ nm_device_class_init (NMDeviceClass *klass) G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); - - signals[CONNECTIVITY_CHANGED] = - g_signal_new (NM_DEVICE_CONNECTIVITY_CHANGED, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); } + +/* Connection defaults from plugins */ +NM_CON_DEFAULT_NOP ("cdma.mtu"); +NM_CON_DEFAULT_NOP ("gsm.mtu"); +NM_CON_DEFAULT_NOP ("wifi.powersave"); +NM_CON_DEFAULT_NOP ("wifi.wake-on-wlan"); +NM_CON_DEFAULT_NOP ("wifi-sec.pmf"); +NM_CON_DEFAULT_NOP ("wifi-sec.fils"); diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index 8f376b20..c2e3c474 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -54,7 +54,7 @@ typedef enum { static inline NMDeviceStateReason nm_device_state_reason_check (NMDeviceStateReason reason) { - /* the device-state-reason serves mostly informational purpse during a state + /* the device-state-reason serves mostly informational purpose during a state * change. In some cases however, decisions are made based on the reason. * I tend to think that interpreting the state reason to derive some behaviors * is confusing, because the cause and effect are so far apart. @@ -76,6 +76,7 @@ nm_device_state_reason_check (NMDeviceStateReason reason) #define NM_PENDING_ACTION_WAITING_FOR_SUPPLICANT "waiting-for-supplicant" #define NM_PENDING_ACTION_WIFI_SCAN "wifi-scan" #define NM_PENDING_ACTION_WAITING_FOR_COMPANION "waiting-for-companion" +#define NM_PENDING_ACTION_LINK_INIT "link-init" #define NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE "queued-state-change-" #define NM_PENDING_ACTIONPREFIX_ACTIVATION "activation-" @@ -142,13 +143,13 @@ nm_device_state_reason_check (NMDeviceStateReason reason) #define NM_DEVICE_STATE_CHANGED "state-changed" #define NM_DEVICE_LINK_INITIALIZED "link-initialized" #define NM_DEVICE_AUTOCONNECT_ALLOWED "autoconnect-allowed" -#define NM_DEVICE_CONNECTIVITY_CHANGED "connectivity-changed" #define NM_DEVICE_STATISTICS_REFRESH_RATE_MS "refresh-rate-ms" #define NM_DEVICE_STATISTICS_TX_BYTES "tx-bytes" #define NM_DEVICE_STATISTICS_RX_BYTES "rx-bytes" -#define NM_DEVICE_CONNECTIVITY "connectivity" +#define NM_DEVICE_IP4_CONNECTIVITY "ip4-connectivity" +#define NM_DEVICE_IP6_CONNECTIVITY "ip6-connectivity" #define NM_TYPE_DEVICE (nm_device_get_type ()) #define NM_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE, NMDevice)) @@ -221,6 +222,10 @@ typedef enum { /*< skip >*/ NM_DEVICE_CHECK_DEV_AVAILABLE_ALL = (1L << 1) - 1, } NMDeviceCheckDevAvailableFlags; +typedef void (*NMDeviceDeactivateCallback) (NMDevice *self, + GError *error, + gpointer user_data); + typedef struct _NMDeviceClass { NMDBusObjectClass parent; @@ -278,7 +283,7 @@ typedef struct _NMDeviceClass { * Hook for derived classes to be notfied during realize_start_setup() * and perform additional setup. * - * The default implemention of NMDevice calls link_changed(). + * The default implementation of NMDevice calls link_changed(). */ void (*realize_start_notify) (NMDevice *self, const NMPlatformLink *pllink); @@ -327,9 +332,11 @@ typedef struct _NMDeviceClass { guint32 (*get_configured_mtu) (NMDevice *self, NMDeviceMtuSource *out_source); + const char *(*get_auto_ip_config_method) (NMDevice *self, int addr_family); + /* Checks whether the connection is compatible with the device using * only the devices type and characteristics. Does not use any live - * network information like WiFi scan lists etc. + * network information like Wi-Fi scan lists etc. */ gboolean (* check_connection_compatible) (NMDevice *self, NMConnection *connection, @@ -378,11 +385,8 @@ typedef struct _NMDeviceClass { /* Async deactivating (in the DEACTIVATING phase) */ void (* deactivate_async) (NMDevice *self, GCancellable *cancellable, - GAsyncReadyCallback callback, + NMDeviceDeactivateCallback callback, gpointer user_data); - gboolean (* deactivate_async_finish) (NMDevice *self, - GAsyncResult *res, - GError **error); void (* deactivate_reset_hw_addr) (NMDevice *self); @@ -481,6 +485,7 @@ int nm_device_get_ifindex (NMDevice *dev); gboolean nm_device_is_software (NMDevice *dev); gboolean nm_device_is_real (NMDevice *dev); const char * nm_device_get_ip_iface (NMDevice *dev); +const char * nm_device_get_ip_iface_from_platform (NMDevice *dev); int nm_device_get_ip_ifindex (const NMDevice *dev); const char * nm_device_get_driver (NMDevice *dev); const char * nm_device_get_driver_version (NMDevice *dev); @@ -541,7 +546,10 @@ NMConnection * nm_device_get_settings_connection_get_connection (NMDevice *self NMConnection * nm_device_get_applied_connection (NMDevice *dev); gboolean nm_device_has_unmodified_applied_connection (NMDevice *self, NMSettingCompareFlags compare_flags); -NMSetting * nm_device_get_applied_setting (NMDevice *dev, GType setting_type); +NMActivationStateFlags nm_device_get_activation_state_flags (NMDevice *self); + +gpointer /* (NMSetting *) */ nm_device_get_applied_setting (NMDevice *dev, + GType setting_type); void nm_device_removed (NMDevice *self, gboolean unconfigure_ip_config); @@ -617,7 +625,7 @@ void nm_device_copy_ip6_dns_config (NMDevice *self, NMDevice *from_device); * the settings plugin (for example keyfile.unmanaged-devices or ifcfg-rh's * NM_CONTROLLED=no). Although this is user-configuration (provided from * the settings plugins, such as NM_CONTROLLED=no in ifcfg-rh), it cannot - * be overruled and is authorative. That is because users may depend on + * be overruled and is authoritative. That is because users may depend on * dropping a ifcfg-rh file to ensure the device is unmanaged. * @NM_UNMANAGED_USER_CONF: %TRUE when unmanaged by user decision via * the NetworkManager.conf ("unmanaged" in the [device] section). @@ -634,7 +642,7 @@ void nm_device_copy_ip6_dns_config (NMDevice *self, NMDevice *from_device); typedef enum { /*< skip >*/ NM_UNMANAGED_NONE = 0, - /* these flags are authorative. If one of them is set, + /* these flags are authoritative. If one of them is set, * the device cannot be managed. */ NM_UNMANAGED_SLEEPING = (1LL << 0), NM_UNMANAGED_QUITTING = (1LL << 1), @@ -722,6 +730,8 @@ typedef enum { NM_DEVICE_AUTOCONNECT_BLOCKED_WRONG_PIN = (1LL << 1), NM_DEVICE_AUTOCONNECT_BLOCKED_MANUAL_DISCONNECT = (1LL << 2), + NM_DEVICE_AUTOCONNECT_BLOCKED_SIM_MISSING = (1LL << 3), + NM_DEVICE_AUTOCONNECT_BLOCKED_INIT_FAILED = (1LL << 4), _NM_DEVICE_AUTOCONNECT_BLOCKED_LAST, @@ -765,7 +775,9 @@ void nm_device_queue_state (NMDevice *self, gboolean nm_device_get_firmware_missing (NMDevice *self); -void nm_device_disconnect_active_connection (NMActiveConnection *active); +void nm_device_disconnect_active_connection (NMActiveConnection *active, + NMDeviceStateReason device_reason, + NMActiveConnectionStateReason active_reason); void nm_device_queue_activation (NMDevice *device, NMActRequest *req); @@ -773,7 +785,13 @@ gboolean nm_device_supports_vlans (NMDevice *device); gboolean nm_device_add_pending_action (NMDevice *device, const char *action, gboolean assert_not_yet_pending); gboolean nm_device_remove_pending_action (NMDevice *device, const char *action, gboolean assert_is_pending); -gboolean nm_device_has_pending_action (NMDevice *device); +const char *nm_device_has_pending_action_reason (NMDevice *device); + +static inline gboolean +nm_device_has_pending_action (NMDevice *device) +{ + return !!nm_device_has_pending_action_reason (device); +} NMSettingsConnection *nm_device_get_best_connection (NMDevice *device, const char *specific_object, @@ -836,12 +854,13 @@ typedef void (*NMDeviceConnectivityCallback) (NMDevice *self, void nm_device_check_connectivity_update_interval (NMDevice *self); NMDeviceConnectivityHandle *nm_device_check_connectivity (NMDevice *self, + int addr_family, NMDeviceConnectivityCallback callback, gpointer user_data); void nm_device_check_connectivity_cancel (NMDeviceConnectivityHandle *handle); -NMConnectivityState nm_device_get_connectivity_state (NMDevice *self); +NMConnectivityState nm_device_get_connectivity_state (NMDevice *self, int addr_family); typedef struct _NMBtVTableNetworkServer NMBtVTableNetworkServer; struct _NMBtVTableNetworkServer { diff --git a/src/devices/nm-lldp-listener.c b/src/devices/nm-lldp-listener.c index c0484ed3..ae180637 100644 --- a/src/devices/nm-lldp-listener.c +++ b/src/devices/nm-lldp-listener.c @@ -128,7 +128,7 @@ typedef struct { int _ifindex = (self) ? NM_LLDP_LISTENER_GET_PRIVATE (self)->ifindex : 0; \ \ _nm_log (_level, _NMLOG_DOMAIN, 0, \ - nm_platform_link_get_name (NM_PLATFORM_GET, _ifindex), \ + _ifindex > 0 ? nm_platform_link_get_name (NM_PLATFORM_GET, _ifindex) : NULL, \ NULL, \ "%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ _NMLOG_PREFIX_NAME, \ diff --git a/src/devices/ovs/meson.build b/src/devices/ovs/meson.build index 9d637fe9..834b27b0 100644 --- a/src/devices/ovs/meson.build +++ b/src/devices/ovs/meson.build @@ -3,12 +3,12 @@ sources = files( 'nm-device-ovs-interface.c', 'nm-device-ovs-port.c', 'nm-ovsdb.c', - 'nm-ovs-factory.c' + 'nm-ovs-factory.c', ) deps = [ jansson_dep, - nm_dep + nm_dep, ] libnm_device_plugin_ovs = shared_module( @@ -18,7 +18,7 @@ libnm_device_plugin_ovs = shared_module( link_args: ldflags_linker_script_devices, link_depends: linker_script_devices, install: true, - install_dir: nm_plugindir + install_dir: nm_plugindir, ) core_plugins += libnm_device_plugin_ovs diff --git a/src/devices/ovs/nm-device-ovs-interface.c b/src/devices/ovs/nm-device-ovs-interface.c index 2b48fae6..960b8f35 100644 --- a/src/devices/ovs/nm-device-ovs-interface.c +++ b/src/devices/ovs/nm-device-ovs-interface.c @@ -121,12 +121,13 @@ link_changed (NMDevice *device, static gboolean _is_internal_interface (NMDevice *device) { - NMConnection *connection = nm_device_get_applied_connection (device); - NMSettingOvsInterface *s_ovs_iface = nm_connection_get_setting_ovs_interface (connection); + NMSettingOvsInterface *s_ovs_iface; + + s_ovs_iface = nm_device_get_applied_setting (device, NM_TYPE_SETTING_OVS_INTERFACE); g_return_val_if_fail (s_ovs_iface, FALSE); - return strcmp (nm_setting_ovs_interface_get_interface_type (s_ovs_iface), "internal") == 0; + return nm_streq (nm_setting_ovs_interface_get_interface_type (s_ovs_iface), "internal"); } static NMActStageReturn diff --git a/src/devices/ovs/nm-ovsdb.c b/src/devices/ovs/nm-ovsdb.c index b00397cf..48f6b4f3 100644 --- a/src/devices/ovs/nm-ovsdb.c +++ b/src/devices/ovs/nm-ovsdb.c @@ -1126,7 +1126,7 @@ ovsdb_got_msg (NMOvsdb *self, json_t *msg) ovsdb_disconnect (self, FALSE); return; } - /* Cool, we found a corresponsing call. Finish it. */ + /* Cool, we found a corresponding call. Finish it. */ _call_trace ("response", call, msg); diff --git a/src/devices/team/meson.build b/src/devices/team/meson.build index 0f0763bd..3f755012 100644 --- a/src/devices/team/meson.build +++ b/src/devices/team/meson.build @@ -1,12 +1,12 @@ sources = files( 'nm-device-team.c', - 'nm-team-factory.c' + 'nm-team-factory.c', ) deps = [ jansson_dep, libteamdctl_dep, - nm_dep + nm_dep, ] libnm_device_plugin_team = shared_module( @@ -16,7 +16,7 @@ libnm_device_plugin_team = shared_module( link_args: ldflags_linker_script_devices, link_depends: linker_script_devices, install: true, - install_dir: nm_plugindir + install_dir: nm_plugindir, ) core_plugins += libnm_device_plugin_team diff --git a/src/devices/team/nm-device-team.c b/src/devices/team/nm-device-team.c index 899932fd..38a6dd8c 100644 --- a/src/devices/team/nm-device-team.c +++ b/src/devices/team/nm-device-team.c @@ -34,6 +34,7 @@ #include "NetworkManagerUtils.h" #include "devices/nm-device-private.h" #include "platform/nm-platform.h" +#include "nm-config.h" #include "nm-core-internal.h" #include "nm-ip4-config.h" #include "nm-dbus-compat.h" @@ -479,11 +480,25 @@ teamd_child_setup (gpointer user_data) signal (SIGPIPE, SIG_IGN); } +static const char ** +teamd_env (void) +{ + const char **env = g_new0 (const char *, 2); + + if (nm_config_get_is_debug (nm_config_get ())) + env[0] = "TEAM_LOG_OUTPUT=stderr"; + else + env[0] = "TEAM_LOG_OUTPUT=syslog"; + + return env; +} + static gboolean teamd_kill (NMDeviceTeam *self, const char *teamd_binary, GError **error) { gs_unref_ptrarray GPtrArray *argv = NULL; gs_free char *tmp_str = NULL; + gs_free const char **envp = NULL; if (!teamd_binary) { teamd_binary = nm_utils_find_helper ("teamd", NULL, error); @@ -500,8 +515,11 @@ teamd_kill (NMDeviceTeam *self, const char *teamd_binary, GError **error) g_ptr_array_add (argv, (gpointer) nm_device_get_iface (NM_DEVICE (self))); g_ptr_array_add (argv, NULL); + envp = teamd_env (); + _LOGD (LOGD_TEAM, "running: %s", (tmp_str = g_strjoinv (" ", (char **) argv->pdata))); - return g_spawn_sync ("/", (char **) argv->pdata, NULL, 0, teamd_child_setup, NULL, NULL, NULL, NULL, error); + return g_spawn_sync ("/", (char **) argv->pdata, (char **) envp, 0, + teamd_child_setup, NULL, NULL, NULL, NULL, error); } static gboolean @@ -518,6 +536,7 @@ teamd_start (NMDevice *device, NMConnection *connection) nm_auto_free const char *config_free = NULL; NMSettingTeam *s_team; gs_free char *cloned_mac = NULL; + gs_free const char **envp = NULL; s_team = nm_connection_get_setting_team (connection); g_return_val_if_fail (s_team, FALSE); @@ -588,8 +607,10 @@ teamd_start (NMDevice *device, NMConnection *connection) g_ptr_array_add (argv, (gpointer) "-gg"); g_ptr_array_add (argv, NULL); + envp = teamd_env (); + _LOGD (LOGD_TEAM, "running: %s", (tmp_str = g_strjoinv (" ", (char **) argv->pdata))); - if (!g_spawn_async ("/", (char **) argv->pdata, NULL, G_SPAWN_DO_NOT_REAP_CHILD, + if (!g_spawn_async ("/", (char **) argv->pdata, (char **) envp, G_SPAWN_DO_NOT_REAP_CHILD, teamd_child_setup, NULL, &priv->teamd_pid, &error)) { _LOGW (LOGD_TEAM, "Activation: (team) failed to start teamd: %s", error->message); teamd_cleanup (device, TRUE); @@ -626,6 +647,7 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) connection = nm_device_get_applied_connection (device); g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); + s_team = nm_connection_get_setting_team (connection); g_return_val_if_fail (s_team, NM_ACT_STAGE_RETURN_FAILURE); @@ -782,15 +804,15 @@ create_and_realize (NMDevice *device, GError **error) { const char *iface = nm_device_get_iface (device); - NMPlatformError plerr; + int r; - plerr = nm_platform_link_team_add (nm_device_get_platform (device), iface, out_plink); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) { + r = nm_platform_link_team_add (nm_device_get_platform (device), iface, out_plink); + if (r < 0) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to create team master interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string_a (plerr)); + nm_strerror (r)); return FALSE; } diff --git a/src/devices/tests/meson.build b/src/devices/tests/meson.build index 02c61ced..11f35a05 100644 --- a/src/devices/tests/meson.build +++ b/src/devices/tests/meson.build @@ -1,18 +1,18 @@ test_units = [ 'test-acd', - 'test-lldp' + 'test-lldp', ] foreach test_unit: test_units exe = executable( test_unit, test_unit + '.c', - dependencies: test_nm_dep + dependencies: test_nm_dep, ) test( 'devices/' + test_unit, test_script, - args: test_args + [exe.full_path()] + args: test_args + [exe.full_path()], ) endforeach diff --git a/src/devices/tests/test-acd.c b/src/devices/tests/test-acd.c index 8a2852a2..aff71825 100644 --- a/src/devices/tests/test-acd.c +++ b/src/devices/tests/test-acd.c @@ -20,6 +20,8 @@ #include "nm-default.h" +#include "n-acd/src/n-acd.h" + #include "devices/nm-acd-manager.h" #include "platform/tests/test-common.h" @@ -31,6 +33,46 @@ #define ADDR3 0x03030303 #define ADDR4 0x04040404 +/*****************************************************************************/ + +static gboolean +_skip_acd_test_check (void) +{ + NAcd *acd; + NAcdConfig *config; + const guint8 hwaddr[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; + int r; + static int skip = -1; + + if (skip == -1) { + r = n_acd_config_new (&config); + g_assert (r == 0); + + n_acd_config_set_ifindex (config, 1); + n_acd_config_set_transport (config, N_ACD_TRANSPORT_ETHERNET); + n_acd_config_set_mac (config, hwaddr, sizeof (hwaddr)); + + r = n_acd_new (&acd, config); + n_acd_config_free (config); + if (r == 0) + n_acd_unref (acd); + + skip = (r != 0); + } + return skip; +} + +#define _skip_acd_test() \ + ({ \ + gboolean _skip = _skip_acd_test_check (); \ + \ + if (_skip) \ + g_test_skip ("Cannot create NAcd. Running under valgind?"); \ + _skip; \ + }) + +/*****************************************************************************/ + typedef struct { int ifindex0; int ifindex1; @@ -61,20 +103,26 @@ typedef struct { } TestInfo; static void -acd_manager_probe_terminated (NMAcdManager *acd_manager, GMainLoop *loop) +acd_manager_probe_terminated (NMAcdManager *acd_manager, gpointer user_data) { - g_main_loop_quit (loop); + g_main_loop_quit (user_data); } static void test_acd_common (test_fixture *fixture, TestInfo *info) { - gs_unref_object NMAcdManager *manager = NULL; + NMAcdManager *manager; GMainLoop *loop; int i; const guint WAIT_TIME_OPTIMISTIC = 50; guint wait_time; - gulong signal_id; + static const NMAcdCallbacks callbacks = { + .probe_terminated_callback = acd_manager_probe_terminated, + .user_data_destroy = (GDestroyNotify) g_main_loop_unref, + }; + + if (_skip_acd_test ()) + return; /* first, try with a short waittime. We hope that this is long enough * to successfully complete the test. Only if that's not the case, we @@ -83,7 +131,13 @@ test_acd_common (test_fixture *fixture, TestInfo *info) wait_time = WAIT_TIME_OPTIMISTIC; again: - manager = nm_acd_manager_new (fixture->ifindex0, fixture->hwaddr0, fixture->hwaddr0_len); + loop = g_main_loop_new (NULL, FALSE); + + manager = nm_acd_manager_new (fixture->ifindex0, + fixture->hwaddr0, + fixture->hwaddr0_len, + &callbacks, + g_main_loop_ref (loop)); g_assert (manager != NULL); for (i = 0; info->addresses[i]; i++) @@ -94,16 +148,13 @@ again: 24, 0, 3600, 1800, 0, NULL); } - loop = g_main_loop_new (NULL, FALSE); - signal_id = g_signal_connect (manager, NM_ACD_MANAGER_PROBE_TERMINATED, - G_CALLBACK (acd_manager_probe_terminated), loop); g_assert (nm_acd_manager_start_probe (manager, wait_time)); g_assert (nmtst_main_loop_run (loop, 2000)); - g_signal_handler_disconnect (manager, signal_id); g_main_loop_unref (loop); for (i = 0; info->addresses[i]; i++) { gboolean val; + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; val = nm_acd_manager_check_address (manager, info->addresses[i]); if (val == info->expected_result[i]) @@ -113,14 +164,16 @@ again: /* probably we just had a glitch and the system took longer than * expected. Re-verify with a large timeout this time. */ wait_time = 1000; - g_clear_object (&manager); + nm_clear_pointer (&manager, nm_acd_manager_free); goto again; } g_error ("expected check for address #%d (%s) to %s, but it didn't", - i, nm_utils_inet4_ntop (info->addresses[i], NULL), + i, nm_utils_inet4_ntop (info->addresses[i], sbuf), info->expected_result[i] ? "detect no duplicated" : "detect a duplicate"); } + + nm_acd_manager_free (manager); } static void @@ -146,10 +199,17 @@ test_acd_probe_2 (test_fixture *fixture, gconstpointer user_data) static void test_acd_announce (test_fixture *fixture, gconstpointer user_data) { - gs_unref_object NMAcdManager *manager = NULL; + NMAcdManager *manager; GMainLoop *loop; - manager = nm_acd_manager_new (fixture->ifindex0, fixture->hwaddr0, fixture->hwaddr0_len); + if (_skip_acd_test ()) + return; + + manager = nm_acd_manager_new (fixture->ifindex0, + fixture->hwaddr0, + fixture->hwaddr0_len, + NULL, + NULL); g_assert (manager != NULL); g_assert (nm_acd_manager_add_address (manager, ADDR1)); @@ -159,6 +219,8 @@ test_acd_announce (test_fixture *fixture, gconstpointer user_data) nm_acd_manager_announce_addresses (manager); g_assert (!nmtst_main_loop_run (loop, 200)); g_main_loop_unref (loop); + + nm_acd_manager_free (manager); } static void diff --git a/src/devices/wifi/meson.build b/src/devices/wifi/meson.build index 2745040b..dd2be540 100644 --- a/src/devices/wifi/meson.build +++ b/src/devices/wifi/meson.build @@ -1,13 +1,13 @@ common_sources = files( 'nm-wifi-ap.c', - 'nm-wifi-utils.c' + 'nm-wifi-utils.c', ) sources = common_sources + files( 'nm-wifi-factory.c', 'nm-wifi-common.c', 'nm-device-wifi.c', - 'nm-device-olpc-mesh.c' + 'nm-device-olpc-mesh.c', ) if enable_iwd @@ -18,7 +18,7 @@ if enable_iwd endif deps = [ - nm_dep + nm_dep, ] libnm_device_plugin_wifi = shared_module( @@ -28,7 +28,7 @@ libnm_device_plugin_wifi = shared_module( link_args: ldflags_linker_script_devices, link_depends: linker_script_devices, install: true, - install_dir: nm_plugindir + install_dir: nm_plugindir, ) core_plugins += libnm_device_plugin_wifi diff --git a/src/devices/wifi/nm-device-iwd.c b/src/devices/wifi/nm-device-iwd.c index 1d1be742..71b8d151 100644 --- a/src/devices/wifi/nm-device-iwd.c +++ b/src/devices/wifi/nm-device-iwd.c @@ -41,6 +41,7 @@ #include "nm-config.h" #include "nm-iwd-manager.h" #include "nm-dbus-manager.h" +#include "nm-dbus-compat.h" #include "devices/nm-device-logging.h" _LOG_DECLARE_SELF(NMDeviceIwd); @@ -69,6 +70,8 @@ typedef struct { GDBusObject * dbus_obj; GDBusProxy * dbus_device_proxy; GDBusProxy * dbus_station_proxy; + GDBusProxy * dbus_ap_proxy; + GDBusProxy * dbus_adhoc_proxy; CList aps_lst_head; NMWifiAP * current_ap; GCancellable * cancellable; @@ -80,6 +83,7 @@ typedef struct { bool can_connect:1; bool scanning:1; bool scan_requested:1; + bool act_mode_switch:1; gint64 last_scan; } NMDeviceIwdPrivate; @@ -104,7 +108,7 @@ G_DEFINE_TYPE (NMDeviceIwd, nm_device_iwd, NM_TYPE_DEVICE) /*****************************************************************************/ static void schedule_periodic_scan (NMDeviceIwd *self, - NMDeviceState current_state); + gboolean initial_scan); /*****************************************************************************/ @@ -229,7 +233,11 @@ vardict_from_network_type (const char *type) } static void -insert_ap_from_network (GHashTable *aps, const char *path, int16_t signal, uint32_t ap_id) +insert_ap_from_network (NMDeviceIwd *self, + GHashTable *aps, + const char *path, + int16_t signal, + uint32_t ap_id) { gs_unref_object GDBusProxy *network_proxy = NULL; gs_unref_variant GVariant *name_value = NULL, *type_value = NULL; @@ -240,6 +248,11 @@ insert_ap_from_network (GHashTable *aps, const char *path, int16_t signal, uint3 uint8_t bssid[6]; NMWifiAP *ap; + if (g_hash_table_lookup (aps, path)) { + _LOGD (LOGD_WIFI, "Duplicate network at %s", path); + return; + } + network_proxy = nm_iwd_manager_get_dbus_interface (nm_iwd_manager_get (), path, NM_IWD_NETWORK_INTERFACE); @@ -304,7 +317,7 @@ static void get_ordered_networks_cb (GObject *source, GAsyncResult *res, gpointer user_data) { NMDeviceIwd *self = user_data; - NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + NMDeviceIwdPrivate *priv; gs_free_error GError *error = NULL; gs_unref_variant GVariant *variant = NULL; GVariantIter *networks; @@ -314,33 +327,42 @@ get_ordered_networks_cb (GObject *source, GAsyncResult *res, gpointer user_data) gboolean changed = FALSE; GHashTableIter ap_iter; gs_unref_hashtable GHashTable *new_aps = NULL; - /* Depending on whether we're using the Station interface or the Device - * interface for compatibility with IWD <= 0.7, the return signature of - * GetOrderedNetworks will be different. - */ - gboolean compat = priv->dbus_station_proxy == priv->dbus_device_proxy; - const char *return_sig = compat ? "(a(osns))" : "(a(on))"; + gboolean compat; + const char *return_sig; static uint32_t ap_id = 0; - variant = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, - G_VARIANT_TYPE (return_sig), - &error); + variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); if (!variant) { _LOGE (LOGD_WIFI, "Station.GetOrderedNetworks failed: %s", error->message); return; } + priv = NM_DEVICE_IWD_GET_PRIVATE (self); + + /* Depending on whether we're using the Station interface or the Device + * interface for compatibility with IWD <= 0.7, the return signature of + * GetOrderedNetworks will be different. + */ + compat = priv->dbus_station_proxy == priv->dbus_device_proxy; + return_sig = compat ? "(a(osns))" : "(a(on))"; + + if (!g_variant_is_of_type (variant, G_VARIANT_TYPE (return_sig))) { + _LOGE (LOGD_WIFI, "Station.GetOrderedNetworks returned type %s instead of %s", + g_variant_get_type_string (variant), return_sig); + return; + } + new_aps = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_object_unref); g_variant_get (variant, return_sig, &networks); if (compat) { while (g_variant_iter_next (networks, "(&o&sn&s)", &path, &name, &signal, &type)) - insert_ap_from_network (new_aps, path, signal, ap_id++); + insert_ap_from_network (self, new_aps, path, signal, ap_id++); } else { while (g_variant_iter_next (networks, "(&on)", &path, &signal)) - insert_ap_from_network (new_aps, path, signal, ap_id++); + insert_ap_from_network (self, new_aps, path, signal, ap_id++); } g_variant_iter_free (networks); @@ -393,7 +415,7 @@ update_aps (NMDeviceIwd *self) priv->cancellable = g_cancellable_new (); g_dbus_proxy_call (priv->dbus_station_proxy, "GetOrderedNetworks", - g_variant_new ("()"), G_DBUS_CALL_FLAGS_NONE, + NULL, G_DBUS_CALL_FLAGS_NONE, 2000, priv->cancellable, get_ordered_networks_cb, self); } @@ -403,8 +425,8 @@ send_disconnect (NMDeviceIwd *self) { NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); - g_dbus_proxy_call (priv->dbus_station_proxy, "Disconnect", g_variant_new ("()"), - G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); + g_dbus_proxy_call (priv->dbus_station_proxy, "Disconnect", + NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); } static void @@ -431,55 +453,104 @@ cleanup_association_attempt (NMDeviceIwd *self, gboolean disconnect) } static void +reset_mode (NMDeviceIwd *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + + g_dbus_proxy_call (priv->dbus_device_proxy, + DBUS_INTERFACE_PROPERTIES ".Set", + g_variant_new ("(ssv)", NM_IWD_DEVICE_INTERFACE, + "Mode", + g_variant_new_string ("station")), + G_DBUS_CALL_FLAGS_NONE, 2000, + cancellable, + callback, + user_data); +} + +static void deactivate (NMDevice *device) { - cleanup_association_attempt (NM_DEVICE_IWD (device), TRUE); + NMDeviceIwd *self = NM_DEVICE_IWD (device); + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + + if (!priv->dbus_obj) + return; + + cleanup_association_attempt (self, TRUE); + priv->act_mode_switch = FALSE; + + if (!priv->dbus_station_proxy) + reset_mode (self, NULL, NULL, NULL); } -static gboolean -deactivate_async_finish (NMDevice *device, GAsyncResult *res, GError **error) +static void +disconnect_cb (GObject *source, GAsyncResult *res, gpointer user_data) { - NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (NM_DEVICE_IWD (device)); + gs_unref_object NMDeviceIwd *self = NULL; + NMDeviceDeactivateCallback callback; + gpointer callback_user_data; gs_unref_variant GVariant *variant = NULL; + gs_free_error GError *error = NULL; - variant = g_dbus_proxy_call_finish (priv->dbus_station_proxy, res, error); - return variant != NULL; -} + nm_utils_user_data_unpack (user_data, &self, &callback, &callback_user_data); -typedef struct { - NMDeviceIwd *self; - GAsyncReadyCallback callback; - gpointer user_data; -} DeactivateContext; + variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + callback (NM_DEVICE (self), error, callback_user_data); +} static void -disconnect_cb (GObject *source, GAsyncResult *res, gpointer user_data) +disconnect_cb_on_idle (gpointer user_data, + GCancellable *cancellable) { - DeactivateContext *ctx = user_data; + gs_unref_object NMDeviceIwd *self = NULL; + NMDeviceDeactivateCallback callback; + gpointer callback_user_data; + gs_free_error GError *cancelled_error = NULL; - ctx->callback (G_OBJECT (ctx->self), res, ctx->user_data); + nm_utils_user_data_unpack (user_data, &self, &callback, &callback_user_data); - g_object_unref (ctx->self); - g_slice_free (DeactivateContext, ctx); + g_cancellable_set_error_if_cancelled (cancellable, &cancelled_error); + callback (NM_DEVICE (self), cancelled_error, callback_user_data); } static void deactivate_async (NMDevice *device, GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) + NMDeviceDeactivateCallback callback, + gpointer callback_user_data) { NMDeviceIwd *self = NM_DEVICE_IWD (device); NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); - DeactivateContext *ctx; + gpointer user_data; + + nm_assert (G_IS_CANCELLABLE (cancellable)); + nm_assert (callback); - ctx = g_slice_new0 (DeactivateContext); - ctx->self = g_object_ref (self); - ctx->callback = callback; - ctx->user_data = user_data; + user_data = nm_utils_user_data_pack (g_object_ref (self), callback, callback_user_data); - g_dbus_proxy_call (priv->dbus_station_proxy, "Disconnect", g_variant_new ("()"), - G_DBUS_CALL_FLAGS_NONE, -1, cancellable, disconnect_cb, ctx); + if (!priv->dbus_obj) { + nm_utils_invoke_on_idle (disconnect_cb_on_idle, user_data, cancellable); + return; + } + + cleanup_association_attempt (self, FALSE); + priv->act_mode_switch = FALSE; + + if (priv->dbus_station_proxy) { + g_dbus_proxy_call (priv->dbus_station_proxy, + "Disconnect", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + disconnect_cb, + user_data); + } else + reset_mode (self, cancellable, disconnect_cb, user_data); } static gboolean @@ -510,13 +581,37 @@ is_connection_known_network (NMConnection *connection) } static gboolean +is_ap_known_network (NMWifiAP *ap) +{ + GDBusProxy *network_proxy; + gs_unref_variant GVariant *known_network = NULL; + + network_proxy = nm_iwd_manager_get_dbus_interface (nm_iwd_manager_get (), + nm_wifi_ap_get_supplicant_path (ap), + NM_IWD_NETWORK_INTERFACE); + if (!network_proxy) + return FALSE; + + known_network = g_dbus_proxy_get_cached_property (network_proxy, "KnownNetwork"); + g_object_unref (network_proxy); + + return known_network + && g_variant_is_of_type (known_network, G_VARIANT_TYPE_OBJECT_PATH); +} + +static gboolean check_connection_compatible (NMDevice *device, NMConnection *connection, GError **error) { + NMDeviceIwd *self = NM_DEVICE_IWD (device); + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); NMSettingWireless *s_wireless; const char *mac; const char * const *mac_blacklist; int i; const char *perm_hw_addr; + const char *mode; + NMIwdNetworkSecurity security; + gboolean mapped; if (!NM_DEVICE_CLASS (nm_device_iwd_parent_class)->check_connection_compatible (device, connection, error)) return FALSE; @@ -549,23 +644,65 @@ check_connection_compatible (NMDevice *device, NMConnection *connection, GError return FALSE; } - if (!NM_IN_STRSET (nm_setting_wireless_get_mode (s_wireless), - NULL, - NM_SETTING_WIRELESS_MODE_INFRA)) { + /* Hidden SSIDs not supported in any mode (client or AP) */ + if (nm_setting_wireless_get_hidden (s_wireless)) { nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, - "IWD only support infrastructure type profiles"); + "hidden networks not supported by the IWD backend"); return FALSE; } - /* 8021x networks can only be used if they've been provisioned on the IWD side and - * thus are Known Networks. - */ - if (nm_wifi_connection_get_iwd_security (connection, NULL) == NM_IWD_NETWORK_SECURITY_8021X) { - if (!is_connection_known_network (connection)) { + security = nm_wifi_connection_get_iwd_security (connection, &mapped); + if (!mapped) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "connection authentication type not supported by IWD backend"); + return FALSE; + } + + mode = nm_setting_wireless_get_mode (s_wireless); + if (NM_IN_STRSET (mode, NULL, NM_SETTING_WIRELESS_MODE_INFRA)) { + /* 8021x networks can only be used if they've been provisioned on the IWD side and + * thus are Known Networks. + */ + if (security == NM_IWD_NETWORK_SECURITY_8021X) { + if (!is_connection_known_network (connection)) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "802.1x connections must have IWD provisioning files"); + return FALSE; + } + } else if (!NM_IN_SET (security, NM_IWD_NETWORK_SECURITY_NONE, NM_IWD_NETWORK_SECURITY_PSK)) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "IWD backend only supports Open, PSK and 802.1x network " + "authentication in Infrastructure mode"); + return FALSE; + } + } else if (nm_streq (mode, NM_SETTING_WIRELESS_MODE_AP)) { + if (!(priv->capabilities & NM_WIFI_DEVICE_CAP_AP)) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "device does not support Access Point mode"); + return FALSE; + } + + if (!NM_IN_SET (security, NM_IWD_NETWORK_SECURITY_PSK)) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "IWD backend only supports PSK authentication in AP mode"); + return FALSE; + } + } else if (nm_streq (mode, NM_SETTING_WIRELESS_MODE_ADHOC)) { + if (!(priv->capabilities & NM_WIFI_DEVICE_CAP_ADHOC)) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "device does not support Ad-Hoc mode"); + return FALSE; + } + + if (!NM_IN_SET (security, NM_IWD_NETWORK_SECURITY_NONE, NM_IWD_NETWORK_SECURITY_PSK)) { nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, - "802.1x profile is not a known network"); + "IWD backend only supports Open and PSK authentication in Ad-Hoc mode"); return FALSE; } + } else { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "%s type profiles not supported by IWD backend"); + return FALSE; } return TRUE; @@ -582,42 +719,15 @@ check_connection_available (NMDevice *device, NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); NMSettingWireless *s_wifi; const char *mode; + NMWifiAP *ap = NULL; s_wifi = nm_connection_get_setting_wireless (connection); g_return_val_if_fail (s_wifi, FALSE); - /* Only Infrastrusture mode at this time */ - mode = nm_setting_wireless_get_mode (s_wifi); - if (!NM_IN_STRSET (mode, NULL, NM_SETTING_WIRELESS_MODE_INFRA)) { - nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, - "iwd only supports infrastructure mode connections"); - return FALSE; - } - - /* Hidden SSIDs not supported yet */ - if (nm_setting_wireless_get_hidden (s_wifi)) { - nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, - "hidden networks not supported by iwd"); - return FALSE; - } - - /* 8021x networks can only be used if they've been provisioned on the IWD side and - * thus are Known Networks. - */ - if (nm_wifi_connection_get_iwd_security (connection, NULL) == NM_IWD_NETWORK_SECURITY_8021X) { - if (!is_connection_known_network (connection)) { - nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, - "network is not known to iwd"); - return FALSE; - } - } - /* a connection that is available for a certain @specific_object, MUST * also be available in general (without @specific_object). */ if (specific_object) { - NMWifiAP *ap; - ap = nm_wifi_ap_lookup_for_device (NM_DEVICE (self), specific_object); if (!ap) { nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, @@ -629,18 +739,36 @@ check_connection_available (NMDevice *device, "requested access point is not compatible with profile"); return FALSE; } - return TRUE; } + /* AP and Ad-Hoc connections can be activated independent of the scan list */ + mode = nm_setting_wireless_get_mode (s_wifi); + if (NM_IN_STRSET (mode, NM_SETTING_WIRELESS_MODE_AP, NM_SETTING_WIRELESS_MODE_ADHOC)) + return TRUE; + if (NM_FLAGS_HAS (flags, _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_IGNORE_AP)) return TRUE; - if (!nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection)) { + if (!ap) + ap = nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection); + + if (!ap) { nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, "no compatible access point found"); return FALSE; } + /* 8021x networks can only be used if they've been provisioned on the IWD side and + * thus are Known Networks. + */ + if (nm_wifi_connection_get_iwd_security (connection, NULL) == NM_IWD_NETWORK_SECURITY_8021X) { + if (!is_ap_known_network (ap)) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "802.1x network is not an IWD Known Network (missing provisioning file?)"); + return FALSE; + } + } + return TRUE; } @@ -666,15 +794,11 @@ complete_connection (NMDevice *device, mode = s_wifi ? nm_setting_wireless_get_mode (s_wifi) : NULL; - if (mode && !nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_INFRA)) { - g_set_error_literal (error, - NM_DEVICE_ERROR, - NM_DEVICE_ERROR_INVALID_CONNECTION, - "Only Infrastructure mode is supported."); - return FALSE; - } - - if (!specific_object) { + if (nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_AP)) { + if (!nm_setting_verify (NM_SETTING (s_wifi), connection, error)) + return FALSE; + ap = NULL; + } else if (!specific_object) { /* If not given a specific object, we need at minimum an SSID */ if (!s_wifi) { g_set_error_literal (error, @@ -696,11 +820,16 @@ complete_connection (NMDevice *device, /* Find a compatible AP in the scan list */ ap = nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection); if (!ap) { - g_set_error_literal (error, - NM_DEVICE_ERROR, - NM_DEVICE_ERROR_INVALID_CONNECTION, - "No compatible AP in the scan list and hidden SSIDs not supported."); - return FALSE; + if (!nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_ADHOC)) { + g_set_error_literal (error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + "No compatible AP in the scan list and hidden SSIDs not supported."); + return FALSE; + } + + if (!nm_setting_verify (NM_SETTING (s_wifi), connection, error)) + return FALSE; } } else { ap = nm_wifi_ap_lookup_for_device (NM_DEVICE (self), specific_object); @@ -720,7 +849,10 @@ complete_connection (NMDevice *device, nm_connection_add_setting (connection, NM_SETTING (s_wifi)); } - ssid = nm_wifi_ap_get_ssid (ap); + ssid = nm_setting_wireless_get_ssid (s_wifi); + if (!ssid && ap) + ssid = nm_wifi_ap_get_ssid (ap); + if (!ssid) { g_set_error_literal (error, NM_DEVICE_ERROR, @@ -729,11 +861,13 @@ complete_connection (NMDevice *device, return FALSE; } - if (!nm_wifi_ap_complete_connection (ap, - connection, - nm_wifi_utils_is_manf_default_ssid (ssid), - error)) - return FALSE; + if (ap) { + if (!nm_wifi_ap_complete_connection (ap, + connection, + nm_wifi_utils_is_manf_default_ssid (ssid), + error)) + return FALSE; + } ssid_utf8 = _nm_utils_ssid_to_utf8 (ssid); nm_utils_complete_generic (nm_device_get_platform (device), @@ -745,19 +879,6 @@ complete_connection (NMDevice *device, NULL, TRUE); - /* 8021x networks can only be used if they've been provisioned on the IWD side and - * thus are Known Networks. - */ - if (nm_wifi_connection_get_iwd_security (connection, NULL) == NM_IWD_NETWORK_SECURITY_8021X) { - if (!is_connection_known_network (connection)) { - g_set_error_literal (error, - NM_CONNECTION_ERROR, - NM_DEVICE_ERROR_INVALID_CONNECTION, - "This 8021x network has not been provisioned on this machine"); - return FALSE; - } - } - perm_hw_addr = nm_device_get_permanent_hw_address (device); if (perm_hw_addr) { setting_mac = nm_setting_wireless_get_mac_address (s_wifi); @@ -820,8 +941,21 @@ is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags) { NMDeviceIwd *self = NM_DEVICE_IWD (device); NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); - - return priv->enabled && priv->dbus_station_proxy; + NMDeviceState state = nm_device_get_state (device); + + /* Available if either the device is UP and in station mode + * or in AP/Ad-Hoc modes while activating or activated. Device + * may be temporarily DOWN while activating or deactivating and + * we don't want it to be marked unavailable because of this. + * + * For reference: + * We call nm_device_queue_recheck_available whenever + * priv->enabled changes or priv->dbus_station_proxy changes. + */ + return priv->dbus_obj + && priv->enabled + && ( priv->dbus_station_proxy + || (state >= NM_DEVICE_STATE_CONFIG && state <= NM_DEVICE_STATE_DEACTIVATING)); } static gboolean @@ -829,8 +963,7 @@ get_autoconnect_allowed (NMDevice *device) { NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (NM_DEVICE_IWD (device)); - return is_available (device, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE) - && priv->can_connect; + return priv->can_connect; } static gboolean @@ -856,7 +989,9 @@ can_auto_connect (NMDevice *device, s_wifi = nm_connection_get_setting_wireless (connection); g_return_val_if_fail (s_wifi, FALSE); - /* Only Infrastrusture mode */ + /* Don't auto-activate AP or Ad-Hoc connections. + * Note the wpa_supplicant backend has the opposite policy. + */ mode = nm_setting_wireless_get_mode (s_wifi); if (mode && g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_INFRA) != 0) return FALSE; @@ -870,14 +1005,6 @@ can_auto_connect (NMDevice *device, return FALSE; } - /* 8021x networks can only be used if they've been provisioned on the IWD side and - * thus are Known Networks. - */ - if (nm_wifi_connection_get_iwd_security (connection, NULL) == NM_IWD_NETWORK_SECURITY_8021X) { - if (!is_connection_known_network (connection)) - return FALSE; - } - ap = nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection); if (ap) { /* All good; connection is usable */ @@ -908,11 +1035,11 @@ scan_cb (GObject *source, GAsyncResult *res, gpointer user_data) { NMDeviceIwd *self = user_data; NMDeviceIwdPrivate *priv; + gs_unref_variant GVariant *variant = NULL; gs_free_error GError *error = NULL; - if ( !_nm_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, - G_VARIANT_TYPE ("()"), &error) - && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (!variant && nm_utils_error_is_cancelled (error, FALSE)) return; priv = NM_DEVICE_IWD_GET_PRIVATE (self); @@ -925,11 +1052,8 @@ scan_cb (GObject *source, GAsyncResult *res, gpointer user_data) * scheduled when priv->scanning goes back to false. On error, * schedule a retry now. */ - if (error && !priv->scanning) { - NMDeviceState state = nm_device_get_state (NM_DEVICE (self)); - - schedule_periodic_scan (self, state); - } + if (error && !priv->scanning) + schedule_periodic_scan (self, FALSE); } static void @@ -958,9 +1082,7 @@ dbus_request_scan_cb (NMDevice *device, priv = NM_DEVICE_IWD_GET_PRIVATE (self); - if ( !priv->can_scan - || nm_device_get_state (device) < NM_DEVICE_STATE_DISCONNECTED - || nm_device_is_activating (device)) { + if (!priv->can_scan) { g_dbus_method_invocation_return_error_literal (context, NM_DEVICE_ERROR, NM_DEVICE_ERROR_NOT_ALLOWED, @@ -982,8 +1104,7 @@ dbus_request_scan_cb (NMDevice *device, if (!priv->scanning && !priv->scan_requested) { g_dbus_proxy_call (priv->dbus_station_proxy, "Scan", - g_variant_new ("()"), - G_DBUS_CALL_FLAGS_NONE, -1, + NULL, G_DBUS_CALL_FLAGS_NONE, -1, priv->cancellable, scan_cb, self); priv->scan_requested = TRUE; } @@ -999,9 +1120,7 @@ _nm_device_iwd_request_scan (NMDeviceIwd *self, NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); NMDevice *device = NM_DEVICE (self); - if ( !priv->can_scan - || nm_device_get_state (device) < NM_DEVICE_STATE_DISCONNECTED - || nm_device_is_activating (device)) { + if (!priv->can_scan) { g_dbus_method_invocation_return_error_literal (invocation, NM_DEVICE_ERROR, NM_DEVICE_ERROR_NOT_ALLOWED, @@ -1041,8 +1160,6 @@ scanning_prohibited (NMDeviceIwd *self, gboolean periodic) return TRUE; case NM_DEVICE_STATE_DISCONNECTED: case NM_DEVICE_STATE_FAILED: - /* Can always scan when disconnected */ - return FALSE; case NM_DEVICE_STATE_ACTIVATED: break; } @@ -1199,7 +1316,7 @@ wifi_secrets_cb (NMActRequest *req, priv->wifi_secrets_id = NULL; - if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + if (nm_utils_error_is_cancelled (error, FALSE)) { g_dbus_method_invocation_return_error_literal (invocation, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION, "NM secrets request cancelled"); @@ -1274,6 +1391,7 @@ network_connect_cb (GObject *source, GAsyncResult *res, gpointer user_data) NMDeviceIwd *self = user_data; NMDevice *device = NM_DEVICE (self); NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + gs_unref_variant GVariant *variant = NULL; gs_free_error GError *error = NULL; NMConnection *connection; NMSettingWireless *s_wifi; @@ -1282,9 +1400,8 @@ network_connect_cb (GObject *source, GAsyncResult *res, gpointer user_data) NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED; GVariant *value; - if (!_nm_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, - G_VARIANT_TYPE ("()"), - &error)) { + variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (!variant) { gs_free char *dbus_error = NULL; /* Connection failed; radio problems or if the network wasn't @@ -1295,7 +1412,7 @@ network_connect_cb (GObject *source, GAsyncResult *res, gpointer user_data) "Activation: (wifi) Network.Connect failed: %s", error->message); - if (nm_utils_error_is_cancelled (error, TRUE)) + if (nm_utils_error_is_cancelled (error, FALSE)) return; if (!NM_IN_SET (nm_device_get_state (device), NM_DEVICE_STATE_CONFIG, NM_DEVICE_STATE_NEED_AUTH)) @@ -1359,12 +1476,262 @@ failed: } static void +act_failed_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + NMDeviceIwd *self = user_data; + NMDevice *device = NM_DEVICE (self); + gs_unref_variant GVariant *variant = NULL; + gs_free_error GError *error = NULL; + + variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (!variant && nm_utils_error_is_cancelled (error, FALSE)) + return; + + /* Change state to FAILED unless already done by state_changed + * which may have been triggered by the station interface + * appearing on DBus. + */ + if (nm_device_get_state (device) == NM_DEVICE_STATE_CONFIG) + nm_device_queue_state (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); +} + +static void +act_start_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + NMDeviceIwd *self = user_data; + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + NMDevice *device = NM_DEVICE (self); + gs_unref_variant GVariant *variant = NULL; + gs_free_error GError *error = NULL; + NMSettingWireless *s_wireless; + GBytes *ssid; + gs_free char *ssid_utf8 = NULL; + + variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (!variant) { + _LOGE (LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) Network.Connect failed: %s", + error->message); + + if (nm_utils_error_is_cancelled (error, FALSE)) + return; + + if (!NM_IN_SET (nm_device_get_state (device), NM_DEVICE_STATE_CONFIG)) + return; + + goto error; + } + + nm_assert (nm_device_get_state (device) == NM_DEVICE_STATE_CONFIG); + + s_wireless = nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRELESS); + if (!s_wireless) + goto error; + + ssid = nm_setting_wireless_get_ssid (s_wireless); + if (!ssid) + goto error; + + ssid_utf8 = _nm_utils_ssid_to_utf8 (ssid); + + _LOGI (LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) Stage 2 of 5 (Device Configure) successful. Started '%s'.", + ssid_utf8); + + nm_device_activate_schedule_stage3_ip_config_start (device); + return; + +error: + reset_mode (self, priv->cancellable, act_failed_cb, self); +} + +/* Check if we're activating an AP/AdHoc connection and if the target + * DBus interface has appeared already. If so proceed to call Start or + * StartOpen on that interface. + */ +static void act_check_interface (NMDeviceIwd *self) +{ + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + NMDevice *device = NM_DEVICE (self); + gs_free_error GError *error = NULL; + NMSettingWireless *s_wireless; + NMSettingWirelessSecurity *s_wireless_sec; + GDBusProxy *proxy = NULL; + GBytes *ssid; + gs_free char *ssid_utf8 = NULL; + const char *mode; + + if (!priv->act_mode_switch) + return; + + s_wireless = (NMSettingWireless *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRELESS); + + mode = nm_setting_wireless_get_mode (s_wireless); + if (nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_AP)) + proxy = priv->dbus_ap_proxy; + else if (nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_ADHOC)) + proxy = priv->dbus_adhoc_proxy; + + if (!proxy) + return; + + priv->act_mode_switch = FALSE; + + if (!NM_IN_SET (nm_device_get_state (device), NM_DEVICE_STATE_CONFIG)) + return; + + ssid = nm_setting_wireless_get_ssid (s_wireless); + if (!ssid) + goto failed; + + ssid_utf8 = _nm_utils_ssid_to_utf8 (ssid); + + s_wireless_sec = (NMSettingWirelessSecurity *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRELESS_SECURITY); + + if (!s_wireless_sec) { + g_dbus_proxy_call (proxy, "StartOpen", + g_variant_new ("(s)", ssid_utf8), + G_DBUS_CALL_FLAGS_NONE, G_MAXINT, + priv->cancellable, act_start_cb, self); + } else { + const char *psk = nm_setting_wireless_security_get_psk (s_wireless_sec); + + if (!psk) { + _LOGE (LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) No PSK for '%s'.", + ssid_utf8); + goto failed; + } + + g_dbus_proxy_call (proxy, "Start", + g_variant_new ("(ss)", ssid_utf8, psk), + G_DBUS_CALL_FLAGS_NONE, G_MAXINT, + priv->cancellable, act_start_cb, self); + } + + _LOGD (LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) Called Start('%s').", + ssid_utf8); + return; + +failed: + reset_mode (self, priv->cancellable, act_failed_cb, self); +} + +static void +act_set_mode_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + NMDeviceIwd *self = user_data; + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + NMDevice *device = NM_DEVICE (self); + gs_unref_variant GVariant *variant = NULL; + gs_free_error GError *error = NULL; + + variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (!variant) { + _LOGE (LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) Setting Device.Mode failed: %s", + error->message); + + if (nm_utils_error_is_cancelled (error, FALSE)) + return; + + if ( !NM_IN_SET (nm_device_get_state (device), NM_DEVICE_STATE_CONFIG) + || !priv->act_mode_switch) + return; + + priv->act_mode_switch = FALSE; + nm_device_queue_state (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + return; + } + + _LOGD (LOGD_DEVICE | LOGD_WIFI, "Activation: (wifi) IWD Device.Mode set successfully"); + + act_check_interface (self); +} + +static void +act_set_mode (NMDeviceIwd *self) +{ + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + NMDevice *device = NM_DEVICE (self); + const char *iwd_mode; + const char *mode; + NMSettingWireless *s_wireless; + + s_wireless = (NMSettingWireless *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRELESS); + mode = nm_setting_wireless_get_mode (s_wireless); + + /* We need to first set interface mode (Device.Mode) to ap or ad-hoc. + * We can't directly queue a call to the Start/StartOpen method on + * the DBus interface that's going to be created after the property + * set call returns. + */ + iwd_mode = nm_streq (mode, NM_SETTING_WIRELESS_MODE_AP) ? "ap" : "ad-hoc"; + + if (!priv->cancellable) + priv->cancellable = g_cancellable_new (); + + g_dbus_proxy_call (priv->dbus_device_proxy, + DBUS_INTERFACE_PROPERTIES ".Set", + g_variant_new ("(ssv)", NM_IWD_DEVICE_INTERFACE, + "Mode", + g_variant_new ("s", iwd_mode)), + G_DBUS_CALL_FLAGS_NONE, 2000, + priv->cancellable, act_set_mode_cb, self); + priv->act_mode_switch = TRUE; +} + +static void +act_psk_cb (NMActRequest *req, + NMActRequestGetSecretsCallId *call_id, + NMSettingsConnection *s_connection, + GError *error, + gpointer user_data) +{ + NMDeviceIwd *self = user_data; + NMDeviceIwdPrivate *priv; + NMDevice *device; + + if (nm_utils_error_is_cancelled (error, FALSE)) + return; + + priv = NM_DEVICE_IWD_GET_PRIVATE (self); + device = NM_DEVICE (self); + + g_return_if_fail (priv->wifi_secrets_id == call_id); + priv->wifi_secrets_id = NULL; + + g_return_if_fail (req == nm_device_get_act_request (device)); + g_return_if_fail (nm_act_request_get_settings_connection (req) == s_connection); + + if (nm_device_get_state (device) != NM_DEVICE_STATE_NEED_AUTH) + goto secrets_error; + + if (error) { + _LOGW (LOGD_WIFI, "%s", error->message); + goto secrets_error; + } + + _LOGD (LOGD_DEVICE | LOGD_WIFI, "Activation: (wifi) missing PSK request completed"); + + /* Change state back to what it was before NEED_AUTH */ + nm_device_state_changed (device, NM_DEVICE_STATE_CONFIG, NM_DEVICE_STATE_REASON_NONE); + act_set_mode (self); + return; + +secrets_error: + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_NO_SECRETS); + cleanup_association_attempt (self, FALSE); +} + +static void set_powered (NMDeviceIwd *self, gboolean powered) { NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); g_dbus_proxy_call (priv->dbus_device_proxy, - "org.freedesktop.DBus.Properties.Set", + DBUS_INTERFACE_PROPERTIES ".Set", g_variant_new ("(ssv)", NM_IWD_DEVICE_INTERFACE, "Powered", g_variant_new ("b", powered)), @@ -1384,6 +1751,7 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) NMActRequest *req; NMConnection *connection; NMSettingWireless *s_wireless; + const char *mode; const char *ap_path; ret = NM_DEVICE_CLASS (nm_device_iwd_parent_class)->act_stage1_prepare (device, out_failure_reason); @@ -1399,20 +1767,51 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) s_wireless = nm_connection_get_setting_wireless (connection); g_return_val_if_fail (s_wireless, NM_ACT_STAGE_RETURN_FAILURE); + /* AP mode never uses a specific object or existing scanned AP */ + mode = nm_setting_wireless_get_mode (s_wireless); + if (nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_AP)) + goto add_new; + ap_path = nm_active_connection_get_specific_object (NM_ACTIVE_CONNECTION (req)); ap = ap_path ? nm_wifi_ap_lookup_for_device (NM_DEVICE (self), ap_path) : NULL; - if (!ap) { - ap = nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection); - if (!ap) { - NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED); - return NM_ACT_STAGE_RETURN_FAILURE; - } + if (ap) { + set_current_ap (self, ap, TRUE); + return NM_ACT_STAGE_RETURN_SUCCESS; + } + ap = nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection); + if (ap) { nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req), nm_dbus_object_get_path (NM_DBUS_OBJECT (ap))); + set_current_ap (self, ap, TRUE); + return NM_ACT_STAGE_RETURN_SUCCESS; } + if (nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_INFRA)) { + /* Hidden networks not supported at this time */ + return NM_ACT_STAGE_RETURN_FAILURE; + } + +add_new: + /* If the user is trying to connect to an AP that NM doesn't yet know about + * (hidden network or something) or starting a Hotspot, create an fake AP + * from the security settings in the connection. This "fake" AP gets used + * until the real one is found in the scan list (Ad-Hoc or Hidden), or until + * the device is deactivated (Ad-Hoc or Hotspot). + */ + ap = nm_wifi_ap_new_fake_from_connection (connection); + g_return_val_if_fail (ap != NULL, NM_ACT_STAGE_RETURN_FAILURE); + + if (nm_wifi_ap_is_hotspot (ap)) + nm_wifi_ap_set_address (ap, nm_device_get_hw_address (device)); + + g_object_freeze_notify (G_OBJECT (self)); + ap_add_remove (self, TRUE, ap, FALSE); + g_object_thaw_notify (G_OBJECT (self)); set_current_ap (self, ap, FALSE); + nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req), + nm_dbus_object_get_path (NM_DBUS_OBJECT (ap))); + g_object_unref (ap); return NM_ACT_STAGE_RETURN_SUCCESS; } @@ -1423,60 +1822,85 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; NMActRequest *req; - NMWifiAP *ap; NMConnection *connection; - GDBusProxy *network_proxy; + NMSettingWireless *s_wireless; + const char *mode; req = nm_device_get_act_request (device); g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE); - ap = priv->current_ap; - if (!ap) { - NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); - goto out; - } - connection = nm_act_request_get_applied_connection (req); g_assert (connection); - /* 802.1x networks that are not IWD Known Networks will definitely - * fail, for other combinations we will let the Connect call fail - * or ask us for any missing secrets through the Agent. - */ - if ( !is_connection_known_network (connection) - && nm_connection_get_setting_802_1x (connection)) { - _LOGI (LOGD_DEVICE | LOGD_WIFI, - "Activation: (wifi) access point '%s' has 802.1x security, but is not configured.", - nm_connection_get_id (connection)); + s_wireless = nm_connection_get_setting_wireless (connection); + g_return_val_if_fail (s_wireless, NM_ACT_STAGE_RETURN_FAILURE); - NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_NO_SECRETS); - ret = NM_ACT_STAGE_RETURN_FAILURE; - goto out; - } + mode = nm_setting_wireless_get_mode (s_wireless); + if (NM_IN_STRSET (mode, NULL, NM_SETTING_WIRELESS_MODE_INFRA)) { + GDBusProxy *network_proxy; + NMWifiAP *ap = priv->current_ap; - network_proxy = nm_iwd_manager_get_dbus_interface (nm_iwd_manager_get (), - nm_wifi_ap_get_supplicant_path (ap), - NM_IWD_NETWORK_INTERFACE); - if (!network_proxy) { - _LOGE (LOGD_DEVICE | LOGD_WIFI, - "Activation: (wifi) could not get Network interface proxy for %s", - nm_wifi_ap_get_supplicant_path (ap)); - NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); - goto out; - } + if (!ap) { + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + goto out; + } - if (!priv->cancellable) - priv->cancellable = g_cancellable_new (); + /* 802.1x networks that are not IWD Known Networks will definitely + * fail, for other combinations we will let the Connect call fail + * or ask us for any missing secrets through the Agent. + */ + if ( nm_connection_get_setting_802_1x (connection) + && !is_ap_known_network (ap)) { + _LOGI (LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) access point '%s' has 802.1x security but is not configured in IWD.", + nm_connection_get_id (connection)); + + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_NO_SECRETS); + goto out; + } - /* Call Network.Connect. No timeout because IWD already handles - * timeouts. - */ - g_dbus_proxy_call (network_proxy, "Connect", - g_variant_new ("()"), - G_DBUS_CALL_FLAGS_NONE, G_MAXINT, - priv->cancellable, network_connect_cb, self); + network_proxy = nm_iwd_manager_get_dbus_interface (nm_iwd_manager_get (), + nm_wifi_ap_get_supplicant_path (ap), + NM_IWD_NETWORK_INTERFACE); + if (!network_proxy) { + _LOGE (LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) could not get Network interface proxy for %s", + nm_wifi_ap_get_supplicant_path (ap)); + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + goto out; + } - g_object_unref (network_proxy); + if (!priv->cancellable) + priv->cancellable = g_cancellable_new (); + + /* Call Network.Connect. No timeout because IWD already handles + * timeouts. + */ + g_dbus_proxy_call (network_proxy, "Connect", + NULL, G_DBUS_CALL_FLAGS_NONE, G_MAXINT, + priv->cancellable, network_connect_cb, self); + + g_object_unref (network_proxy); + } else if (NM_IN_STRSET (mode, NM_SETTING_WIRELESS_MODE_AP, NM_SETTING_WIRELESS_MODE_ADHOC)) { + NMSettingWirelessSecurity *s_wireless_sec; + + s_wireless_sec = nm_connection_get_setting_wireless_security (connection); + if (s_wireless_sec && !nm_setting_wireless_security_get_psk (s_wireless_sec)) { + /* PSK is missing from the settings, have to request it */ + + wifi_secrets_cancel (self); + + priv->wifi_secrets_id = nm_act_request_get_secrets (req, + TRUE, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION, + "psk", + act_psk_cb, + self); + nm_device_state_changed (device, NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE); + } else + act_set_mode (self); + } /* We'll get stage3 started when the supplicant connects */ ret = NM_ACT_STAGE_RETURN_POSTPONE; @@ -1507,8 +1931,8 @@ periodic_scan_timeout_cb (gpointer user_data) if (priv->scanning || priv->scan_requested) return FALSE; - g_dbus_proxy_call (priv->dbus_station_proxy, "Scan", g_variant_new ("()"), - G_DBUS_CALL_FLAGS_NONE, -1, + g_dbus_proxy_call (priv->dbus_station_proxy, "Scan", + NULL, G_DBUS_CALL_FLAGS_NONE, -1, priv->cancellable, scan_cb, self); priv->scan_requested = TRUE; @@ -1516,18 +1940,37 @@ periodic_scan_timeout_cb (gpointer user_data) } static void -schedule_periodic_scan (NMDeviceIwd *self, NMDeviceState current_state) +schedule_periodic_scan (NMDeviceIwd *self, gboolean initial_scan) { NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + GVariant *value; + gboolean disconnected; guint interval; - if (current_state <= NM_DEVICE_STATE_UNAVAILABLE) + if (!priv->can_scan || priv->scan_requested) return; - if (current_state == NM_DEVICE_STATE_DISCONNECTED) - interval = 10; + value = g_dbus_proxy_get_cached_property (priv->dbus_station_proxy, "State"); + disconnected = nm_streq0 (get_variant_state (value), "disconnected"); + g_variant_unref (value); + + /* Start scan immediately after a disconnect, mode change or + * device UP, otherwise wait a period dependent on the current + * state. + * + * (initial_scan && disconnected) override priv->scanning below + * because of an IWD quirk where a device will often be in the + * autoconnect state and scanning at the time of our initial_scan, + * but our logic will the send it a Disconnect() causeing IWD to + * exit autoconnect and interrupt the ongoing scan, meaning that + * we still want a new scan ASAP. + */ + if (initial_scan && disconnected) + interval = 0; + else if (!priv->periodic_scan_id && !priv->scanning) + interval = disconnected ? 10 : 20; else - interval = 20; + return; nm_clear_g_source (&priv->periodic_scan_id); priv->periodic_scan_id = g_timeout_add_seconds (interval, @@ -1536,6 +1979,19 @@ schedule_periodic_scan (NMDeviceIwd *self, NMDeviceState current_state) } static void +set_can_scan (NMDeviceIwd *self, gboolean can_scan) +{ + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + + if (priv->can_scan == can_scan) + return; + + priv->can_scan = can_scan; + + schedule_periodic_scan (self, TRUE); +} + +static void device_state_changed (NMDevice *device, NMDeviceState new_state, NMDeviceState old_state, @@ -1544,14 +2000,6 @@ device_state_changed (NMDevice *device, NMDeviceIwd *self = NM_DEVICE_IWD (device); NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); - if (new_state <= NM_DEVICE_STATE_UNAVAILABLE) { - remove_all_aps (self); - nm_clear_g_source (&priv->periodic_scan_id); - } else if (old_state <= NM_DEVICE_STATE_UNAVAILABLE) { - update_aps (self); - schedule_periodic_scan (self, new_state); - } - switch (new_state) { case NM_DEVICE_STATE_UNMANAGED: break; @@ -1669,10 +2117,13 @@ get_property (GObject *object, guint prop_id, switch (prop_id) { case PROP_MODE: - if (priv->current_ap) - g_value_set_uint (value, NM_802_11_MODE_INFRA); - else + if (!priv->current_ap) g_value_set_uint (value, NM_802_11_MODE_UNKNOWN); + else if (nm_wifi_ap_is_hotspot (priv->current_ap)) + g_value_set_uint (value, NM_802_11_MODE_AP); + else + g_value_set_uint (value, nm_wifi_ap_get_mode (priv->current_ap)); + break; case PROP_BITRATE: g_value_set_uint (value, 65000); @@ -1702,24 +2153,6 @@ get_property (GObject *object, guint prop_id, } } -static void -set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) -{ - NMDeviceIwd *device = NM_DEVICE_IWD (object); - NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (device); - - switch (prop_id) { - case PROP_CAPABILITIES: - /* construct-only */ - priv->capabilities = g_value_get_uint (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - /*****************************************************************************/ static void @@ -1729,7 +2162,7 @@ state_changed (NMDeviceIwd *self, const char *new_state) NMDevice *device = NM_DEVICE (self); NMDeviceState dev_state = nm_device_get_state (device); gboolean iwd_connection = FALSE; - gboolean can_connect; + gboolean can_connect = priv->can_connect; _LOGI (LOGD_DEVICE | LOGD_WIFI, "new IWD device state is %s", new_state); @@ -1738,7 +2171,9 @@ state_changed (NMDeviceIwd *self, const char *new_state) iwd_connection = TRUE; /* Don't allow scanning while connecting, disconnecting or roaming */ - priv->can_scan = NM_IN_STRSET (new_state, "connected", "disconnected"); + set_can_scan (self, NM_IN_STRSET (new_state, "connected", "disconnected")); + + priv->can_connect = FALSE; if (NM_IN_STRSET (new_state, "connecting", "connected", "roaming")) { /* If we were connecting, do nothing, the confirmation of @@ -1766,7 +2201,7 @@ state_changed (NMDeviceIwd *self, const char *new_state) * callback will have more information on the specific failure * reason. */ - if (dev_state == NM_DEVICE_STATE_CONFIG) + if (NM_IN_SET (dev_state, NM_DEVICE_STATE_CONFIG, NM_DEVICE_STATE_NEED_AUTH)) return; if (iwd_connection) @@ -1781,10 +2216,10 @@ state_changed (NMDeviceIwd *self, const char *new_state) /* Don't allow new connection until iwd exits disconnecting and no * Connect callback is pending. */ - can_connect = NM_IN_STRSET (new_state, "disconnected"); - if (can_connect != priv->can_connect) { - priv->can_connect = can_connect; - nm_device_emit_recheck_auto_activate (device); + if (NM_IN_STRSET (new_state, "disconnected")) { + priv->can_connect = TRUE; + if (!can_connect) + nm_device_emit_recheck_auto_activate (device); } } @@ -1792,7 +2227,6 @@ static void scanning_changed (NMDeviceIwd *self, gboolean new_scanning) { NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); - NMDeviceState state = nm_device_get_state (NM_DEVICE (self)); if (new_scanning == priv->scanning) return; @@ -1805,7 +2239,7 @@ scanning_changed (NMDeviceIwd *self, gboolean new_scanning) update_aps (self); if (!priv->scan_requested) - schedule_periodic_scan (self, state); + schedule_periodic_scan (self, FALSE); } } @@ -1814,40 +2248,81 @@ station_properties_changed (GDBusProxy *proxy, GVariant *changed_properties, GStrv invalidate_properties, gpointer user_data) { NMDeviceIwd *self = user_data; - GVariantIter *iter; - const char *key; - GVariant *value; + const char *new_str; + gboolean new_bool; - g_variant_get (changed_properties, "a{sv}", &iter); - while (g_variant_iter_next (iter, "{&sv}", &key, &value)) { - if (!strcmp (key, "State")) - state_changed (self, get_variant_state (value)); + if (g_variant_lookup (changed_properties, "State", "&s", &new_str)) + state_changed (self, new_str); - if (!strcmp (key, "Scanning")) - scanning_changed (self, get_variant_boolean (value, "Scanning")); + if (g_variant_lookup (changed_properties, "Scanning", "b", &new_bool)) + scanning_changed (self, new_bool); +} - g_variant_unref (value); - } +static void +ap_adhoc_properties_changed (GDBusProxy *proxy, GVariant *changed_properties, + GStrv invalidate_properties, gpointer user_data) +{ + NMDeviceIwd *self = user_data; + gboolean new_bool; - g_variant_iter_free (iter); + if (g_variant_lookup (changed_properties, "Started", "b", &new_bool)) + _LOGI (LOGD_DEVICE | LOGD_WIFI, "IWD AP/AdHoc state is now %s", new_bool ? "Started" : "Stopped"); } static void powered_changed (NMDeviceIwd *self, gboolean new_powered) { NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + GDBusInterface *interface; + GVariant *value; nm_device_queue_recheck_available (NM_DEVICE (self), NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); - if (new_powered) { - GDBusInterface *interface; - GVariant *value; + interface = new_powered ? g_dbus_object_get_interface (priv->dbus_obj, NM_IWD_AP_INTERFACE) : NULL; - if (priv->dbus_station_proxy) - return; + if (priv->dbus_ap_proxy) { + g_signal_handlers_disconnect_by_func (priv->dbus_ap_proxy, + ap_adhoc_properties_changed, self); + g_clear_object (&priv->dbus_ap_proxy); + } + + if (interface) { + priv->dbus_ap_proxy = G_DBUS_PROXY (interface); + g_signal_connect (priv->dbus_ap_proxy, "g-properties-changed", + G_CALLBACK (ap_adhoc_properties_changed), self); + + if (priv->act_mode_switch) + act_check_interface (self); + else + reset_mode (self, NULL, NULL, NULL); + } + + interface = new_powered ? g_dbus_object_get_interface (priv->dbus_obj, NM_IWD_ADHOC_INTERFACE) : NULL; + + if (priv->dbus_adhoc_proxy) { + g_signal_handlers_disconnect_by_func (priv->dbus_adhoc_proxy, + ap_adhoc_properties_changed, self); + g_clear_object (&priv->dbus_adhoc_proxy); + } + + if (interface) { + priv->dbus_adhoc_proxy = G_DBUS_PROXY (interface); + g_signal_connect (priv->dbus_adhoc_proxy, "g-properties-changed", + G_CALLBACK (ap_adhoc_properties_changed), self); + if (priv->act_mode_switch) + act_check_interface (self); + else + reset_mode (self, NULL, NULL, NULL); + } + + /* We expect one of the three interfaces to always be present when + * device is Powered so if AP and AdHoc are not present we should + * be in station mode. + */ + if (new_powered && !priv->dbus_ap_proxy && !priv->dbus_adhoc_proxy) { interface = g_dbus_object_get_interface (priv->dbus_obj, NM_IWD_STATION_INTERFACE); if (!interface) { /* No Station interface on the device object. Check if the @@ -1858,18 +2333,30 @@ powered_changed (NMDeviceIwd *self, gboolean new_powered) * priv->dbus_station_proxy both point at the Device interface. */ value = g_dbus_proxy_get_cached_property (priv->dbus_device_proxy, "State"); - if (!value) { + if (value) { + g_variant_unref (value); + interface = g_object_ref (priv->dbus_device_proxy); + } else { _LOGE (LOGD_WIFI, "Interface %s not found on obj %s", NM_IWD_STATION_INTERFACE, g_dbus_object_get_object_path (priv->dbus_obj)); - return; + interface = NULL; } - g_variant_unref (value); - interface = g_object_ref (priv->dbus_device_proxy); } + } else + interface = NULL; + + if (priv->dbus_station_proxy) { + g_signal_handlers_disconnect_by_func (priv->dbus_station_proxy, + station_properties_changed, self); + g_clear_object (&priv->dbus_station_proxy); + } + if (interface) { priv->dbus_station_proxy = G_DBUS_PROXY (interface); + g_signal_connect (priv->dbus_station_proxy, "g-properties-changed", + G_CALLBACK (station_properties_changed), self); value = g_dbus_proxy_get_cached_property (priv->dbus_station_proxy, "Scanning"); priv->scanning = get_variant_boolean (value, "Scanning"); @@ -1879,27 +2366,15 @@ powered_changed (NMDeviceIwd *self, gboolean new_powered) state_changed (self, get_variant_state (value)); g_variant_unref (value); - g_signal_connect (priv->dbus_station_proxy, "g-properties-changed", - G_CALLBACK (station_properties_changed), self); - - /* Call Disconnect to make sure IWD's autoconnect is disabled. - * Autoconnect is the default state after device is brought UP. - */ - if (priv->enabled) - send_disconnect (self); + update_aps (self); } else { - if (!priv->dbus_station_proxy) - return; - - g_signal_handlers_disconnect_by_func (priv->dbus_station_proxy, - station_properties_changed, self); - g_clear_object (&priv->dbus_station_proxy); - - priv->can_scan = FALSE; + set_can_scan (self, FALSE); + nm_clear_g_source (&priv->periodic_scan_id); priv->scanning = FALSE; priv->scan_requested = FALSE; priv->can_connect = FALSE; cleanup_association_attempt (self, FALSE); + remove_all_aps (self); } } @@ -1908,19 +2383,10 @@ device_properties_changed (GDBusProxy *proxy, GVariant *changed_properties, GStrv invalidate_properties, gpointer user_data) { NMDeviceIwd *self = user_data; - GVariantIter *iter; - const char *key; - GVariant *value; - - g_variant_get (changed_properties, "a{sv}", &iter); - while (g_variant_iter_next (iter, "{&sv}", &key, &value)) { - if (!strcmp (key, "Powered")) - powered_changed (self, get_variant_boolean (value, "Powered")); + gboolean new_bool; - g_variant_unref (value); - } - - g_variant_iter_free (iter); + if (g_variant_lookup (changed_properties, "Powered", "b", &new_bool)) + powered_changed (self, new_bool); } void @@ -1928,24 +2394,24 @@ nm_device_iwd_set_dbus_object (NMDeviceIwd *self, GDBusObject *object) { NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); GDBusInterface *interface; - GVariant *value; + gs_unref_variant GVariant *value = NULL; + gs_unref_object GDBusProxy *adapter_proxy = NULL; + GVariantIter *iter; + const char *mode; gboolean powered; + NMDeviceWifiCapabilities capabilities; if (!nm_g_object_ref_set ((GObject **) &priv->dbus_obj, (GObject *) object)) return; - if (priv->enabled && priv->dbus_station_proxy) { - nm_device_queue_recheck_available (NM_DEVICE (self), - NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE, - NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); - } - if (priv->dbus_device_proxy) { g_signal_handlers_disconnect_by_func (priv->dbus_device_proxy, device_properties_changed, self); g_clear_object (&priv->dbus_device_proxy); powered_changed (self, FALSE); + + priv->act_mode_switch = FALSE; } if (!object) @@ -1965,14 +2431,63 @@ nm_device_iwd_set_dbus_object (NMDeviceIwd *self, GDBusObject *object) g_signal_connect (priv->dbus_device_proxy, "g-properties-changed", G_CALLBACK (device_properties_changed), self); + /* Parse list of interface modes supported by adapter (wiphy) */ + + value = g_dbus_proxy_get_cached_property (priv->dbus_device_proxy, "Adapter"); + if (!value || !g_variant_is_of_type (value, G_VARIANT_TYPE_OBJECT_PATH)) { + nm_log_warn (LOGD_DEVICE | LOGD_WIFI, + "Adapter property not cached or not an object path"); + goto error; + } + + adapter_proxy = nm_iwd_manager_get_dbus_interface (nm_iwd_manager_get (), + g_variant_get_string (value, NULL), + NM_IWD_WIPHY_INTERFACE); + if (!adapter_proxy) { + nm_log_warn (LOGD_DEVICE | LOGD_WIFI, + "Can't get DBus proxy for IWD Adapter for IWD Device"); + goto error; + } + + g_variant_unref (value); + value = g_dbus_proxy_get_cached_property (adapter_proxy, "SupportedModes"); + if (!value || !g_variant_is_of_type (value, G_VARIANT_TYPE_STRING_ARRAY)) { + nm_log_warn (LOGD_DEVICE | LOGD_WIFI, + "SupportedModes property not cached or not a string array"); + goto error; + } + + capabilities = NM_WIFI_DEVICE_CAP_CIPHER_CCMP | NM_WIFI_DEVICE_CAP_RSN; + + g_variant_get (value, "as", &iter); + while (g_variant_iter_next (iter, "&s", &mode)) { + if (nm_streq (mode, "ap")) + capabilities |= NM_WIFI_DEVICE_CAP_AP; + else if (nm_streq (mode, "ad-hoc")) + capabilities |= NM_WIFI_DEVICE_CAP_ADHOC; + } + g_variant_iter_free (iter); + + if (priv->capabilities != capabilities) { + priv->capabilities = capabilities; + _notify (self, PROP_CAPABILITIES); + } + + g_variant_unref (value); value = g_dbus_proxy_get_cached_property (priv->dbus_device_proxy, "Powered"); powered = get_variant_boolean (value, "Powered"); - g_variant_unref (value); if (powered != priv->enabled) set_powered (self, priv->enabled); else if (powered) powered_changed (self, TRUE); + + return; + +error: + g_signal_handlers_disconnect_by_func (priv->dbus_device_proxy, + device_properties_changed, self); + g_clear_object (&priv->dbus_device_proxy); } gboolean @@ -2041,15 +2556,14 @@ nm_device_iwd_init (NMDeviceIwd *self) } NMDevice * -nm_device_iwd_new (const char *iface, NMDeviceWifiCapabilities capabilities) +nm_device_iwd_new (const char *iface) { return g_object_new (NM_TYPE_DEVICE_IWD, NM_DEVICE_IFACE, iface, - NM_DEVICE_TYPE_DESC, "802.11 WiFi", + NM_DEVICE_TYPE_DESC, "802.11 Wi-Fi", NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_WIFI, NM_DEVICE_LINK_TYPE, NM_LINK_TYPE_WIFI, NM_DEVICE_RFKILL_TYPE, RFKILL_TYPE_WLAN, - NM_DEVICE_IWD_CAPABILITIES, (guint) capabilities, NULL); } @@ -2067,6 +2581,8 @@ dispose (GObject *object) g_clear_object (&priv->dbus_device_proxy); g_clear_object (&priv->dbus_station_proxy); + g_clear_object (&priv->dbus_ap_proxy); + g_clear_object (&priv->dbus_adhoc_proxy); g_clear_object (&priv->dbus_obj); remove_all_aps (self); @@ -2084,7 +2600,6 @@ nm_device_iwd_class_init (NMDeviceIwdClass *klass) NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); object_class->get_property = get_property; - object_class->set_property = set_property; object_class->dispose = dispose; dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&nm_interface_info_device_wireless); @@ -2108,7 +2623,6 @@ nm_device_iwd_class_init (NMDeviceIwdClass *klass) device_class->get_configured_mtu = get_configured_mtu; device_class->deactivate = deactivate; device_class->deactivate_async = deactivate_async; - device_class->deactivate_async_finish = deactivate_async_finish; device_class->can_reapply_change = can_reapply_change; device_class->state_changed = device_state_changed; @@ -2144,8 +2658,7 @@ nm_device_iwd_class_init (NMDeviceIwdClass *klass) obj_properties[PROP_CAPABILITIES] = g_param_spec_uint (NM_DEVICE_IWD_CAPABILITIES, "", "", 0, G_MAXUINT32, NM_WIFI_DEVICE_CAP_NONE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_properties[PROP_SCANNING] = diff --git a/src/devices/wifi/nm-device-iwd.h b/src/devices/wifi/nm-device-iwd.h index 825123b1..4a2bd31e 100644 --- a/src/devices/wifi/nm-device-iwd.h +++ b/src/devices/wifi/nm-device-iwd.h @@ -47,7 +47,7 @@ typedef struct _NMDeviceIwdClass NMDeviceIwdClass; GType nm_device_iwd_get_type (void); -NMDevice *nm_device_iwd_new (const char *iface, NMDeviceWifiCapabilities capabilities); +NMDevice *nm_device_iwd_new (const char *iface); void nm_device_iwd_set_dbus_object (NMDeviceIwd *device, GDBusObject *object); diff --git a/src/devices/wifi/nm-device-olpc-mesh.c b/src/devices/wifi/nm-device-olpc-mesh.c index 9ae81139..193779a7 100644 --- a/src/devices/wifi/nm-device-olpc-mesh.c +++ b/src/devices/wifi/nm-device-olpc-mesh.c @@ -82,7 +82,19 @@ G_DEFINE_TYPE (NMDeviceOlpcMesh, nm_device_olpc_mesh, NM_TYPE_DEVICE) static gboolean get_autoconnect_allowed (NMDevice *device) { - return FALSE; + NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (device); + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); + + /* We shall always have a companion if we're >= DISCONENCTED, and this + * ought not be called until then. */ + g_return_val_if_fail (priv->companion, FALSE); + + /* We must not attempt to autoconnect when the companion is connected or + * connecting, * because we'd tear down its connection. */ + if (nm_device_get_state (priv->companion) > NM_DEVICE_STATE_DISCONNECTED) + return FALSE; + + return TRUE; } #define DEFAULT_SSID "olpc-mesh" @@ -154,7 +166,7 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) nm_device_get_iface (priv->companion)); } - /* wait with continuing configuration untill the companion device is done scanning */ + /* wait with continuing configuration until the companion device is done scanning */ g_object_get (priv->companion, NM_DEVICE_WIFI_SCANNING, &scanning, NULL); if (scanning) { priv->stage1_waiting = TRUE; @@ -181,16 +193,13 @@ static NMActStageReturn act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) { NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (device); - NMConnection *connection; NMSettingOlpcMesh *s_mesh; guint32 channel; GBytes *ssid; const char *anycast_addr; - connection = nm_device_get_applied_connection (device); - g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); + s_mesh = nm_device_get_applied_setting (device, NM_TYPE_SETTING_OLPC_MESH); - s_mesh = nm_connection_get_setting_olpc_mesh (connection); g_return_val_if_fail (s_mesh, NM_ACT_STAGE_RETURN_FAILURE); channel = nm_setting_olpc_mesh_get_channel (s_mesh); @@ -265,6 +274,11 @@ companion_state_changed_cb (NMDeviceWifi *companion, NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data); NMDeviceState self_state = nm_device_get_state (NM_DEVICE (self)); + if ( old_state > NM_DEVICE_STATE_DISCONNECTED + && state <= NM_DEVICE_STATE_DISCONNECTED) { + nm_device_emit_recheck_auto_activate (NM_DEVICE (self)); + } + if ( self_state < NM_DEVICE_STATE_PREPARE || self_state > NM_DEVICE_STATE_ACTIVATED || state < NM_DEVICE_STATE_PREPARE @@ -316,7 +330,7 @@ check_companion (NMDeviceOlpcMesh *self, NMDevice *other) g_assert (priv->companion == NULL); priv->companion = g_object_ref (other); - _LOGI (LOGD_OLPC, "found companion WiFi device %s", + _LOGI (LOGD_OLPC, "found companion Wi-Fi device %s", nm_device_get_iface (other)); g_signal_connect (G_OBJECT (other), NM_DEVICE_STATE_CHANGED, diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c index 2ce84618..e0be38c3 100644 --- a/src/devices/wifi/nm-device-wifi.c +++ b/src/devices/wifi/nm-device-wifi.c @@ -732,7 +732,7 @@ check_connection_available (NMDevice *device, /* Hidden SSIDs obviously don't always appear in the scan list either. * - * For an explict user-activation-request, a connection is considered + * For an explicit user-activation-request, a connection is considered * available because for hidden Wi-Fi, clients didn't consistently * set the 'hidden' property to indicate hidden SSID networks. If * activating but the network isn't available let the device recheck @@ -838,7 +838,7 @@ complete_connection (NMDevice *device, ssid = nm_wifi_ap_get_ssid (ap); if (ssid == NULL) { - /* The AP must be hidden. Connecting to a WiFi AP requires the SSID + /* The AP must be hidden. Connecting to a Wi-Fi AP requires the SSID * as part of the initial handshake, so check the connection details * for the SSID. The AP object will still be used for encryption * settings and such. @@ -909,18 +909,6 @@ complete_connection (NMDevice *device, g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_MAC_ADDRESS); return FALSE; } - } else { - guint8 tmp[ETH_ALEN]; - - /* Lock the connection to this device by default if it uses a - * permanent MAC address (ie not a 'locally administered' one) - */ - nm_utils_hwaddr_aton (perm_hw_addr, tmp, ETH_ALEN); - if (!(tmp[0] & 0x02)) { - g_object_set (G_OBJECT (s_wifi), - NM_SETTING_WIRELESS_MAC_ADDRESS, perm_hw_addr, - NULL); - } } } @@ -981,7 +969,7 @@ can_auto_connect (NMDevice *device, g_return_val_if_fail (s_wifi, FALSE); /* Always allow autoconnect for AP and non-autoconf Ad-Hoc */ - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); + method = nm_utils_get_ip_config_method (connection, AF_INET); mode = nm_setting_wireless_get_mode (s_wifi); if (nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_AP)) return TRUE; @@ -1236,7 +1224,7 @@ scanning_prohibited (NMDeviceWifi *self, gboolean periodic) return FALSE; case NM_DEVICE_STATE_ACTIVATED: /* Prohibit periodic scans when connected; we ask the supplicant to - * background scan for us, unless the connection is locked to a specifc + * background scan for us, unless the connection is locked to a specific * BSSID. */ if (periodic) @@ -1685,20 +1673,37 @@ wifi_secrets_cb (NMActRequest *req, g_return_if_fail (nm_act_request_get_settings_connection (req) == connection); if (error) { - _LOGW (LOGD_WIFI, "%s", error->message); - - if (g_error_matches (error, NM_AGENT_MANAGER_ERROR, - NM_AGENT_MANAGER_ERROR_USER_CANCELED)) { - /* Don't wait for WPS timeout on an explicit cancel. */ - nm_clear_g_source (&priv->wps_timeout_id); - } + _LOGW (LOGD_WIFI, "no secrets: %s", error->message); - if (!priv->wps_timeout_id) { - /* Fail the device only if the WPS period is over too. */ - nm_device_state_changed (device, - NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_NO_SECRETS); - } + /* Even if WPS is still pending, let's abort the activation when the secret + * request returns. + * + * This means, a user can only effectively use WPS when also running a secret + * agent, and pressing the push button while being prompted for the password. + * Note, that in the secret prompt the user can see that WPS is in progress + * (via the NM_SECRET_AGENT_GET_SECRETS_FLAG_WPS_PBC_ACTIVE flag). + * + * Previously, WPS was not cancelled when the secret request returns. + * Note that in common use-cases WPS is enabled in the connection profile + * but it won't succeed (because it's disabled in the AP or because the + * user is not prepared to press the push button). + * That means for example, during boot we would try to autoconnect with WPS. + * At that point, there is no secret-agent running, and WPS is pending for + * full 30 seconds. If in the meantime a secret agent registers (because + * of logging into the DE), the profile is still busy waiting for WPS to time + * out. Only after that delay, autoconnect starts again (note that autoconnect gets + * not blocked in this case, because a secret agent registered in the meantime). + * + * It seems wrong to continue doing WPS if the user is not aware + * that WPS is ongoing. The user is required to perform an action (push button), + * and must be told via the secret prompt. + * If no secret-agent is running, if the user cancels the secret-request, or any + * other error to obtain secrets, the user apparently does not want WPS either. + */ + nm_clear_g_source (&priv->wps_timeout_id); + nm_device_state_changed (device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_NO_SECRETS); } else nm_device_activate_schedule_stage1_device_prepare (device); } @@ -1851,9 +1856,10 @@ need_new_8021x_secrets (NMDeviceWifi *self, NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE; NMConnection *connection; - g_assert (setting_name != NULL); + g_return_val_if_fail (setting_name, FALSE); connection = nm_device_get_applied_connection (NM_DEVICE (self)); + g_return_val_if_fail (connection != NULL, FALSE); /* 802.1x stuff only happens in the supplicant's ASSOCIATED state when it's @@ -1905,10 +1911,11 @@ need_new_wpa_psk (NMDeviceWifi *self, NMConnection *connection; const char *key_mgmt = NULL; - g_assert (setting_name != NULL); + g_return_val_if_fail (setting_name, FALSE); connection = nm_device_get_applied_connection (NM_DEVICE (self)); - g_return_val_if_fail (connection != NULL, FALSE); + + g_return_val_if_fail (connection, FALSE); /* A bad PSK will cause the supplicant to disconnect during the 4-way handshake */ if (old_state != NM_SUPPLICANT_INTERFACE_STATE_4WAY_HANDSHAKE) @@ -2040,15 +2047,12 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface, * schedule the next activation stage. */ if (devstate == NM_DEVICE_STATE_CONFIG) { - NMConnection *connection; NMSettingWireless *s_wifi; GBytes *ssid; gs_free char *ssid_str = NULL; - connection = nm_device_get_applied_connection (NM_DEVICE (self)); - g_return_if_fail (connection); + s_wifi = nm_device_get_applied_setting (NM_DEVICE (self), NM_TYPE_SETTING_WIRELESS); - s_wifi = nm_connection_get_setting_wireless (connection); g_return_if_fail (s_wifi); ssid = nm_setting_wireless_get_ssid (s_wifi); @@ -2469,7 +2473,7 @@ wake_on_wlan_enable (NMDeviceWifi *self) NMSettingWirelessWakeOnWLan wowl; NMSettingWireless *s_wireless; - s_wireless = (NMSettingWireless *) nm_device_get_applied_setting (NM_DEVICE (self), NM_TYPE_SETTING_WIRELESS); + s_wireless = nm_device_get_applied_setting (NM_DEVICE (self), NM_TYPE_SETTING_WIRELESS); if (s_wireless) { wowl = nm_setting_wireless_get_wake_on_wlan (s_wireless); if (wowl != NM_SETTING_WIRELESS_WAKE_ON_WLAN_DEFAULT) @@ -2646,7 +2650,8 @@ set_powersave (NMDevice *device) NMSettingWireless *s_wireless; NMSettingWirelessPowersave val; - s_wireless = (NMSettingWireless *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRELESS); + s_wireless = nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRELESS); + g_return_if_fail (s_wireless); val = nm_setting_wireless_get_powersave (s_wireless); @@ -2796,6 +2801,7 @@ act_stage3_ip4_config_start (NMDevice *device, const char *method = NM_SETTING_IP4_CONFIG_METHOD_AUTO; connection = nm_device_get_applied_connection (device); + g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); s_ip4 = nm_connection_get_setting_ip4_config (connection); @@ -2819,6 +2825,7 @@ act_stage3_ip6_config_start (NMDevice *device, const char *method = NM_SETTING_IP6_CONFIG_METHOD_AUTO; connection = nm_device_get_applied_connection (device); + g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); s_ip6 = nm_connection_get_setting_ip6_config (connection); @@ -2883,7 +2890,7 @@ handle_ip_config_timeout (NMDeviceWifi *self, /* If IP configuration times out and it's a static WEP connection, that * usually means the WEP key is wrong. WEP's Open System auth mode has * no provision for figuring out if the WEP key is wrong, so you just have - * to wait for DHCP to fail to figure it out. For all other WiFi security + * to wait for DHCP to fail to figure it out. For all other Wi-Fi security * types (open, WPA, 802.1x, etc) if the secrets/certs were wrong the * connection would have failed before IP configuration. */ @@ -2918,6 +2925,7 @@ act_stage4_ip4_config_timeout (NMDevice *device, NMDeviceStateReason *out_failur NMActStageReturn ret; connection = nm_device_get_applied_connection (device); + g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); s_ip4 = nm_connection_get_setting_ip4_config (connection); @@ -2939,6 +2947,7 @@ act_stage4_ip6_config_timeout (NMDevice *device, NMDeviceStateReason *out_failur NMActStageReturn ret; connection = nm_device_get_applied_connection (device); + g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); s_ip6 = nm_connection_get_setting_ip6_config (connection); @@ -3279,7 +3288,7 @@ nm_device_wifi_new (const char *iface, NMDeviceWifiCapabilities capabilities) { return g_object_new (NM_TYPE_DEVICE_WIFI, NM_DEVICE_IFACE, iface, - NM_DEVICE_TYPE_DESC, "802.11 WiFi", + NM_DEVICE_TYPE_DESC, "802.11 Wi-Fi", NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_WIFI, NM_DEVICE_LINK_TYPE, NM_LINK_TYPE_WIFI, NM_DEVICE_RFKILL_TYPE, RFKILL_TYPE_WLAN, diff --git a/src/devices/wifi/nm-iwd-manager.c b/src/devices/wifi/nm-iwd-manager.c index a3da9791..736a1a23 100644 --- a/src/devices/wifi/nm-iwd-manager.c +++ b/src/devices/wifi/nm-iwd-manager.c @@ -48,6 +48,7 @@ typedef struct { typedef struct { NMManager *manager; + NMSettings *settings; GCancellable *cancellable; gboolean running; GDBusObjectManager *object_manager; @@ -363,31 +364,35 @@ set_device_dbus_object (NMIwdManager *self, GDBusProxy *proxy, nm_device_iwd_set_dbus_object (NM_DEVICE_IWD (device), object); } -/* Create an in-memory NMConnection for a WPA2-Enterprise network that - * has been preprovisioned with an IWD config file so that NM autoconnect - * mechanism and the clients know this networks needs no additional EAP - * configuration from the user. Only do this if no existing connection - * SSID and security type match that network yet. +/* Look up an existing NMSettingsConnection for a WPA2-Enterprise network + * that has been preprovisioned with an IWD config file, or create a new + * in-memory connection object so that NM autoconnect mechanism and the + * clients know this networks needs no additional EAP configuration from + * the user. */ static NMSettingsConnection * mirror_8021x_connection (NMIwdManager *self, - const char *name) + const char *name, + gboolean create_new) { - NMSettings *settings = NM_SETTINGS_GET; + NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); NMSettingsConnection *const*iter; gs_unref_object NMConnection *connection = NULL; - NMSettingsConnection *settings_connection; + NMSettingsConnection *settings_connection = NULL; char uuid[37]; NMSetting *setting; GError *error = NULL; gs_unref_bytes GBytes *new_ssid = NULL; - for (iter = nm_settings_get_connections (settings, NULL); *iter; iter++) { + for (iter = nm_settings_get_connections (priv->settings, NULL); *iter; iter++) { NMSettingsConnection *sett_conn = *iter; NMConnection *conn = nm_settings_connection_get_connection (sett_conn); NMIwdNetworkSecurity security; gs_free char *ssid_name = NULL; NMSettingWireless *s_wifi; + NMSetting8021x *s_8021x; + gboolean external = FALSE; + guint i; security = nm_wifi_connection_get_iwd_security (conn, NULL); if (security != NM_IWD_NETWORK_SECURITY_8021X) @@ -399,14 +404,30 @@ mirror_8021x_connection (NMIwdManager *self, ssid_name = _nm_utils_ssid_to_utf8 (nm_setting_wireless_get_ssid (s_wifi)); - /* We already have an NMSettingsConnection matching this - * KnownNetwork, whether it's saved or an in-memory connection - * potentially created by ourselves. Nothing to do here. - */ - if (nm_streq (ssid_name, name)) - return NULL; + if (!nm_streq (ssid_name, name)) + continue; + + s_8021x = nm_connection_get_setting_802_1x (conn); + for (i = 0; i < nm_setting_802_1x_get_num_eap_methods (s_8021x); i++) { + if (nm_streq (nm_setting_802_1x_get_eap_method (s_8021x, i), "external")) { + external = TRUE; + break; + } + } + + /* Prefer returning connections for EAP method "external" */ + if (!settings_connection || external) + settings_connection = sett_conn; } + /* If we already have an NMSettingsConnection matching this + * KnownNetwork, whether it's saved or an in-memory connection + * potentially created by ourselves then we have nothing left to + * do here. + */ + if (settings_connection || !create_new) + return settings_connection; + connection = nm_simple_connection_new (); setting = NM_SETTING (g_object_new (NM_TYPE_SETTING_CONNECTION, @@ -446,7 +467,7 @@ mirror_8021x_connection (NMIwdManager *self, if (!nm_connection_normalize (connection, NULL, NULL, NULL)) return NULL; - settings_connection = nm_settings_add_connection (settings, connection, + settings_connection = nm_settings_add_connection (priv->settings, connection, FALSE, &error); if (!settings_connection) { _LOGW ("failed to add a mirror NMConnection for IWD's Known Network '%s': %s", @@ -526,16 +547,19 @@ interface_added (GDBusObjectManager *object_manager, GDBusObject *object, id = known_network_id_new (name, security); data = g_hash_table_lookup (priv->known_networks, id); - if (data) + if (data) { + _LOGW ("DBus error: KnownNetwork already exists ('%s', %s)", + name, type_str); g_free (id); - else { + nm_g_object_ref_set (&data->known_network, proxy); + } else { data = g_slice_new0 (KnownNetworkData); data->known_network = g_object_ref (proxy); g_hash_table_insert (priv->known_networks, id, data); } if (security == NM_IWD_NETWORK_SECURITY_8021X) { - sett_conn = mirror_8021x_connection (self, name); + sett_conn = mirror_8021x_connection (self, name, TRUE); if ( sett_conn && sett_conn != data->mirror_connection) { @@ -593,6 +617,55 @@ interface_removed (GDBusObjectManager *object_manager, GDBusObject *object, } } +static void +connection_removed (NMSettings *settings, + NMSettingsConnection *sett_conn, + gpointer user_data) +{ + NMIwdManager *self = user_data; + NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); + NMConnection *conn = nm_settings_connection_get_connection (sett_conn); + NMSettingWireless *s_wireless; + gboolean mapped; + KnownNetworkData *data; + KnownNetworkId id; + + id.security = nm_wifi_connection_get_iwd_security (conn, &mapped); + if (!mapped) + return; + + s_wireless = nm_connection_get_setting_wireless (conn); + id.name = _nm_utils_ssid_to_utf8 (nm_setting_wireless_get_ssid (s_wireless)); + data = g_hash_table_lookup (priv->known_networks, &id); + g_free ((char *) id.name); + if (!data) + return; + + if (id.security == NM_IWD_NETWORK_SECURITY_8021X) { + NMSettingsConnection *new_mirror_conn; + + if (data->mirror_connection != sett_conn) + return; + + g_clear_object (&data->mirror_connection); + + /* Don't call Forget for an 8021x network until there's no + * longer *any* matching NMSettingsConnection (debatable) + */ + new_mirror_conn = mirror_8021x_connection (self, id.name, FALSE); + if (new_mirror_conn) { + data->mirror_connection = g_object_ref (new_mirror_conn); + return; + } + } + + if (!priv->running) + return; + + g_dbus_proxy_call (data->known_network, "Forget", + NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); +} + static gboolean _om_has_name_owner (GDBusObjectManager *object_manager) { @@ -744,7 +817,7 @@ got_object_manager (GObject *object, GAsyncResult *result, gpointer user_data) &priv->agent_path, &error); if (!priv->agent_id) { - _LOGE ("failed to export the IWD Agent: PSK/8021x WiFi networks may not work: %s", + _LOGE ("failed to export the IWD Agent: PSK/8021x Wi-Fi networks may not work: %s", error->message); g_clear_error (&error); } @@ -824,6 +897,10 @@ nm_iwd_manager_init (NMIwdManager *self) g_signal_connect (priv->manager, NM_MANAGER_DEVICE_ADDED, G_CALLBACK (device_added), self); + priv->settings = g_object_ref (nm_settings_get ()); + g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_REMOVED, + G_CALLBACK (connection_removed), self); + priv->cancellable = g_cancellable_new (); priv->known_networks = g_hash_table_new_full ((GHashFunc) known_network_id_hash, @@ -844,6 +921,14 @@ dispose (GObject *object) nm_clear_g_cancellable (&priv->cancellable); + if (priv->settings) { + g_signal_handlers_disconnect_by_data (priv->settings, self); + g_clear_object (&priv->settings); + } + + /* This may trigger mirror connection removals so it happens + * after the g_signal_handlers_disconnect_by_data above. + */ nm_clear_pointer (&priv->known_networks, g_hash_table_destroy); if (priv->manager) { diff --git a/src/devices/wifi/nm-wifi-factory.c b/src/devices/wifi/nm-wifi-factory.c index 6b8e5fb8..4e9d1ecb 100644 --- a/src/devices/wifi/nm-wifi-factory.c +++ b/src/devices/wifi/nm-wifi-factory.c @@ -75,8 +75,6 @@ create_device (NMDeviceFactory *factory, NMConnection *connection, gboolean *out_ignore) { - NMDeviceWifiCapabilities capabilities; - NM80211Mode mode; gs_free char *backend = NULL; g_return_val_if_fail (iface != NULL, NULL); @@ -84,23 +82,6 @@ create_device (NMDeviceFactory *factory, g_return_val_if_fail (g_strcmp0 (iface, plink->name) == 0, NULL); g_return_val_if_fail (NM_IN_SET (plink->type, NM_LINK_TYPE_WIFI, NM_LINK_TYPE_OLPC_MESH), NULL); - if (!nm_platform_wifi_get_capabilities (NM_PLATFORM_GET, - plink->ifindex, - &capabilities)) { - nm_log_warn (LOGD_PLATFORM | LOGD_WIFI, "(%s) failed to initialize Wi-Fi driver for ifindex %d", iface, plink->ifindex); - return NULL; - } - - /* Ignore monitor-mode and other unhandled interface types. - * FIXME: keep TYPE_MONITOR devices in UNAVAILABLE state and manage - * them if/when they change to a handled type. - */ - mode = nm_platform_wifi_get_mode (NM_PLATFORM_GET, plink->ifindex); - if (mode == NM_802_11_MODE_UNKNOWN) { - *out_ignore = TRUE; - return NULL; - } - if (plink->type != NM_LINK_TYPE_WIFI) return nm_device_olpc_mesh_new (iface); @@ -116,11 +97,34 @@ create_device (NMDeviceFactory *factory, iface, NM_PRINT_FMT_QUOTE_STRING (backend), WITH_IWD ? " (iwd support enabled)" : ""); - if (!backend || !strcasecmp (backend, "wpa_supplicant")) + if (!backend || !strcasecmp (backend, "wpa_supplicant")) { + NMDeviceWifiCapabilities capabilities; + NM80211Mode mode; + + if (!nm_platform_wifi_get_capabilities (NM_PLATFORM_GET, + plink->ifindex, + &capabilities)) { + nm_log_warn (LOGD_PLATFORM | LOGD_WIFI, + "(%s) failed to initialize Wi-Fi driver for ifindex %d", + iface, plink->ifindex); + return NULL; + } + + /* Ignore monitor-mode and other unhandled interface types. + * FIXME: keep TYPE_MONITOR devices in UNAVAILABLE state and manage + * them if/when they change to a handled type. + */ + mode = nm_platform_wifi_get_mode (NM_PLATFORM_GET, plink->ifindex); + if (mode == NM_802_11_MODE_UNKNOWN) { + *out_ignore = TRUE; + return NULL; + } + return nm_device_wifi_new (iface, capabilities); + } #if WITH_IWD else if (!strcasecmp (backend, "iwd")) - return nm_device_iwd_new (iface, capabilities); + return nm_device_iwd_new (iface); #endif nm_log_warn (LOGD_PLATFORM | LOGD_WIFI, "(%s) config: unknown or unsupported wifi-backend %s", iface, backend); diff --git a/src/devices/wifi/tests/meson.build b/src/devices/wifi/tests/meson.build index bb8f7c27..ee60f349 100644 --- a/src/devices/wifi/tests/meson.build +++ b/src/devices/wifi/tests/meson.build @@ -3,11 +3,11 @@ test_unit = 'test-general' exe = executable( 'wifi-' + test_unit, [test_unit + '.c'] + common_sources, - dependencies: test_nm_dep + dependencies: test_nm_dep, ) test( 'devices/wifi/' + test_unit, test_script, - args: test_args + [exe.full_path()] + args: test_args + [exe.full_path()], ) diff --git a/src/devices/wwan/libnm-wwan.ver b/src/devices/wwan/libnm-wwan.ver index 70b954c5..ea966afe 100644 --- a/src/devices/wwan/libnm-wwan.ver +++ b/src/devices/wwan/libnm-wwan.ver @@ -6,7 +6,6 @@ global: nm_modem_complete_connection; nm_modem_deactivate; nm_modem_deactivate_async; - nm_modem_deactivate_async_finish; nm_modem_device_state_changed; nm_modem_get_capabilities; nm_modem_get_configured_mtu; diff --git a/src/devices/wwan/meson.build b/src/devices/wwan/meson.build index 5fe6e433..482dc205 100644 --- a/src/devices/wwan/meson.build +++ b/src/devices/wwan/meson.build @@ -1,13 +1,13 @@ sources = files( 'nm-modem-broadband.c', 'nm-modem.c', - 'nm-modem-manager.c' + 'nm-modem-manager.c', ) deps = [ libsystemd_dep, mm_glib_dep, - nm_dep + nm_dep, ] if enable_ofono @@ -25,12 +25,12 @@ libnm_wwan = shared_module( ], link_depends: linker_script, install: true, - install_dir: nm_plugindir + install_dir: nm_plugindir, ) libnm_wwan_dep = declare_dependency( include_directories: include_directories('.'), - link_with: libnm_wwan + link_with: libnm_wwan, ) core_plugins += libnm_wwan @@ -43,7 +43,7 @@ test( sources = files( 'nm-device-modem.c', - 'nm-wwan-factory.c' + 'nm-wwan-factory.c', ) libnm_device_plugin_wwan = shared_module( @@ -55,7 +55,7 @@ libnm_device_plugin_wwan = shared_module( link_depends: linker_script_devices, install: true, install_dir: nm_plugindir, - install_rpath: nm_plugindir + install_rpath: nm_plugindir, ) core_plugins += libnm_device_plugin_wwan @@ -63,7 +63,7 @@ core_plugins += libnm_device_plugin_wwan run_target( 'check-local-devices-wwan', command: [check_exports, libnm_device_plugin_wwan.full_path(), linker_script_devices], - depends: libnm_device_plugin_wwan + depends: libnm_device_plugin_wwan, ) # FIXME: check_so_symbols replacement diff --git a/src/devices/wwan/nm-device-modem.c b/src/devices/wwan/nm-device-modem.c index 4119d598..bd9ee3bb 100644 --- a/src/devices/wwan/nm-device-modem.c +++ b/src/devices/wwan/nm-device-modem.c @@ -61,7 +61,7 @@ struct _NMDeviceModemClass { G_DEFINE_TYPE (NMDeviceModem, nm_device_modem, NM_TYPE_DEVICE) -#define NM_DEVICE_MODEM_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDeviceModem, NM_IS_DEVICE_MODEM) +#define NM_DEVICE_MODEM_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDeviceModem, NM_IS_DEVICE_MODEM, NMDevice) /*****************************************************************************/ @@ -125,14 +125,40 @@ modem_prepare_result (NMModem *modem, if (success) nm_device_activate_schedule_stage2_device_config (device); else { - if (nm_device_state_reason_check (reason) == NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT) { - /* If the connect failed because the SIM PIN was wrong don't allow - * the device to be auto-activated anymore, which would risk locking - * the SIM if the incorrect PIN continues to be used. - */ + /* There are several reasons to block autoconnection at device level: + * + * - Wrong SIM-PIN: The device won't autoconnect because it doesn't make sense + * to retry the connection with the same PIN. This error also makes autoconnection + * blocked at settings level, so not even a modem unplug and replug will allow + * autoconnection again. It is somewhat redundant to block autoconnection at + * both device and setting level really. + * + * - SIM wrong or not inserted: If the modem is reporting a SIM not inserted error, + * we can block autoconnection at device level, so that if the same device is + * unplugged and replugged with a SIM (or if a SIM hotplug event happens in MM, + * recreating the device completely), we can try the autoconnection again. + * + * - Modem initialization failed: For some reason unknown to NM, the modem wasn't + * initialized correctly, which leads to an unusable device. A device unplug and + * replug may solve the issue, so make it a device-level autoconnection blocking + * reason. + */ + switch (nm_device_state_reason_check (reason)) { + case NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED: + case NM_DEVICE_STATE_REASON_GSM_SIM_PUK_REQUIRED: + case NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT: nm_device_autoconnect_blocked_set (device, NM_DEVICE_AUTOCONNECT_BLOCKED_WRONG_PIN); + break; + case NM_DEVICE_STATE_REASON_GSM_SIM_NOT_INSERTED: + case NM_DEVICE_STATE_REASON_GSM_SIM_WRONG: + nm_device_autoconnect_blocked_set (device, NM_DEVICE_AUTOCONNECT_BLOCKED_SIM_MISSING); + break; + case NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED: + nm_device_autoconnect_blocked_set (device, NM_DEVICE_AUTOCONNECT_BLOCKED_INIT_FAILED); + break; + default: + break; } - nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason); } } @@ -218,7 +244,7 @@ modem_ip6_config_result (NMModem *modem, } /* Re-enable IPv6 on the interface */ - nm_device_ipv6_sysctl_set (device, "disable_ipv6", "0"); + nm_device_sysctl_ip_conf_set (device, AF_INET6, "disable_ipv6", "0"); if (config) nm_device_set_wwan_ip6_config (device, config); @@ -277,7 +303,7 @@ ip_ifindex_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data) * internally, and leaving it enabled could allow the kernel's IPv6 * RA handling code to run before NM is ready. */ - nm_device_ipv6_sysctl_set (device, "disable_ipv6", "1"); + nm_device_sysctl_ip_conf_set (device, AF_INET6, "disable_ipv6", "1"); } static void @@ -448,7 +474,7 @@ check_connection_available (NMDevice *device, state = nm_modem_get_state (priv->modem); if (state <= NM_MODEM_STATE_INITIALIZING) { nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, - "modem not initalized"); + "modem not initialized"); return FALSE; } @@ -483,44 +509,35 @@ deactivate (NMDevice *device) /*****************************************************************************/ -static gboolean -deactivate_async_finish (NMDevice *self, - GAsyncResult *res, - GError **error) -{ - return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); -} - static void -modem_deactivate_async_ready (NMModem *modem, - GAsyncResult *res, - GSimpleAsyncResult *simple) +modem_deactivate_async_cb (NMModem *modem, + GError *error, + gpointer user_data) { - GError *error = NULL; + gs_unref_object NMDevice *self = NULL; + NMDeviceDeactivateCallback callback; + gpointer callback_user_data; - if (!nm_modem_deactivate_async_finish (modem, res, &error)) - g_simple_async_result_take_error (simple, error); - g_simple_async_result_complete (simple); - g_object_unref (simple); + nm_utils_user_data_unpack (user_data, &self, &callback, &callback_user_data); + callback (self, error, callback_user_data); } static void deactivate_async (NMDevice *self, GCancellable *cancellable, - GAsyncReadyCallback callback, + NMDeviceDeactivateCallback callback, gpointer user_data) { - GSimpleAsyncResult *simple; + nm_assert (G_IS_CANCELLABLE (cancellable)); + nm_assert (callback); - simple = g_simple_async_result_new (G_OBJECT (self), - callback, - user_data, - deactivate_async); - nm_modem_deactivate_async (NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) self)->modem, + nm_modem_deactivate_async (NM_DEVICE_MODEM_GET_PRIVATE (self)->modem, self, cancellable, - (GAsyncReadyCallback) modem_deactivate_async_ready, - simple); + modem_deactivate_async_cb, + nm_utils_user_data_pack (g_object_ref (self), + callback, + user_data)); } /*****************************************************************************/ @@ -805,7 +822,6 @@ nm_device_modem_class_init (NMDeviceModemClass *klass) device_class->check_connection_available = check_connection_available; device_class->complete_connection = complete_connection; device_class->deactivate_async = deactivate_async; - device_class->deactivate_async_finish = deactivate_async_finish; device_class->deactivate = deactivate; device_class->act_stage1_prepare = act_stage1_prepare; device_class->act_stage2_config = act_stage2_config; diff --git a/src/devices/wwan/nm-modem-broadband.c b/src/devices/wwan/nm-modem-broadband.c index 04cb8599..82e9e2f1 100644 --- a/src/devices/wwan/nm-modem-broadband.c +++ b/src/devices/wwan/nm-modem-broadband.c @@ -279,11 +279,6 @@ create_gsm_connect_properties (NMConnection *connection) setting = nm_connection_get_setting_gsm (connection); properties = mm_simple_connect_properties_new (); - /* TODO: not needed */ - str = nm_setting_gsm_get_number (setting); - if (str) - mm_simple_connect_properties_set_number (properties, str); - /* Blank APN ("") means the default subscription APN */ str = nm_setting_gsm_get_apn (setting); mm_simple_connect_properties_set_apn (properties, str ?: ""); @@ -693,10 +688,6 @@ complete_connection (NMModem *_self, return FALSE; } - /* TODO: This is not needed */ - if (!nm_setting_gsm_get_number (s_gsm)) - g_object_set (G_OBJECT (s_gsm), NM_SETTING_GSM_NUMBER, "*99#", NULL); - nm_utils_complete_generic (NM_PLATFORM_GET, connection, NM_SETTING_GSM_SETTING_NAME, @@ -884,7 +875,7 @@ static_stage3_ip4_done (NMModemBroadband *self) /* Fully fail if invalid IP address retrieved */ address_string = mm_bearer_ip_config_get_address (self->_priv.ipv4_config); if ( !address_string - || !nm_utils_parse_inaddr_bin (AF_INET, address_string, &address_network)) { + || !nm_utils_parse_inaddr_bin (AF_INET, address_string, NULL, &address_network)) { error = g_error_new (NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION, "(%s) retrieving IP4 configuration failed: invalid address given %s%s%s", @@ -896,7 +887,7 @@ static_stage3_ip4_done (NMModemBroadband *self) /* Missing gateway not a hard failure */ gw_string = mm_bearer_ip_config_get_gateway (self->_priv.ipv4_config); if ( gw_string - && !nm_utils_parse_inaddr_bin (AF_INET, gw_string, &gw)) { + && !nm_utils_parse_inaddr_bin (AF_INET, gw_string, NULL, &gw)) { error = g_error_new (NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION, "(%s) retrieving IP4 configuration failed: invalid gateway address \"%s\"", @@ -937,7 +928,7 @@ static_stage3_ip4_done (NMModemBroadband *self) /* DNS servers */ dns = mm_bearer_ip_config_get_dns (self->_priv.ipv4_config); for (i = 0; dns && dns[i]; i++) { - if ( nm_utils_parse_inaddr_bin (AF_INET, dns[i], &address_network) + if ( nm_utils_parse_inaddr_bin (AF_INET, dns[i], NULL, &address_network) && address_network > 0) { nm_ip4_config_add_nameserver (config, address_network); _LOGI (" DNS %s", dns[i]); @@ -1098,100 +1089,101 @@ stage3_ip6_config_request (NMModem *modem, NMDeviceStateReason *out_failure_reas typedef struct { NMModemBroadband *self; - GSimpleAsyncResult *result; + _NMModemDisconnectCallback callback; + gpointer callback_user_data; GCancellable *cancellable; gboolean warn; } DisconnectContext; static void -disconnect_context_complete (DisconnectContext *ctx) +disconnect_context_complete (DisconnectContext *ctx, GError *error) { - g_simple_async_result_complete_in_idle (ctx->result); - if (ctx->cancellable) - g_object_unref (ctx->cancellable); - g_object_unref (ctx->result); + if (ctx->callback) + ctx->callback (NM_MODEM (ctx->self), error, ctx->callback_user_data); + nm_g_object_unref (ctx->cancellable); g_object_unref (ctx->self); g_slice_free (DisconnectContext, ctx); } -static gboolean -disconnect_finish (NMModem *self, - GAsyncResult *res, - GError **error) +static void +disconnect_context_complete_on_idle (gpointer user_data, + GCancellable *cancellable) { - return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); + DisconnectContext *ctx = user_data; + gs_free_error GError *cancelled_error = NULL; + + g_cancellable_set_error_if_cancelled (cancellable, &cancelled_error); + disconnect_context_complete (ctx, cancelled_error); } static void -simple_disconnect_ready (MMModemSimple *modem_iface, +simple_disconnect_ready (GObject *source_object, GAsyncResult *res, - DisconnectContext *ctx) + gpointer user_data) { + MMModemSimple *modem_iface = MM_MODEM_SIMPLE (source_object); + DisconnectContext *ctx = user_data; GError *error = NULL; if (!mm_modem_simple_disconnect_finish (modem_iface, res, &error)) { - if (ctx->warn && !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN)) { + if ( ctx->warn + && !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN)) { NMModemBroadband *self = ctx->self; _LOGW ("failed to disconnect modem: %s", error->message); } - g_simple_async_result_take_error (ctx->result, error); } - disconnect_context_complete (ctx); + disconnect_context_complete (ctx, error); } static void disconnect (NMModem *modem, gboolean warn, GCancellable *cancellable, - GAsyncReadyCallback callback, + _NMModemDisconnectCallback callback, gpointer user_data) { NMModemBroadband *self = NM_MODEM_BROADBAND (modem); DisconnectContext *ctx; - GError *error = NULL; connect_context_clear (self); - ctx = g_slice_new (DisconnectContext); - ctx->cancellable = NULL; + + ctx = g_slice_new0 (DisconnectContext); ctx->self = g_object_ref (self); - ctx->result = g_simple_async_result_new (G_OBJECT (self), - callback, - user_data, - disconnect); + ctx->cancellable = nm_g_object_ref (cancellable); + ctx->callback = callback; + ctx->callback_user_data = user_data; + /* Don't bother warning on FAILED since the modem is already gone */ ctx->warn = warn; - /* Already cancelled? */ - if (g_cancellable_set_error_if_cancelled (cancellable, &error)) { - g_simple_async_result_take_error (ctx->result, error); - disconnect_context_complete (ctx); - return; - } - - /* If no simple iface, we're done */ - if (!ctx->self->_priv.simple_iface) { - disconnect_context_complete (ctx); + /* Already cancelled or no simple-iface? We are done. */ + if ( !ctx->self->_priv.simple_iface + || g_cancellable_is_cancelled (cancellable)) { + nm_utils_invoke_on_idle (disconnect_context_complete_on_idle, + ctx, + cancellable); return; } _LOGD ("notifying ModemManager about the modem disconnection"); - ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL; - mm_modem_simple_disconnect (ctx->self->_priv.simple_iface, + mm_modem_simple_disconnect (self->_priv.simple_iface, NULL, /* bearer path; if NULL given ALL get disconnected */ cancellable, - (GAsyncReadyCallback) simple_disconnect_ready, + simple_disconnect_ready, ctx); } /*****************************************************************************/ static void -deactivate_cleanup (NMModem *_self, NMDevice *device) +deactivate_cleanup (NMModem *modem, + NMDevice *device, + gboolean stop_ppp_manager) { - NMModemBroadband *self = NM_MODEM_BROADBAND (_self); + NMModemBroadband *self = NM_MODEM_BROADBAND (modem); /* TODO: cancel SimpleConnect() if any */ @@ -1202,8 +1194,9 @@ deactivate_cleanup (NMModem *_self, NMDevice *device) self->_priv.pin_tries = 0; - /* Chain up parent's */ - NM_MODEM_CLASS (nm_modem_broadband_parent_class)->deactivate_cleanup (_self, device); + NM_MODEM_CLASS (nm_modem_broadband_parent_class)->deactivate_cleanup (modem, + device, + stop_ppp_manager); } /*****************************************************************************/ @@ -1468,7 +1461,6 @@ nm_modem_broadband_class_init (NMModemBroadbandClass *klass) modem_class->static_stage3_ip4_config_start = static_stage3_ip4_config_start; modem_class->stage3_ip6_config_request = stage3_ip6_config_request; modem_class->disconnect = disconnect; - modem_class->disconnect_finish = disconnect_finish; modem_class->deactivate_cleanup = deactivate_cleanup; modem_class->set_mm_enabled = set_mm_enabled; modem_class->get_user_pass = get_user_pass; diff --git a/src/devices/wwan/nm-modem-ofono.c b/src/devices/wwan/nm-modem-ofono.c index ea668590..8efe0253 100644 --- a/src/devices/wwan/nm-modem-ofono.c +++ b/src/devices/wwan/nm-modem-ofono.c @@ -146,30 +146,36 @@ update_modem_state (NMModemOfono *self) /* Disconnect */ typedef struct { NMModemOfono *self; - GSimpleAsyncResult *result; + _NMModemDisconnectCallback callback; + gpointer callback_user_data; GCancellable *cancellable; gboolean warn; } DisconnectContext; static void -disconnect_context_complete (DisconnectContext *ctx) +disconnect_context_complete (DisconnectContext *ctx, GError *error) { - if (ctx->cancellable) - g_object_unref (ctx->cancellable); - if (ctx->result) { - g_simple_async_result_complete_in_idle (ctx->result); - g_object_unref (ctx->result); - } + if (ctx->callback) + ctx->callback (NM_MODEM (ctx->self), error, ctx->callback_user_data); + nm_g_object_unref (ctx->cancellable); g_object_unref (ctx->self); g_slice_free (DisconnectContext, ctx); } -static gboolean -disconnect_finish (NMModem *self, - GAsyncResult *result, - GError **error) +static void +disconnect_context_complete_on_idle (gpointer user_data, + GCancellable *cancellable) { - return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error); + DisconnectContext *ctx = user_data; + gs_free_error GError *error = NULL; + + if (!g_cancellable_set_error_if_cancelled (cancellable, &error)) { + g_set_error_literal (&error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + ("modem is currently not connected")); + } + disconnect_context_complete (ctx, error); } static void @@ -177,16 +183,14 @@ disconnect_done (GObject *source, GAsyncResult *result, gpointer user_data) { - DisconnectContext *ctx = (DisconnectContext*) user_data; + DisconnectContext *ctx = user_data; NMModemOfono *self = ctx->self; gs_free_error GError *error = NULL; gs_unref_variant GVariant *v = NULL; v = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), result, &error); if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { - if (ctx->result) - g_simple_async_result_take_error (ctx->result, g_steal_pointer (&error)); - disconnect_context_complete (ctx); + disconnect_context_complete (ctx, error); return; } @@ -196,21 +200,21 @@ disconnect_done (GObject *source, _LOGD ("modem disconnected"); update_modem_state (self); - disconnect_context_complete (ctx); + disconnect_context_complete (ctx, error); } static void disconnect (NMModem *modem, gboolean warn, GCancellable *cancellable, - GAsyncReadyCallback callback, + _NMModemDisconnectCallback callback, gpointer user_data) { NMModemOfono *self = NM_MODEM_OFONO (modem); NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self); DisconnectContext *ctx; NMModemState state = nm_modem_get_state (NM_MODEM (self)); - GError *error = NULL; + gs_free_error GError *error = NULL; _LOGD ("warn: %s modem_state: %s", warn ? "TRUE" : "FALSE", @@ -218,37 +222,19 @@ disconnect (NMModem *modem, ctx = g_slice_new0 (DisconnectContext); ctx->self = g_object_ref (self); + ctx->cancellable = nm_g_object_ref (cancellable); ctx->warn = warn; - if (callback) { - ctx->result = g_simple_async_result_new (G_OBJECT (self), - callback, - user_data, - disconnect); - } - - if (state != NM_MODEM_STATE_CONNECTED) { - if (ctx->result) { - g_set_error_literal (&error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - ("modem is currently not connected")); - g_simple_async_result_take_error (ctx->result, error); - } - disconnect_context_complete (ctx); + ctx->callback = callback; + ctx->callback_user_data = user_data; + + if ( state != NM_MODEM_STATE_CONNECTED + || g_cancellable_is_cancelled (cancellable)) { + nm_utils_invoke_on_idle (disconnect_context_complete_on_idle, + ctx, + cancellable); return; } - if (g_cancellable_set_error_if_cancelled (cancellable, &error)) { - if (ctx->result) - g_simple_async_result_take_error (ctx->result, error); - else - g_clear_error (&error); - disconnect_context_complete (ctx); - return; - } - - ctx->cancellable = nm_g_object_ref (cancellable); - nm_modem_set_state (NM_MODEM (self), NM_MODEM_STATE_DISCONNECTING, nm_modem_state_to_string (NM_MODEM_STATE_DISCONNECTING)); @@ -266,7 +252,9 @@ disconnect (NMModem *modem, } static void -deactivate_cleanup (NMModem *modem, NMDevice *device) +deactivate_cleanup (NMModem *modem, + NMDevice *device, + gboolean stop_ppp_manager) { NMModemOfono *self = NM_MODEM_OFONO (modem); NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self); @@ -275,7 +263,9 @@ deactivate_cleanup (NMModem *modem, NMDevice *device) g_clear_object (&priv->ip4_config); - NM_MODEM_CLASS (nm_modem_ofono_parent_class)->deactivate_cleanup (modem, device); + NM_MODEM_CLASS (nm_modem_ofono_parent_class)->deactivate_cleanup (modem, + device, + stop_ppp_manager); } static gboolean @@ -883,7 +873,7 @@ context_property_changed (GDBusProxy *proxy, goto out; } if ( !s - || !nm_utils_parse_inaddr_bin (AF_INET, s, &address_network)) { + || !nm_utils_parse_inaddr_bin (AF_INET, s, NULL, &address_network)) { _LOGW ("can't convert 'Address' %s to addr", s ?: ""); goto out; } @@ -897,7 +887,7 @@ context_property_changed (GDBusProxy *proxy, goto out; } if ( !s - || !nm_utils_parse_inaddr_bin (AF_INET, s, &address_network)) { + || !nm_utils_parse_inaddr_bin (AF_INET, s, NULL, &address_network)) { _LOGW ("invalid 'Netmask': %s", s ?: ""); goto out; } @@ -911,7 +901,7 @@ context_property_changed (GDBusProxy *proxy, _LOGW ("Settings 'Gateway' missing"); goto out; } - if (!nm_utils_parse_inaddr_bin (AF_INET, s, &gateway_network)) { + if (!nm_utils_parse_inaddr_bin (AF_INET, s, NULL, &gateway_network)) { _LOGW ("invalid 'Gateway': %s", s); goto out; } @@ -938,7 +928,7 @@ context_property_changed (GDBusProxy *proxy, } if (array) { for (iter = array; *iter; iter++) { - if ( nm_utils_parse_inaddr_bin (AF_INET, *iter, &address_network) + if ( nm_utils_parse_inaddr_bin (AF_INET, *iter, NULL, &address_network) && address_network) { _LOGI ("DNS: %s", *iter); nm_ip4_config_add_nameserver (priv->ip4_config, address_network); @@ -958,7 +948,7 @@ context_property_changed (GDBusProxy *proxy, if (g_variant_lookup (v_dict, "MessageProxy", "&s", &s)) { _LOGI ("MessageProxy: %s", s); if ( s - && nm_utils_parse_inaddr_bin (AF_INET, s, &address_network)) { + && nm_utils_parse_inaddr_bin (AF_INET, s, NULL, &address_network)) { nm_modem_get_route_parameters (NM_MODEM (self), &ip4_route_table, &ip4_route_metric, @@ -1319,7 +1309,6 @@ nm_modem_ofono_class_init (NMModemOfonoClass *klass) modem_class->get_capabilities = get_capabilities; modem_class->disconnect = disconnect; - modem_class->disconnect_finish = disconnect_finish; modem_class->deactivate_cleanup = deactivate_cleanup; modem_class->check_connection_compatible_with_modem = check_connection_compatible_with_modem; diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c index 59b081e9..11ada549 100644 --- a/src/devices/wwan/nm-modem.c +++ b/src/devices/wwan/nm-modem.c @@ -706,13 +706,13 @@ nm_modem_stage3_ip4_config_start (NMModem *self, nm_modem_set_route_parameters_from_device (self, device); - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); + method = nm_utils_get_ip_config_method (connection, AF_INET); /* Only Disabled and Auto methods make sense for WWAN */ - if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0) + if (nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) return NM_ACT_STAGE_RETURN_SUCCESS; - if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) != 0) { + if (!nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) { _LOGE ("unhandled WWAN IPv4 method '%s'; will fail", method); NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_IP_METHOD_UNSUPPORTED); return NM_ACT_STAGE_RETURN_FAILURE; @@ -823,13 +823,13 @@ nm_modem_stage3_ip6_config_start (NMModem *self, nm_modem_set_route_parameters_from_device (self, device); - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); + method = nm_utils_get_ip_config_method (connection, AF_INET6); /* Only Ignore and Auto methods make sense for WWAN */ - if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0) + if (nm_streq (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) return NM_ACT_STAGE_RETURN_IP_DONE; - if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) != 0) { + if (!nm_streq (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) { _LOGW ("unhandled WWAN IPv6 method '%s'; will fail", method); NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); @@ -1105,7 +1105,9 @@ nm_modem_complete_connection (NMModem *self, /*****************************************************************************/ static void -deactivate_cleanup (NMModem *self, NMDevice *device) +deactivate_cleanup (NMModem *self, + NMDevice *device, + gboolean stop_ppp_manager) { NMModemPrivate *priv; int ifindex; @@ -1126,7 +1128,8 @@ deactivate_cleanup (NMModem *self, NMDevice *device) if (priv->ppp_manager) { g_signal_handlers_disconnect_by_data (priv->ppp_manager, self); - nm_ppp_manager_stop (priv->ppp_manager, NULL, NULL); + if (stop_ppp_manager) + nm_ppp_manager_stop (priv->ppp_manager, NULL, NULL, NULL); g_clear_object (&priv->ppp_manager); } @@ -1157,189 +1160,109 @@ deactivate_cleanup (NMModem *self, NMDevice *device) /*****************************************************************************/ -typedef enum { - DEACTIVATE_CONTEXT_STEP_FIRST, - DEACTIVATE_CONTEXT_STEP_CLEANUP, - DEACTIVATE_CONTEXT_STEP_PPP_MANAGER_STOP, - DEACTIVATE_CONTEXT_STEP_MM_DISCONNECT, - DEACTIVATE_CONTEXT_STEP_LAST -} DeactivateContextStep; - typedef struct { NMModem *self; NMDevice *device; GCancellable *cancellable; - GSimpleAsyncResult *result; - DeactivateContextStep step; - NMPPPManager *ppp_manager; - NMPPPManagerStopHandle *ppp_stop_handle; - gulong ppp_stop_cancellable_id; + NMModemDeactivateCallback callback; + gpointer callback_user_data; } DeactivateContext; static void -deactivate_context_complete (DeactivateContext *ctx) +deactivate_context_complete (DeactivateContext *ctx, GError *error) { - if (ctx->ppp_stop_handle) - nm_ppp_manager_stop_cancel (ctx->ppp_stop_handle); + NMModem *self = ctx->self; - nm_assert (!ctx->ppp_stop_handle); - nm_assert (ctx->ppp_stop_cancellable_id == 0); + _LOGD ("modem deactivation finished %s%s%s", + NM_PRINT_FMT_QUOTED (error, "with failure: ", error->message, "", "successfully")); - if (ctx->ppp_manager) - g_object_unref (ctx->ppp_manager); - if (ctx->cancellable) - g_object_unref (ctx->cancellable); - g_simple_async_result_complete_in_idle (ctx->result); - g_object_unref (ctx->result); + if (ctx->callback) + ctx->callback (ctx->self, error, ctx->callback_user_data); + nm_g_object_unref (ctx->cancellable); g_object_unref (ctx->device); g_object_unref (ctx->self); g_slice_free (DeactivateContext, ctx); } -gboolean -nm_modem_deactivate_async_finish (NMModem *self, - GAsyncResult *res, - GError **error) -{ - return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); -} - -static void deactivate_step (DeactivateContext *ctx); - static void -disconnect_ready (NMModem *self, - GAsyncResult *res, - DeactivateContext *ctx) +_deactivate_call_disconnect_cb (NMModem *self, + GError *error, + gpointer user_data) { - GError *error = NULL; - - if (!NM_MODEM_GET_CLASS (self)->disconnect_finish (self, res, &error)) { - g_simple_async_result_take_error (ctx->result, error); - deactivate_context_complete (ctx); - return; - } - - /* Go on */ - ctx->step++; - deactivate_step (ctx); + deactivate_context_complete (user_data, error); } static void -ppp_manager_stop_ready (NMPPPManager *ppp_manager, - NMPPPManagerStopHandle *handle, - gboolean was_cancelled, - gpointer user_data) +_deactivate_call_disconnect (DeactivateContext *ctx) { - DeactivateContext *ctx = user_data; - - nm_assert (ctx->ppp_stop_handle == handle); - ctx->ppp_stop_handle = NULL; - - if (ctx->ppp_stop_cancellable_id) { - g_cancellable_disconnect (ctx->cancellable, - nm_steal_int (&ctx->ppp_stop_cancellable_id)); - } - - if (was_cancelled) - return; - - ctx->step++; - deactivate_step (ctx); + NM_MODEM_GET_CLASS (ctx->self)->disconnect (ctx->self, + FALSE, + ctx->cancellable, + _deactivate_call_disconnect_cb, + ctx); } static void -ppp_manager_stop_cancelled (GCancellable *cancellable, - gpointer user_data) +_deactivate_ppp_manager_stop_cb (NMPPPManager *ppp_manager, + NMPPPManagerStopHandle *handle, + gboolean was_cancelled, + gpointer user_data) { DeactivateContext *ctx = user_data; - nm_ppp_manager_stop_cancel (ctx->ppp_stop_handle); -} - -static void -deactivate_step (DeactivateContext *ctx) -{ - NMModem *self = ctx->self; - NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); - GError *error = NULL; + g_object_unref (ppp_manager); - /* Check cancellable in each step */ - if (g_cancellable_set_error_if_cancelled (ctx->cancellable, &error)) { - g_simple_async_result_take_error (ctx->result, error); - deactivate_context_complete (ctx); - return; - } - - switch (ctx->step) { - case DEACTIVATE_CONTEXT_STEP_FIRST: - ctx->step++; - /* fall through */ - case DEACTIVATE_CONTEXT_STEP_CLEANUP: - /* Make sure we keep a ref to the PPP manager if there is one */ - if (priv->ppp_manager) - ctx->ppp_manager = g_object_ref (priv->ppp_manager); - /* Run cleanup */ - NM_MODEM_GET_CLASS (self)->deactivate_cleanup (self, ctx->device); - ctx->step++; - /* fall through */ - case DEACTIVATE_CONTEXT_STEP_PPP_MANAGER_STOP: - /* If we have a PPP manager, stop it */ - if (ctx->ppp_manager) { - nm_assert (!ctx->ppp_stop_handle); - if (ctx->cancellable) { - ctx->ppp_stop_cancellable_id = g_cancellable_connect (ctx->cancellable, - G_CALLBACK (ppp_manager_stop_cancelled), - ctx, - NULL); - } - ctx->ppp_stop_handle = nm_ppp_manager_stop (ctx->ppp_manager, - ppp_manager_stop_ready, - ctx); - return; - } - ctx->step++; - /* fall through */ - case DEACTIVATE_CONTEXT_STEP_MM_DISCONNECT: - /* Disconnect asynchronously */ - NM_MODEM_GET_CLASS (self)->disconnect (self, - FALSE, - ctx->cancellable, - (GAsyncReadyCallback) disconnect_ready, - ctx); - return; + if (was_cancelled) { + gs_free_error GError *error = NULL; - case DEACTIVATE_CONTEXT_STEP_LAST: - _LOGD ("modem deactivation finished"); - deactivate_context_complete (ctx); + if (!g_cancellable_set_error_if_cancelled (ctx->cancellable, &error)) + nm_assert_not_reached (); + deactivate_context_complete (ctx, error); return; } - g_assert_not_reached (); + nm_assert (!g_cancellable_is_cancelled (ctx->cancellable)); + _deactivate_call_disconnect (ctx); } void nm_modem_deactivate_async (NMModem *self, NMDevice *device, GCancellable *cancellable, - GAsyncReadyCallback callback, + NMModemDeactivateCallback callback, gpointer user_data) { + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); DeactivateContext *ctx; + NMPPPManager *ppp_manager; + + g_return_if_fail (NM_IS_MODEM (self)); + g_return_if_fail (NM_IS_DEVICE (device)); + g_return_if_fail (G_IS_CANCELLABLE (cancellable)); - ctx = g_slice_new0 (DeactivateContext); + ctx = g_slice_new (DeactivateContext); ctx->self = g_object_ref (self); ctx->device = g_object_ref (device); - ctx->result = g_simple_async_result_new (G_OBJECT (self), - callback, - user_data, - nm_modem_deactivate_async); - /* FIXME(shutdown): we always require a cancellable, otherwise we cannot - * do a coordinated shutdown. */ - ctx->cancellable = nm_g_object_ref (cancellable); + ctx->cancellable = g_object_ref (cancellable); + ctx->callback = callback; + ctx->callback_user_data = user_data; + + ppp_manager = nm_g_object_ref (priv->ppp_manager); + + NM_MODEM_GET_CLASS (self)->deactivate_cleanup (self, ctx->device, FALSE); + + if (ppp_manager) { + /* If we have a PPP manager, stop it. + * + * Pass on the reference in @ppp_manager. */ + nm_ppp_manager_stop (ppp_manager, + ctx->cancellable, + _deactivate_ppp_manager_stop_cb, + ctx); + return; + } - /* Start */ - ctx->step = DEACTIVATE_CONTEXT_STEP_FIRST; - deactivate_step (ctx); + _deactivate_call_disconnect (ctx); } /*****************************************************************************/ @@ -1348,7 +1271,7 @@ void nm_modem_deactivate (NMModem *self, NMDevice *device) { /* First cleanup */ - NM_MODEM_GET_CLASS (self)->deactivate_cleanup (self, device); + NM_MODEM_GET_CLASS (self)->deactivate_cleanup (self, device, TRUE); /* Then disconnect without waiting */ NM_MODEM_GET_CLASS (self)->disconnect (self, FALSE, NULL, NULL, NULL); } @@ -1387,7 +1310,7 @@ nm_modem_device_state_changed (NMModem *self, if (new_state == NM_DEVICE_STATE_FAILED || new_state == NM_DEVICE_STATE_DISCONNECTED) warn = FALSE; /* First cleanup */ - NM_MODEM_GET_CLASS (self)->deactivate_cleanup (self, NULL); + NM_MODEM_GET_CLASS (self)->deactivate_cleanup (self, NULL, TRUE); NM_MODEM_GET_CLASS (self)->disconnect (self, warn, NULL, NULL, NULL); } break; diff --git a/src/devices/wwan/nm-modem.h b/src/devices/wwan/nm-modem.h index c73745ce..f7b6bfe9 100644 --- a/src/devices/wwan/nm-modem.h +++ b/src/devices/wwan/nm-modem.h @@ -109,6 +109,10 @@ struct _NMModem { typedef struct _NMModem NMModem; +typedef void (*_NMModemDisconnectCallback) (NMModem *modem, + GError *error, + gpointer user_data); + typedef struct { GObjectClass parent; @@ -149,13 +153,12 @@ typedef struct { void (*disconnect) (NMModem *self, gboolean warn, GCancellable *cancellable, - GAsyncReadyCallback callback, + _NMModemDisconnectCallback callback, gpointer user_data); - gboolean (*disconnect_finish) (NMModem *self, - GAsyncResult *res, - GError **error); - void (*deactivate_cleanup) (NMModem *self, NMDevice *device); + void (*deactivate_cleanup) (NMModem *self, + NMDevice *device, + gboolean stop_ppp_manager); gboolean (*owns_port) (NMModem *self, const char *iface); } NMModemClass; @@ -236,14 +239,15 @@ void nm_modem_get_secrets (NMModem *modem, void nm_modem_deactivate (NMModem *modem, NMDevice *device); +typedef void (*NMModemDeactivateCallback) (NMModem *self, + GError *error, + gpointer user_data); + void nm_modem_deactivate_async (NMModem *self, NMDevice *device, GCancellable *cancellable, - GAsyncReadyCallback callback, + NMModemDeactivateCallback callback, gpointer user_data); -gboolean nm_modem_deactivate_async_finish (NMModem *self, - GAsyncResult *res, - GError **error); void nm_modem_device_state_changed (NMModem *modem, NMDeviceState new_state, diff --git a/src/dhcp/meson.build b/src/dhcp/meson.build index 76707bca..a5dd3151 100644 --- a/src/dhcp/meson.build +++ b/src/dhcp/meson.build @@ -13,7 +13,7 @@ executable( link_args: ldflags_linker_script_binary, link_depends: linker_script_binary, install: true, - install_dir: nm_libexecdir + install_dir: nm_libexecdir, ) if enable_tests diff --git a/src/dhcp/nm-dhcp-client.c b/src/dhcp/nm-dhcp-client.c index 16db8306..7ed7a686 100644 --- a/src/dhcp/nm-dhcp-client.c +++ b/src/dhcp/nm-dhcp-client.c @@ -28,7 +28,6 @@ #include <unistd.h> #include <stdio.h> #include <stdlib.h> -#include <uuid/uuid.h> #include <linux/rtnetlink.h> #include "nm-utils/nm-dedup-multi.h" @@ -62,6 +61,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDhcpClient, PROP_ROUTE_TABLE, PROP_TIMEOUT, PROP_UUID, + PROP_HOSTNAME, ); typedef struct _NMDhcpClientPrivate { @@ -69,7 +69,6 @@ typedef struct _NMDhcpClientPrivate { char * iface; GBytes * hwaddr; char * uuid; - GBytes * duid; GBytes * client_id; char * hostname; pid_t pid; @@ -140,14 +139,6 @@ nm_dhcp_client_get_uuid (NMDhcpClient *self) } GBytes * -nm_dhcp_client_get_duid (NMDhcpClient *self) -{ - g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL); - - return NM_DHCP_CLIENT_GET_PRIVATE (self)->duid; -} - -GBytes * nm_dhcp_client_get_hw_addr (NMDhcpClient *self) { g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL); @@ -230,6 +221,18 @@ _set_client_id (NMDhcpClient *self, GBytes *client_id, gboolean take) priv->client_id = client_id; if (!take && client_id) g_bytes_ref (client_id); + + { + gs_free char *s = NULL; + + _LOGT ("%s: set %s", + nm_dhcp_client_get_addr_family (self) == AF_INET6 + ? "duid" + : "client-id", + priv->client_id + ? (s = nm_dhcp_utils_duid_to_string (priv->client_id)) + : "default"); + } } void @@ -288,12 +291,13 @@ nm_dhcp_client_get_use_fqdn (NMDhcpClient *self) /*****************************************************************************/ static const char *state_table[NM_DHCP_STATE_MAX + 1] = { - [NM_DHCP_STATE_UNKNOWN] = "unknown", - [NM_DHCP_STATE_BOUND] = "bound", - [NM_DHCP_STATE_TIMEOUT] = "timeout", - [NM_DHCP_STATE_EXPIRE] = "expire", - [NM_DHCP_STATE_DONE] = "done", - [NM_DHCP_STATE_FAIL] = "fail", + [NM_DHCP_STATE_UNKNOWN] = "unknown", + [NM_DHCP_STATE_BOUND] = "bound", + [NM_DHCP_STATE_TIMEOUT] = "timeout", + [NM_DHCP_STATE_EXPIRE] = "expire", + [NM_DHCP_STATE_DONE] = "done", + [NM_DHCP_STATE_FAIL] = "fail", + [NM_DHCP_STATE_TERMINATED] = "terminated", }; static const char * @@ -362,7 +366,7 @@ nm_dhcp_client_stop_pid (pid_t pid, const char *iface) } static void -stop (NMDhcpClient *self, gboolean release, GBytes *duid) +stop (NMDhcpClient *self, gboolean release) { NMDhcpClientPrivate *priv; @@ -449,7 +453,6 @@ daemon_watch_cb (GPid pid, int status, gpointer user_data) { NMDhcpClient *self = NM_DHCP_CLIENT (user_data); NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self); - NMDhcpState new_state; g_return_if_fail (priv->watch_id); priv->watch_id = 0; @@ -465,14 +468,9 @@ daemon_watch_cb (GPid pid, int status, gpointer user_data) else _LOGW ("client died abnormally"); - if (!WIFEXITED (status)) - new_state = NM_DHCP_STATE_FAIL; - else - new_state = NM_DHCP_STATE_DONE; - priv->pid = -1; - nm_dhcp_client_set_state (self, new_state, NULL, NULL); + nm_dhcp_client_set_state (self, NM_DHCP_STATE_TERMINATED, NULL, NULL); } void @@ -509,7 +507,6 @@ gboolean nm_dhcp_client_start_ip4 (NMDhcpClient *self, GBytes *client_id, const char *dhcp_anycast_addr, - const char *hostname, const char *last_ip4_address, GError **error) { @@ -529,9 +526,6 @@ nm_dhcp_client_start_ip4 (NMDhcpClient *self, nm_dhcp_client_set_client_id (self, client_id); - g_clear_pointer (&priv->hostname, g_free); - priv->hostname = g_strdup (hostname); - return NM_DHCP_CLIENT_GET_CLASS (self)->ip4_start (self, dhcp_anycast_addr, last_ip4_address, @@ -550,34 +544,29 @@ nm_dhcp_client_start_ip6 (NMDhcpClient *self, gboolean enforce_duid, const char *dhcp_anycast_addr, const struct in6_addr *ll_addr, - const char *hostname, NMSettingIP6ConfigPrivacy privacy, guint needed_prefixes, GError **error) { NMDhcpClientPrivate *priv; - gs_free char *str = NULL; + gs_unref_bytes GBytes *own_client_id = NULL; g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE); + g_return_val_if_fail (client_id, FALSE); priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + g_return_val_if_fail (priv->pid == -1, FALSE); g_return_val_if_fail (priv->addr_family == AF_INET6, FALSE); g_return_val_if_fail (priv->uuid != NULL, FALSE); - - nm_assert (!priv->duid); - nm_assert (client_id); + g_return_val_if_fail (!priv->client_id, FALSE); if (!enforce_duid) - priv->duid = NM_DHCP_CLIENT_GET_CLASS (self)->get_duid (self); - - if (!priv->duid) - priv->duid = g_bytes_ref (client_id); + own_client_id = NM_DHCP_CLIENT_GET_CLASS (self)->get_duid (self); - _LOGD ("DUID is '%s'", (str = nm_dhcp_utils_duid_to_string (priv->duid))); - - g_clear_pointer (&priv->hostname, g_free); - priv->hostname = g_strdup (hostname); + _set_client_id (self, + own_client_id ?: client_id, + FALSE); if (priv->timeout == NM_DHCP_TIMEOUT_INFINITY) _LOGI ("activation: beginning transaction (no timeout)"); @@ -588,7 +577,6 @@ nm_dhcp_client_start_ip6 (NMDhcpClient *self, dhcp_anycast_addr, ll_addr, privacy, - priv->duid, needed_prefixes, error); } @@ -655,7 +643,7 @@ nm_dhcp_client_stop (NMDhcpClient *self, gboolean release) /* Kill the DHCP client */ old_pid = priv->pid; - NM_DHCP_CLIENT_GET_CLASS (self)->stop (self, release, priv->duid); + NM_DHCP_CLIENT_GET_CLASS (self)->stop (self, release); if (old_pid > 0) _LOGI ("canceled DHCP transaction, DHCP client pid %d", old_pid); else @@ -859,6 +847,9 @@ get_property (GObject *object, guint prop_id, case PROP_UUID: g_value_set_string (value, priv->uuid); break; + case PROP_HOSTNAME: + g_value_set_string (value, priv->hostname); + break; case PROP_ROUTE_METRIC: g_value_set_uint (value, priv->route_metric); break; @@ -899,11 +890,13 @@ set_property (GObject *object, guint prop_id, case PROP_IFACE: /* construct-only */ priv->iface = g_value_dup_string (value); + g_return_if_fail ( priv->iface + && nm_utils_is_valid_iface_name (priv->iface, NULL)); break; case PROP_IFINDEX: /* construct-only */ priv->ifindex = g_value_get_int (value); - g_warn_if_fail (priv->ifindex > 0); + g_return_if_fail (priv->ifindex > 0); break; case PROP_HWADDR: /* construct-only */ @@ -919,6 +912,10 @@ set_property (GObject *object, guint prop_id, /* construct-only */ priv->uuid = g_value_dup_string (value); break; + case PROP_HOSTNAME: + /* construct-only */ + priv->hostname = g_value_dup_string (value); + break; case PROP_ROUTE_TABLE: priv->route_table = g_value_get_uint (value); break; @@ -971,7 +968,6 @@ dispose (GObject *object) g_clear_pointer (&priv->uuid, g_free); g_clear_pointer (&priv->client_id, g_bytes_unref); g_clear_pointer (&priv->hwaddr, g_bytes_unref); - g_clear_pointer (&priv->duid, g_bytes_unref); G_OBJECT_CLASS (nm_dhcp_client_parent_class)->dispose (object); @@ -1028,6 +1024,12 @@ nm_dhcp_client_class_init (NMDhcpClientClass *client_class) G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_HOSTNAME] = + g_param_spec_string (NM_DHCP_CLIENT_HOSTNAME, "", "", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + obj_properties[PROP_ROUTE_TABLE] = g_param_spec_uint (NM_DHCP_CLIENT_ROUTE_TABLE, "", "", 0, G_MAXUINT32, RT_TABLE_MAIN, @@ -1058,7 +1060,7 @@ nm_dhcp_client_class_init (NMDhcpClientClass *client_class) g_signal_new (NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (NMDhcpClientClass, state_changed), + 0, NULL, NULL, NULL, G_TYPE_NONE, 4, G_TYPE_UINT, G_TYPE_OBJECT, G_TYPE_HASH_TABLE, G_TYPE_STRING); @@ -1066,7 +1068,7 @@ nm_dhcp_client_class_init (NMDhcpClientClass *client_class) g_signal_new (NM_DHCP_CLIENT_SIGNAL_PREFIX_DELEGATED, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (NMDhcpClientClass, state_changed), + 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_POINTER); } diff --git a/src/dhcp/nm-dhcp-client.h b/src/dhcp/nm-dhcp-client.h index 86d60e38..1db7eac6 100644 --- a/src/dhcp/nm-dhcp-client.h +++ b/src/dhcp/nm-dhcp-client.h @@ -41,6 +41,7 @@ #define NM_DHCP_CLIENT_IFINDEX "ifindex" #define NM_DHCP_CLIENT_INTERFACE "iface" #define NM_DHCP_CLIENT_MULTI_IDX "multi-idx" +#define NM_DHCP_CLIENT_HOSTNAME "hostname" #define NM_DHCP_CLIENT_ROUTE_METRIC "route-metric" #define NM_DHCP_CLIENT_ROUTE_TABLE "route-table" #define NM_DHCP_CLIENT_TIMEOUT "timeout" @@ -56,6 +57,7 @@ typedef enum { NM_DHCP_STATE_DONE, /* client quit or stopped */ NM_DHCP_STATE_EXPIRE, /* lease expired or NAKed */ NM_DHCP_STATE_FAIL, /* failed for some reason */ + NM_DHCP_STATE_TERMINATED, /* client is no longer running */ __NM_DHCP_STATE_MAX, NM_DHCP_STATE_MAX = __NM_DHCP_STATE_MAX - 1, } NMDhcpState; @@ -85,13 +87,11 @@ typedef struct { const char *anycast_addr, const struct in6_addr *ll_addr, NMSettingIP6ConfigPrivacy privacy, - GBytes *duid, guint needed_prefixes, GError **error); void (*stop) (NMDhcpClient *self, - gboolean release, - GBytes *duid); + gboolean release); /** * get_duid: @@ -103,12 +103,6 @@ typedef struct { * returned. */ GBytes *(*get_duid) (NMDhcpClient *self); - - /* Signals */ - void (*state_changed) (NMDhcpClient *self, - NMDhcpState state, - GObject *ip_config, - GHashTable *options); } NMDhcpClientClass; GType nm_dhcp_client_get_type (void); @@ -150,7 +144,6 @@ gboolean nm_dhcp_client_get_use_fqdn (NMDhcpClient *self); gboolean nm_dhcp_client_start_ip4 (NMDhcpClient *self, GBytes *client_id, const char *dhcp_anycast_addr, - const char *hostname, const char *last_ip4_address, GError **error); @@ -159,7 +152,6 @@ gboolean nm_dhcp_client_start_ip6 (NMDhcpClient *self, gboolean enforce_duid, const char *dhcp_anycast_addr, const struct in6_addr *ll_addr, - const char *hostname, NMSettingIP6ConfigPrivacy privacy, guint needed_prefixes, GError **error); diff --git a/src/dhcp/nm-dhcp-dhclient-utils.c b/src/dhcp/nm-dhcp-dhclient-utils.c index a2c3bfb6..d6da3f5c 100644 --- a/src/dhcp/nm-dhcp-dhclient-utils.c +++ b/src/dhcp/nm-dhcp-dhclient-utils.c @@ -24,6 +24,7 @@ #include <string.h> #include <ctype.h> #include <arpa/inet.h> +#include <net/if.h> #include "nm-utils/nm-dedup-multi.h" @@ -233,29 +234,6 @@ read_client_id (const char *str) return nm_utils_hexstr2bin (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, NM_STRLEN (CLIENTID_TAG))) - return read_client_id (*line); - } - return NULL; -} - static gboolean read_interface (const char *line, char *interface, guint size) { @@ -449,6 +427,8 @@ nm_dhcp_dhclient_create_config (const char *interface, add_hostname6 (new_contents, hostname); add_request (reqs, "dhcp6.name-servers"); add_request (reqs, "dhcp6.domain-search"); + + /* FIXME: internal client does not support requesting client-id option. Does this even work? */ add_request (reqs, "dhcp6.client-id"); } @@ -570,6 +550,7 @@ error: #define DUID_PREFIX "default-duid \"" +/* Beware: @error may be unset even if the function returns %NULL. */ GBytes * nm_dhcp_dhclient_read_duid (const char *leasefile, GError **error) { @@ -606,9 +587,10 @@ nm_dhcp_dhclient_read_duid (const char *leasefile, GError **error) gboolean nm_dhcp_dhclient_save_duid (const char *leasefile, - const char *escaped_duid, + GBytes *duid, GError **error) { + gs_free char *escaped_duid = NULL; gs_strfreev char **lines = NULL; char **iter, *l; GString *s; @@ -616,6 +598,14 @@ nm_dhcp_dhclient_save_duid (const char *leasefile, gsize len = 0; g_return_val_if_fail (leasefile != NULL, FALSE); + + if (!duid) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, + "missing duid"); + g_return_val_if_reached (FALSE); + } + + escaped_duid = nm_dhcp_dhclient_escape_duid (duid); g_return_val_if_fail (escaped_duid != NULL, FALSE); if (g_file_test (leasefile, G_FILE_TEST_EXISTS)) { diff --git a/src/dhcp/nm-dhcp-dhclient-utils.h b/src/dhcp/nm-dhcp-dhclient-utils.h index fab9196a..57a711db 100644 --- a/src/dhcp/nm-dhcp-dhclient-utils.h +++ b/src/dhcp/nm-dhcp-dhclient-utils.h @@ -40,10 +40,7 @@ GBytes *nm_dhcp_dhclient_unescape_duid (const char *duid); GBytes *nm_dhcp_dhclient_read_duid (const char *leasefile, GError **error); gboolean nm_dhcp_dhclient_save_duid (const char *leasefile, - const char *escaped_duid, + GBytes *duid, GError **error); -GBytes *nm_dhcp_dhclient_get_client_id_from_config_file (const char *path); - #endif /* __NETWORKMANAGER_DHCP_DHCLIENT_UTILS_H__ */ - diff --git a/src/dhcp/nm-dhcp-dhclient.c b/src/dhcp/nm-dhcp-dhclient.c index 33c26712..0146c8b4 100644 --- a/src/dhcp/nm-dhcp-dhclient.c +++ b/src/dhcp/nm-dhcp-dhclient.c @@ -121,8 +121,8 @@ get_dhclient_leasefile (int addr_family, const char *uuid, char **out_preferred_path) { - char *rundir_path; - char *path; + gs_free char *rundir_path = NULL; + gs_free char *path = NULL; /* First, see if the lease file is in /run */ rundir_path = g_strdup_printf (NMRUNDIR "/dhclient%s-%s-%s.lease", @@ -132,7 +132,7 @@ get_dhclient_leasefile (int addr_family, if (g_file_test (rundir_path, G_FILE_TEST_EXISTS)) { NM_SET_OUT (out_preferred_path, g_strdup (rundir_path)); - return rundir_path; + return g_steal_pointer (&rundir_path); } /* /var/lib/NetworkManager is the preferred leasefile path */ @@ -142,18 +142,14 @@ get_dhclient_leasefile (int addr_family, iface); if (g_file_test (path, G_FILE_TEST_EXISTS)) { - g_free (rundir_path); NM_SET_OUT (out_preferred_path, g_strdup (path)); - return path; + return g_steal_pointer (&path); } - if (nm_config_get_configure_and_quit (nm_config_get ()) == NM_CONFIG_CONFIGURE_AND_QUIT_INITRD) { - g_free (path); - path = rundir_path; - } else { - g_free (rundir_path); - } - NM_SET_OUT (out_preferred_path, g_steal_pointer (&path)); + if (nm_config_get_configure_and_quit (nm_config_get ()) == NM_CONFIG_CONFIGURE_AND_QUIT_INITRD) + NM_SET_OUT (out_preferred_path, g_steal_pointer (&rundir_path)); + else + NM_SET_OUT (out_preferred_path, g_steal_pointer (&path)); /* If the leasefile we're looking for doesn't exist yet in the new location * (eg, /var/lib/NetworkManager) then look in old locations to maintain @@ -166,17 +162,16 @@ get_dhclient_leasefile (int addr_family, path = g_strdup_printf (LOCALSTATEDIR "/lib/dhcp/dhclient%s-%s-%s.lease", _addr_family_to_path_part (addr_family), uuid, iface); if (g_file_test (path, G_FILE_TEST_EXISTS)) - return path; + return g_steal_pointer (&path); /* Old Red Hat and Fedora location */ g_free (path); path = g_strdup_printf (LOCALSTATEDIR "/lib/dhclient/dhclient%s-%s-%s.lease", _addr_family_to_path_part (addr_family), uuid, iface); if (g_file_test (path, G_FILE_TEST_EXISTS)) - return path; + return g_steal_pointer (&path); /* Fail */ - g_free (path); return NULL; } @@ -327,8 +322,18 @@ create_dhclient_config (NMDhcpDhclient *self, else _LOGD ("no existing dhclient configuration to merge"); - if (!merge_dhclient_config (self, addr_family, iface, new, client_id, dhcp_anycast_addr, - hostname, timeout, use_fqdn, orig, out_new_client_id, &error)) { + if (!merge_dhclient_config (self, + addr_family, + iface, + new, + client_id, + dhcp_anycast_addr, + hostname, + timeout, + use_fqdn, + orig, + out_new_client_id, + &error)) { _LOGW ("error creating dhclient configuration: %s", error->message); g_clear_error (&error); } @@ -339,7 +344,6 @@ create_dhclient_config (NMDhcpDhclient *self, static gboolean dhclient_start (NMDhcpClient *client, const char *mode_opt, - GBytes *duid, gboolean release, pid_t *out_pid, int prefixes, @@ -418,10 +422,9 @@ dhclient_start (NMDhcpClient *client, /* Save the DUID to the leasefile dhclient will actually use */ if (addr_family == AF_INET6) { - gs_free char *escaped = NULL; - - escaped = nm_dhcp_dhclient_escape_duid (duid); - if (!nm_dhcp_dhclient_save_duid (priv->lease_file, escaped, &local)) { + if (!nm_dhcp_dhclient_save_duid (priv->lease_file, + nm_dhcp_client_get_client_id (client), + &local)) { nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, "failed to save DUID to '%s': %s", @@ -541,7 +544,6 @@ ip4_start (NMDhcpClient *client, } return dhclient_start (client, NULL, - NULL, FALSE, NULL, 0, @@ -553,7 +555,6 @@ ip6_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const struct in6_addr *ll_addr, NMSettingIP6ConfigPrivacy privacy, - GBytes *duid, guint needed_prefixes, GError **error) { @@ -581,7 +582,6 @@ ip6_start (NMDhcpClient *client, nm_dhcp_client_get_info_only (NM_DHCP_CLIENT (self)) ? "-S" : "-N", - duid, FALSE, NULL, needed_prefixes, @@ -589,12 +589,12 @@ ip6_start (NMDhcpClient *client, } static void -stop (NMDhcpClient *client, gboolean release, GBytes *duid) +stop (NMDhcpClient *client, gboolean release) { NMDhcpDhclient *self = NM_DHCP_DHCLIENT (client); NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (self); - NM_DHCP_CLIENT_CLASS (nm_dhcp_dhclient_parent_class)->stop (client, release, duid); + NM_DHCP_CLIENT_CLASS (nm_dhcp_dhclient_parent_class)->stop (client, release); if (priv->conf_file) if (remove (priv->conf_file) == -1) @@ -613,7 +613,6 @@ stop (NMDhcpClient *client, gboolean release, GBytes *duid) if (dhclient_start (client, NULL, - duid, TRUE, &rpid, 0, @@ -624,31 +623,13 @@ stop (NMDhcpClient *client, gboolean release, GBytes *duid) } } -static void -state_changed (NMDhcpClient *client, - NMDhcpState state, - GObject *ip_config, - GHashTable *options) -{ - NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE ((NMDhcpDhclient *) 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 GBytes * get_duid (NMDhcpClient *client) { NMDhcpDhclient *self = NM_DHCP_DHCLIENT (client); NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (self); GBytes *duid = NULL; - char *leasefile; + gs_free char *leasefile = NULL; GError *error = NULL; /* Look in interface-specific leasefile first for backwards compat */ @@ -659,25 +640,23 @@ get_duid (NMDhcpClient *client) if (leasefile) { _LOGD ("looking for DUID in '%s'", leasefile); duid = nm_dhcp_dhclient_read_duid (leasefile, &error); - if (error) { _LOGW ("failed to read leasefile '%s': %s", leasefile, error->message); g_clear_error (&error); } - g_free (leasefile); + if (duid) + return duid; } - if (!duid) { - /* Otherwise read the default machine-wide DUID */ - _LOGD ("looking for default DUID in '%s'", priv->def_leasefile); - duid = nm_dhcp_dhclient_read_duid (priv->def_leasefile, &error); - if (error) { - _LOGW ("failed to read leasefile '%s': %s", - priv->def_leasefile, - error->message); - g_clear_error (&error); - } + /* Otherwise read the default machine-wide DUID */ + _LOGD ("looking for default DUID in '%s'", priv->def_leasefile); + duid = nm_dhcp_dhclient_read_duid (priv->def_leasefile, &error); + if (error) { + _LOGW ("failed to read leasefile '%s': %s", + priv->def_leasefile, + error->message); + g_clear_error (&error); } return duid; @@ -742,7 +721,6 @@ 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; } const NMDhcpClientFactory _nm_dhcp_client_factory_dhclient = { diff --git a/src/dhcp/nm-dhcp-dhcpcanon.c b/src/dhcp/nm-dhcp-dhcpcanon.c index de403020..0f033e22 100644 --- a/src/dhcp/nm-dhcp-dhcpcanon.c +++ b/src/dhcp/nm-dhcp-dhcpcanon.c @@ -193,20 +193,20 @@ ip6_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const struct in6_addr *ll_addr, NMSettingIP6ConfigPrivacy privacy, - GBytes *duid, guint needed_prefixes, GError **error) { nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, "dhcpcanon plugin does not support IPv6"); return FALSE; } + static void -stop (NMDhcpClient *client, gboolean release, GBytes *duid) +stop (NMDhcpClient *client, gboolean release) { NMDhcpDhcpcanon *self = NM_DHCP_DHCPCANON (client); NMDhcpDhcpcanonPrivate *priv = NM_DHCP_DHCPCANON_GET_PRIVATE (self); - NM_DHCP_CLIENT_CLASS (nm_dhcp_dhcpcanon_parent_class)->stop (client, release, duid); + NM_DHCP_CLIENT_CLASS (nm_dhcp_dhcpcanon_parent_class)->stop (client, release); if (priv->pid_file) { if (remove (priv->pid_file) == -1) @@ -216,18 +216,6 @@ stop (NMDhcpClient *client, gboolean release, GBytes *duid) } } -static void -state_changed (NMDhcpClient *client, - NMDhcpState state, - GObject *ip_config, - GHashTable *options) -{ - if (nm_dhcp_client_get_client_id (client)) - return; - if (state != NM_DHCP_STATE_BOUND) - return; -} - /*****************************************************************************/ static void @@ -270,7 +258,6 @@ nm_dhcp_dhcpcanon_class_init (NMDhcpDhcpcanonClass *dhcpcanon_class) client_class->ip4_start = ip4_start; client_class->ip6_start = ip6_start; client_class->stop = stop; - client_class->state_changed = state_changed; } const NMDhcpClientFactory _nm_dhcp_client_factory_dhcpcanon = { diff --git a/src/dhcp/nm-dhcp-dhcpcd.c b/src/dhcp/nm-dhcp-dhcpcd.c index 98ab5342..e2a1354f 100644 --- a/src/dhcp/nm-dhcp-dhcpcd.c +++ b/src/dhcp/nm-dhcp-dhcpcd.c @@ -187,7 +187,6 @@ ip6_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const struct in6_addr *ll_addr, NMSettingIP6ConfigPrivacy privacy, - GBytes *duid, guint needed_prefixes, GError **error) { @@ -196,12 +195,12 @@ ip6_start (NMDhcpClient *client, } static void -stop (NMDhcpClient *client, gboolean release, GBytes *duid) +stop (NMDhcpClient *client, gboolean release) { NMDhcpDhcpcd *self = NM_DHCP_DHCPCD (client); NMDhcpDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE (self); - NM_DHCP_CLIENT_CLASS (nm_dhcp_dhcpcd_parent_class)->stop (client, release, duid); + NM_DHCP_CLIENT_CLASS (nm_dhcp_dhcpcd_parent_class)->stop (client, release); if (priv->pid_file) { if (remove (priv->pid_file) == -1) diff --git a/src/dhcp/nm-dhcp-helper.c b/src/dhcp/nm-dhcp-helper.c index 7f1d2a7b..83cc4600 100644 --- a/src/dhcp/nm-dhcp-helper.c +++ b/src/dhcp/nm-dhcp-helper.c @@ -190,7 +190,7 @@ do_notify: _LOGW ("failure to call notify: %s (try signal via Event)", error->message); g_clear_error (&error); - /* for backward compatibilty, try to emit the signal. There is no stable + /* for backward compatibility, try to emit the signal. There is no stable * API between the dhcp-helper and NetworkManager. However, while upgrading * the NetworkManager package, a newer helper might want to notify an * older server, which still uses the "Event". */ diff --git a/src/dhcp/nm-dhcp-manager.c b/src/dhcp/nm-dhcp-manager.c index 5ae16d72..c13c3043 100644 --- a/src/dhcp/nm-dhcp-manager.c +++ b/src/dhcp/nm-dhcp-manager.c @@ -181,6 +181,7 @@ client_start (NMDhcpManager *self, gsize hwaddr_len; g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL); + g_return_val_if_fail (iface, NULL); g_return_val_if_fail (ifindex > 0, NULL); g_return_val_if_fail (uuid != NULL, NULL); g_return_val_if_fail (!dhcp_client_id || g_bytes_get_size (dhcp_client_id) >= 2, NULL); @@ -221,6 +222,7 @@ client_start (NMDhcpManager *self, NM_DHCP_CLIENT_IFINDEX, ifindex, NM_DHCP_CLIENT_HWADDR, hwaddr, NM_DHCP_CLIENT_UUID, uuid, + NM_DHCP_CLIENT_HOSTNAME, hostname, NM_DHCP_CLIENT_ROUTE_TABLE, (guint) route_table, NM_DHCP_CLIENT_ROUTE_METRIC, (guint) route_metric, NM_DHCP_CLIENT_TIMEOUT, (guint) timeout, @@ -233,11 +235,36 @@ client_start (NMDhcpManager *self, c_list_link_tail (&priv->dhcp_client_lst_head, &client->dhcp_client_lst); g_signal_connect (client, NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED, G_CALLBACK (client_state_changed), self); + /* unfortunately, our implementations work differently per address-family regarding client-id/DUID. + * + * - for IPv4, the calling code may determine a client-id (from NM's connection profile). + * If present, it is taken. If not present, the DHCP plugin uses a plugin specific default. + * - for "internal" plugin, the default is just "mac". + * - for "dhclient", we try to get the configuration from dhclient's /etc/dhcp or fallback + * to whatever dhclient uses by default. + * We do it this way, because for dhclient the user may configure a default + * outside of NM, and we want to honor that. Worse, dhclient could be a wapper + * script where the wrapper script overwrites the client-id. We need to distinguish + * between: force a particular client-id and leave it unspecified to whatever dhclient + * wants. + * + * - for IPv6, the calling code always determines a client-id. It also specifies @enforce_duid, + * to determine whether the given client-id must be used. + * - for "internal" plugin @enforce_duid doesn't matter and the given client-id is + * always used. + * - for "dhclient", @enforce_duid FALSE means to first try to load the DUID from the + * lease file, and only otherwise fallback to the given client-id. + * - other plugins don't support DHCPv6. + * It's done this way, so that existing dhclient setups don't change behavior on upgrade. + * + * This difference is cumbersome and only exists because of "dhclient" which supports hacking the + * default outside of NetworkManager API. + */ + if (addr_family == AF_INET) { success = nm_dhcp_client_start_ip4 (client, dhcp_client_id, dhcp_anycast_addr, - hostname, last_ip4_address, error); } else { @@ -246,7 +273,6 @@ client_start (NMDhcpManager *self, enforce_duid, dhcp_anycast_addr, ipv6_ll_addr, - hostname, privacy, needed_prefixes, error); @@ -311,10 +337,27 @@ nm_dhcp_manager_start_ip4 (NMDhcpManager *self, } } - return client_start (self, AF_INET, multi_idx, iface, ifindex, hwaddr, uuid, - route_table, route_metric, NULL, - dhcp_client_id, 0, timeout, dhcp_anycast_addr, hostname, - use_fqdn, FALSE, 0, last_ip_address, 0, error); + return client_start (self, + AF_INET, + multi_idx, + iface, + ifindex, + hwaddr, + uuid, + route_table, + route_metric, + NULL, + dhcp_client_id, + FALSE, + timeout, + dhcp_anycast_addr, + hostname, + use_fqdn, + FALSE, + 0, + last_ip_address, + 0, + error); } /* Caller owns a reference to the NMDhcpClient on return */ @@ -349,10 +392,27 @@ nm_dhcp_manager_start_ip6 (NMDhcpManager *self, /* Always prefer the explicit dhcp-hostname if given */ hostname = dhcp_hostname ?: priv->default_hostname; } - return client_start (self, AF_INET6, multi_idx, iface, ifindex, hwaddr, uuid, - route_table, route_metric, ll_addr, duid, enforce_duid, - timeout, dhcp_anycast_addr, hostname, TRUE, info_only, - privacy, NULL, needed_prefixes, error); + return client_start (self, + AF_INET6, + multi_idx, + iface, + ifindex, + hwaddr, + uuid, + route_table, + route_metric, + ll_addr, + duid, + enforce_duid, + timeout, + dhcp_anycast_addr, + hostname, + TRUE, + info_only, + privacy, + NULL, + needed_prefixes, + error); } void @@ -384,6 +444,12 @@ nm_dhcp_manager_get_config (NMDhcpManager *self) NM_DEFINE_SINGLETON_GETTER (NMDhcpManager, nm_dhcp_manager_get, NM_TYPE_DHCP_MANAGER); +void +nmtst_dhcp_manager_unget (gpointer self) +{ + _nmtst_nm_dhcp_manager_get_reset (self); +} + static void nm_dhcp_manager_init (NMDhcpManager *self) { @@ -446,6 +512,10 @@ nm_dhcp_manager_init (NMDhcpManager *self) nm_log_info (LOGD_DHCP, "dhcp-init: Using DHCP client '%s'", client_factory->name); + /* NOTE: currently the DHCP plugin is chosen once at start. It's not + * possible to reload that configuration. If that ever becomes possible, + * beware that the "dhcp-plugin" device spec made decisions based on + * the previous plugin and may need reevaluation. */ priv->client_factory = client_factory; } diff --git a/src/dhcp/nm-dhcp-manager.h b/src/dhcp/nm-dhcp-manager.h index 1d9e5c21..f8f39e53 100644 --- a/src/dhcp/nm-dhcp-manager.h +++ b/src/dhcp/nm-dhcp-manager.h @@ -87,4 +87,6 @@ extern const char* nm_dhcp_helper_path; extern const NMDhcpClientFactory *const _nm_dhcp_manager_factories[4]; +void nmtst_dhcp_manager_unget (gpointer singleton_instance); + #endif /* __NETWORKMANAGER_DHCP_MANAGER_H__ */ diff --git a/src/dhcp/nm-dhcp-systemd.c b/src/dhcp/nm-dhcp-systemd.c index 5b7b5fbe..bcbe916f 100644 --- a/src/dhcp/nm-dhcp-systemd.c +++ b/src/dhcp/nm-dhcp-systemd.c @@ -34,6 +34,7 @@ #include "nm-utils.h" #include "nm-config.h" #include "nm-dhcp-utils.h" +#include "nm-core-utils.h" #include "NetworkManagerUtils.h" #include "platform/nm-platform.h" #include "nm-dhcp-client-logging.h" @@ -97,63 +98,77 @@ G_DEFINE_TYPE (NMDhcpSystemd, nm_dhcp_systemd, NM_TYPE_DHCP_CLIENT) #define DHCP6_OPTION_IAID 1034 typedef struct { - guint num; const char *name; - gboolean include; + uint16_t option_num; + bool include; } ReqOption; #define REQPREFIX "requested_" +#define REQ(_num, _name, _include) \ + { \ + .name = REQPREFIX""_name, \ + .option_num = _num, \ + .include = _include, \ + } + static const ReqOption dhcp4_requests[] = { - { SD_DHCP_OPTION_SUBNET_MASK, REQPREFIX "subnet_mask", TRUE }, - { SD_DHCP_OPTION_TIME_OFFSET, REQPREFIX "time_offset", TRUE }, - { SD_DHCP_OPTION_ROUTER, REQPREFIX "routers", TRUE }, - { SD_DHCP_OPTION_DOMAIN_NAME_SERVER, REQPREFIX "domain_name_servers", TRUE }, - { SD_DHCP_OPTION_HOST_NAME, REQPREFIX "host_name", TRUE }, - { SD_DHCP_OPTION_DOMAIN_NAME, REQPREFIX "domain_name", TRUE }, - { SD_DHCP_OPTION_INTERFACE_MTU, REQPREFIX "interface_mtu", TRUE }, - { SD_DHCP_OPTION_BROADCAST, REQPREFIX "broadcast_address", TRUE }, - { SD_DHCP_OPTION_STATIC_ROUTE, REQPREFIX "static_routes", TRUE }, - { DHCP_OPTION_NIS_DOMAIN, REQPREFIX "nis_domain", TRUE }, - { DHCP_OPTION_NIS_SERVERS, REQPREFIX "nis_servers", TRUE }, - { SD_DHCP_OPTION_NTP_SERVER, REQPREFIX "ntp_servers", TRUE }, - { SD_DHCP_OPTION_SERVER_IDENTIFIER, REQPREFIX "dhcp_server_identifier", TRUE }, - { SD_DHCP_OPTION_DOMAIN_SEARCH_LIST, REQPREFIX "domain_search", TRUE }, - { SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, REQPREFIX "rfc3442_classless_static_routes", TRUE }, - { SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE, REQPREFIX "ms_classless_static_routes", TRUE }, - { SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY, REQPREFIX "wpad", TRUE }, - { SD_DHCP_OPTION_ROOT_PATH, REQPREFIX "root_path", TRUE }, + REQ (SD_DHCP_OPTION_SUBNET_MASK, "subnet_mask", TRUE ), + REQ (SD_DHCP_OPTION_TIME_OFFSET, "time_offset", TRUE ), + REQ (SD_DHCP_OPTION_DOMAIN_NAME_SERVER, "domain_name_servers", TRUE ), + REQ (SD_DHCP_OPTION_HOST_NAME, "host_name", TRUE ), + REQ (SD_DHCP_OPTION_DOMAIN_NAME, "domain_name", TRUE ), + REQ (SD_DHCP_OPTION_INTERFACE_MTU, "interface_mtu", TRUE ), + REQ (SD_DHCP_OPTION_BROADCAST, "broadcast_address", TRUE ), + + /* RFC 3442: The Classless Static Routes option code MUST appear in the parameter + * request list prior to both the Router option code and the Static + * Routes option code, if present. */ + REQ (SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, "rfc3442_classless_static_routes", TRUE ), + REQ (SD_DHCP_OPTION_ROUTER, "routers", TRUE ), + REQ (SD_DHCP_OPTION_STATIC_ROUTE, "static_routes", TRUE ), + + REQ (DHCP_OPTION_NIS_DOMAIN, "nis_domain", TRUE ), + REQ (DHCP_OPTION_NIS_SERVERS, "nis_servers", TRUE ), + REQ (SD_DHCP_OPTION_NTP_SERVER, "ntp_servers", TRUE ), + REQ (SD_DHCP_OPTION_SERVER_IDENTIFIER, "dhcp_server_identifier", TRUE ), + REQ (SD_DHCP_OPTION_DOMAIN_SEARCH_LIST, "domain_search", TRUE ), + REQ (SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE, "ms_classless_static_routes", TRUE ), + REQ (SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY, "wpad", TRUE ), + REQ (SD_DHCP_OPTION_ROOT_PATH, "root_path", TRUE ), /* Internal values */ - { SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME, REQPREFIX "expiry", FALSE }, - { SD_DHCP_OPTION_CLIENT_IDENTIFIER, REQPREFIX "dhcp_client_identifier", FALSE }, - { DHCP_OPTION_IP_ADDRESS, REQPREFIX "ip_address", FALSE }, - { 0, NULL, FALSE } + REQ (SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME, "expiry", FALSE ), + REQ (SD_DHCP_OPTION_CLIENT_IDENTIFIER, "dhcp_client_identifier", FALSE ), + REQ (DHCP_OPTION_IP_ADDRESS, "ip_address", FALSE ), + + { 0 } }; static const ReqOption dhcp6_requests[] = { - { SD_DHCP6_OPTION_CLIENTID, REQPREFIX "dhcp6_client_id", TRUE }, + REQ (SD_DHCP6_OPTION_CLIENTID, "dhcp6_client_id", FALSE ), /* Don't request server ID by default; some servers don't reply to * Information Requests that request the Server ID. */ - { SD_DHCP6_OPTION_SERVERID, REQPREFIX "dhcp6_server_id", FALSE }, + REQ (SD_DHCP6_OPTION_SERVERID, "dhcp6_server_id", FALSE ), - { SD_DHCP6_OPTION_DNS_SERVERS, REQPREFIX "dhcp6_name_servers", TRUE }, - { SD_DHCP6_OPTION_DOMAIN_LIST, REQPREFIX "dhcp6_domain_search", TRUE }, - { SD_DHCP6_OPTION_SNTP_SERVERS, REQPREFIX "dhcp6_sntp_servers", TRUE }, + REQ (SD_DHCP6_OPTION_DNS_SERVERS, "dhcp6_name_servers", TRUE ), + REQ (SD_DHCP6_OPTION_DOMAIN_LIST, "dhcp6_domain_search", TRUE ), + REQ (SD_DHCP6_OPTION_SNTP_SERVERS, "dhcp6_sntp_servers", TRUE ), /* Internal values */ - { DHCP6_OPTION_IP_ADDRESS, REQPREFIX "ip6_address", FALSE }, - { DHCP6_OPTION_PREFIXLEN, REQPREFIX "ip6_prefixlen", FALSE }, - { DHCP6_OPTION_PREFERRED_LIFE, REQPREFIX "preferred_life", FALSE }, - { DHCP6_OPTION_MAX_LIFE, REQPREFIX "max_life", FALSE }, - { DHCP6_OPTION_STARTS, REQPREFIX "starts", FALSE }, - { DHCP6_OPTION_LIFE_STARTS, REQPREFIX "life_starts", FALSE }, - { DHCP6_OPTION_RENEW, REQPREFIX "renew", FALSE }, - { DHCP6_OPTION_REBIND, REQPREFIX "rebind", FALSE }, - { DHCP6_OPTION_IAID, REQPREFIX "iaid", FALSE }, - { 0, NULL, FALSE } + REQ (DHCP6_OPTION_IP_ADDRESS, "ip6_address", FALSE ), + REQ (DHCP6_OPTION_PREFIXLEN, "ip6_prefixlen", FALSE ), + REQ (DHCP6_OPTION_PREFERRED_LIFE, "preferred_life", FALSE ), + REQ (DHCP6_OPTION_MAX_LIFE, "max_life", FALSE ), + REQ (DHCP6_OPTION_STARTS, "starts", FALSE ), + REQ (DHCP6_OPTION_LIFE_STARTS, "life_starts", FALSE ), + REQ (DHCP6_OPTION_RENEW, "renew", FALSE ), + REQ (DHCP6_OPTION_REBIND, "rebind", FALSE ), + REQ (DHCP6_OPTION_IAID, "iaid", FALSE ), + + { 0 } }; static void @@ -164,18 +179,22 @@ take_option (GHashTable *options, { guint i; - g_return_if_fail (value != NULL); + nm_assert (options); + nm_assert (requests); + nm_assert (value); for (i = 0; requests[i].name; i++) { - if (requests[i].num == option) { + nm_assert (g_str_has_prefix (requests[i].name, REQPREFIX)); + if (requests[i].option_num == option) { g_hash_table_insert (options, (gpointer) (requests[i].name + NM_STRLEN (REQPREFIX)), value); - break; + return; } } + /* Option should always be found */ - g_assert (requests[i].name); + nm_assert_not_reached (); } static void @@ -186,13 +205,6 @@ add_option (GHashTable *options, const ReqOption *requests, guint option, const } static void -add_option_u32 (GHashTable *options, const ReqOption *requests, guint option, guint32 value) -{ - if (options) - take_option (options, requests, option, g_strdup_printf ("%u", value)); -} - -static void add_option_u64 (GHashTable *options, const ReqOption *requests, guint option, guint64 value) { if (options) @@ -204,12 +216,21 @@ add_requests_to_options (GHashTable *options, const ReqOption *requests) { guint i; - for (i = 0; options && requests[i].name; i++) { + if (!options) + return; + + for (i = 0; requests[i].name; i++) { if (requests[i].include) g_hash_table_insert (options, (gpointer) requests[i].name, g_strdup ("1")); } } +static GHashTable * +create_options_dict (void) +{ + return g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_free); +} + #define LOG_LEASE(domain, ...) \ G_STMT_START { \ if (log_lease) { \ @@ -222,76 +243,91 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx, const char *iface, int ifindex, sd_dhcp_lease *lease, - GHashTable *options, guint32 route_table, guint32 route_metric, gboolean log_lease, + GHashTable **out_options, GError **error) { - NMIP4Config *ip4_config = NULL; - struct in_addr tmp_addr; + gs_unref_object NMIP4Config *ip4_config = NULL; + gs_unref_hashtable GHashTable *options = NULL; const struct in_addr *addr_list; - char buf[INET_ADDRSTRLEN]; + char addr_str[NM_UTILS_INET_ADDRSTRLEN]; const char *s; - guint32 lifetime = 0, i; - NMPlatformIP4Address address; nm_auto_free_gstring GString *str = NULL; gs_free sd_dhcp_route **routes = NULL; const char *const*search_domains = NULL; guint16 mtu; - int r, num; - guint64 end_time; + int i, num; const void *data; gsize data_len; gboolean metered = FALSE; - gboolean static_default_gateway = FALSE; - gboolean gateway_has = FALSE; - in_addr_t gateway = 0; + gboolean has_router_from_classless = FALSE; + gboolean has_classless_route = FALSE; + gboolean has_static_route = FALSE; + const gint32 ts = nm_utils_get_monotonic_timestamp_s (); + gint64 ts_time = time (NULL); + struct in_addr a_address; + struct in_addr a_netmask; + struct in_addr a_router; + guint32 a_plen; + guint32 a_lifetime; g_return_val_if_fail (lease != NULL, NULL); ip4_config = nm_ip4_config_new (multi_idx, ifindex); - /* Address */ - sd_dhcp_lease_get_address (lease, &tmp_addr); - memset (&address, 0, sizeof (address)); - address.address = tmp_addr.s_addr; - address.peer_address = tmp_addr.s_addr; - s = nm_utils_inet4_ntop (tmp_addr.s_addr, NULL); - LOG_LEASE (LOGD_DHCP4, "address %s", s); - add_option (options, dhcp4_requests, DHCP_OPTION_IP_ADDRESS, s); - - /* Prefix/netmask */ - sd_dhcp_lease_get_netmask (lease, &tmp_addr); - address.plen = nm_utils_ip4_netmask_to_prefix (tmp_addr.s_addr); - LOG_LEASE (LOGD_DHCP4, "plen %d", address.plen); + options = out_options ? create_options_dict () : NULL; + + if (sd_dhcp_lease_get_address (lease, &a_address) < 0) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, "could not get address from lease"); + return NULL; + } + nm_utils_inet4_ntop (a_address.s_addr, addr_str); + LOG_LEASE (LOGD_DHCP4, "address %s", addr_str); + add_option (options, dhcp4_requests, DHCP_OPTION_IP_ADDRESS, addr_str); + + if (sd_dhcp_lease_get_netmask (lease, &a_netmask) < 0) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, "could not get netmask from lease"); + return NULL; + } + a_plen = nm_utils_ip4_netmask_to_prefix (a_netmask.s_addr); + LOG_LEASE (LOGD_DHCP4, "plen %u", (guint) a_plen); add_option (options, dhcp4_requests, SD_DHCP_OPTION_SUBNET_MASK, - nm_utils_inet4_ntop (tmp_addr.s_addr, NULL)); - - /* Lease time */ - sd_dhcp_lease_get_lifetime (lease, &lifetime); - address.timestamp = nm_utils_get_monotonic_timestamp_s (); - address.lifetime = address.preferred = lifetime; - end_time = (guint64) time (NULL) + lifetime; - LOG_LEASE (LOGD_DHCP4, "expires in %" G_GUINT32_FORMAT " seconds", lifetime); + nm_utils_inet4_ntop (a_netmask.s_addr, addr_str)); + + if (sd_dhcp_lease_get_lifetime (lease, &a_lifetime) < 0) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, "could not get lifetime from lease"); + return NULL; + } + LOG_LEASE (LOGD_DHCP4, "expires in %u seconds (at %lld)", + (guint) a_lifetime, + (long long) (ts_time + a_lifetime)); add_option_u64 (options, dhcp4_requests, SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME, - end_time); - - address.addr_source = NM_IP_CONFIG_SOURCE_DHCP; - nm_ip4_config_add_address (ip4_config, &address); + (guint64) (ts_time + a_lifetime)); + + nm_ip4_config_add_address (ip4_config, + &((const NMPlatformIP4Address) { + .address = a_address.s_addr, + .peer_address = a_address.s_addr, + .plen = a_plen, + .addr_source = NM_IP_CONFIG_SOURCE_DHCP, + .timestamp = ts, + .lifetime = a_lifetime, + .preferred = a_lifetime, + })); - /* DNS Servers */ num = sd_dhcp_lease_get_dns (lease, &addr_list); if (num > 0) { nm_gstring_prepare (&str); for (i = 0; i < num; i++) { if (addr_list[i].s_addr) { nm_ip4_config_add_nameserver (ip4_config, addr_list[i].s_addr); - s = nm_utils_inet4_ntop (addr_list[i].s_addr, NULL); + s = nm_utils_inet4_ntop (addr_list[i].s_addr, addr_str); LOG_LEASE (LOGD_DHCP4, "nameserver '%s'", s); g_string_append_printf (str, "%s%s", str->len ? " " : "", s); } @@ -300,7 +336,6 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx, add_option (options, dhcp4_requests, SD_DHCP_OPTION_DOMAIN_NAME_SERVER, str->str); } - /* Search domains */ num = sd_dhcp_lease_get_search_domains (lease, (char ***) &search_domains); if (num > 0) { nm_gstring_prepare (&str); @@ -312,141 +347,191 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx, add_option (options, dhcp4_requests, SD_DHCP_OPTION_DOMAIN_SEARCH_LIST, str->str); } - /* Domain Name */ - r = sd_dhcp_lease_get_domainname (lease, &s); - if (r == 0) { - /* Multiple domains sometimes stuffed into option 15 "Domain Name". - * As systemd escapes such characters, split them at \\032. */ - char **domains = g_strsplit (s, "\\032", 0); + if ( sd_dhcp_lease_get_domainname (lease, &s) >= 0 + && s) { + gs_strfreev char **domains = NULL; char **d; + /* Multiple domains sometimes stuffed into option 15 "Domain Name". + * As systemd escapes such characters, split them at \\032. */ + domains = g_strsplit (s, "\\032", 0); for (d = domains; *d; d++) { LOG_LEASE (LOGD_DHCP4, "domain name '%s'", *d); nm_ip4_config_add_domain (ip4_config, *d); } - g_strfreev (domains); add_option (options, dhcp4_requests, SD_DHCP_OPTION_DOMAIN_NAME, s); } - /* Hostname */ - r = sd_dhcp_lease_get_hostname (lease, &s); - if (r == 0) { + if (sd_dhcp_lease_get_hostname (lease, &s) >= 0) { LOG_LEASE (LOGD_DHCP4, "hostname '%s'", s); add_option (options, dhcp4_requests, SD_DHCP_OPTION_HOST_NAME, s); } - /* Routes */ num = sd_dhcp_lease_get_routes (lease, &routes); if (num > 0) { - nm_gstring_prepare (&str); + nm_auto_free_gstring GString *str_classless = NULL; + nm_auto_free_gstring GString *str_static = NULL; + guint32 default_route_metric = route_metric; + for (i = 0; i < num; i++) { - NMPlatformIP4Route route = { 0 }; - const char *gw_str; - guint8 plen; - struct in_addr a; + switch (sd_dhcp_route_get_option (routes[i])) { + case SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE: + has_classless_route = TRUE; + break; + case SD_DHCP_OPTION_STATIC_ROUTE: + has_static_route = TRUE; + break; + } + } + + if (has_classless_route) + str_classless = g_string_sized_new (30); + if (has_static_route) + str_static = g_string_sized_new (30); - if (sd_dhcp_route_get_destination (routes[i], &a) < 0) + for (i = 0; i < num; i++) { + char network_net_str[NM_UTILS_INET_ADDRSTRLEN]; + char gateway_str[NM_UTILS_INET_ADDRSTRLEN]; + guint8 r_plen; + struct in_addr r_network; + struct in_addr r_gateway; + in_addr_t network_net; + int option; + guint32 m; + + option = sd_dhcp_route_get_option (routes[i]); + if (!NM_IN_SET (option, SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, + SD_DHCP_OPTION_STATIC_ROUTE)) continue; - if ( sd_dhcp_route_get_destination_prefix_length (routes[i], &plen) < 0 - || plen > 32) + if (sd_dhcp_route_get_destination (routes[i], &r_network) < 0) + continue; + if ( sd_dhcp_route_get_destination_prefix_length (routes[i], &r_plen) < 0 + || r_plen > 32) + continue; + if (sd_dhcp_route_get_gateway (routes[i], &r_gateway) < 0) continue; - route.plen = plen; - route.network = nm_utils_ip4_address_clear_host_address (a.s_addr, plen); + network_net = nm_utils_ip4_address_clear_host_address (r_network.s_addr, + r_plen); + nm_utils_inet4_ntop (network_net, network_net_str); + nm_utils_inet4_ntop (r_gateway.s_addr, gateway_str); + + LOG_LEASE (LOGD_DHCP4, + "%sstatic route %s/%d gw %s", + option == SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE + ? "classless " + : "", + network_net_str, + (int) r_plen, + gateway_str); + g_string_append_printf (nm_gstring_add_space_delimiter ( option == SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE + ? str_classless + : str_static), + "%s/%d %s", + network_net_str, + (int) r_plen, + gateway_str); + + if ( option == SD_DHCP_OPTION_STATIC_ROUTE + && has_classless_route) { + /* RFC 3443: if the DHCP server returns both a Classless Static Routes + * option and a Static Routes option, the DHCP client MUST ignore the + * Static Routes option. */ + continue; + } - if (sd_dhcp_route_get_gateway (routes[i], &a) < 0) + if ( r_plen == 0 + && option == SD_DHCP_OPTION_STATIC_ROUTE) { + /* for option 33 (static route), RFC 2132 says: + * + * The default route (0.0.0.0) is an illegal destination for a static + * route. */ continue; - route.gateway = a.s_addr; - - if (route.plen) { - route.rt_source = NM_IP_CONFIG_SOURCE_DHCP; - route.metric = route_metric; - route.table_coerced = nm_platform_route_table_coerce (route_table); - nm_ip4_config_add_route (ip4_config, &route, NULL); - - s = nm_utils_inet4_ntop (route.network, buf); - gw_str = nm_utils_inet4_ntop (route.gateway, NULL); - LOG_LEASE (LOGD_DHCP4, "static route %s/%d gw %s", s, route.plen, gw_str); - - g_string_append_printf (str, "%s%s/%d %s", str->len ? " " : "", s, route.plen, gw_str); - } else { - if (!static_default_gateway) { - static_default_gateway = TRUE; - gateway_has = TRUE; - gateway = route.gateway; - - s = nm_utils_inet4_ntop (route.gateway, NULL); - LOG_LEASE (LOGD_DHCP4, "gateway %s", s); - add_option (options, dhcp4_requests, SD_DHCP_OPTION_ROUTER, s); - } } - } - if (str->len) - add_option (options, dhcp4_requests, SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, str->str); - } - /* If the DHCP server returns both a Classless Static Routes option and a - * Router option, the DHCP client MUST ignore the Router option [RFC 3442]. - * Be more lenient and ignore the Router option only if Classless Static - * Routes contain a default gateway (as other DHCP backends do). - */ - /* Gateway */ - if (!static_default_gateway) { - r = sd_dhcp_lease_get_router (lease, &tmp_addr); - if (r == 0) { - gateway_has = TRUE; - gateway = tmp_addr.s_addr; - s = nm_utils_inet4_ntop (tmp_addr.s_addr, NULL); - LOG_LEASE (LOGD_DHCP4, "gateway %s", s); - add_option (options, dhcp4_requests, SD_DHCP_OPTION_ROUTER, s); + if (r_plen == 0) { + /* if there are multiple default routes, we add them with differing + * metrics. */ + m = default_route_metric; + if (default_route_metric < G_MAXUINT32) + default_route_metric++; + + has_router_from_classless = TRUE; + } else + m = route_metric; + + nm_ip4_config_add_route (ip4_config, + &((const NMPlatformIP4Route) { + .network = network_net, + .plen = r_plen, + .gateway = r_gateway.s_addr, + .rt_source = NM_IP_CONFIG_SOURCE_DHCP, + .metric = m, + .table_coerced = nm_platform_route_table_coerce (route_table), + }), + NULL); } - } - if (gateway_has) { - const NMPlatformIP4Route rt = { - .rt_source = NM_IP_CONFIG_SOURCE_DHCP, - .gateway = gateway, - .table_coerced = nm_platform_route_table_coerce (route_table), - .metric = route_metric, - }; + if (str_classless && str_classless->len > 0) + add_option (options, dhcp4_requests, SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, str_classless->str); + if (str_static && str_static->len > 0) + add_option (options, dhcp4_requests, SD_DHCP_OPTION_STATIC_ROUTE, str_static->str); + } - nm_ip4_config_add_route (ip4_config, &rt, NULL); + /* FIXME: internal client only supports returing the first router. */ + if (sd_dhcp_lease_get_router (lease, &a_router) >= 0) { + s = nm_utils_inet4_ntop (a_router.s_addr, addr_str); + LOG_LEASE (LOGD_DHCP4, "gateway %s", s); + add_option (options, dhcp4_requests, SD_DHCP_OPTION_ROUTER, s); + + /* If the DHCP server returns both a Classless Static Routes option and a + * Router option, the DHCP client MUST ignore the Router option [RFC 3442]. + * + * Be more lenient and ignore the Router option only if Classless Static + * Routes contain a default gateway (as other DHCP backends do). + */ + if (!has_router_from_classless) { + nm_ip4_config_add_route (ip4_config, + &((const NMPlatformIP4Route) { + .rt_source = NM_IP_CONFIG_SOURCE_DHCP, + .gateway = a_router.s_addr, + .table_coerced = nm_platform_route_table_coerce (route_table), + .metric = route_metric, + }), + NULL); + } } - /* MTU */ - r = sd_dhcp_lease_get_mtu (lease, &mtu); - if (r == 0 && mtu) { + if ( sd_dhcp_lease_get_mtu (lease, &mtu) >= 0 + && mtu) { nm_ip4_config_set_mtu (ip4_config, mtu, NM_IP_CONFIG_SOURCE_DHCP); - add_option_u32 (options, dhcp4_requests, SD_DHCP_OPTION_INTERFACE_MTU, mtu); + add_option_u64 (options, dhcp4_requests, SD_DHCP_OPTION_INTERFACE_MTU, mtu); LOG_LEASE (LOGD_DHCP4, "mtu %u", mtu); } - /* NTP servers */ num = sd_dhcp_lease_get_ntp (lease, &addr_list); if (num > 0) { nm_gstring_prepare (&str); for (i = 0; i < num; i++) { - s = nm_utils_inet4_ntop (addr_list[i].s_addr, buf); + s = nm_utils_inet4_ntop (addr_list[i].s_addr, addr_str); LOG_LEASE (LOGD_DHCP4, "ntp server '%s'", s); g_string_append_printf (str, "%s%s", str->len ? " " : "", s); } add_option (options, dhcp4_requests, SD_DHCP_OPTION_NTP_SERVER, str->str); } - /* Root path */ - r = sd_dhcp_lease_get_root_path (lease, &s); - if (r >= 0) { + if (sd_dhcp_lease_get_root_path (lease, &s) >= 0) { LOG_LEASE (LOGD_DHCP4, "root path '%s'", s); add_option (options, dhcp4_requests, SD_DHCP_OPTION_ROOT_PATH, s); } - r = sd_dhcp_lease_get_vendor_specific (lease, &data, &data_len); - if (r >= 0) + if (sd_dhcp_lease_get_vendor_specific (lease, &data, &data_len) >= 0) metered = !!memmem (data, data_len, "ANDROID_METERED", NM_STRLEN ("ANDROID_METERED")); nm_ip4_config_set_metered (ip4_config, metered); - return ip4_config; + NM_SET_OUT (out_options, g_steal_pointer (&options)); + return g_steal_pointer (&ip4_config); } /*****************************************************************************/ @@ -483,34 +568,17 @@ get_leasefile_path (int addr_family, const char *iface, const char *uuid) /*****************************************************************************/ static void -_save_client_id (NMDhcpSystemd *self, - uint8_t type, - const uint8_t *client_id, - size_t len) -{ - 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))) { - nm_dhcp_client_set_client_id_bin (NM_DHCP_CLIENT (self), - type, client_id, len); - } -} - -static void bound4_handle (NMDhcpSystemd *self) { NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self); const char *iface = nm_dhcp_client_get_iface (NM_DHCP_CLIENT (self)); sd_dhcp_lease *lease; - NMIP4Config *ip4_config; - GHashTable *options; + gs_unref_object NMIP4Config *ip4_config = NULL; + gs_unref_hashtable GHashTable *options = NULL; GError *error = NULL; - int r; - r = sd_dhcp_client_get_lease (priv->client4, &lease); - if (r < 0 || !lease) { + if ( sd_dhcp_client_get_lease (priv->client4, &lease) < 0 + || !lease) { _LOGW ("no lease!"); nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), NM_DHCP_STATE_FAIL, NULL, NULL); return; @@ -518,40 +586,29 @@ bound4_handle (NMDhcpSystemd *self) _LOGD ("lease available"); - options = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_free); ip4_config = lease_to_ip4_config (nm_dhcp_client_get_multi_idx (NM_DHCP_CLIENT (self)), iface, nm_dhcp_client_get_ifindex (NM_DHCP_CLIENT (self)), lease, - options, nm_dhcp_client_get_route_table (NM_DHCP_CLIENT (self)), nm_dhcp_client_get_route_metric (NM_DHCP_CLIENT (self)), TRUE, + &options, &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); - dhcp_lease_save (lease, priv->lease_file); - - sd_dhcp_client_get_client_id (priv->client4, &type, &client_id, &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, - NM_IP_CONFIG_CAST (ip4_config), - options); - } else { + if (!ip4_config) { _LOGW ("%s", error->message); - nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), NM_DHCP_STATE_FAIL, NULL, NULL); g_clear_error (&error); + nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), NM_DHCP_STATE_FAIL, NULL, NULL); + return; } - g_hash_table_destroy (options); - g_clear_object (&ip4_config); + add_requests_to_options (options, dhcp4_requests); + dhcp_lease_save (lease, priv->lease_file); + + nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), + NM_DHCP_STATE_BOUND, + NM_IP_CONFIG_CAST (ip4_config), + options); } static void @@ -582,127 +639,124 @@ dhcp_event_cb (sd_dhcp_client *client, int event, gpointer user_data) } } -static guint16 -get_arp_type (GBytes *hwaddr) -{ - switch (g_bytes_get_size (hwaddr)) { - case ETH_ALEN: - return ARPHRD_ETHER; - case INFINIBAND_ALEN: - return ARPHRD_INFINIBAND; - default: - return ARPHRD_NONE; - } -} - static gboolean ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last_ip4_address, GError **error) { + nm_auto (sd_dhcp_client_unrefp) sd_dhcp_client *sd_client = NULL; NMDhcpSystemd *self = NM_DHCP_SYSTEMD (client); NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self); - const char *iface = nm_dhcp_client_get_iface (client); + gs_free char *lease_file = NULL; GBytes *hwaddr; - sd_dhcp_lease *lease = NULL; - GBytes *override_client_id; - const uint8_t *client_id = NULL; - size_t client_id_len = 0; + const uint8_t *hwaddr_arr; + gsize hwaddr_len; + int arp_type; + GBytes *client_id; + gs_unref_bytes GBytes *client_id_new = NULL; + const uint8_t *client_id_arr; + size_t client_id_len; struct in_addr last_addr = { 0 }; const char *hostname; int r, i; - gboolean success = FALSE; - g_assert (priv->client4 == NULL); - g_assert (priv->client6 == NULL); + g_return_val_if_fail (!priv->client4, FALSE); + g_return_val_if_fail (!priv->client6, FALSE); - g_free (priv->lease_file); - priv->lease_file = get_leasefile_path (AF_INET, iface, nm_dhcp_client_get_uuid (client)); - - r = sd_dhcp_client_new (&priv->client4, FALSE); + r = sd_dhcp_client_new (&sd_client, FALSE); if (r < 0) { nm_utils_error_set_errno (error, r, "failed to create dhcp-client: %s"); return FALSE; } - _LOGT ("dhcp-client4: set %p", priv->client4); + _LOGT ("dhcp-client4: set %p", sd_client); - r = sd_dhcp_client_attach_event (priv->client4, NULL, 0); + r = sd_dhcp_client_attach_event (sd_client, NULL, 0); if (r < 0) { nm_utils_error_set_errno (error, r, "failed to attach event: %s"); - goto errout; + return FALSE; } hwaddr = nm_dhcp_client_get_hw_addr (client); - if (hwaddr) { - const uint8_t *data; - gsize len; - - data = g_bytes_get_data (hwaddr, &len); - r = sd_dhcp_client_set_mac (priv->client4, - data, - len, - get_arp_type (hwaddr)); - if (r < 0) { - nm_utils_error_set_errno (error, r, "failed to set MAC address: %s"); - goto errout; - } + if ( !hwaddr + || !(hwaddr_arr = g_bytes_get_data (hwaddr, &hwaddr_len)) + || (arp_type = nm_utils_arp_type_detect_from_hwaddrlen (hwaddr_len)) < 0) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, "invalid MAC address"); + return FALSE; } - - r = sd_dhcp_client_set_ifindex (priv->client4, nm_dhcp_client_get_ifindex (client)); + r = sd_dhcp_client_set_mac (sd_client, + hwaddr_arr, + hwaddr_len, + (guint16) arp_type); if (r < 0) { - nm_utils_error_set_errno (error, r, "failed to set ifindex: %s"); - goto errout; + nm_utils_error_set_errno (error, r, "failed to set MAC address: %s"); + return FALSE; } - r = sd_dhcp_client_set_callback (priv->client4, dhcp_event_cb, client); + r = sd_dhcp_client_set_ifindex (sd_client, + nm_dhcp_client_get_ifindex (client)); if (r < 0) { - nm_utils_error_set_errno (error, r, "failed to set callback: %s"); - goto errout; + nm_utils_error_set_errno (error, r, "failed to set ifindex: %s"); + return FALSE; } - dhcp_lease_load (&lease, priv->lease_file); + lease_file = get_leasefile_path (AF_INET, + nm_dhcp_client_get_iface (client), + nm_dhcp_client_get_uuid (client)); if (last_ip4_address) inet_pton (AF_INET, last_ip4_address, &last_addr); - else if (lease) - sd_dhcp_lease_get_address (lease, &last_addr); + else { + nm_auto (sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL; + + dhcp_lease_load (&lease, lease_file); + 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); + r = sd_dhcp_client_set_request_address (sd_client, &last_addr); if (r < 0) { nm_utils_error_set_errno (error, r, "failed to set last IPv4 address: %s"); - goto errout; + return FALSE; } } - 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); - nm_assert (client_id && client_id_len >= 2); - sd_dhcp_client_set_client_id (priv->client4, - client_id[0], - client_id + 1, - NM_MIN (client_id_len - 1, _NM_SD_MAX_CLIENT_ID_LEN)); - } else if (lease) { - r = sd_dhcp_lease_get_client_id (lease, (const void **) &client_id, &client_id_len); - if (r == 0 && client_id_len >= 2) { - 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); - } + client_id = nm_dhcp_client_get_client_id (client); + if (!client_id) { + client_id_new = nm_utils_dhcp_client_id_mac (arp_type, hwaddr_arr, hwaddr_len); + client_id = client_id_new; + } + + if ( !(client_id_arr = g_bytes_get_data (client_id, &client_id_len)) + || client_id_len < 2) { + + /* invalid client-ids are not expected. */ + nm_assert_not_reached (); + + nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, "no valid IPv4 client-id"); + return FALSE; + } + + /* Note that we always set a client-id. In particular for infiniband that is necessary, + * see https://tools.ietf.org/html/rfc4390#section-2.1 . */ + r = sd_dhcp_client_set_client_id (sd_client, + client_id_arr[0], + client_id_arr + 1, + NM_MIN (client_id_len - 1, _NM_SD_MAX_CLIENT_ID_LEN)); + if (r < 0) { + nm_utils_error_set_errno (error, r, "failed to set IPv4 client-id: %s"); + return FALSE; } /* Add requested options */ for (i = 0; dhcp4_requests[i].name; i++) { - if (dhcp4_requests[i].include) - sd_dhcp_client_set_request_option (priv->client4, dhcp4_requests[i].num); + if (dhcp4_requests[i].include) { + nm_assert (dhcp4_requests[i].option_num <= 255); + r = sd_dhcp_client_set_request_option (sd_client, dhcp4_requests[i].option_num); + nm_assert (r >= 0 || r == -EEXIST); + } } hostname = nm_dhcp_client_get_hostname (client); @@ -711,28 +765,36 @@ ip4_start (NMDhcpClient *client, * only based on whether the hostname has a domain part or not. At the * moment there is no way to force one or another. */ - r = sd_dhcp_client_set_hostname (priv->client4, hostname); + r = sd_dhcp_client_set_hostname (sd_client, hostname); if (r < 0) { nm_utils_error_set_errno (error, r, "failed to set DHCP hostname: %s"); - goto errout; + return FALSE; } } + r = sd_dhcp_client_set_callback (sd_client, dhcp_event_cb, client); + if (r < 0) { + nm_utils_error_set_errno (error, r, "failed to set callback: %s"); + return FALSE; + } + + priv->client4 = g_steal_pointer (&sd_client); + + g_free (priv->lease_file); + priv->lease_file = g_steal_pointer (&lease_file); + + nm_dhcp_client_set_client_id (client, client_id); + r = sd_dhcp_client_start (priv->client4); if (r < 0) { + sd_dhcp_client_set_callback (priv->client4, NULL, NULL); + nm_clear_pointer (&priv->client4, sd_dhcp_client_unref); nm_utils_error_set_errno (error, r, "failed to start DHCP client: %s"); - goto errout; + return FALSE; } nm_dhcp_client_start_timeout (client); - - success = TRUE; - -errout: - sd_dhcp_lease_unref (lease); - if (!success) - sd_dhcp_client_unref (g_steal_pointer (&priv->client4)); - return success; + return TRUE; } static NMIP6Config * @@ -740,41 +802,45 @@ lease_to_ip6_config (NMDedupMultiIndex *multi_idx, const char *iface, int ifindex, sd_dhcp6_lease *lease, - GHashTable *options, gboolean log_lease, gboolean info_only, + GHashTable **out_options, GError **error) { + gs_unref_object NMIP6Config *ip6_config = NULL; + gs_unref_hashtable GHashTable *options = NULL; struct in6_addr tmp_addr, *dns; uint32_t lft_pref, lft_valid; - NMIP6Config *ip6_config; - const char *addr_str; + char addr_str[NM_UTILS_INET_ADDRSTRLEN]; char **domains; nm_auto_free_gstring GString *str = NULL; int num, i; - gint32 ts; + const gint32 ts = nm_utils_get_monotonic_timestamp_s (); g_return_val_if_fail (lease, NULL); + ip6_config = nm_ip6_config_new (multi_idx, ifindex); - ts = nm_utils_get_monotonic_timestamp_s (); - /* Addresses */ + options = out_options ? create_options_dict () : NULL; + sd_dhcp6_lease_reset_address_iter (lease); nm_gstring_prepare (&str); while (sd_dhcp6_lease_get_address (lease, &tmp_addr, &lft_pref, &lft_valid) >= 0) { - NMPlatformIP6Address address = { - .plen = 128, - .address = tmp_addr, - .timestamp = ts, - .lifetime = lft_valid, - .preferred = lft_pref, + const NMPlatformIP6Address address = { + .plen = 128, + .address = tmp_addr, + .timestamp = ts, + .lifetime = lft_valid, + .preferred = lft_pref, .addr_source = NM_IP_CONFIG_SOURCE_DHCP, }; nm_ip6_config_add_address (ip6_config, &address); - addr_str = nm_utils_inet6_ntop (&tmp_addr, NULL); - g_string_append_printf (str, "%s%s", str->len ? " " : "", addr_str); + nm_utils_inet6_ntop (&tmp_addr, addr_str); + if (str->len) + g_string_append_c (str, ' '); + g_string_append (str, addr_str); LOG_LEASE (LOGD_DHCP6, "address %s", @@ -784,8 +850,8 @@ lease_to_ip6_config (NMDedupMultiIndex *multi_idx, if (str->len) add_option (options, dhcp6_requests, DHCP6_OPTION_IP_ADDRESS, str->str); - if (!info_only && nm_ip6_config_get_num_addresses (ip6_config) == 0) { - g_object_unref (ip6_config); + if ( !info_only + && nm_ip6_config_get_num_addresses (ip6_config) == 0) { g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED, @@ -793,20 +859,20 @@ lease_to_ip6_config (NMDedupMultiIndex *multi_idx, return NULL; } - /* DNS servers */ num = sd_dhcp6_lease_get_dns (lease, &dns); if (num > 0) { nm_gstring_prepare (&str); for (i = 0; i < num; i++) { nm_ip6_config_add_nameserver (ip6_config, &dns[i]); - addr_str = nm_utils_inet6_ntop (&dns[i], NULL); - g_string_append_printf (str, "%s%s", str->len ? " " : "", addr_str); + nm_utils_inet6_ntop (&dns[i], addr_str); + if (str->len) + g_string_append_c (str, ' '); + g_string_append (str, addr_str); LOG_LEASE (LOGD_DHCP6, "nameserver %s", addr_str); } add_option (options, dhcp6_requests, SD_DHCP6_OPTION_DNS_SERVERS, str->str); } - /* Search domains */ num = sd_dhcp6_lease_get_domains (lease, &domains); if (num > 0) { nm_gstring_prepare (&str); @@ -818,7 +884,8 @@ lease_to_ip6_config (NMDedupMultiIndex *multi_idx, add_option (options, dhcp6_requests, SD_DHCP6_OPTION_DOMAIN_LIST, str->str); } - return ip6_config; + NM_SET_OUT (out_options, g_steal_pointer (&options)); + return g_steal_pointer (&ip6_config); } static void @@ -830,10 +897,9 @@ bound6_handle (NMDhcpSystemd *self) gs_unref_hashtable GHashTable *options = NULL; gs_free_error GError *error = NULL; sd_dhcp6_lease *lease; - int r; - r = sd_dhcp6_client_get_lease (priv->client6, &lease); - if (r < 0 || !lease) { + if ( sd_dhcp6_client_get_lease (priv->client6, &lease) < 0 + || !lease) { _LOGW (" no lease!"); nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), NM_DHCP_STATE_FAIL, NULL, NULL); return; @@ -841,25 +907,25 @@ bound6_handle (NMDhcpSystemd *self) _LOGD ("lease available"); - options = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_free); ip6_config = lease_to_ip6_config (nm_dhcp_client_get_multi_idx (NM_DHCP_CLIENT (self)), iface, nm_dhcp_client_get_ifindex (NM_DHCP_CLIENT (self)), lease, - options, TRUE, nm_dhcp_client_get_info_only (NM_DHCP_CLIENT (self)), + &options, &error); - if (ip6_config) { - nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), - NM_DHCP_STATE_BOUND, - NM_IP_CONFIG_CAST (ip6_config), - options); - } else { + if (!ip6_config) { _LOGW ("%s", error->message); nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), NM_DHCP_STATE_FAIL, NULL, NULL); + return; } + + nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), + NM_DHCP_STATE_BOUND, + NM_IP_CONFIG_CAST (ip6_config), + options); } static void @@ -895,31 +961,34 @@ ip6_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const struct in6_addr *ll_addr, NMSettingIP6ConfigPrivacy privacy, - GBytes *duid, guint needed_prefixes, GError **error) { NMDhcpSystemd *self = NM_DHCP_SYSTEMD (client); NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self); - const char *iface = nm_dhcp_client_get_iface (client); + nm_auto (sd_dhcp6_client_unrefp) sd_dhcp6_client *sd_client = NULL; GBytes *hwaddr; const char *hostname; + const char *iface; int r, i; const guint8 *duid_arr; gsize duid_len; - - g_assert (priv->client4 == NULL); - g_assert (priv->client6 == NULL); - g_return_val_if_fail (duid != NULL, FALSE); - - duid_arr = g_bytes_get_data (duid, &duid_len); - if (!duid_arr || duid_len < 2) + GBytes *duid; + const uint8_t *hwaddr_arr; + gsize hwaddr_len; + int arp_type; + + g_return_val_if_fail (!priv->client4, FALSE); + g_return_val_if_fail (!priv->client6, FALSE); + + if ( !(duid = nm_dhcp_client_get_client_id (client)) + || !(duid_arr = g_bytes_get_data (duid, &duid_len)) + || duid_len < 2) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, "missing DUID"); g_return_val_if_reached (FALSE); + } - g_free (priv->lease_file); - priv->lease_file = get_leasefile_path (AF_INET6, iface, nm_dhcp_client_get_uuid (client)); - - r = sd_dhcp6_client_new (&priv->client6); + r = sd_dhcp6_client_new (&sd_client); if (r < 0) { nm_utils_error_set_errno (error, r, "failed to create dhcp-client: %s"); return FALSE; @@ -930,12 +999,23 @@ ip6_start (NMDhcpClient *client, needed_prefixes); } - _LOGT ("dhcp-client6: set %p", priv->client6); + _LOGT ("dhcp-client6: set %p", sd_client); if (nm_dhcp_client_get_info_only (client)) - sd_dhcp6_client_set_information_request (priv->client6, 1); + sd_dhcp6_client_set_information_request (sd_client, 1); + + iface = nm_dhcp_client_get_iface (client); - r = sd_dhcp6_client_set_duid (priv->client6, + r = sd_dhcp6_client_set_iaid (sd_client, + nm_utils_create_dhcp_iaid (TRUE, + (const guint8 *) iface, + strlen (iface))); + if (r < 0) { + nm_utils_error_set_errno (error, r, "failed to set IAID: %s"); + return FALSE; + } + + r = sd_dhcp6_client_set_duid (sd_client, unaligned_read_be16 (&duid_arr[0]), &duid_arr[2], duid_len - 2); @@ -944,82 +1024,84 @@ ip6_start (NMDhcpClient *client, return FALSE; } - r = sd_dhcp6_client_attach_event (priv->client6, NULL, 0); + r = sd_dhcp6_client_attach_event (sd_client, NULL, 0); if (r < 0) { nm_utils_error_set_errno (error, r, "failed to attach event: %s"); - goto errout; + return FALSE; } hwaddr = nm_dhcp_client_get_hw_addr (client); - if (hwaddr) { - const uint8_t *data; - gsize len; - - data = g_bytes_get_data (hwaddr, &len); - r = sd_dhcp6_client_set_mac (priv->client6, - data, - len, - get_arp_type (hwaddr)); - if (r < 0) { - nm_utils_error_set_errno (error, r, "failed to set MAC address: %s"); - goto errout; - } + if ( !hwaddr + || !(hwaddr_arr = g_bytes_get_data (hwaddr, &hwaddr_len)) + || (arp_type = nm_utils_arp_type_detect_from_hwaddrlen (hwaddr_len)) < 0) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, "invalid MAC address"); + return FALSE; } - - r = sd_dhcp6_client_set_ifindex (priv->client6, nm_dhcp_client_get_ifindex (client)); + r = sd_dhcp6_client_set_mac (sd_client, + hwaddr_arr, + hwaddr_len, + (guint16) arp_type); if (r < 0) { - nm_utils_error_set_errno (error, r, "failed to set ifindex: %s"); - goto errout; + nm_utils_error_set_errno (error, r, "failed to set MAC address: %s"); + return FALSE; } - r = sd_dhcp6_client_set_callback (priv->client6, dhcp6_event_cb, client); + r = sd_dhcp6_client_set_ifindex (sd_client, + nm_dhcp_client_get_ifindex (client)); if (r < 0) { - nm_utils_error_set_errno (error, r, "failed to set callback: %s"); - goto errout; + nm_utils_error_set_errno (error, r, "failed to set ifindex: %s"); + return FALSE; } /* Add requested options */ for (i = 0; dhcp6_requests[i].name; i++) { - if (dhcp6_requests[i].include) - sd_dhcp6_client_set_request_option (priv->client6, dhcp6_requests[i].num); + if (dhcp6_requests[i].include) { + r = sd_dhcp6_client_set_request_option (sd_client, dhcp6_requests[i].option_num); + nm_assert (r >= 0 || r == -EEXIST); + } } - r = sd_dhcp6_client_set_local_address (priv->client6, ll_addr); + r = sd_dhcp6_client_set_local_address (sd_client, ll_addr); if (r < 0) { nm_utils_error_set_errno (error, r, "failed to set local address: %s"); - goto errout; + return FALSE; } hostname = nm_dhcp_client_get_hostname (client); - r = sd_dhcp6_client_set_fqdn (priv->client6, hostname); + r = sd_dhcp6_client_set_fqdn (sd_client, hostname); if (r < 0) { nm_utils_error_set_errno (error, r, "failed to set DHCP hostname: %s"); - goto errout; + return FALSE; + } + + r = sd_dhcp6_client_set_callback (sd_client, dhcp6_event_cb, client); + if (r < 0) { + nm_utils_error_set_errno (error, r, "failed to set callback: %s"); + return FALSE; } + priv->client6 = g_steal_pointer (&sd_client); + r = sd_dhcp6_client_start (priv->client6); if (r < 0) { + sd_dhcp6_client_set_callback (priv->client6, NULL, NULL); + nm_clear_pointer (&priv->client6, sd_dhcp6_client_unref); nm_utils_error_set_errno (error, r, "failed to start client: %s"); - goto errout; + return FALSE; } nm_dhcp_client_start_timeout (client); - return TRUE; - -errout: - sd_dhcp6_client_unref (g_steal_pointer (&priv->client6)); - return FALSE; } static void -stop (NMDhcpClient *client, gboolean release, GBytes *duid) +stop (NMDhcpClient *client, gboolean release) { NMDhcpSystemd *self = NM_DHCP_SYSTEMD (client); NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self); int r = 0; - NM_DHCP_CLIENT_CLASS (nm_dhcp_systemd_parent_class)->stop (client, release, duid); + NM_DHCP_CLIENT_CLASS (nm_dhcp_systemd_parent_class)->stop (client, release); _LOGT ("dhcp-client%d: stop %p", priv->client4 ? '4' : '6', diff --git a/src/dhcp/nm-dhcp-utils.c b/src/dhcp/nm-dhcp-utils.c index 6bbc670b..768f9fd7 100644 --- a/src/dhcp/nm-dhcp-utils.c +++ b/src/dhcp/nm-dhcp-utils.c @@ -197,7 +197,8 @@ ip4_process_dhclient_rfc3442_routes (const char *iface, /* gateway passed as classless static route */ *gwaddr = route.gateway; } else { - char addr[INET_ADDRSTRLEN]; + char b1[INET_ADDRSTRLEN]; + char b2[INET_ADDRSTRLEN]; /* normal route */ route.rt_source = NM_IP_CONFIG_SOURCE_DHCP; @@ -206,8 +207,9 @@ ip4_process_dhclient_rfc3442_routes (const char *iface, nm_ip4_config_add_route (ip4_config, &route, NULL); _LOG2I (LOGD_DHCP4, iface, " classless static route %s/%d gw %s", - nm_utils_inet4_ntop (route.network, addr), route.plen, - nm_utils_inet4_ntop (route.gateway, NULL)); + nm_utils_inet4_ntop (route.network, b1), + route.plen, + nm_utils_inet4_ntop (route.gateway, b2)); } } @@ -408,6 +410,7 @@ nm_dhcp_utils_ip4_config_from_options (NMDedupMultiIndex *multi_idx, gboolean gateway_has = FALSE; guint32 gateway = 0; guint8 plen = 0; + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; g_return_val_if_fail (options != NULL, NULL); @@ -439,7 +442,7 @@ nm_dhcp_utils_ip4_config_from_options (NMDedupMultiIndex *multi_idx, process_classful_routes (iface, options, route_table, route_metric, ip4_config); if (gateway) { - _LOG2I (LOGD_DHCP4, iface, " gateway %s", nm_utils_inet4_ntop (gateway, NULL)); + _LOG2I (LOGD_DHCP4, iface, " gateway %s", nm_utils_inet4_ntop (gateway, sbuf)); gateway_has = TRUE; } else { /* If the gateway wasn't provided as a classless static route with a @@ -726,10 +729,10 @@ nm_dhcp_utils_duid_to_string (GBytes *duid) gconstpointer data; gsize len; - g_return_val_if_fail (duid != NULL, NULL); + g_return_val_if_fail (duid, NULL); data = g_bytes_get_data (duid, &len); - return _nm_utils_bin2str (data, len, FALSE); + return _nm_utils_bin2hexstr_full (data, len, ':', FALSE, NULL); } /** diff --git a/src/dhcp/tests/meson.build b/src/dhcp/tests/meson.build index 0fee26b2..d2de4dc4 100644 --- a/src/dhcp/tests/meson.build +++ b/src/dhcp/tests/meson.build @@ -1,6 +1,6 @@ test_units = [ 'test-dhcp-dhclient', - 'test-dhcp-utils' + 'test-dhcp-utils', ] foreach test_unit: test_units @@ -13,6 +13,6 @@ foreach test_unit: test_units test( 'dhcp/' + test_unit, test_script, - args: test_args + [exe.full_path()] + args: test_args + [exe.full_path()], ) endforeach diff --git a/src/dhcp/tests/test-dhcp-dhclient.c b/src/dhcp/tests/test-dhcp-dhclient.c index edac4257..ab1f5551 100644 --- a/src/dhcp/tests/test-dhcp-dhclient.c +++ b/src/dhcp/tests/test-dhcp-dhclient.c @@ -760,62 +760,74 @@ test_read_commented_duid_from_leasefile (void) g_assert (duid == NULL); } +/*****************************************************************************/ + +static void +_save_duid (const char *path, + const guint8 *duid_bin, + gsize duid_len) +{ + gs_unref_bytes GBytes *duid = NULL; + GError *error = NULL; + gboolean success; + + g_assert (path); + g_assert (duid_bin); + g_assert (duid_len > 0); + + duid = g_bytes_new (duid_bin, duid_len); + success = nm_dhcp_dhclient_save_duid (path, duid, &error); + nmtst_assert_success (success, error); +} + static void test_write_duid (void) { - const char *duid = "\\000\\001\\000\\001\\027X\\350X\\000#\\025\\010~\\254"; + const guint8 duid[] = { 000, 001, 000, 001, 027, 'X', 0350, 'X', 0, '#', 025, 010, '~', 0254 }; const char *expected_contents = "default-duid \"\\000\\001\\000\\001\\027X\\350X\\000#\\025\\010~\\254\";\n"; GError *error = NULL; - char *contents = NULL; + gs_free char *contents = NULL; gboolean success; const char *path = "test-dhclient-write-duid.leases"; - success = nm_dhcp_dhclient_save_duid (path, duid, &error); - g_assert_no_error (error); - g_assert (success); + _save_duid (path, duid, G_N_ELEMENTS (duid)); success = g_file_get_contents (path, &contents, NULL, &error); - g_assert_no_error (error); - g_assert (success); + nmtst_assert_success (success, error); unlink (path); - g_assert_cmpstr (expected_contents, ==, contents); - g_free (contents); + g_assert_cmpstr (expected_contents, ==, contents); } static void test_write_existing_duid (void) { - const char *duid = "\\000\\001\\000\\001\\023o\\023n\\000\\\"\\372\\214\\326\\302"; + const guint8 duid[] = { 000, 001, 000, 001, 023, 'o', 023, 'n', 000, '\"', 0372, 0214, 0326, 0302 }; const char *original_contents = "default-duid \"\\000\\001\\000\\001\\027X\\350X\\000#\\025\\010~\\254\";\n"; const char *expected_contents = "default-duid \"\\000\\001\\000\\001\\023o\\023n\\000\\\"\\372\\214\\326\\302\";\n"; GError *error = NULL; - char *contents = NULL; + gs_free char *contents = NULL; gboolean success; const char *path = "test-dhclient-write-existing-duid.leases"; success = g_file_set_contents (path, original_contents, -1, &error); - g_assert_no_error (error); - g_assert (success); + nmtst_assert_success (success, error); /* Save other DUID; should be overwritten */ - success = nm_dhcp_dhclient_save_duid (path, duid, &error); - g_assert_no_error (error); - g_assert (success); + _save_duid (path, duid, G_N_ELEMENTS (duid)); /* reread original contents */ success = g_file_get_contents (path, &contents, NULL, &error); - g_assert_no_error (error); - g_assert (success); + nmtst_assert_success (success, error); unlink (path); g_assert_cmpstr (expected_contents, ==, contents); - - g_free (contents); } +static const guint8 DUID_BIN[] = { 000, 001, 000, 001, 023, 'o', 023, 'n', 000, '\"', 0372, 0214, 0326, 0302 }; #define DUID "\\000\\001\\000\\001\\023o\\023n\\000\\\"\\372\\214\\326\\302" + static void test_write_existing_commented_duid (void) { @@ -824,28 +836,22 @@ test_write_existing_commented_duid (void) "default-duid \"" DUID "\";\n" ORIG_CONTENTS; GError *error = NULL; - char *contents = NULL; + gs_free char *contents = NULL; gboolean success; const char *path = "test-dhclient-write-existing-commented-duid.leases"; success = g_file_set_contents (path, ORIG_CONTENTS, -1, &error); - g_assert_no_error (error); - g_assert (success); + nmtst_assert_success (success, error); /* Save other DUID; should be saved on top */ - success = nm_dhcp_dhclient_save_duid (path, DUID, &error); - g_assert_no_error (error); - g_assert (success); + _save_duid (path, DUID_BIN, G_N_ELEMENTS (DUID_BIN)); /* reread original contents */ success = g_file_get_contents (path, &contents, NULL, &error); - g_assert_no_error (error); - g_assert (success); + nmtst_assert_success (success, error); unlink (path); g_assert_cmpstr (expected_contents, ==, contents); - - g_free (contents); #undef ORIG_CONTENTS } @@ -865,8 +871,7 @@ test_write_existing_multiline_duid (void) success = g_file_set_contents (path, ORIG_CONTENTS, -1, &error); nmtst_assert_success (success, error); - success = nm_dhcp_dhclient_save_duid (path, DUID, &error); - nmtst_assert_success (success, error); + _save_duid (path, DUID_BIN, G_N_ELEMENTS (DUID_BIN)); success = g_file_get_contents (path, &contents, NULL, &error); nmtst_assert_success (success, error); diff --git a/src/dns/nm-dns-dnsmasq.c b/src/dns/nm-dns-dnsmasq.c index b48c6b87..b54df730 100644 --- a/src/dns/nm-dns-dnsmasq.c +++ b/src/dns/nm-dns-dnsmasq.c @@ -254,9 +254,16 @@ name_owner_changed (GObject *object, priv->running = TRUE; send_dnsmasq_update (self); } else { - _LOGI ("dnsmasq disappeared"); - priv->running = FALSE; - g_signal_emit_by_name (self, NM_DNS_PLUGIN_FAILED); + if (priv->running) { + _LOGI ("dnsmasq disappeared"); + priv->running = FALSE; + g_signal_emit_by_name (self, NM_DNS_PLUGIN_FAILED); + } else { + /* The only reason for which (!priv->running) here + * is that the dnsmasq process quit. We don't care + * of that here, the manager handles child restarts + * by itself. */ + } } } diff --git a/src/dns/nm-dns-manager.c b/src/dns/nm-dns-manager.c index 6a59b41c..2a30a540 100644 --- a/src/dns/nm-dns-manager.c +++ b/src/dns/nm-dns-manager.c @@ -55,7 +55,7 @@ #include "nm-dns-systemd-resolved.h" #include "nm-dns-unbound.h" -#define HASH_LEN 20 +#define HASH_LEN NM_UTILS_CHECKSUM_LENGTH_SHA1 #ifndef RESOLVCONF_PATH #define RESOLVCONF_PATH "/sbin/resolvconf" @@ -122,6 +122,7 @@ typedef struct { NMDnsManagerResolvConfManager rc_manager; char *mode; + NMDnsPlugin *sd_resolve_plugin; NMDnsPlugin *plugin; NMConfig *config; @@ -311,37 +312,23 @@ _config_data_free (NMDnsConfigData *data) } static int -_ip_config_data_cmp (const NMDnsIPConfigData *a, const NMDnsIPConfigData *b) +_ip_config_lst_cmp (const CList *a_lst, + const CList *b_lst, + const void *user_data) { - int a_prio, b_prio; - - a_prio = nm_ip_config_get_dns_priority (a->ip_config); - b_prio = nm_ip_config_get_dns_priority (b->ip_config); + const NMDnsIPConfigData *a = c_list_entry (a_lst, NMDnsIPConfigData, ip_config_lst); + const NMDnsIPConfigData *b = c_list_entry (b_lst, NMDnsIPConfigData, ip_config_lst); /* Configurations with lower priority value first */ - if (a_prio < b_prio) - return -1; - else if (a_prio > b_prio) - return 1; + NM_CMP_DIRECT (nm_ip_config_get_dns_priority (a->ip_config), + nm_ip_config_get_dns_priority (b->ip_config)); - /* Sort also according to type */ - if (a->ip_config_type > b->ip_config_type) - return -1; - else if (a->ip_config_type < b->ip_config_type) - return 1; + /* Sort according to type (descendingly) */ + NM_CMP_FIELD (b, a, ip_config_type); return 0; } -static int -_ip_config_lst_cmp (const CList *a, - const CList *b, - const void *user_data) -{ - return _ip_config_data_cmp (c_list_entry (a, NMDnsIPConfigData, ip_config_lst), - c_list_entry (b, NMDnsIPConfigData, ip_config_lst)); -} - static CList * _ip_config_lst_head (NMDnsManager *self) { @@ -357,6 +344,21 @@ _ip_config_lst_head (NMDnsManager *self) /*****************************************************************************/ +gboolean +nm_dns_manager_has_systemd_resolved (NMDnsManager *self) +{ + NMDnsManagerPrivate *priv; + + g_return_val_if_fail (NM_IS_DNS_MANAGER (self), FALSE); + + priv = NM_DNS_MANAGER_GET_PRIVATE (self); + + return priv->sd_resolve_plugin + || NM_IS_DNS_SYSTEMD_RESOLVED (priv->plugin); +} + +/*****************************************************************************/ + static void add_string_item (GPtrArray *array, const char *str, gboolean dup) { @@ -582,53 +584,85 @@ again: } static char * -create_resolv_conf (char **searches, - char **nameservers, - char **options) +create_resolv_conf (const char *const*searches, + const char *const*nameservers, + const char *const*options) { - gs_free char *searches_str = NULL; - gs_free char *nameservers_str = NULL; - gs_free char *options_str = NULL; - char *tmp_str; GString *str; - int i; + gsize i; - if (searches) { - tmp_str = g_strjoinv (" ", searches); - searches_str = g_strconcat ("search ", tmp_str, "\n", NULL); - g_free (tmp_str); - } + str = g_string_new_len (NULL, 245); - if (options) { - tmp_str = g_strjoinv (" ", options); - options_str = g_strconcat ("options ", tmp_str, "\n", NULL); - g_free (tmp_str); - } + g_string_append (str, "# Generated by NetworkManager\n"); - if (nameservers) { - int num = g_strv_length (nameservers); + if (searches && searches[0]) { + gsize search_base_idx; - str = g_string_new (""); - for (i = 0; i < num; i++) { - if (i == 3) { - g_string_append (str, "# "); - g_string_append (str, "NOTE: the libc resolver may not support more than 3 nameservers."); - g_string_append (str, "\n# "); - g_string_append (str, "The nameservers listed below may not be recognized."); - g_string_append_c (str, '\n'); + g_string_append (str, "search"); + search_base_idx = str->len; + + for (i = 0; searches[i]; i++) { + const char *s = searches[i]; + gsize l = strlen (s); + + if ( l == 0 + || NM_STRCHAR_ANY (s, ch, NM_IN_SET (ch, ' ', '\t', '\n'))) { + /* there should be no such characters in the search entry. Also, + * because glibc parser would treat them as line/word separator. + * + * Skip the value silently. */ + continue; + } + + if (search_base_idx > 0) { + if (str->len - search_base_idx + 1 + l > 254) { + /* this entry crosses the 256 character boundery. Older glibc versions + * would truncate the entry at this point. + * + * Fill the line with spaces to cross the 256 char boundary and continue + * afterwards. This way, the truncation happens between two search entries. */ + while (str->len - search_base_idx < 257) + g_string_append_c (str, ' '); + search_base_idx = 0; + } } + g_string_append_c (str, ' '); + g_string_append_len (str, s, l); + } + g_string_append_c (str, '\n'); + } + + if (nameservers && nameservers[0]) { + for (i = 0; nameservers[i]; i++) { + if (i == 3) { + g_string_append (str, "# NOTE: the libc resolver may not support more than 3 nameservers.\n"); + g_string_append (str, "# The nameservers listed below may not be recognized.\n"); + } g_string_append (str, "nameserver "); g_string_append (str, nameservers[i]); g_string_append_c (str, '\n'); } - nameservers_str = g_string_free (str, FALSE); } - return g_strdup_printf ("# Generated by NetworkManager\n%s%s%s", - searches_str ?: "", - nameservers_str ?: "", - options_str ?: ""); + if (options && options[0]) { + g_string_append (str, "options"); + for (i = 0; options[i]; i++) { + g_string_append_c (str, ' '); + g_string_append (str, options[i]); + } + g_string_append_c (str, '\n'); + } + + return g_string_free (str, FALSE); +} + +char * +nmtst_dns_create_resolv_conf (const char *const*searches, + const char *const*nameservers, + const char *const*options) +{ + return create_resolv_conf (searches, nameservers, options); } static gboolean @@ -654,9 +688,9 @@ write_resolv_conf_contents (FILE *f, static gboolean write_resolv_conf (FILE *f, - char **searches, - char **nameservers, - char **options, + const char *const*searches, + const char *const*nameservers, + const char *const*options, GError **error) { gs_free char *content = NULL; @@ -718,7 +752,11 @@ dispatch_resolvconf (NMDnsManager *self, return SR_ERROR; } - success = write_resolv_conf (f, searches, nameservers, options, error); + success = write_resolv_conf (f, + NM_CAST_STRV_CC (searches), + NM_CAST_STRV_CC (nameservers), + NM_CAST_STRV_CC (options), + error); err = pclose (f); if (err < 0) { errnosv = errno; @@ -751,15 +789,42 @@ _read_link_cached (const char *path, gboolean *is_cached, char **cached) return (*cached = g_file_read_link (path, NULL)); } -#define MY_RESOLV_CONF NMRUNDIR "/resolv.conf" -#define MY_RESOLV_CONF_TMP MY_RESOLV_CONF ".tmp" -#define RESOLV_CONF_TMP "/etc/.resolv.conf.NetworkManager" +#define MY_RESOLV_CONF NMRUNDIR"/resolv.conf" +#define MY_RESOLV_CONF_TMP MY_RESOLV_CONF".tmp" +#define RESOLV_CONF_TMP "/etc/.resolv.conf.NetworkManager" + +#define NO_STUB_RESOLV_CONF NMRUNDIR "/no-stub-resolv.conf" + +static void +update_resolv_conf_no_stub (NMDnsManager *self, + const char *const*searches, + const char *const*nameservers, + const char *const*options) +{ + gs_free char *content = NULL; + GError *local = NULL; + + content = create_resolv_conf (searches, nameservers, options); + + if (!g_file_set_contents (NO_STUB_RESOLV_CONF, + content, + -1, + &local)) { + _LOGD ("update-resolv-no-stub: failure to write file: %s", + local->message); + g_error_free (local); + return; + } + + _LOGT ("update-resolv-no-stub: '%s' successfully written", + NO_STUB_RESOLV_CONF); +} static SpawnResult update_resolv_conf (NMDnsManager *self, - char **searches, - char **nameservers, - char **options, + const char *const*searches, + const char *const*nameservers, + const char *const*options, GError **error, NMDnsManagerResolvConfManager rc_manager) { @@ -771,22 +836,6 @@ update_resolv_conf (NMDnsManager *self, gboolean resconf_link_cached = FALSE; gs_free char *resconf_link = NULL; - /* If we are not managing /etc/resolv.conf and it points to - * MY_RESOLV_CONF, don't write the private DNS configuration to - * MY_RESOLV_CONF otherwise we would overwrite the changes done by - * some external application. - * - * This is the only situation, where we don't try to update our - * internal resolv.conf file. */ - if (rc_manager == NM_DNS_MANAGER_RESOLV_CONF_MAN_UNMANAGED) { - if (nm_streq0 (_read_link_cached (_PATH_RESCONF, &resconf_link_cached, &resconf_link), - MY_RESOLV_CONF)) { - _LOGD ("update-resolv-conf: not updating " _PATH_RESCONF - " since it points to " MY_RESOLV_CONF); - return SR_SUCCESS; - } - } - content = create_resolv_conf (searches, nameservers, options); if ( rc_manager == NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE @@ -958,12 +1007,11 @@ update_resolv_conf (NMDnsManager *self, static void compute_hash (NMDnsManager *self, const NMGlobalDnsConfig *global, guint8 buffer[HASH_LEN]) { - GChecksum *sum; - gsize len = HASH_LEN; + nm_auto_free_checksum GChecksum *sum = NULL; NMDnsIPConfigData *ip_data; sum = g_checksum_new (G_CHECKSUM_SHA1); - nm_assert (len == g_checksum_type_get_length (G_CHECKSUM_SHA1)); + nm_assert (HASH_LEN == g_checksum_type_get_length (G_CHECKSUM_SHA1)); if (global) nm_global_dns_config_update_checksum (global, sum); @@ -977,8 +1025,7 @@ compute_hash (NMDnsManager *self, const NMGlobalDnsConfig *global, guint8 buffer nm_ip_config_hash (ip_data->ip_config, sum, TRUE); } - g_checksum_get_digest (sum, buffer, &len); - g_checksum_free (sum); + nm_utils_checksum_get_digest_len (sum, buffer, HASH_LEN); } static gboolean @@ -1066,7 +1113,6 @@ _collect_resolv_conf_data (NMDnsManager *self, const char **out_nis_domain) { NMDnsManagerPrivate *priv; - guint i, num, len; NMResolvConfData rc = { .nameservers = g_ptr_array_new (), .searches = g_ptr_array_new (), @@ -1136,17 +1182,6 @@ _collect_resolv_conf_data (NMDnsManager *self, } } - /* Per 'man resolv.conf', the search list is limited to 6 domains - * totalling 256 characters. - */ - num = MIN (rc.searches->len, 6u); - for (i = 0, len = 0; i < num; i++) { - len += strlen (rc.searches->pdata[i]) + 1; /* +1 for spaces */ - if (len > 256) - break; - } - g_ptr_array_set_size (rc.searches, i); - *out_searches = _ptrarray_to_strv (rc.searches); *out_options = _ptrarray_to_strv (rc.options); *out_nameservers = _ptrarray_to_strv (rc.nameservers); @@ -1392,6 +1427,16 @@ update_dns (NMDnsManager *self, &searches, &options, &nameservers, &nis_servers, &nis_domain); + if (priv->plugin || priv->sd_resolve_plugin) + rebuild_domain_lists (self); + + if (priv->sd_resolve_plugin) { + nm_dns_plugin_update (priv->sd_resolve_plugin, + global_config, + _ip_config_lst_head (self), + priv->hostname); + } + /* Let any plugins do their thing first */ if (priv->plugin) { NMDnsPlugin *plugin = priv->plugin; @@ -1407,7 +1452,6 @@ update_dns (NMDnsManager *self, } _LOGD ("update-dns: updating plugin %s", plugin_name); - rebuild_domain_lists (self); if (!nm_dns_plugin_update (plugin, global_config, _ip_config_lst_head (self), @@ -1419,15 +1463,21 @@ update_dns (NMDnsManager *self, */ caching = FALSE; } - /* Clear the generated search list as it points to - * strings owned by IP configurations and we can't - * guarantee they stay alive. */ - clear_domain_lists (self); skip: ; } + /* Clear the generated search list as it points to + * strings owned by IP configurations and we can't + * guarantee they stay alive. */ + clear_domain_lists (self); + + update_resolv_conf_no_stub (self, + NM_CAST_STRV_CC (searches), + NM_CAST_STRV_CC (nameservers), + NM_CAST_STRV_CC (options)); + /* If caching was successful, we only send 127.0.0.1 to /etc/resolv.conf * to ensure that the glibc resolver doesn't try to round-robin nameservers, * but only uses the local caching nameserver. @@ -1449,7 +1499,12 @@ update_dns (NMDnsManager *self, switch (priv->rc_manager) { case NM_DNS_MANAGER_RESOLV_CONF_MAN_SYMLINK: case NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE: - result = update_resolv_conf (self, searches, nameservers, options, error, priv->rc_manager); + result = update_resolv_conf (self, + NM_CAST_STRV_CC (searches), + NM_CAST_STRV_CC (nameservers), + NM_CAST_STRV_CC (options), + error, + priv->rc_manager); resolv_conf_updated = TRUE; /* If we have ended with no nameservers avoid updating again resolv.conf * on stop, as some external changes may be applied to it in the meanwhile */ @@ -1474,15 +1529,26 @@ update_dns (NMDnsManager *self, if (result == SR_NOTFOUND) { _LOGD ("update-dns: program not available, writing to resolv.conf"); g_clear_error (error); - result = update_resolv_conf (self, searches, nameservers, options, error, NM_DNS_MANAGER_RESOLV_CONF_MAN_SYMLINK); + result = update_resolv_conf (self, + NM_CAST_STRV_CC (searches), + NM_CAST_STRV_CC (nameservers), + NM_CAST_STRV_CC (options), + error, + NM_DNS_MANAGER_RESOLV_CONF_MAN_SYMLINK); resolv_conf_updated = TRUE; } } /* Unless we've already done it, update private resolv.conf in NMRUNDIR ignoring any errors */ - if (!resolv_conf_updated) - update_resolv_conf (self, searches, nameservers, options, NULL, NM_DNS_MANAGER_RESOLV_CONF_MAN_UNMANAGED); + if (!resolv_conf_updated) { + update_resolv_conf (self, + NM_CAST_STRV_CC (searches), + NM_CAST_STRV_CC (nameservers), + NM_CAST_STRV_CC (options), + NULL, + NM_DNS_MANAGER_RESOLV_CONF_MAN_UNMANAGED); + } /* signal that resolv.conf was changed */ if (update && result == SR_SUCCESS) @@ -1542,6 +1608,7 @@ plugin_child_quit (NMDnsPlugin *plugin, int exit_status, gpointer user_data) } else { priv->plugin_ratelimit.num_restarts++; if (priv->plugin_ratelimit.num_restarts > PLUGIN_RATELIMIT_BURST) { + plugin_failed (plugin, self); _LOGW ("plugin %s child respawning too fast, delaying update for %u seconds", nm_dns_plugin_get_name (plugin), PLUGIN_RATELIMIT_DELAY); priv->plugin_ratelimit.timer = g_timeout_add_seconds (PLUGIN_RATELIMIT_DELAY, @@ -1924,9 +1991,13 @@ init_resolv_conf_mode (NMDnsManager *self, gboolean force_reload_plugin) NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self); NMDnsManagerResolvConfManager rc_manager; const char *mode; - gboolean param_changed = FALSE, plugin_changed = FALSE; + gboolean systemd_resolved; + gboolean param_changed = FALSE; + gboolean plugin_changed = FALSE; + gboolean systemd_resolved_changed = FALSE; mode = nm_config_data_get_dns_mode (nm_config_get_data (priv->config)); + systemd_resolved = nm_config_data_get_systemd_resolved (nm_config_get_data (priv->config)); if (nm_streq0 (mode, "none")) rc_manager = NM_DNS_MANAGER_RESOLV_CONF_MAN_UNMANAGED; @@ -1972,6 +2043,7 @@ again: plugin_changed = TRUE; } mode = "systemd-resolved"; + systemd_resolved = FALSE; } else if (nm_streq0 (mode, "dnsmasq")) { if (force_reload_plugin || !NM_IS_DNS_DNSMASQ (priv->plugin)) { _clear_plugin (self); @@ -1994,7 +2066,18 @@ again: plugin_changed = TRUE; } - if (plugin_changed && priv->plugin) { + /* The systemd-resolved plugin is special. We typically always want to keep + * systemd-resolved up to date even if the configured plugin is different. */ + if (systemd_resolved) { + if (!priv->sd_resolve_plugin) { + priv->sd_resolve_plugin = nm_dns_systemd_resolved_new (); + systemd_resolved_changed = TRUE; + } + } else if (nm_clear_g_object (&priv->sd_resolve_plugin)) + systemd_resolved_changed = TRUE; + + if ( plugin_changed + && priv->plugin) { g_signal_connect (priv->plugin, NM_DNS_PLUGIN_FAILED, G_CALLBACK (plugin_failed), self); g_signal_connect (priv->plugin, NM_DNS_PLUGIN_CHILD_QUIT, G_CALLBACK (plugin_child_quit), self); } @@ -2014,9 +2097,11 @@ again: _notify (self, PROP_RC_MANAGER); } - if (param_changed || plugin_changed) { - _LOGI ("init: dns=%s, rc-manager=%s%s%s%s", - mode, _rc_manager_to_string (rc_manager), + if (param_changed || plugin_changed || systemd_resolved_changed) { + _LOGI ("init: dns=%s%s rc-manager=%s%s%s%s", + mode, + (systemd_resolved ? ",systemd-resolved" : ""), + _rc_manager_to_string (rc_manager), NM_PRINT_FMT_QUOTED (priv->plugin, ", plugin=", nm_dns_plugin_get_name (priv->plugin), "", "")); } @@ -2277,6 +2362,7 @@ dispose (GObject *object) if (priv->config) g_signal_handlers_disconnect_by_func (priv->config, config_changed_cb, self); + g_clear_object (&priv->sd_resolve_plugin); _clear_plugin (self); priv->best_ip_config_4 = NULL; diff --git a/src/dns/nm-dns-manager.h b/src/dns/nm-dns-manager.h index ed1974a5..7f6ed3ed 100644 --- a/src/dns/nm-dns-manager.h +++ b/src/dns/nm-dns-manager.h @@ -129,4 +129,12 @@ typedef enum { void nm_dns_manager_stop (NMDnsManager *self); +gboolean nm_dns_manager_has_systemd_resolved (NMDnsManager *self); + +/*****************************************************************************/ + +char *nmtst_dns_create_resolv_conf (const char *const*searches, + const char *const*nameservers, + const char *const*options); + #endif /* __NETWORKMANAGER_DNS_MANAGER_H__ */ diff --git a/src/dnsmasq/nm-dnsmasq-manager.c b/src/dnsmasq/nm-dnsmasq-manager.c index 3fe2f489..1afc6e0d 100644 --- a/src/dnsmasq/nm-dnsmasq-manager.c +++ b/src/dnsmasq/nm-dnsmasq-manager.c @@ -73,51 +73,6 @@ G_DEFINE_TYPE (NMDnsMasqManager, nm_dnsmasq_manager, G_TYPE_OBJECT) /*****************************************************************************/ -typedef struct { - GPtrArray *array; - GStringChunk *chunk; -} NMCmdLine; - -static NMCmdLine * -nm_cmd_line_new (void) -{ - NMCmdLine *cmd; - - cmd = g_slice_new (NMCmdLine); - cmd->array = g_ptr_array_new (); - cmd->chunk = g_string_chunk_new (1024); - - return cmd; -} - -static void -nm_cmd_line_destroy (NMCmdLine *cmd) -{ - g_ptr_array_free (cmd->array, TRUE); - g_string_chunk_free (cmd->chunk); - g_slice_free (NMCmdLine, cmd); -} - -static char * -nm_cmd_line_to_str (NMCmdLine *cmd) -{ - char *str; - - g_ptr_array_add (cmd->array, NULL); - str = g_strjoinv (" ", (char **) cmd->array->pdata); - g_ptr_array_remove_index (cmd->array, cmd->array->len - 1); - - return str; -} - -static void -nm_cmd_line_add_string (NMCmdLine *cmd, const char *str) -{ - g_ptr_array_add (cmd->array, g_string_chunk_insert (cmd->chunk, str)); -} - -/*****************************************************************************/ - static void dm_watch_cb (GPid pid, int status, gpointer user_data) { @@ -145,40 +100,40 @@ dm_watch_cb (GPid pid, int status, gpointer user_data) g_signal_emit (manager, signals[STATE_CHANGED], 0, NM_DNSMASQ_STATUS_DEAD); } -static NMCmdLine * +static GPtrArray * create_dm_cmd_line (const char *iface, const NMIP4Config *ip4_config, const char *pidfile, + gboolean announce_android_metered, GError **error) { - NMCmdLine *cmd; + gs_unref_ptrarray GPtrArray *cmd = NULL; nm_auto_free_gstring GString *s = NULL; char first[INET_ADDRSTRLEN]; char last[INET_ADDRSTRLEN]; - char localaddr[INET_ADDRSTRLEN]; + char listen_address_s[INET_ADDRSTRLEN]; char tmpaddr[INET_ADDRSTRLEN]; - char *error_desc = NULL; + gs_free char *error_desc = NULL; const char *dm_binary; const NMPlatformIP4Address *listen_address; guint i, n; listen_address = nm_ip4_config_get_first_address (ip4_config); + g_return_val_if_fail (listen_address, NULL); dm_binary = nm_utils_find_helper ("dnsmasq", DNSMASQ_PATH, error); if (!dm_binary) return NULL; - s = g_string_sized_new (100); + cmd = g_ptr_array_new_with_free_func (g_free); - /* Create dnsmasq command line */ - cmd = nm_cmd_line_new (); - nm_cmd_line_add_string (cmd, dm_binary); + nm_strv_ptrarray_add_string_dup (cmd, dm_binary); if ( nm_logging_enabled (LOGL_TRACE, LOGD_SHARING) || getenv ("NM_DNSMASQ_DEBUG")) { - nm_cmd_line_add_string (cmd, "--log-dhcp"); - nm_cmd_line_add_string (cmd, "--log-queries"); + nm_strv_ptrarray_add_string_dup (cmd, "--log-dhcp"); + nm_strv_ptrarray_add_string_dup (cmd, "--log-queries"); } /* dnsmasq may read from its default config file location, which if that @@ -187,25 +142,23 @@ create_dm_cmd_line (const char *iface, * as the gateway or whatever. So tell dnsmasq not to use any config file * at all. */ - nm_cmd_line_add_string (cmd, "--conf-file=/dev/null"); + nm_strv_ptrarray_add_string_dup (cmd, "--conf-file=/dev/null"); - nm_cmd_line_add_string (cmd, "--no-hosts"); - nm_cmd_line_add_string (cmd, "--keep-in-foreground"); - nm_cmd_line_add_string (cmd, "--bind-interfaces"); - nm_cmd_line_add_string (cmd, "--except-interface=lo"); - nm_cmd_line_add_string (cmd, "--clear-on-reload"); + nm_strv_ptrarray_add_string_dup (cmd, "--no-hosts"); + nm_strv_ptrarray_add_string_dup (cmd, "--keep-in-foreground"); + nm_strv_ptrarray_add_string_dup (cmd, "--bind-interfaces"); + nm_strv_ptrarray_add_string_dup (cmd, "--except-interface=lo"); + nm_strv_ptrarray_add_string_dup (cmd, "--clear-on-reload"); /* Use strict order since in the case of VPN connections, the VPN's * nameservers will be first in resolv.conf, and those need to be tried * first by dnsmasq to successfully resolve names from the VPN. */ - nm_cmd_line_add_string (cmd, "--strict-order"); + nm_strv_ptrarray_add_string_dup (cmd, "--strict-order"); + + nm_utils_inet4_ntop (listen_address->address, listen_address_s); - nm_utils_inet4_ntop (listen_address->address, localaddr); - g_string_append (s, "--listen-address="); - g_string_append (s, localaddr); - nm_cmd_line_add_string (cmd, s->str); - g_string_truncate (s, 0); + nm_strv_ptrarray_add_string_concat (cmd, "--listen-address=", listen_address_s); if (!nm_dnsmasq_utils_get_range (listen_address, first, last, &error_desc)) { g_set_error_literal (error, @@ -213,59 +166,61 @@ create_dm_cmd_line (const char *iface, NM_MANAGER_ERROR_FAILED, error_desc); _LOGW ("failed to find DHCP address ranges: %s", error_desc); - g_free (error_desc); - nm_cmd_line_destroy (cmd); return NULL; } - g_string_append_printf (s, "--dhcp-range=%s,%s,60m", first, last); - nm_cmd_line_add_string (cmd, s->str); - g_string_truncate (s, 0); + nm_strv_ptrarray_add_string_printf (cmd, + "--dhcp-range=%s,%s,60m", + first, + last); if (nm_ip4_config_best_default_route_get (ip4_config)) { - g_string_append (s, "--dhcp-option=option:router,"); - g_string_append (s, localaddr); - nm_cmd_line_add_string (cmd, s->str); - g_string_truncate (s, 0); + nm_strv_ptrarray_add_string_concat (cmd, + "--dhcp-option=option:router,", + listen_address_s); } if ((n = nm_ip4_config_get_num_nameservers (ip4_config))) { + nm_gstring_prepare (&s); g_string_append (s, "--dhcp-option=option:dns-server"); for (i = 0; i < n; i++) { g_string_append_c (s, ','); g_string_append (s, nm_utils_inet4_ntop (nm_ip4_config_get_nameserver (ip4_config, i), tmpaddr)); } - nm_cmd_line_add_string (cmd, s->str); - g_string_truncate (s, 0); + nm_strv_ptrarray_take_gstring (cmd, &s); } if ((n = nm_ip4_config_get_num_searches (ip4_config))) { + nm_gstring_prepare (&s); g_string_append (s, "--dhcp-option=option:domain-search"); for (i = 0; i < n; i++) { g_string_append_c (s, ','); g_string_append (s, nm_ip4_config_get_search (ip4_config, i)); } - nm_cmd_line_add_string (cmd, s->str); - g_string_truncate (s, 0); + nm_strv_ptrarray_take_gstring (cmd, &s); } - nm_cmd_line_add_string (cmd, "--dhcp-lease-max=50"); + if (announce_android_metered) { + /* force option 43 to announce ANDROID_METERED. Do this, even if the client + * did not ask for this option. See https://www.lorier.net/docs/android-metered.html */ + nm_strv_ptrarray_add_string_dup (cmd, "--dhcp-option-force=43,ANDROID_METERED"); + } + + nm_strv_ptrarray_add_string_dup (cmd, "--dhcp-lease-max=50"); - g_string_append (s, "--dhcp-leasefile=" NMSTATEDIR); - g_string_append_printf (s, "/dnsmasq-%s.leases", iface); - nm_cmd_line_add_string (cmd, s->str); - g_string_truncate (s, 0); + nm_strv_ptrarray_add_string_printf (cmd, + "--dhcp-leasefile=%s/dnsmasq-%s.leases", + NMSTATEDIR, + iface); - g_string_append (s, "--pid-file="); - g_string_append (s, pidfile); - nm_cmd_line_add_string (cmd, s->str); - g_string_truncate (s, 0); + nm_strv_ptrarray_add_string_concat (cmd, "--pid-file=", pidfile); /* dnsmasq exits if the conf dir is not present */ if (g_file_test (CONFDIR, G_FILE_TEST_IS_DIR)) - nm_cmd_line_add_string (cmd, "--conf-dir=" CONFDIR); + nm_strv_ptrarray_add_string_dup (cmd, "--conf-dir=" CONFDIR); - return cmd; + g_ptr_array_add (cmd, NULL); + return g_steal_pointer (&cmd); } static void @@ -310,10 +265,11 @@ out: gboolean nm_dnsmasq_manager_start (NMDnsMasqManager *manager, NMIP4Config *ip4_config, + gboolean announce_android_metered, GError **error) { NMDnsMasqManagerPrivate *priv; - NMCmdLine *dm_cmd; + gs_unref_ptrarray GPtrArray *dm_cmd = NULL; gs_free char *cmd_str = NULL; g_return_val_if_fail (NM_IS_DNSMASQ_MANAGER (manager), FALSE); @@ -324,32 +280,35 @@ nm_dnsmasq_manager_start (NMDnsMasqManager *manager, kill_existing_by_pidfile (priv->pidfile); - dm_cmd = create_dm_cmd_line (priv->iface, ip4_config, priv->pidfile, error); + dm_cmd = create_dm_cmd_line (priv->iface, + ip4_config, + priv->pidfile, + announce_android_metered, + error); if (!dm_cmd) return FALSE; - g_ptr_array_add (dm_cmd->array, NULL); - _LOGI ("starting dnsmasq..."); - _LOGD ("command line: %s", (cmd_str = nm_cmd_line_to_str (dm_cmd))); + _LOGD ("command line: %s", (cmd_str = g_strjoinv (" ", (char **) dm_cmd->pdata))); priv->pid = 0; - if (!g_spawn_async (NULL, (char **) dm_cmd->array->pdata, NULL, + if (!g_spawn_async (NULL, + (char **) dm_cmd->pdata, + NULL, G_SPAWN_DO_NOT_REAP_CHILD, - nm_utils_setpgid, NULL, - &priv->pid, error)) { - goto out; - } + nm_utils_setpgid, + NULL, + &priv->pid, + error)) + return FALSE; + + nm_assert (priv->pid > 0); _LOGD ("dnsmasq started with pid %d", priv->pid); priv->dm_watch_id = g_child_watch_add (priv->pid, (GChildWatchFunc) dm_watch_cb, manager); - out: - if (dm_cmd) - nm_cmd_line_destroy (dm_cmd); - - return priv->pid > 0; + return TRUE; } void diff --git a/src/dnsmasq/nm-dnsmasq-manager.h b/src/dnsmasq/nm-dnsmasq-manager.h index a0ad295c..dd4a9069 100644 --- a/src/dnsmasq/nm-dnsmasq-manager.h +++ b/src/dnsmasq/nm-dnsmasq-manager.h @@ -48,6 +48,7 @@ NMDnsMasqManager *nm_dnsmasq_manager_new (const char *iface); gboolean nm_dnsmasq_manager_start (NMDnsMasqManager *manager, NMIP4Config *ip4_config, + gboolean announce_android_metered, GError **error); void nm_dnsmasq_manager_stop (NMDnsMasqManager *manager); diff --git a/src/dnsmasq/tests/meson.build b/src/dnsmasq/tests/meson.build index 12106c52..09ef52e5 100644 --- a/src/dnsmasq/tests/meson.build +++ b/src/dnsmasq/tests/meson.build @@ -9,5 +9,5 @@ exe = executable( test( 'dnsmasq/' + test_unit, test_script, - args: test_args + [exe.full_path()] + args: test_args + [exe.full_path()], ) diff --git a/src/initrd/meson.build b/src/initrd/meson.build new file mode 100644 index 00000000..a12b718a --- /dev/null +++ b/src/initrd/meson.build @@ -0,0 +1,32 @@ +sources = files( + 'nmi-cmdline-reader.c', + 'nmi-ibft-reader.c', +) + +nm_cflags = ['-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_DAEMON'] + +libnmi_core = static_library( + 'nmi-core', + c_args: nm_cflags, + sources: sources, + include_directories: src_inc, + dependencies: nm_core_dep, +) + +name = 'nm-initrd-generator' +executable( + name, + name + '.c', + c_args: nm_cflags, + include_directories: src_inc, + dependencies: [ nm_core_dep ], + link_with: [libnetwork_manager_base, libnmi_core], + link_args: ldflags_linker_script_binary, + link_depends: linker_script_binary, + install: true, + install_dir: nm_libexecdir, +) + +if enable_tests + subdir('tests') +endif diff --git a/src/initrd/nm-initrd-generator.c b/src/initrd/nm-initrd-generator.c index eb9a38df..70f23e17 100644 --- a/src/initrd/nm-initrd-generator.c +++ b/src/initrd/nm-initrd-generator.c @@ -40,45 +40,39 @@ output_conn (gpointer key, gpointer value, gpointer user_data) const char *basename = key; NMConnection *connection = value; char *connections_dir = user_data; - GKeyFile *file; + gs_unref_keyfile GKeyFile *file = NULL; gs_free char *data = NULL; - GError *error = NULL; + gs_free_error GError *error = NULL; gsize len; - if (!nm_connection_normalize (connection, NULL, NULL, &error)) { - g_print ("%s\n", error->message); - g_error_free (error); - return; - } + if (!nm_connection_normalize (connection, NULL, NULL, &error)) + goto err_out; file = nm_keyfile_write (connection, NULL, NULL, &error); - if (file == NULL) { - g_print ("%s\n", error->message); - g_error_free (error); - return; - } + if (file == NULL) + goto err_out; data = g_key_file_to_data (file, &len, &error); - if (!data) { - g_print ("%s\n", error->message); - g_error_free (error); - } else if (connections_dir) { - gs_free char *basename_w_ext = g_strconcat (basename, ".nmconnection", NULL); - char *filename = g_build_filename (connections_dir, basename_w_ext, NULL); - - if (!nm_utils_file_set_contents (filename, data, len, 0600, &error)) { - g_print ("%s\n", error->message); - g_error_free (error); - } - g_free (filename); - } else { + if (!data) + goto err_out; + + if (connections_dir) { + gs_free char *filename = NULL; + gs_free char *full_filename = NULL; + + filename = nm_keyfile_utils_create_filename (basename, TRUE); + full_filename = g_build_filename (connections_dir, filename, NULL); + + if (!nm_utils_file_set_contents (full_filename, data, len, 0600, &error)) + goto err_out; + } else g_print ("\n*** Connection '%s' ***\n\n%s\n", basename, data); - } - g_key_file_free (file); + return; +err_out: + g_print ("%s\n", error->message); } -#define DEFAULT_CONNECTIONS_DIR NMRUNDIR "/system-connections" #define DEFAULT_SYSFS_DIR "/sys" int @@ -90,7 +84,7 @@ main (int argc, char *argv[]) gboolean dump_to_stdout = FALSE; gs_strfreev char **remaining = NULL; GOptionEntry option_entries[] = { - { "connections-dir", 'c', 0, G_OPTION_ARG_FILENAME, &connections_dir, "Output connection directory", DEFAULT_CONNECTIONS_DIR }, + { "connections-dir", 'c', 0, G_OPTION_ARG_FILENAME, &connections_dir, "Output connection directory", NM_KEYFILE_PATH_NAME_RUN }, { "sysfs-dir", 'd', 0, G_OPTION_ARG_FILENAME, &sysfs_dir, "The sysfs mount point", DEFAULT_SYSFS_DIR }, { "stdout", 's', 0, G_OPTION_ARG_NONE, &dump_to_stdout, "Dump connections to standard output", NULL }, { G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_STRING_ARRAY, &remaining, NULL, NULL }, @@ -110,7 +104,7 @@ main (int argc, char *argv[]) g_option_context_add_main_entries (option_context, option_entries, GETTEXT_PACKAGE); if (!g_option_context_parse (option_context, &argc, &argv, &error)) { - _LOGW (LOGD_CORE, "%s\n", error->message); + _LOGW (LOGD_CORE, "%s", error->message); return 1; } @@ -120,14 +114,14 @@ main (int argc, char *argv[]) } if (!connections_dir) - connections_dir = g_strdup (DEFAULT_CONNECTIONS_DIR); + connections_dir = g_strdup (NM_KEYFILE_PATH_NAME_RUN); if (!sysfs_dir) sysfs_dir = g_strdup (DEFAULT_SYSFS_DIR); if (dump_to_stdout) g_clear_pointer (&connections_dir, g_free); if (connections_dir && g_mkdir_with_parents (connections_dir, 0755) != 0) { - _LOGW (LOGD_CORE, "%s: %s\n", connections_dir, strerror (errno)); + _LOGW (LOGD_CORE, "%s: %s", connections_dir, strerror (errno)); return 1; } diff --git a/src/initrd/nmi-cmdline-reader.c b/src/initrd/nmi-cmdline-reader.c index e3b1bb63..e812b086 100644 --- a/src/initrd/nmi-cmdline-reader.c +++ b/src/initrd/nmi-cmdline-reader.c @@ -39,7 +39,17 @@ get_conn (GHashTable *connections, const char *ifname, const char *type_name) { NMConnection *connection; NMSetting *setting; - const char *basename = ifname ?: "default_connection"; + const char *basename; + NMConnectionMultiConnect multi_connect; + + if (ifname) { + basename = ifname; + multi_connect = NM_CONNECTION_MULTI_CONNECT_SINGLE; + } else { + /* This is essentially for the "ip=dhcp" scenario. */ + basename = "default_connection"; + multi_connect = NM_CONNECTION_MULTI_CONNECT_MULTIPLE; + } connection = g_hash_table_lookup (connections, (gpointer)basename); @@ -71,6 +81,7 @@ get_conn (GHashTable *connections, const char *ifname, const char *type_name) NM_SETTING_CONNECTION_ID, ifname ?: "Wired Connection", NM_SETTING_CONNECTION_UUID, nm_utils_uuid_generate_a (), NM_SETTING_CONNECTION_INTERFACE_NAME, ifname, + NM_SETTING_CONNECTION_MULTI_CONNECT, multi_connect, NULL); if (!type_name) @@ -132,7 +143,7 @@ _base_setting_set (NMConnection *connection, const char *property, const char *v GParamSpec *spec = g_object_class_find_property (object_class, property); if (!spec) { - _LOGW (LOGD_CORE, "'%s' does not support setting %s\n", type_name, property); + _LOGW (LOGD_CORE, "'%s' does not support setting %s", type_name, property); return; } @@ -151,7 +162,7 @@ _base_setting_set (NMConnection *connection, const char *property, const char *v } else if (G_IS_PARAM_SPEC_STRING (spec)) g_object_set (setting, property, value, NULL); else - _LOGW (LOGD_CORE, "Don't know how to set '%s' of %s\n", property, type_name); + _LOGW (LOGD_CORE, "Don't know how to set '%s' of %s", property, type_name); g_type_class_unref (object_class); } @@ -211,7 +222,7 @@ parse_ip (GHashTable *connections, const char *sysfs_dir, char *argument) dns[1] = get_word (&argument, ':'); dns_addr_family[1] = guess_ip_address_family (dns[1]); if (argument && *argument) - _LOGW (LOGD_CORE, "Ignoring extra: '%s'.\n", argument); + _LOGW (LOGD_CORE, "Ignoring extra: '%s'.", argument); } else { mtu = tmp; macaddr = argument; @@ -236,12 +247,12 @@ parse_ip (GHashTable *connections, const char *sysfs_dir, char *argument) index = g_hash_table_lookup (nic, "index"); if (!index) { - _LOGW (LOGD_CORE, "Ignoring an iBFT entry without an index\n"); + _LOGW (LOGD_CORE, "Ignoring an iBFT entry without an index"); continue; } if (!nmi_ibft_update_connection_from_nic (connection, nic, &error)) { - _LOGW (LOGD_CORE, "Unable to merge iBFT configuration: %s\n", error->message); + _LOGW (LOGD_CORE, "Unable to merge iBFT configuration: %s", error->message); g_error_free (error); } @@ -261,10 +272,10 @@ parse_ip (GHashTable *connections, const char *sysfs_dir, char *argument) if (netmask && *netmask) { NMIPAddr addr; - if (nm_utils_parse_inaddr_bin (AF_INET, netmask, &addr)) { + if (nm_utils_parse_inaddr_bin (AF_INET, netmask, NULL, &addr)) { client_ip_prefix = nm_utils_ip4_netmask_to_prefix (addr.addr4); } else { - _LOGW (LOGD_CORE, "Unrecognized address: %s\n", client_ip); + _LOGW (LOGD_CORE, "Unrecognized address: %s", client_ip); } } @@ -273,7 +284,7 @@ parse_ip (GHashTable *connections, const char *sysfs_dir, char *argument) NMIPAddress *address = NULL; NMIPAddr addr; - if (nm_utils_parse_inaddr_prefix_bin (client_ip_family, client_ip, &addr, + if (nm_utils_parse_inaddr_prefix_bin (client_ip_family, client_ip, NULL, &addr, client_ip_prefix == -1 ? &client_ip_prefix : NULL)) { if (client_ip_prefix == -1) { switch (client_ip_family) { @@ -288,11 +299,11 @@ parse_ip (GHashTable *connections, const char *sysfs_dir, char *argument) address = nm_ip_address_new_binary (client_ip_family, &addr.addr_ptr, client_ip_prefix, &error); if (!address) { - _LOGW (LOGD_CORE, "Invalid address '%s': %s\n", client_ip, error->message); + _LOGW (LOGD_CORE, "Invalid address '%s': %s", client_ip, error->message); g_clear_error (&error); } } else { - _LOGW (LOGD_CORE, "Unrecognized address: %s\n", client_ip); + _LOGW (LOGD_CORE, "Unrecognized address: %s", client_ip); } if (address) { @@ -312,7 +323,7 @@ parse_ip (GHashTable *connections, const char *sysfs_dir, char *argument) nm_setting_ip_config_add_address (s_ip6, address); break; default: - _LOGW (LOGD_CORE, "Unknown address family: %s\n", client_ip); + _LOGW (LOGD_CORE, "Unknown address family: %s", client_ip); break; } nm_ip_address_unref (address); @@ -377,12 +388,12 @@ parse_ip (GHashTable *connections, const char *sysfs_dir, char *argument) ibft = nmi_ibft_read (sysfs_dir); nic = g_hash_table_lookup (ibft, mac_up); if (!nic) - _LOGW (LOGD_CORE, "No iBFT NIC for %s (%s)\n", ifname, mac_up); + _LOGW (LOGD_CORE, "No iBFT NIC for %s (%s)", ifname, mac_up); } if (nic) { if (!nmi_ibft_update_connection_from_nic (connection, nic, &error)) { - _LOGW (LOGD_CORE, "Unable to merge iBFT configuration: %s\n", error->message); + _LOGW (LOGD_CORE, "Unable to merge iBFT configuration: %s", error->message); g_clear_error (&error); } } @@ -403,11 +414,11 @@ parse_ip (GHashTable *connections, const char *sysfs_dir, char *argument) g_object_set (s_ip6, NM_SETTING_IP_CONFIG_GATEWAY, gateway_ip, NULL); break; default: - _LOGW (LOGD_CORE, "Unknown address family: %s\n", gateway_ip); + _LOGW (LOGD_CORE, "Unknown address family: %s", gateway_ip); break; } } else { - _LOGW (LOGD_CORE, "Invalid gateway: %s\n", gateway_ip); + _LOGW (LOGD_CORE, "Invalid gateway: %s", gateway_ip); } } @@ -428,11 +439,11 @@ parse_ip (GHashTable *connections, const char *sysfs_dir, char *argument) nm_setting_ip_config_add_dns (s_ip6, dns[i]); break; default: - _LOGW (LOGD_CORE, "Unknown address family: %s\n", dns[i]); + _LOGW (LOGD_CORE, "Unknown address family: %s", dns[i]); break; } } else { - _LOGW (LOGD_CORE, "Invalid name server: %s\n", dns[i]); + _LOGW (LOGD_CORE, "Invalid name server: %s", dns[i]); } } @@ -496,7 +507,7 @@ parse_master (GHashTable *connections, char *argument, const char *type_name) } while (slaves && *slaves != '\0'); if (argument && *argument) - _LOGW (LOGD_CORE, "Ignoring extra: '%s'.\n", argument); + _LOGW (LOGD_CORE, "Ignoring extra: '%s'.", argument); } static void @@ -518,19 +529,18 @@ parse_rd_route (GHashTable *connections, char *argument) gateway = get_word (&argument, ':'); interface = get_word (&argument, ':'); - family = guess_ip_address_family (net); connection = get_conn (connections, interface, NULL); if (net && *net) { - if (!nm_utils_parse_inaddr_prefix_bin (family, net, &net_addr, &net_prefix)) { - _LOGW (LOGD_CORE, "Unrecognized address: %s\n", net); + if (!nm_utils_parse_inaddr_prefix_bin (family, net, &family, &net_addr, &net_prefix)) { + _LOGW (LOGD_CORE, "Unrecognized address: %s", net); return; } } - if (gateway && *net) { - if (!nm_utils_parse_inaddr_bin (family, gateway, &gateway_addr)) { - _LOGW (LOGD_CORE, "Unrecognized address: %s\n", gateway); + if (gateway && *gateway) { + if (!nm_utils_parse_inaddr_bin (family, gateway, &family, &gateway_addr)) { + _LOGW (LOGD_CORE, "Unrecognized address: %s", gateway); return; } } @@ -547,7 +557,7 @@ parse_rd_route (GHashTable *connections, char *argument) net_prefix = 128; break; default: - _LOGW (LOGD_CORE, "Unknown address family: %s\n", net); + _LOGW (LOGD_CORE, "Unknown address family: %s", net); return; } @@ -588,7 +598,7 @@ parse_vlan (GHashTable *connections, char *argument) NULL); if (argument && *argument) - _LOGW (LOGD_CORE, "Ignoring extra: '%s'.\n", argument); + _LOGW (LOGD_CORE, "Ignoring extra: '%s'.", argument); } static void @@ -624,14 +634,14 @@ parse_nameserver (GHashTable *connections, char *argument) s_ip = nm_connection_get_setting_ip6_config (connection); break; default: - _LOGW (LOGD_CORE, "Unknown address family: %s\n", dns); + _LOGW (LOGD_CORE, "Unknown address family: %s", dns); break; } nm_setting_ip_config_add_dns (s_ip, dns); if (argument && *argument) - _LOGW (LOGD_CORE, "xIgnoring extra: '%s'.\n", argument); + _LOGW (LOGD_CORE, "Ignoring extra: '%s'.", argument); } static void diff --git a/src/initrd/tests/meson.build b/src/initrd/tests/meson.build new file mode 100644 index 00000000..0ef72fff --- /dev/null +++ b/src/initrd/tests/meson.build @@ -0,0 +1,23 @@ +test_units = [ + 'test-ibft-reader', + 'test-cmdline-reader', +] + +cflags = [ + '-DTEST_INITRD_DIR="@0@"'.format(meson.current_source_dir()), +] + +foreach test_unit : test_units + exe = executable( + test_unit, + test_unit + '.c', + dependencies: test_nm_dep, + c_args: cflags, + link_with: libnmi_core, + ) + test( + 'initrd/' + test_unit, + test_script, + args: test_args + [exe.full_path()], + ) +endforeach diff --git a/src/initrd/tests/test-cmdline-reader.c b/src/initrd/tests/test-cmdline-reader.c index 4db71147..95084e92 100644 --- a/src/initrd/tests/test-cmdline-reader.c +++ b/src/initrd/tests/test-cmdline-reader.c @@ -62,6 +62,8 @@ test_auto (void) g_assert_cmpstr (nm_setting_connection_get_connection_type (s_con), ==, NM_SETTING_WIRED_SETTING_NAME); g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "Wired Connection"); g_assert_cmpint (nm_setting_connection_get_timestamp (s_con), ==, 0); + g_assert_cmpint (nm_setting_connection_get_multi_connect (s_con), ==, NM_CONNECTION_MULTI_CONNECT_MULTIPLE); + g_assert (nm_setting_connection_get_autoconnect (s_con)); s_wired = nm_connection_get_setting_wired (connection); @@ -328,6 +330,7 @@ test_some_more (void) g_assert_cmpstr (nm_setting_connection_get_connection_type (s_con), ==, NM_SETTING_WIRED_SETTING_NAME); g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "Wired Connection"); g_assert_cmpstr (nm_setting_connection_get_interface_name (s_con), ==, "eth1"); + g_assert_cmpint (nm_setting_connection_get_multi_connect (s_con), ==, NM_CONNECTION_MULTI_CONNECT_MULTIPLE); s_wired = nm_connection_get_setting_wired (connection); g_assert (s_wired); @@ -361,6 +364,7 @@ test_some_more (void) g_assert_cmpstr (nm_setting_connection_get_connection_type (s_con), ==, NM_SETTING_WIRED_SETTING_NAME); g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "ens10"); g_assert_cmpstr (nm_setting_connection_get_interface_name (s_con), ==, "ens10"); + g_assert_cmpint (nm_setting_connection_get_multi_connect (s_con), ==, NM_CONNECTION_MULTI_CONNECT_SINGLE); s_wired = nm_connection_get_setting_wired (connection); g_assert (s_wired); @@ -454,6 +458,7 @@ test_bond (void) g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "eth0"); g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, NM_SETTING_BOND_SETTING_NAME); g_assert_cmpstr (nm_setting_connection_get_master (s_con), ==, master_uuid); + g_assert_cmpint (nm_setting_connection_get_multi_connect (s_con), ==, NM_CONNECTION_MULTI_CONNECT_SINGLE); connection = g_hash_table_lookup (connections, "eth1"); g_assert (connection); @@ -466,6 +471,7 @@ test_bond (void) g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "eth1"); g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, NM_SETTING_BOND_SETTING_NAME); g_assert_cmpstr (nm_setting_connection_get_master (s_con), ==, master_uuid); + g_assert_cmpint (nm_setting_connection_get_multi_connect (s_con), ==, NM_CONNECTION_MULTI_CONNECT_SINGLE); } static void @@ -525,6 +531,7 @@ test_bond_default (void) g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "eth0"); g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, NM_SETTING_BOND_SETTING_NAME); g_assert_cmpstr (nm_setting_connection_get_master (s_con), ==, master_uuid); + g_assert_cmpint (nm_setting_connection_get_multi_connect (s_con), ==, NM_CONNECTION_MULTI_CONNECT_SINGLE); } static void @@ -588,6 +595,7 @@ test_bridge (void) g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "eth0"); g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, NM_SETTING_BRIDGE_SETTING_NAME); g_assert_cmpstr (nm_setting_connection_get_master (s_con), ==, master_uuid); + g_assert_cmpint (nm_setting_connection_get_multi_connect (s_con), ==, NM_CONNECTION_MULTI_CONNECT_SINGLE); connection = g_hash_table_lookup (connections, "eth1"); g_assert (connection); @@ -600,6 +608,7 @@ test_bridge (void) g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "eth1"); g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, NM_SETTING_BRIDGE_SETTING_NAME); g_assert_cmpstr (nm_setting_connection_get_master (s_con), ==, master_uuid); + g_assert_cmpint (nm_setting_connection_get_multi_connect (s_con), ==, NM_CONNECTION_MULTI_CONNECT_SINGLE); } static void @@ -657,6 +666,7 @@ test_bridge_default (void) g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "eth0"); g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, NM_SETTING_BRIDGE_SETTING_NAME); g_assert_cmpstr (nm_setting_connection_get_master (s_con), ==, master_uuid); + g_assert_cmpint (nm_setting_connection_get_multi_connect (s_con), ==, NM_CONNECTION_MULTI_CONNECT_SINGLE); } static void @@ -713,6 +723,7 @@ test_team (void) g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "eth0"); g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, NM_SETTING_TEAM_SETTING_NAME); g_assert_cmpstr (nm_setting_connection_get_master (s_con), ==, master_uuid); + g_assert_cmpint (nm_setting_connection_get_multi_connect (s_con), ==, NM_CONNECTION_MULTI_CONNECT_SINGLE); connection = g_hash_table_lookup (connections, "eth1"); g_assert (connection); @@ -725,6 +736,7 @@ test_team (void) g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "eth1"); g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, NM_SETTING_TEAM_SETTING_NAME); g_assert_cmpstr (nm_setting_connection_get_master (s_con), ==, master_uuid); + g_assert_cmpint (nm_setting_connection_get_multi_connect (s_con), ==, NM_CONNECTION_MULTI_CONNECT_SINGLE); } static void diff --git a/src/main.c b/src/main.c index f834fa94..36356a7a 100644 --- a/src/main.c +++ b/src/main.c @@ -155,7 +155,7 @@ nm_main_config_reload (int signal) * * Hence, a NMConfig singleton instance must always be * available. */ - nm_config_reload (nm_config_get (), reload_flags); + nm_config_reload (nm_config_get (), reload_flags, TRUE); } static void @@ -232,6 +232,7 @@ main (int argc, char *argv[]) NMConfigCmdLineOptions *config_cli; guint sd_id = 0; GError *error_invalid_logging_config = NULL; + const char *const *warnings; /* Known to cause a possible deadlock upon GDBus initialization: * https://bugzilla.gnome.org/show_bug.cgi?id=674885 */ @@ -376,6 +377,11 @@ main (int argc, char *argv[]) nm_clear_g_free (&bad_domains); } + warnings = nm_config_get_warnings (config); + for ( ; warnings && *warnings; warnings++) + nm_log_warn (LOGD_CORE, "config: %s", *warnings); + nm_config_clear_warnings (config); + /* the first access to State causes the file to be read (and possibly print a warning) */ nm_config_state_get (config); diff --git a/src/meson.build b/src/meson.build index 28fcfa8f..ff2276e3 100644 --- a/src/meson.build +++ b/src/meson.build @@ -2,7 +2,7 @@ src_inc = include_directories('.') install_data( 'org.freedesktop.NetworkManager.conf', - install_dir: dbus_conf_dir + install_dir: dbus_conf_dir, ) subdir('systemd') @@ -14,7 +14,7 @@ nm_cflags = ['-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_DAEMON' nm_dep = declare_dependency( include_directories: src_inc, dependencies: nm_core_dep, - compile_args: nm_cflags + compile_args: nm_cflags, ) cflags = nm_cflags @@ -42,13 +42,15 @@ sources = files( 'nm-dbus-utils.c', 'nm-ip4-config.c', 'nm-ip6-config.c', - 'nm-logging.c' + 'nm-logging.c', ) +sources += shared_files_time_utils + deps = [ libsystemd_dep, libudev_dep, - nm_core_dep + nm_core_dep, ] if enable_wext @@ -60,7 +62,7 @@ libnetwork_manager_base = static_library( sources: sources, dependencies: deps, c_args: cflags, - link_with: libnm_core + link_with: libnm_core, ) sources = files( @@ -133,6 +135,7 @@ sources = files( 'nm-dispatcher.c', 'nm-firewall-manager.c', 'nm-hostname-manager.c', + 'nm-keep-alive.c', 'nm-manager.c', 'nm-netns.c', 'nm-pacrunner-manager.c', @@ -140,7 +143,7 @@ sources = files( 'nm-proxy-config.c', 'nm-rfkill-manager.c', 'nm-session-monitor.c', - 'nm-sleep-monitor.c' + 'nm-sleep-monitor.c', ) nm_deps = [ @@ -173,14 +176,18 @@ libnetwork_manager = static_library( sources: sources, dependencies: nm_deps, c_args: cflags, - link_with: [libnetwork_manager_base, libsystemd_nm] + link_with: [ + libnetwork_manager_base, + libnm_systemd_core, + libnm_systemd_shared, + ], ) deps = [ dl_dep, libndp_dep, libudev_dep, - nm_core_dep + nm_core_dep, ] name = 'nm-iface-helper' @@ -190,23 +197,27 @@ executable( name + '.c', dependencies: deps, c_args: cflags, - link_with: [libnetwork_manager_base, libsystemd_nm], + link_with: [ + libnetwork_manager_base, + libnm_systemd_core, + libnm_systemd_shared, + ], link_args: ldflags_linker_script_binary, link_depends: linker_script_binary, install: true, - install_dir: nm_libexecdir + install_dir: nm_libexecdir, ) if enable_tests sources = files( 'ndisc/nm-fake-ndisc.c', 'platform/tests/test-common.c', - 'platform/nm-fake-platform.c' + 'platform/nm-fake-platform.c', ) deps = [ libudev_dep, - nm_core_dep + nm_core_dep, ] test_cflags = ['-DNETWORKMANAGER_COMPILATION_TEST'] @@ -219,13 +230,13 @@ if enable_tests sources: sources, dependencies: deps, c_args: cflags + test_cflags, - link_with: libnetwork_manager + link_with: libnetwork_manager, ) test_nm_dep = declare_dependency( dependencies: nm_dep, compile_args: test_cflags, - link_with: libnetwork_manager_test + link_with: libnetwork_manager_test, ) test_nm_dep_fake = declare_dependency( @@ -281,7 +292,7 @@ ver_script = custom_target( input: meson.source_root(), output: symbol_map_name, depends: [ network_manager_sym, core_plugins ], - command: [create_exports_networkmanager, '--called-from-build', '@INPUT@'] + command: [create_exports_networkmanager, '--called-from-build', '@INPUT@'], ) ldflags = ['-rdynamic', '-Wl,--version-script,@0@'.format(ver_script.full_path())] @@ -295,7 +306,7 @@ network_manager = executable( link_args: ldflags, link_depends: ver_script, install: true, - install_dir: nm_sbindir + install_dir: nm_sbindir, ) if enable_tests @@ -306,3 +317,9 @@ if enable_tests env: ['LD_BIND_NOW=1', 'LD_PRELOAD=' + plugin.full_path()]) endforeach endif + +test( + 'check-config-options', + find_program(join_paths(meson.source_root(), 'tools', 'check-config-options.sh')), + args: [meson.source_root()] +) diff --git a/src/ndisc/nm-lndp-ndisc.c b/src/ndisc/nm-lndp-ndisc.c index e1003ad1..53548050 100644 --- a/src/ndisc/nm-lndp-ndisc.c +++ b/src/ndisc/nm-lndp-ndisc.c @@ -259,7 +259,7 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) /* Pad the lifetime somewhat to give a bit of slack in cases * where one RA gets lost or something (which can happen on unreliable - * links like WiFi where certain types of frames are not retransmitted). + * links like Wi-Fi where certain types of frames are not retransmitted). * Note that 0 has special meaning and is therefore not adjusted. */ if (dns_server.lifetime && dns_server.lifetime < 7200) @@ -281,7 +281,7 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data) /* Pad the lifetime somewhat to give a bit of slack in cases * where one RA gets lost or something (which can happen on unreliable - * links like WiFi where certain types of frames are not retransmitted). + * links like Wi-Fi where certain types of frames are not retransmitted). * Note that 0 has special meaning and is therefore not adjusted. */ if (dns_domain.lifetime && dns_domain.lifetime < 7200) @@ -539,14 +539,14 @@ start (NMNDisc *ndisc) static inline int ipv6_sysctl_get (NMPlatform *platform, const char *ifname, const char *property, int min, int max, int defval) { - char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; - - return (int) nm_platform_sysctl_get_int_checked (platform, - NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET6, buf, ifname, property)), - 10, - min, - max, - defval); + return nm_platform_sysctl_ip_conf_get_int_checked (platform, + AF_INET6, + ifname, + property, + 10, + min, + max, + defval); } static void diff --git a/src/ndisc/nm-ndisc.c b/src/ndisc/nm-ndisc.c index 1dd8398c..2da08c5f 100644 --- a/src/ndisc/nm-ndisc.c +++ b/src/ndisc/nm-ndisc.c @@ -965,7 +965,9 @@ nm_ndisc_dad_failed (NMNDisc *ndisc, const struct in6_addr *address, gboolean em NMNDiscAddress *item = &g_array_index (rdata->addresses, NMNDiscAddress, i); if (IN6_ARE_ADDR_EQUAL (&item->address, address)) { - _LOGD ("DAD failed for discovered address %s", nm_utils_inet6_ntop (address, NULL)); + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + + _LOGD ("DAD failed for discovered address %s", nm_utils_inet6_ntop (address, sbuf)); changed = TRUE; if (!complete_address (ndisc, item)) { g_array_remove_index (rdata->addresses, i); @@ -1056,10 +1058,11 @@ _config_changed_log (NMNDisc *ndisc, NMNDiscConfigMap changed) } for (i = 0; i < rdata->routes->len; i++) { NMNDiscRoute *route = &g_array_index (rdata->routes, NMNDiscRoute, i); + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; inet_ntop (AF_INET6, &route->network, addrstr, sizeof (addrstr)); _LOGD (" route %s/%u via %s pref %s exp %s", addrstr, (guint) route->plen, - nm_utils_inet6_ntop (&route->gateway, NULL), + nm_utils_inet6_ntop (&route->gateway, sbuf), nm_icmpv6_router_pref_to_string (route->preference, str_pref, sizeof (str_pref)), get_exp (str_exp, now_ns, route)); } diff --git a/src/ndisc/nm-ndisc.h b/src/ndisc/nm-ndisc.h index 73eef368..6c0c0264 100644 --- a/src/ndisc/nm-ndisc.h +++ b/src/ndisc/nm-ndisc.h @@ -23,6 +23,7 @@ #include <stdlib.h> #include <netinet/in.h> +#include <linux/if_addr.h> #include "nm-setting-ip6-config.h" #include "NetworkManagerUtils.h" diff --git a/src/ndisc/tests/meson.build b/src/ndisc/tests/meson.build index e0dc9aa6..0395e406 100644 --- a/src/ndisc/tests/meson.build +++ b/src/ndisc/tests/meson.build @@ -9,7 +9,7 @@ exe = executable( test( 'ndisc/' + test_unit, test_script, - args: test_args + [exe.full_path()] + args: test_args + [exe.full_path()], ) test = 'test-ndisc-linux' diff --git a/src/nm-act-request.c b/src/nm-act-request.c index 0ac20b85..cd816964 100644 --- a/src/nm-act-request.c +++ b/src/nm-act-request.c @@ -534,11 +534,12 @@ nm_act_request_init (NMActRequest *req) * * @settings_connection: (allow-none): the connection to activate @device with * @applied_connection: (allow-none): the applied connection - * @specific_object: the object path of the specific object (ie, WiFi access point, + * @specific_object: the object path of the specific object (ie, Wi-Fi access point, * etc) that will be used to activate @connection and @device * @subject: the #NMAuthSubject representing the requestor of the activation * @activation_type: the #NMActivationType * @activation_reason: the reason for activation + * @initial_state_flags: the initial state flags. * @device: the device/interface to configure according to @connection * * Creates a new device-based activation request. If an applied connection is @@ -553,6 +554,7 @@ nm_act_request_new (NMSettingsConnection *settings_connection, NMAuthSubject *subject, NMActivationType activation_type, NMActivationReason activation_reason, + NMActivationStateFlags initial_state_flags, NMDevice *device) { g_return_val_if_fail (!settings_connection || NM_IS_SETTINGS_CONNECTION (settings_connection), NULL); @@ -567,6 +569,7 @@ nm_act_request_new (NMSettingsConnection *settings_connection, NM_ACTIVE_CONNECTION_INT_SUBJECT, subject, NM_ACTIVE_CONNECTION_INT_ACTIVATION_TYPE, (int) activation_type, NM_ACTIVE_CONNECTION_INT_ACTIVATION_REASON, (int) activation_reason, + NM_ACTIVE_CONNECTION_STATE_FLAGS, (guint) initial_state_flags, NULL); } diff --git a/src/nm-act-request.h b/src/nm-act-request.h index a8f09271..af2b7490 100644 --- a/src/nm-act-request.h +++ b/src/nm-act-request.h @@ -42,6 +42,7 @@ NMActRequest *nm_act_request_new (NMSettingsConnection *settings_connec NMAuthSubject *subject, NMActivationType activation_type, NMActivationReason activation_reason, + NMActivationStateFlags initial_state_flags, NMDevice *device); NMSettingsConnection *nm_act_request_get_settings_connection (NMActRequest *req); diff --git a/src/nm-active-connection.c b/src/nm-active-connection.c index a2f9ae4a..7b9f2cc7 100644 --- a/src/nm-active-connection.c +++ b/src/nm-active-connection.c @@ -30,6 +30,7 @@ #include "nm-auth-utils.h" #include "nm-auth-manager.h" #include "nm-auth-subject.h" +#include "nm-keep-alive.h" #include "NetworkManagerUtils.h" #include "nm-core-internal.h" @@ -75,6 +76,7 @@ typedef struct _NMActiveConnectionPrivate { gpointer user_data; } auth; + NMKeepAlive *keep_alive; } NMActiveConnectionPrivate; NM_GOBJECT_PROPERTIES_DEFINE (NMActiveConnection, @@ -159,16 +161,21 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_state_to_string, NMActiveConnectionState, NM_UTILS_LOOKUP_STR_ITEM (NM_ACTIVE_CONNECTION_STATE_DEACTIVATING, "deactivating"), NM_UTILS_LOOKUP_STR_ITEM (NM_ACTIVE_CONNECTION_STATE_DEACTIVATED, "deactivated"), ); -#define state_to_string(state) NM_UTILS_LOOKUP_STR (_state_to_string, state) + +#define state_to_string_a(state) NM_UTILS_LOOKUP_STR_A (_state_to_string, state) + +/* the maximum required buffer size for _state_flags_to_string(). */ +#define _NM_ACTIVATION_STATE_FLAG_TO_STRING_BUFSIZE (255) NM_UTILS_FLAGS2STR_DEFINE_STATIC (_state_flags_to_string, NMActivationStateFlags, - NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_NONE, "none"), - NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_IS_MASTER, "is-master"), - NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_IS_SLAVE, "is-slave"), - NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_LAYER2_READY, "layer2-ready"), - NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_IP4_READY, "ip4-ready"), - NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_IP6_READY, "ip6-ready"), - NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_MASTER_HAS_SLAVES, "master-has-slaves"), + NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_NONE, "none"), + NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_IS_MASTER, "is-master"), + NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_IS_SLAVE, "is-slave"), + NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_LAYER2_READY, "layer2-ready"), + NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_IP4_READY, "ip4-ready"), + NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_IP6_READY, "ip6-ready"), + NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_MASTER_HAS_SLAVES, "master-has-slaves"), + NM_UTILS_FLAGS2STR (NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY, "lifetime-bound-to-profile-visibility"), ); /*****************************************************************************/ @@ -250,8 +257,14 @@ nm_active_connection_set_state (NMActiveConnection *self, return; _LOGD ("set state %s (was %s)", - state_to_string (new_state), - state_to_string (priv->state)); + state_to_string_a (new_state), + state_to_string_a (priv->state)); + + if (new_state > NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { + /* once we are about to deactivate, we don't need the keep-alive instance + * anymore. Freeze/disarm it. */ + nm_keep_alive_disarm (priv->keep_alive); + } if ( new_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED && priv->activation_type == NM_ACTIVATION_TYPE_ASSUME) { @@ -353,14 +366,20 @@ nm_active_connection_set_state_flags_full (NMActiveConnection *self, f = (priv->state_flags & ~mask) | (state_flags & mask); if (f != priv->state_flags) { - char buf1[G_N_ELEMENTS (_nm_utils_to_string_buffer)]; - char buf2[G_N_ELEMENTS (_nm_utils_to_string_buffer)]; + char buf1[_NM_ACTIVATION_STATE_FLAG_TO_STRING_BUFSIZE]; + char buf2[_NM_ACTIVATION_STATE_FLAG_TO_STRING_BUFSIZE]; _LOGD ("set state-flags %s (was %s)", _state_flags_to_string (f, buf1, sizeof (buf1)), _state_flags_to_string (priv->state_flags, buf2, sizeof (buf2))); priv->state_flags = f; _notify (self, PROP_STATE_FLAGS); + + nm_keep_alive_set_settings_connection_watch_visible (priv->keep_alive, + NM_FLAGS_HAS (priv->state_flags, + NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY) + ? priv->settings_connection.obj + : NULL); } } @@ -768,11 +787,11 @@ check_master_ready (NMActiveConnection *self) signalling ? "signal" : (priv->master_ready ? "already signalled" : "not signalling"), - state_to_string (priv->state), + state_to_string_a (priv->state), priv->master ? nm_sprintf_bufa (128, "master %p is in state %s", priv->master, - state_to_string (nm_active_connection_get_state (priv->master))) + state_to_string_a (nm_active_connection_get_state (priv->master))) : "no master"); if (signalling) { @@ -836,7 +855,7 @@ nm_active_connection_set_master (NMActiveConnection *self, NMActiveConnection *m _LOGD ("set master %p, %s, state %s", master, nm_active_connection_get_settings_connection_id (master), - state_to_string (nm_active_connection_get_state (master))); + state_to_string_a (nm_active_connection_get_state (master))); priv->master = g_object_ref (master); g_signal_connect (priv->master, @@ -906,6 +925,32 @@ nm_active_connection_get_activation_reason (NMActiveConnection *self) /*****************************************************************************/ +/** + * nm_active_connection_get_keep_alive: + * @self: the #NMActiveConnection instance + * + * Gives the #NMKeepAlive instance of the active connection. Note that + * @self is guaranteed not to swap the keep-alive instance, so it is + * in particular safe to assume that the keep-alive instance is alive + * as long as @self, and that nm_active_connection_get_keep_alive() + * will return always the same instance. + * + * In particular this means, that it is safe and encouraged, that you + * register to the notify:alive property changed signal of the returned + * instance. + * + * Returns: the #NMKeepAlive instance. + */ +NMKeepAlive * +nm_active_connection_get_keep_alive (NMActiveConnection *self) +{ + g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (self), NULL); + + return NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->keep_alive; +} + +/*****************************************************************************/ + static void _settings_connection_flags_changed (NMSettingsConnection *settings_connection, NMActiveConnection *self) @@ -1350,6 +1395,12 @@ set_property (GObject *object, guint prop_id, g_return_if_reached (); _set_activation_type (self, (NMActivationType) i); break; + case PROP_STATE_FLAGS: + /* construct-only */ + priv->state_flags = g_value_get_uint (value); + nm_assert ((guint) priv->state_flags == g_value_get_uint (value)); + nm_assert (!NM_FLAGS_ANY (priv->state_flags, ~NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY)); + break; case PROP_INT_ACTIVATION_REASON: /* construct-only */ i = g_value_get_int (value); @@ -1400,6 +1451,10 @@ nm_active_connection_init (NMActiveConnection *self) priv->activation_type = NM_ACTIVATION_TYPE_MANAGED; priv->version_id = _version_id_new (); + + /* the keep-alive instance must never change. Callers rely on that. */ + priv->keep_alive = nm_keep_alive_new (); + _nm_keep_alive_set_owner (priv->keep_alive, G_OBJECT (self)); } static void @@ -1429,6 +1484,10 @@ constructed (GObject *object) g_steal_pointer (&priv->applied_connection)); } + if (NM_FLAGS_HAS (priv->state_flags, + NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY)) + nm_keep_alive_set_settings_connection_watch_visible (priv->keep_alive, priv->settings_connection.obj); + g_return_if_fail (priv->subject); g_return_if_fail (priv->activation_reason != NM_ACTIVATION_REASON_UNSET); } @@ -1475,6 +1534,9 @@ finalize (GObject *object) nm_dbus_track_obj_path_set (&priv->settings_connection, NULL, FALSE); + _nm_keep_alive_set_owner (priv->keep_alive, NULL); + g_clear_object (&priv->keep_alive); + G_OBJECT_CLASS (nm_active_connection_parent_class)->finalize (object); } @@ -1579,7 +1641,7 @@ nm_active_connection_class_init (NMActiveConnectionClass *ac_class) obj_properties[PROP_STATE_FLAGS] = g_param_spec_uint (NM_ACTIVE_CONNECTION_STATE_FLAGS, "", "", 0, G_MAXUINT32, NM_ACTIVATION_STATE_FLAG_NONE, - G_PARAM_READABLE | + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); obj_properties[PROP_DEFAULT] = diff --git a/src/nm-active-connection.h b/src/nm-active-connection.h index b28c2b02..1d4ce29e 100644 --- a/src/nm-active-connection.h +++ b/src/nm-active-connection.h @@ -163,6 +163,13 @@ nm_active_connection_set_state_flags (NMActiveConnection *self, nm_active_connection_set_state_flags_full (self, state_flags, state_flags); } +static inline void +nm_active_connection_set_state_flags_clear (NMActiveConnection *self, + NMActivationStateFlags state_flags) +{ + nm_active_connection_set_state_flags_full (self, NM_ACTIVATION_STATE_FLAG_NONE, state_flags); +} + NMDevice * nm_active_connection_get_device (NMActiveConnection *self); gboolean nm_active_connection_set_device (NMActiveConnection *self, NMDevice *device); @@ -185,6 +192,8 @@ NMActivationType nm_active_connection_get_activation_type (NMActiveConnection *s NMActivationReason nm_active_connection_get_activation_reason (NMActiveConnection *self); +NMKeepAlive *nm_active_connection_get_keep_alive (NMActiveConnection *self); + void nm_active_connection_clear_secrets (NMActiveConnection *self); #endif /* __NETWORKMANAGER_ACTIVE_CONNECTION_H__ */ diff --git a/src/nm-audit-manager.c b/src/nm-audit-manager.c index 93bd9551..64d9bf4a 100644 --- a/src/nm-audit-manager.c +++ b/src/nm-audit-manager.c @@ -339,7 +339,7 @@ init_auditd (NMAuditManager *self) NMConfigData *data = nm_config_get_data (priv->config); if (nm_config_data_get_value_boolean (data, NM_CONFIG_KEYFILE_GROUP_LOGGING, - NM_CONFIG_KEYFILE_KEY_AUDIT, + NM_CONFIG_KEYFILE_KEY_LOGGING_AUDIT, NM_CONFIG_DEFAULT_LOGGING_AUDIT_BOOL)) { if (priv->auditd_fd < 0) { priv->auditd_fd = audit_open (); diff --git a/src/nm-auth-utils.c b/src/nm-auth-utils.c index b41f6efa..0c4a4bac 100644 --- a/src/nm-auth-utils.c +++ b/src/nm-auth-utils.c @@ -139,7 +139,7 @@ nm_auth_chain_get_data (NMAuthChain *self, const char *tag) * @self: A #NMAuthChain. * @tag: A "tag" uniquely identifying the data to steal. * - * Removes the datum assocated with @tag from the chain's data associations, + * Removes the datum associated with @tag from the chain's data associations, * without invoking the association's destroy handler. The caller assumes * ownership over the returned value. * diff --git a/src/nm-checkpoint.c b/src/nm-checkpoint.c index b0cf1f51..5489ed49 100644 --- a/src/nm-checkpoint.c +++ b/src/nm-checkpoint.c @@ -46,6 +46,7 @@ typedef struct { guint64 ac_version_id; NMDeviceState state; bool realized:1; + bool activation_lifetime_bound_to_profile_visiblity:1; NMUnmanFlagOp unmanaged_explicit; NMActivationReason activation_reason; } DeviceCheckpoint; @@ -335,6 +336,9 @@ activate: subject, NM_ACTIVATION_TYPE_MANAGED, dev_checkpoint->activation_reason, + dev_checkpoint->activation_lifetime_bound_to_profile_visiblity + ? NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY + : NM_ACTIVATION_STATE_FLAG_NONE, &local_error)) { _LOGW ("rollback: reactivation of connection %s/%s failed: %s", nm_settings_connection_get_id (connection), @@ -439,6 +443,8 @@ device_checkpoint_create (NMDevice *device) dev_checkpoint->settings_connection = nm_simple_connection_new_clone (nm_settings_connection_get_connection (settings_connection)); dev_checkpoint->ac_version_id = nm_active_connection_version_id_get (NM_ACTIVE_CONNECTION (act_request)); dev_checkpoint->activation_reason = nm_active_connection_get_activation_reason (NM_ACTIVE_CONNECTION (act_request)); + dev_checkpoint->activation_lifetime_bound_to_profile_visiblity = NM_FLAGS_HAS (nm_active_connection_get_state_flags (NM_ACTIVE_CONNECTION (act_request)), + NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY); } return dev_checkpoint; diff --git a/src/nm-config-data.c b/src/nm-config-data.c index 8d84e74a..8ef91516 100644 --- a/src/nm-config-data.c +++ b/src/nm-config-data.c @@ -110,6 +110,8 @@ typedef struct { char *rc_manager; NMGlobalDnsConfig *global_dns; + + bool systemd_resolved:1; } NMConfigDataPrivate; struct _NMConfigData { @@ -323,6 +325,14 @@ nm_config_data_get_rc_manager (const NMConfigData *self) } gboolean +nm_config_data_get_systemd_resolved (const NMConfigData *self) +{ + g_return_val_if_fail (self, FALSE); + + return NM_CONFIG_DATA_GET_PRIVATE (self)->systemd_resolved; +} + +gboolean nm_config_data_get_ignore_carrier (const NMConfigData *self, NMDevice *device) { gs_free char *value = NULL; @@ -913,7 +923,10 @@ load_global_dns (GKeyFile *keyfile, gboolean internal) dns_config->domains = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, (GDestroyNotify) global_dns_domain_free); - strv = g_key_file_get_string_list (keyfile, group, "searches", NULL, NULL); + strv = g_key_file_get_string_list (keyfile, + group, + NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_SEARCHES, + NULL, NULL); if (strv) { _nm_utils_strv_cleanup (strv, TRUE, TRUE, TRUE); if (!strv[0]) @@ -922,7 +935,10 @@ load_global_dns (GKeyFile *keyfile, gboolean internal) dns_config->searches = strv; } - strv = g_key_file_get_string_list (keyfile, group, "options", NULL, NULL); + strv = g_key_file_get_string_list (keyfile, + group, + NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_OPTIONS, + NULL, NULL); if (strv) { _nm_utils_strv_cleanup (strv, TRUE, TRUE, TRUE); for (i = 0, j = 0; strv[i]; i++) { @@ -949,7 +965,10 @@ load_global_dns (GKeyFile *keyfile, gboolean internal) || !groups[g][domain_prefix_len]) continue; - strv = g_key_file_get_string_list (keyfile, groups[g], "servers", NULL, NULL); + strv = g_key_file_get_string_list (keyfile, + groups[g], + NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_DOMAIN_SERVERS, + NULL, NULL); if (strv) { _nm_utils_strv_cleanup (strv, TRUE, TRUE, TRUE); for (i = 0, j = 0; strv[i]; i++) { @@ -970,7 +989,10 @@ load_global_dns (GKeyFile *keyfile, gboolean internal) if (!servers) continue; - strv = g_key_file_get_string_list (keyfile, groups[g], "options", NULL, NULL); + strv = g_key_file_get_string_list (keyfile, + groups[g], + NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_DOMAIN_OPTIONS, + NULL, NULL); if (strv) { options = _nm_utils_strv_cleanup (strv, TRUE, TRUE, TRUE); if (!options[0]) @@ -1241,9 +1263,13 @@ _match_section_infos_lookup (const MatchSectionInfo *match_section_infos, const char *match_device_type, char **out_value) { + const char *match_dhcp_plugin; + if (!match_section_infos) return NULL; + match_dhcp_plugin = nm_dhcp_manager_get_config (nm_dhcp_manager_get ()); + for (; match_section_infos->group_name; match_section_infos++) { char *value = NULL; gboolean match; @@ -1263,7 +1289,7 @@ _match_section_infos_lookup (const MatchSectionInfo *match_section_infos, if (device) match = nm_device_spec_match_list (device, match_section_infos->match_device.spec); else if (pllink) - match = nm_match_spec_device_by_pllink (pllink, match_device_type, match_section_infos->match_device.spec, FALSE); + match = nm_match_spec_device_by_pllink (pllink, match_device_type, match_dhcp_plugin, match_section_infos->match_device.spec, FALSE); else match = FALSE; } else @@ -1361,6 +1387,19 @@ nm_config_data_get_connection_default (const NMConfigData *self, priv = NM_CONFIG_DATA_GET_PRIVATE (self); +#if NM_MORE_ASSERTS > 10 + { + const char **ptr; + + for (ptr = __start_connection_defaults; ptr < __stop_connection_defaults; ptr++) { + if (nm_streq (property, *ptr)) + break; + } + + nm_assert (ptr < __stop_connection_defaults); + } +#endif + _match_section_infos_lookup (&priv->connection_infos[0], priv->keyfile, property, @@ -1393,9 +1432,12 @@ _get_connection_info_init (MatchSectionInfo *connection_info, GKeyFile *keyfile, connection_info->match_device.spec = nm_config_get_match_spec (keyfile, group, - "match-device", + NM_CONFIG_KEYFILE_KEY_MATCH_DEVICE, &connection_info->match_device.has); - connection_info->stop_match = nm_config_keyfile_get_boolean (keyfile, group, "stop-match", FALSE); + connection_info->stop_match = nm_config_keyfile_get_boolean (keyfile, + group, + NM_CONFIG_KEYFILE_KEY_STOP_MATCH, + FALSE); } static void @@ -1638,27 +1680,58 @@ constructed (GObject *object) priv->connection_infos = _match_section_infos_construct (priv->keyfile, NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION); priv->device_infos = _match_section_infos_construct (priv->keyfile, NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE); - priv->connectivity.enabled = nm_config_keyfile_get_boolean (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "enabled", TRUE); - priv->connectivity.uri = nm_strstrip (g_key_file_get_string (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "uri", NULL)); - priv->connectivity.response = g_key_file_get_string (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "response", NULL); - - str = nm_config_keyfile_get_value (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, NM_CONFIG_KEYFILE_KEY_MAIN_AUTOCONNECT_RETRIES_DEFAULT, NM_CONFIG_GET_VALUE_NONE); + priv->connectivity.enabled = nm_config_keyfile_get_boolean (priv->keyfile, + NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, + NM_CONFIG_KEYFILE_KEY_CONNECTIVITY_ENABLED, + TRUE); + priv->connectivity.uri = nm_strstrip (g_key_file_get_string (priv->keyfile, + NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, + NM_CONFIG_KEYFILE_KEY_CONNECTIVITY_URI, + NULL)); + priv->connectivity.response = g_key_file_get_string (priv->keyfile, + NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, + NM_CONFIG_KEYFILE_KEY_CONNECTIVITY_RESPONSE, + NULL); + str = nm_config_keyfile_get_value (priv->keyfile, + NM_CONFIG_KEYFILE_GROUP_MAIN, + NM_CONFIG_KEYFILE_KEY_MAIN_AUTOCONNECT_RETRIES_DEFAULT, + NM_CONFIG_GET_VALUE_NONE); priv->autoconnect_retries_default = _nm_utils_ascii_str_to_int64 (str, 10, 0, G_MAXINT32, 4); g_free (str); /* On missing config value, fallback to 300. On invalid value, disable connectivity checking by setting * the interval to zero. */ - str = g_key_file_get_string (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "interval", NULL); + str = g_key_file_get_string (priv->keyfile, + NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, + NM_CONFIG_KEYFILE_KEY_CONNECTIVITY_INTERVAL, + NULL); priv->connectivity.interval = _nm_utils_ascii_str_to_int64 (str, 10, 0, G_MAXUINT, NM_CONFIG_DEFAULT_CONNECTIVITY_INTERVAL); g_free (str); - priv->dns_mode = nm_strstrip (g_key_file_get_string (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "dns", NULL)); - priv->rc_manager = nm_strstrip (g_key_file_get_string (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "rc-manager", NULL)); - - priv->ignore_carrier = nm_config_get_match_spec (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "ignore-carrier", NULL); - priv->assume_ipv6ll_only = nm_config_get_match_spec (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "assume-ipv6ll-only", NULL); - - priv->no_auto_default.specs_config = nm_config_get_match_spec (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "no-auto-default", NULL); + priv->dns_mode = nm_strstrip (g_key_file_get_string (priv->keyfile, + NM_CONFIG_KEYFILE_GROUP_MAIN, + NM_CONFIG_KEYFILE_KEY_MAIN_DNS, + NULL)); + priv->rc_manager = nm_strstrip (g_key_file_get_string (priv->keyfile, + NM_CONFIG_KEYFILE_GROUP_MAIN, + NM_CONFIG_KEYFILE_KEY_MAIN_RC_MANAGER, + NULL)); + priv->systemd_resolved = nm_config_keyfile_get_boolean (priv->keyfile, + NM_CONFIG_KEYFILE_GROUP_MAIN, + NM_CONFIG_KEYFILE_KEY_MAIN_SYSTEMD_RESOLVED, + TRUE); + priv->ignore_carrier = nm_config_get_match_spec (priv->keyfile, + NM_CONFIG_KEYFILE_GROUP_MAIN, + NM_CONFIG_KEYFILE_KEY_MAIN_IGNORE_CARRIER, + NULL); + priv->assume_ipv6ll_only = nm_config_get_match_spec (priv->keyfile, + NM_CONFIG_KEYFILE_GROUP_MAIN, + NM_CONFIG_KEYFILE_KEY_MAIN_ASSUME_IPV6LL_ONLY, + NULL); + priv->no_auto_default.specs_config = nm_config_get_match_spec (priv->keyfile, + NM_CONFIG_KEYFILE_GROUP_MAIN, + NM_CONFIG_KEYFILE_KEY_MAIN_NO_AUTO_DEFAULT, + NULL); priv->global_dns = load_global_dns (priv->keyfile_user, FALSE); if (!priv->global_dns) diff --git a/src/nm-config-data.h b/src/nm-config-data.h index b52ddcc4..c043aa35 100644 --- a/src/nm-config-data.h +++ b/src/nm-config-data.h @@ -170,6 +170,7 @@ gboolean nm_config_data_get_no_auto_default_for_device (const NMConfigD const char *nm_config_data_get_dns_mode (const NMConfigData *self); const char *nm_config_data_get_rc_manager (const NMConfigData *self); +gboolean nm_config_data_get_systemd_resolved (const NMConfigData *self); gboolean nm_config_data_get_ignore_carrier (const NMConfigData *self, NMDevice *device); gboolean nm_config_data_get_assume_ipv6ll_only (const NMConfigData *self, NMDevice *device); @@ -177,6 +178,21 @@ int nm_config_data_get_sriov_num_vfs (const NMConfigData *self, NMDevice *d NMGlobalDnsConfig *nm_config_data_get_global_dns_config (const NMConfigData *self); +extern const char *__start_connection_defaults[]; +extern const char *__stop_connection_defaults[]; + +#define NM_CON_DEFAULT_NOP(name) \ + static const char *NM_UNIQ_T (connection_default, NM_UNIQ) \ + _nm_used _nm_section ("connection_defaults") = "" name + +#define NM_CON_DEFAULT(name) \ + ({ \ + static const char *__con_default_prop \ + _nm_used _nm_section ("connection_defaults") = "" name; \ + \ + name; \ + }) + char *nm_config_data_get_connection_default (const NMConfigData *self, const char *property, NMDevice *device); @@ -232,5 +248,14 @@ GKeyFile *_nm_config_data_get_keyfile (const NMConfigData *self); GKeyFile *_nm_config_data_get_keyfile_user (const NMConfigData *self); GKeyFile *_nm_config_data_get_keyfile_intern (const NMConfigData *self); +/*****************************************************************************/ + +/* nm-config-data.c requires getting the DHCP manager's configuration. That is a bit + * ugly, and optimally, NMConfig* is independent of NMDhcpManager. Instead of + * including the header, forward declare the two functions that we need. */ +struct _NMDhcpManager; +struct _NMDhcpManager *nm_dhcp_manager_get (void); +const char *nm_dhcp_manager_get_config (struct _NMDhcpManager *self); + #endif /* NM_CONFIG_DATA_H */ diff --git a/src/nm-config.c b/src/nm-config.c index 628eca4f..d028be67 100644 --- a/src/nm-config.c +++ b/src/nm-config.c @@ -130,6 +130,8 @@ typedef struct { * that they are changed outside of NM (at least not while NM is running). * Hence, we read them once, that's it. */ GHashTable *device_states; + + char **warnings; } NMConfigPrivate; struct _NMConfig { @@ -281,6 +283,18 @@ nm_config_keyfile_set_string_list (GKeyFile *keyfile, /*****************************************************************************/ +const char *const* +nm_config_get_warnings (NMConfig *config) +{ + return (const char *const *) NM_CONFIG_GET_PRIVATE (config)->warnings; +} + +void +nm_config_clear_warnings (NMConfig *config) +{ + g_clear_pointer (&NM_CONFIG_GET_PRIVATE (config)->warnings, g_strfreev); +} + NMConfigData * nm_config_get_data (NMConfig *config) { @@ -704,26 +718,184 @@ static gboolean _setting_is_device_spec (const char *group, const char *key) { #define _IS(group_v, key_v) (strcmp (group, (""group_v)) == 0 && strcmp (key, (""key_v)) == 0) - return _IS (NM_CONFIG_KEYFILE_GROUP_MAIN, "no-auto-default") - || _IS (NM_CONFIG_KEYFILE_GROUP_MAIN, "ignore-carrier") - || _IS (NM_CONFIG_KEYFILE_GROUP_MAIN, "assume-ipv6ll-only") - || _IS (NM_CONFIG_KEYFILE_GROUP_KEYFILE, "unmanaged-devices") - || (g_str_has_prefix (group, NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION) && !strcmp (key, "match-device")) - || (g_str_has_prefix (group, NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE ) && !strcmp (key, "match-device")); + return _IS (NM_CONFIG_KEYFILE_GROUP_MAIN, NM_CONFIG_KEYFILE_KEY_MAIN_NO_AUTO_DEFAULT) + || _IS (NM_CONFIG_KEYFILE_GROUP_MAIN, NM_CONFIG_KEYFILE_KEY_MAIN_IGNORE_CARRIER) + || _IS (NM_CONFIG_KEYFILE_GROUP_MAIN, NM_CONFIG_KEYFILE_KEY_MAIN_ASSUME_IPV6LL_ONLY) + || _IS (NM_CONFIG_KEYFILE_GROUP_KEYFILE, NM_CONFIG_KEYFILE_KEY_KEYFILE_UNMANAGED_DEVICES) + || (g_str_has_prefix (group, NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION) && !strcmp (key, NM_CONFIG_KEYFILE_KEY_MATCH_DEVICE)) + || (g_str_has_prefix (group, NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE ) && !strcmp (key, NM_CONFIG_KEYFILE_KEY_MATCH_DEVICE)); } static gboolean _setting_is_string_list (const char *group, const char *key) { - return _IS (NM_CONFIG_KEYFILE_GROUP_MAIN, "plugins") + return _IS (NM_CONFIG_KEYFILE_GROUP_MAIN, NM_CONFIG_KEYFILE_KEY_MAIN_PLUGINS) || _IS (NM_CONFIG_KEYFILE_GROUP_MAIN, NM_CONFIG_KEYFILE_KEY_MAIN_DEBUG) - || _IS (NM_CONFIG_KEYFILE_GROUP_LOGGING, "domains") + || _IS (NM_CONFIG_KEYFILE_GROUP_LOGGING, NM_CONFIG_KEYFILE_KEY_LOGGING_DOMAINS) || g_str_has_prefix (group, NM_CONFIG_KEYFILE_GROUPPREFIX_TEST_APPEND_STRINGLIST); #undef _IS } +typedef struct { + char *group; + const char *const *keys; + bool is_prefix:1; + bool is_connection:1; +} ConfigGroup; + +/* The following comment is used by check-config-options.sh, don't remove it. */ +/* START OPTION LIST */ + +static const ConfigGroup config_groups[] = { + { + .group = NM_CONFIG_KEYFILE_GROUP_MAIN, + .keys = NM_MAKE_STRV ( + NM_CONFIG_KEYFILE_KEY_MAIN_ASSUME_IPV6LL_ONLY, + NM_CONFIG_KEYFILE_KEY_MAIN_AUTH_POLKIT, + NM_CONFIG_KEYFILE_KEY_MAIN_AUTOCONNECT_RETRIES_DEFAULT, + NM_CONFIG_KEYFILE_KEY_MAIN_CONFIGURE_AND_QUIT, + NM_CONFIG_KEYFILE_KEY_MAIN_DEBUG, + NM_CONFIG_KEYFILE_KEY_MAIN_DHCP, + NM_CONFIG_KEYFILE_KEY_MAIN_DNS, + NM_CONFIG_KEYFILE_KEY_MAIN_HOSTNAME_MODE, + NM_CONFIG_KEYFILE_KEY_MAIN_IGNORE_CARRIER, + NM_CONFIG_KEYFILE_KEY_MAIN_MONITOR_CONNECTION_FILES, + NM_CONFIG_KEYFILE_KEY_MAIN_NO_AUTO_DEFAULT, + NM_CONFIG_KEYFILE_KEY_MAIN_PLUGINS, + NM_CONFIG_KEYFILE_KEY_MAIN_RC_MANAGER, + NM_CONFIG_KEYFILE_KEY_MAIN_SLAVES_ORDER, + NM_CONFIG_KEYFILE_KEY_MAIN_SYSTEMD_RESOLVED, + ), + }, + { + .group = NM_CONFIG_KEYFILE_GROUP_LOGGING, + .keys = NM_MAKE_STRV ( + NM_CONFIG_KEYFILE_KEY_LOGGING_AUDIT, + NM_CONFIG_KEYFILE_KEY_LOGGING_BACKEND, + NM_CONFIG_KEYFILE_KEY_LOGGING_DOMAINS, + NM_CONFIG_KEYFILE_KEY_LOGGING_LEVEL, + ), + }, + { + .group = NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, + .keys = NM_MAKE_STRV ( + NM_CONFIG_KEYFILE_KEY_CONNECTIVITY_ENABLED, + NM_CONFIG_KEYFILE_KEY_CONNECTIVITY_INTERVAL, + NM_CONFIG_KEYFILE_KEY_CONNECTIVITY_RESPONSE, + NM_CONFIG_KEYFILE_KEY_CONNECTIVITY_URI, + ), + }, + { + .group = NM_CONFIG_KEYFILE_GROUP_KEYFILE, + .keys = NM_MAKE_STRV ( + NM_CONFIG_KEYFILE_KEY_KEYFILE_HOSTNAME, + NM_CONFIG_KEYFILE_KEY_KEYFILE_PATH, + NM_CONFIG_KEYFILE_KEY_KEYFILE_UNMANAGED_DEVICES, + ), + }, + { + .group = NM_CONFIG_KEYFILE_GROUP_IFUPDOWN, + .keys = NM_MAKE_STRV ( + NM_CONFIG_KEYFILE_KEY_IFUPDOWN_MANAGED, + ), + }, + { + .group = NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE, + .is_prefix = TRUE, + .keys = NM_MAKE_STRV ( + NM_CONFIG_KEYFILE_KEY_DEVICE_CARRIER_WAIT_TIMEOUT, + NM_CONFIG_KEYFILE_KEY_DEVICE_IGNORE_CARRIER, + NM_CONFIG_KEYFILE_KEY_DEVICE_MANAGED, + NM_CONFIG_KEYFILE_KEY_DEVICE_SRIOV_NUM_VFS, + NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_BACKEND, + NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_SCAN_RAND_MAC_ADDRESS, + NM_CONFIG_KEYFILE_KEY_MATCH_DEVICE, + NM_CONFIG_KEYFILE_KEY_STOP_MATCH, + ), + }, + { + .group = NM_CONFIG_KEYFILE_GROUP_GLOBAL_DNS, + .keys = NM_MAKE_STRV ( + NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_OPTIONS, + NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_SEARCHES, + ), + }, + { + .group = NM_CONFIG_KEYFILE_GROUPPREFIX_GLOBAL_DNS_DOMAIN, + .is_prefix = TRUE, + .keys = NM_MAKE_STRV ( + NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_DOMAIN_SERVERS, + NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_DOMAIN_OPTIONS, + ), + }, + { + .group = NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION, + .is_prefix = TRUE, + .is_connection = TRUE, + .keys = NM_MAKE_STRV ( + NM_CONFIG_KEYFILE_KEY_MATCH_DEVICE, + NM_CONFIG_KEYFILE_KEY_STOP_MATCH, + ), + }, + { } /* sentinel */ +}; + +/* The following comment is used by check-config-options.sh, don't remove it. */ +/* END OPTION LIST */ + +static gboolean +check_config_key (const char *group, const char *key) +{ + const ConfigGroup *g; + const char *const *k; + const char **ptr; + +#if NM_MORE_ASSERTS > 10 + { + static gboolean checked = FALSE; + const char **ptr1, **ptr2; + + /* check for duplicate elements in the static list */ + + if (!checked) { + for (ptr1 = __start_connection_defaults; ptr1 < __stop_connection_defaults; ptr1++) { + for (ptr2 = ptr1 + 1; ptr2 < __stop_connection_defaults; ptr2++) + nm_assert (!nm_streq (*ptr1, *ptr2)); + } + checked = TRUE; + } + } +#endif + + for (g = config_groups; g->group; g++) { + if ( (!g->is_prefix && nm_streq (group, g->group)) + || (g->is_prefix && g_str_has_prefix (group, g->group))) + break; + } + + if (!g->group) + return FALSE; + + for (k = g->keys; *k; k++) { + if (nm_streq (key, *k)) + return TRUE; + } + + if (g->is_connection) { + for (ptr = __start_connection_defaults; ptr < __stop_connection_defaults; ptr++) { + if (nm_streq (key, *ptr)) + return TRUE; + } + return FALSE; + } + + return FALSE; +} + static gboolean -read_config (GKeyFile *keyfile, gboolean is_base_config, const char *dirname, const char *path, GError **error) +read_config (GKeyFile *keyfile, gboolean is_base_config, + const char *dirname, const char *path, + GPtrArray *warnings, GError **error) { GKeyFile *kf; char **groups, **keys; @@ -847,7 +1019,7 @@ read_config (GKeyFile *keyfile, gboolean is_base_config, const char *dirname, co new_val = _nm_utils_slist_to_strv (new_specs, FALSE); } - /* merge the string lists, by omiting duplicates. */ + /* merge the string lists, by omitting duplicates. */ for (iter_val = old_val; iter_val && *iter_val; iter_val++) { if ( last_char != '-' @@ -892,6 +1064,12 @@ read_config (GKeyFile *keyfile, gboolean is_base_config, const char *dirname, co new_value = g_key_file_get_value (kf, group, key, NULL); g_key_file_set_value (keyfile, group, key, new_value); + + if (!check_config_key (group, key)) { + g_ptr_array_add (warnings, + g_strdup_printf ("unknown key '%s' in section [%s] of file '%s'", + key, group, path)); + } g_free (new_value); } g_strfreev (keys); @@ -906,6 +1084,7 @@ static gboolean read_base_config (GKeyFile *keyfile, const char *cli_config_main_file, char **out_config_main_file, + GPtrArray *warnings, GError **error) { GError *my_error = NULL; @@ -917,7 +1096,7 @@ read_base_config (GKeyFile *keyfile, /* Try a user-specified config file first */ if (cli_config_main_file) { /* Bad user-specific config file path is a hard error */ - if (read_config (keyfile, TRUE, NULL, cli_config_main_file, error)) { + if (read_config (keyfile, TRUE, NULL, cli_config_main_file, warnings, error)) { *out_config_main_file = g_strdup (cli_config_main_file); return TRUE; } else @@ -932,7 +1111,7 @@ read_base_config (GKeyFile *keyfile, */ /* Try deprecated nm-system-settings.conf first */ - if (read_config (keyfile, TRUE, NULL, DEFAULT_CONFIG_MAIN_FILE_OLD, &my_error)) { + if (read_config (keyfile, TRUE, NULL, DEFAULT_CONFIG_MAIN_FILE_OLD, warnings, &my_error)) { *out_config_main_file = g_strdup (DEFAULT_CONFIG_MAIN_FILE_OLD); return TRUE; } @@ -944,7 +1123,7 @@ read_base_config (GKeyFile *keyfile, g_clear_error (&my_error); /* Try the standard config file location next */ - if (read_config (keyfile, TRUE, NULL, DEFAULT_CONFIG_MAIN_FILE, &my_error)) { + if (read_config (keyfile, TRUE, NULL, DEFAULT_CONFIG_MAIN_FILE, warnings, &my_error)) { *out_config_main_file = g_strdup (DEFAULT_CONFIG_MAIN_FILE); return TRUE; } @@ -1022,6 +1201,7 @@ read_entire_config (const NMConfigCmdLineOptions *cli, const char *system_config_dir, char **out_config_main_file, char **out_config_description, + char ***out_warnings, GError **error) { gs_unref_keyfile GKeyFile *keyfile = NULL; @@ -1031,12 +1211,14 @@ read_entire_config (const NMConfigCmdLineOptions *cli, guint i; gs_free char *o_config_main_file = NULL; const char *run_config_dir = ""; + gs_unref_ptrarray GPtrArray *warnings = NULL; g_return_val_if_fail (config_dir, NULL); g_return_val_if_fail (system_config_dir, NULL); g_return_val_if_fail (!out_config_main_file || !*out_config_main_file, FALSE); g_return_val_if_fail (!out_config_description || !*out_config_description, NULL); g_return_val_if_fail (!error || !*error, FALSE); + g_return_val_if_fail (out_warnings && !*out_warnings, FALSE); if ( (""RUN_CONFIG_DIR)[0] == '/' && !nm_streq (RUN_CONFIG_DIR, system_config_dir) @@ -1045,6 +1227,7 @@ read_entire_config (const NMConfigCmdLineOptions *cli, /* create a default configuration file. */ keyfile = nm_config_create_keyfile (); + warnings = g_ptr_array_new_with_free_func (g_free); system_confs = _get_config_dir_files (system_config_dir); confs = _get_config_dir_files (config_dir); @@ -1060,7 +1243,7 @@ read_entire_config (const NMConfigCmdLineOptions *cli, continue; } - if (!read_config (keyfile, FALSE, system_config_dir, filename, error)) + if (!read_config (keyfile, FALSE, system_config_dir, filename, warnings, error)) return NULL; i++; } @@ -1074,19 +1257,19 @@ read_entire_config (const NMConfigCmdLineOptions *cli, continue; } - if (!read_config (keyfile, FALSE, run_config_dir, filename, error)) + if (!read_config (keyfile, FALSE, run_config_dir, filename, warnings, error)) return NULL; i++; } /* First read the base config file */ - if (!read_base_config (keyfile, cli ? cli->config_main_file : NULL, &o_config_main_file, error)) + if (!read_base_config (keyfile, cli ? cli->config_main_file : NULL, &o_config_main_file, warnings, error)) return NULL; g_assert (o_config_main_file); for (i = 0; i < confs->len; i++) { - if (!read_config (keyfile, FALSE, config_dir, confs->pdata[i], error)) + if (!read_config (keyfile, FALSE, config_dir, confs->pdata[i], warnings, error)) return NULL; } @@ -1133,6 +1316,11 @@ read_entire_config (const NMConfigCmdLineOptions *cli, *out_config_description = g_string_free (str, FALSE); } NM_SET_OUT (out_config_main_file, g_steal_pointer (&o_config_main_file)); + + g_ptr_array_add (warnings, NULL); + *out_warnings = (char **) g_ptr_array_free (warnings, warnings->len == 1); + g_steal_pointer (&warnings); + return g_steal_pointer (&keyfile); } @@ -1657,11 +1845,13 @@ nm_config_set_global_dns (NMConfig *self, NMGlobalDnsConfig *global_dns, GError /* Set new values */ nm_config_keyfile_set_string_list (keyfile, NM_CONFIG_KEYFILE_GROUP_INTERN_GLOBAL_DNS, - "searches", nm_global_dns_config_get_searches (global_dns), + NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_SEARCHES, + nm_global_dns_config_get_searches (global_dns), -1); nm_config_keyfile_set_string_list (keyfile, NM_CONFIG_KEYFILE_GROUP_INTERN_GLOBAL_DNS, - "options", nm_global_dns_config_get_options (global_dns), + NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_OPTIONS, + nm_global_dns_config_get_options (global_dns), -1); for (i = 0; i < nm_global_dns_config_get_num_domains (global_dns); i++) { @@ -1671,9 +1861,9 @@ nm_config_set_global_dns (NMConfig *self, NMGlobalDnsConfig *global_dns, GError group_name = g_strdup_printf (NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_GLOBAL_DNS_DOMAIN "%s", nm_global_dns_domain_get_name (domain)); - nm_config_keyfile_set_string_list (keyfile, group_name, "servers", + nm_config_keyfile_set_string_list (keyfile, group_name, NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_DOMAIN_SERVERS, nm_global_dns_domain_get_servers (domain), -1); - nm_config_keyfile_set_string_list (keyfile, group_name, "options", + nm_config_keyfile_set_string_list (keyfile, group_name, NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_DOMAIN_OPTIONS, nm_global_dns_domain_get_options (domain), -1); } @@ -1924,7 +2114,7 @@ state_write (NMConfig *self) &error)) { _LOGD ("state: error writing state file \"%s\": %s", filename, error->message); g_clear_error (&error); - /* we leave the state dirty. That potentally means, that we try to + /* we leave the state dirty. That potentially means, that we try to * write the file over and over again, although it isn't possible. */ priv->state->p.dirty = TRUE; } else @@ -2354,7 +2544,7 @@ nm_config_device_state_get (NMConfig *self, /*****************************************************************************/ void -nm_config_reload (NMConfig *self, NMConfigChangeFlags reload_flags) +nm_config_reload (NMConfig *self, NMConfigChangeFlags reload_flags, gboolean emit_warnings) { NMConfigPrivate *priv; GError *error = NULL; @@ -2364,6 +2554,8 @@ nm_config_reload (NMConfig *self, NMConfigChangeFlags reload_flags) char *config_description = NULL; gs_strfreev char **no_auto_default = NULL; gboolean intern_config_needs_rewrite; + gs_strfreev char **warnings = NULL; + guint i; g_return_if_fail (NM_IS_CONFIG (self)); g_return_if_fail ( reload_flags @@ -2388,6 +2580,7 @@ nm_config_reload (NMConfig *self, NMConfigChangeFlags reload_flags) priv->system_config_dir, &config_main_file, &config_description, + &warnings, &error); if (!keyfile) { _LOGE ("Failed to reload the configuration: %s", error->message); @@ -2396,6 +2589,11 @@ nm_config_reload (NMConfig *self, NMConfigChangeFlags reload_flags) return; } + if (emit_warnings && warnings) { + for (i = 0; warnings[i]; i++) + _LOGW ("%s", warnings[i]); + } + no_auto_default = no_auto_default_from_file (priv->no_auto_default_file); keyfile_intern = intern_config_read (priv->intern_config_file, @@ -2557,10 +2755,12 @@ init_sync (GInitable *initable, GCancellable *cancellable, GError **error) { NMConfig *self = NM_CONFIG (initable); NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (self); - GKeyFile *keyfile, *keyfile_intern; - char *config_main_file = NULL; - char *config_description = NULL; + gs_unref_keyfile GKeyFile *keyfile = NULL; + gs_unref_keyfile GKeyFile *keyfile_intern = NULL; + gs_free char *config_main_file = NULL; + gs_free char *config_description = NULL; gs_strfreev char **no_auto_default = NULL; + gs_strfreev char **warnings = NULL; gs_free char *configure_and_quit = NULL; gboolean intern_config_needs_rewrite; const char *s; @@ -2592,23 +2792,34 @@ init_sync (GInitable *initable, GCancellable *cancellable, GError **error) priv->system_config_dir, &config_main_file, &config_description, + &warnings, error); if (!keyfile) return FALSE; - /* Initialize read only private members */ + /* Initialize read-only private members */ if (priv->cli.no_auto_default_file) priv->no_auto_default_file = g_strdup (priv->cli.no_auto_default_file); else priv->no_auto_default_file = g_strdup (DEFAULT_NO_AUTO_DEFAULT_FILE); - priv->monitor_connection_files = nm_config_keyfile_get_boolean (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "monitor-connection-files", FALSE); - - priv->log_level = nm_strstrip (g_key_file_get_string (keyfile, NM_CONFIG_KEYFILE_GROUP_LOGGING, "level", NULL)); - priv->log_domains = nm_strstrip (g_key_file_get_string (keyfile, NM_CONFIG_KEYFILE_GROUP_LOGGING, "domains", NULL)); - - configure_and_quit = nm_strstrip (g_key_file_get_string (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "configure-and-quit", NULL)); + priv->monitor_connection_files = nm_config_keyfile_get_boolean (keyfile, + NM_CONFIG_KEYFILE_GROUP_MAIN, + NM_CONFIG_KEYFILE_KEY_MAIN_MONITOR_CONNECTION_FILES, + FALSE); + priv->log_level = nm_strstrip (g_key_file_get_string (keyfile, + NM_CONFIG_KEYFILE_GROUP_LOGGING, + NM_CONFIG_KEYFILE_KEY_LOGGING_LEVEL, + NULL)); + priv->log_domains = nm_strstrip (g_key_file_get_string (keyfile, + NM_CONFIG_KEYFILE_GROUP_LOGGING, + NM_CONFIG_KEYFILE_KEY_LOGGING_DOMAINS, + NULL)); + configure_and_quit = nm_strstrip (g_key_file_get_string (keyfile, + NM_CONFIG_KEYFILE_GROUP_MAIN, + NM_CONFIG_KEYFILE_KEY_MAIN_CONFIGURE_AND_QUIT, + NULL)); priv->configure_and_quit = string_to_configure_and_quit (configure_and_quit, error); if (priv->configure_and_quit == NM_CONFIG_CONFIGURE_AND_QUIT_INVALID) return FALSE; @@ -2632,12 +2843,7 @@ init_sync (GInitable *initable, GCancellable *cancellable, GError **error) keyfile_intern); priv->config_data = g_object_ref (priv->config_data_orig); - - g_free (config_main_file); - g_free (config_description); - g_key_file_unref (keyfile); - if (keyfile_intern) - g_key_file_unref (keyfile_intern); + priv->warnings = g_steal_pointer (&warnings); return TRUE; } @@ -2673,6 +2879,7 @@ finalize (GObject *gobject) g_free (priv->log_level); g_free (priv->log_domains); g_strfreev (priv->atomic_section_prefixes); + g_strfreev (priv->warnings); _nm_config_cmd_line_options_clear (&priv->cli); diff --git a/src/nm-config.h b/src/nm-config.h index c65572ce..66f1b69c 100644 --- a/src/nm-config.h +++ b/src/nm-config.h @@ -52,28 +52,48 @@ #define NM_CONFIG_KEYFILE_GROUP_MAIN "main" #define NM_CONFIG_KEYFILE_GROUP_LOGGING "logging" #define NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY "connectivity" -#define NM_CONFIG_KEYFILE_GROUP_GLOBAL_DNS "global-dns" -#define NM_CONFIG_KEYFILE_GROUP_CONFIG ".config" - #define NM_CONFIG_KEYFILE_GROUP_KEYFILE "keyfile" #define NM_CONFIG_KEYFILE_GROUP_IFUPDOWN "ifupdown" +#define NM_CONFIG_KEYFILE_GROUP_GLOBAL_DNS "global-dns" +#define NM_CONFIG_KEYFILE_GROUP_CONFIG ".config" +#define NM_CONFIG_KEYFILE_KEY_MAIN_ASSUME_IPV6LL_ONLY "assume-ipv6ll-only" #define NM_CONFIG_KEYFILE_KEY_MAIN_AUTH_POLKIT "auth-polkit" #define NM_CONFIG_KEYFILE_KEY_MAIN_AUTOCONNECT_RETRIES_DEFAULT "autoconnect-retries-default" -#define NM_CONFIG_KEYFILE_KEY_MAIN_DHCP "dhcp" +#define NM_CONFIG_KEYFILE_KEY_MAIN_CONFIGURE_AND_QUIT "configure-and-quit" #define NM_CONFIG_KEYFILE_KEY_MAIN_DEBUG "debug" +#define NM_CONFIG_KEYFILE_KEY_MAIN_DHCP "dhcp" +#define NM_CONFIG_KEYFILE_KEY_MAIN_DNS "dns" #define NM_CONFIG_KEYFILE_KEY_MAIN_HOSTNAME_MODE "hostname-mode" +#define NM_CONFIG_KEYFILE_KEY_MAIN_IGNORE_CARRIER "ignore-carrier" +#define NM_CONFIG_KEYFILE_KEY_MAIN_MONITOR_CONNECTION_FILES "monitor-connection-files" +#define NM_CONFIG_KEYFILE_KEY_MAIN_NO_AUTO_DEFAULT "no-auto-default" +#define NM_CONFIG_KEYFILE_KEY_MAIN_PLUGINS "plugins" +#define NM_CONFIG_KEYFILE_KEY_MAIN_RC_MANAGER "rc-manager" #define NM_CONFIG_KEYFILE_KEY_MAIN_SLAVES_ORDER "slaves-order" +#define NM_CONFIG_KEYFILE_KEY_MAIN_SYSTEMD_RESOLVED "systemd-resolved" + +#define NM_CONFIG_KEYFILE_KEY_LOGGING_AUDIT "audit" #define NM_CONFIG_KEYFILE_KEY_LOGGING_BACKEND "backend" -#define NM_CONFIG_KEYFILE_KEY_CONFIG_ENABLE "enable" -#define NM_CONFIG_KEYFILE_KEY_ATOMIC_SECTION_WAS ".was" +#define NM_CONFIG_KEYFILE_KEY_LOGGING_DOMAINS "domains" +#define NM_CONFIG_KEYFILE_KEY_LOGGING_LEVEL "level" + +#define NM_CONFIG_KEYFILE_KEY_CONNECTIVITY_ENABLED "enabled" +#define NM_CONFIG_KEYFILE_KEY_CONNECTIVITY_INTERVAL "interval" +#define NM_CONFIG_KEYFILE_KEY_CONNECTIVITY_RESPONSE "response" +#define NM_CONFIG_KEYFILE_KEY_CONNECTIVITY_URI "uri" + #define NM_CONFIG_KEYFILE_KEY_KEYFILE_PATH "path" #define NM_CONFIG_KEYFILE_KEY_KEYFILE_UNMANAGED_DEVICES "unmanaged-devices" #define NM_CONFIG_KEYFILE_KEY_KEYFILE_HOSTNAME "hostname" -#define NM_CONFIG_KEYFILE_KEY_IFNET_AUTO_REFRESH "auto_refresh" -#define NM_CONFIG_KEYFILE_KEY_IFNET_MANAGED "managed" + #define NM_CONFIG_KEYFILE_KEY_IFUPDOWN_MANAGED "managed" -#define NM_CONFIG_KEYFILE_KEY_AUDIT "audit" + +#define NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_SEARCHES "searches" +#define NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_OPTIONS "options" + +#define NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_DOMAIN_SERVERS "servers" +#define NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_DOMAIN_OPTIONS "options" #define NM_CONFIG_KEYFILE_KEY_DEVICE_MANAGED "managed" #define NM_CONFIG_KEYFILE_KEY_DEVICE_IGNORE_CARRIER "ignore-carrier" @@ -82,6 +102,12 @@ #define NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_SCAN_RAND_MAC_ADDRESS "wifi.scan-rand-mac-address" #define NM_CONFIG_KEYFILE_KEY_DEVICE_CARRIER_WAIT_TIMEOUT "carrier-wait-timeout" +#define NM_CONFIG_KEYFILE_KEY_MATCH_DEVICE "match-device" +#define NM_CONFIG_KEYFILE_KEY_STOP_MATCH "stop-match" + +#define NM_CONFIG_KEYFILE_KEY_ATOMIC_SECTION_WAS ".was" /* check-config-options skip */ +#define NM_CONFIG_KEYFILE_KEY_CONFIG_ENABLE "enable" /* check-config-options skip */ + #define NM_CONFIG_KEYFILE_KEYPREFIX_WAS ".was." #define NM_CONFIG_KEYFILE_KEYPREFIX_SET ".set." @@ -155,7 +181,7 @@ void nm_config_set_no_auto_default_for_device (NMConfig *config, NMDevice *devi NMConfig *nm_config_new (const NMConfigCmdLineOptions *cli, char **atomic_section_prefixes, GError **error); NMConfig *nm_config_setup (const NMConfigCmdLineOptions *cli, char **atomic_section_prefixes, GError **error); -void nm_config_reload (NMConfig *config, NMConfigChangeFlags reload_flags); +void nm_config_reload (NMConfig *config, NMConfigChangeFlags reload_flags, gboolean emit_warnings); const NMConfigState *nm_config_state_get (NMConfig *config); @@ -252,6 +278,9 @@ const GHashTable *nm_config_device_state_get_all (NMConfig *self); const NMConfigDeviceStateData *nm_config_device_state_get (NMConfig *self, int ifindex); +const char *const *nm_config_get_warnings (NMConfig *config); +void nm_config_clear_warnings (NMConfig *config); + /*****************************************************************************/ #endif /* __NETWORKMANAGER_CONFIG_H__ */ diff --git a/src/nm-connectivity.c b/src/nm-connectivity.c index 8a6e955a..5f0567e9 100644 --- a/src/nm-connectivity.c +++ b/src/nm-connectivity.c @@ -17,7 +17,7 @@ * * Copyright (C) 2011 Thomas Bechtold <thomasbechtold@jpberlin.de> * Copyright (C) 2011 Dan Williams <dcbw@redhat.com> - * Copyright (C) 2016,2017 Red Hat, Inc. + * Copyright (C) 2016 - 2018 Red Hat, Inc. */ #include "nm-default.h" @@ -31,8 +31,11 @@ #endif #include "c-list/src/c-list.h" +#include "nm-core-internal.h" #include "nm-config.h" #include "NetworkManagerUtils.h" +#include "nm-dbus-manager.h" +#include "dns/nm-dns-manager.h" #define HEADER_STATUS_ONLINE "X-NetworkManager-Status: online\r\n" @@ -60,6 +63,14 @@ nm_connectivity_state_to_string (NMConnectivityState state) /*****************************************************************************/ +typedef struct { + guint ref_count; + char *uri; + char *host; + char *port; + char *response; +} ConConfig; + struct _NMConnectivityCheckHandle { CList handles_lst; NMConnectivity *self; @@ -68,22 +79,35 @@ struct _NMConnectivityCheckHandle { char *ifspec; + const char *completed_log_message; + char *completed_log_message_free; + #if WITH_CONCHECK struct { - char *response; + ConConfig *con_config; + GCancellable *resolve_cancellable; + CURLM *curl_mhandle; CURL *curl_ehandle; struct curl_slist *request_headers; + struct curl_slist *hosts; GString *recv_msg; + + guint curl_timer; + int ch_ifindex; } concheck; #endif - const char *completed_log_message; - char *completed_log_message_free; - NMConnectivityState completed_state; + guint64 request_counter; + + int addr_family; guint timeout_id; + + NMConnectivityState completed_state; + + bool fail_reason_no_dbus_connection:1; }; enum { @@ -97,17 +121,12 @@ static guint signals[LAST_SIGNAL] = { 0 }; typedef struct { CList handles_lst_head; CList completed_handles_lst_head; - char *uri; - char *response; - gboolean enabled; - guint interval; NMConfig *config; -#if WITH_CONCHECK - struct { - CURLM *curl_mhandle; - guint curl_timer; - } concheck; -#endif + ConConfig *con_config; + guint interval; + + bool enabled:1; + bool uri_valid:1; } NMConnectivityPrivate; struct _NMConnectivity { @@ -139,15 +158,53 @@ NM_DEFINE_SINGLETON_GETTER (NMConnectivity, nm_connectivity_get, NM_TYPE_CONNECT _nm_log (__level, _NMLOG2_DOMAIN, 0, \ (cb_data->ifspec ? &cb_data->ifspec[3] : NULL), \ NULL, \ - "connectivity: (%s) " \ + "connectivity: (%s,IPv%c,%"G_GUINT64_FORMAT") " \ _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ - (cb_data->ifspec ? &cb_data->ifspec[3] : "") \ + (cb_data->ifspec ? &cb_data->ifspec[3] : ""), \ + nm_utils_addr_family_to_char (cb_data->addr_family), \ + cb_data->request_counter \ _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ } \ } G_STMT_END /*****************************************************************************/ +static ConConfig * +_con_config_ref (ConConfig *con_config) +{ + if (con_config) { + nm_assert (con_config->ref_count > 0); + ++con_config->ref_count; + } + return con_config; +} + +static void +_con_config_unref (ConConfig *con_config) +{ + if (!con_config) + return; + + nm_assert (con_config->ref_count > 0); + + if (--con_config->ref_count != 0) + return; + + g_free (con_config->uri); + g_free (con_config->host); + g_free (con_config->port); + g_free (con_config->response); + g_slice_free (ConConfig, con_config); +} + +static const char * +_con_config_get_response (const ConConfig *con_config) +{ + return con_config->response ?: NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE; +} + +/*****************************************************************************/ + static void cb_data_complete (NMConnectivityCheckHandle *cb_data, NMConnectivityState state, @@ -171,8 +228,6 @@ cb_data_complete (NMConnectivityCheckHandle *cb_data, #if WITH_CONCHECK if (cb_data->concheck.curl_ehandle) { - NMConnectivityPrivate *priv; - /* Contrary to what cURL manual claim it is *not* safe to remove * the easy handle "at any moment"; specifically it's not safe to * remove *any* handle from within a libcurl callback. That is @@ -187,13 +242,16 @@ cb_data_complete (NMConnectivityCheckHandle *cb_data, curl_easy_setopt (cb_data->concheck.curl_ehandle, CURLOPT_PRIVATE, NULL); curl_easy_setopt (cb_data->concheck.curl_ehandle, CURLOPT_HTTPHEADER, NULL); - priv = NM_CONNECTIVITY_GET_PRIVATE (self); - - curl_multi_remove_handle (priv->concheck.curl_mhandle, cb_data->concheck.curl_ehandle); + curl_multi_remove_handle (cb_data->concheck.curl_mhandle, + cb_data->concheck.curl_ehandle); curl_easy_cleanup (cb_data->concheck.curl_ehandle); + curl_multi_cleanup (cb_data->concheck.curl_mhandle); curl_slist_free_all (cb_data->concheck.request_headers); + curl_slist_free_all (cb_data->concheck.hosts); } + nm_clear_g_source (&cb_data->concheck.curl_timer); + nm_clear_g_cancellable (&cb_data->concheck.resolve_cancellable); #endif nm_clear_g_source (&cb_data->timeout_id); @@ -212,7 +270,7 @@ cb_data_complete (NMConnectivityCheckHandle *cb_data, * not use the self pointer too. */ #if WITH_CONCHECK - g_free (cb_data->concheck.response); + _con_config_unref (cb_data->concheck.con_config); if (cb_data->concheck.recv_msg) g_string_free (cb_data->concheck.recv_msg, TRUE); #endif @@ -265,12 +323,6 @@ _complete_queued (NMConnectivity *self) nm_g_object_unref (self_keep_alive); } -static const char * -_check_handle_get_response (NMConnectivityCheckHandle *cb_data) -{ - return cb_data->concheck.response ?: NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE; -} - static gboolean _con_curl_check_connectivity (CURLM *mhandle, int sockfd, int ev_bitmask) { @@ -285,7 +337,7 @@ _con_curl_check_connectivity (CURLM *mhandle, int sockfd, int ev_bitmask) ret = curl_multi_socket_action (mhandle, sockfd, ev_bitmask, &running_handles); if (ret != CURLM_OK) { - _LOGD ("connectivity check failed: %d", ret); + _LOGD ("connectivity check failed: (%d) %s", ret, curl_easy_strerror (ret)); success = FALSE; } @@ -297,7 +349,8 @@ _con_curl_check_connectivity (CURLM *mhandle, int sockfd, int ev_bitmask) /* Here we have completed a session. Check easy session result. */ eret = curl_easy_getinfo (msg->easy_handle, CURLINFO_PRIVATE, (char **) &cb_data); if (eret != CURLE_OK) { - _LOGD ("curl cannot extract cb_data for easy handle, skipping msg"); + _LOGD ("curl cannot extract cb_data for easy handle, skipping msg: (%d) %s", + eret, curl_easy_strerror (eret)); success = FALSE; continue; } @@ -314,8 +367,10 @@ _con_curl_check_connectivity (CURLM *mhandle, int sockfd, int ev_bitmask) cb_data_queue_completed (cb_data, NM_CONNECTIVITY_LIMITED, NULL, - g_strdup_printf ("check failed with curl status %d", msg->data.result)); - } else if ( !((_check_handle_get_response (cb_data))[0]) + g_strdup_printf ("check failed: (%d) %s", + msg->data.result, + curl_easy_strerror (msg->data.result))); + } else if ( !((_con_config_get_response (cb_data->concheck.con_config))[0]) && (curl_easy_getinfo (msg->easy_handle, CURLINFO_RESPONSE_CODE, &response_code) == CURLE_OK) && response_code == 204) { /* If we got a 204 response code (no content) and we actually @@ -346,29 +401,27 @@ _con_curl_check_connectivity (CURLM *mhandle, int sockfd, int ev_bitmask) static gboolean _con_curl_timeout_cb (gpointer user_data) { - gs_unref_object NMConnectivity *self = g_object_ref (NM_CONNECTIVITY (user_data)); - NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); + NMConnectivityCheckHandle *cb_data = user_data; - priv->concheck.curl_timer = 0; - _con_curl_check_connectivity (priv->concheck.curl_mhandle, CURL_SOCKET_TIMEOUT, 0); - _complete_queued (self); + cb_data->concheck.curl_timer = 0; + _con_curl_check_connectivity (cb_data->concheck.curl_mhandle, CURL_SOCKET_TIMEOUT, 0); + _complete_queued (cb_data->self); return G_SOURCE_REMOVE; } static int multi_timer_cb (CURLM *multi, long timeout_ms, void *userdata) { - NMConnectivity *self = NM_CONNECTIVITY (userdata); - NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); + NMConnectivityCheckHandle *cb_data = userdata; - nm_clear_g_source (&priv->concheck.curl_timer); + nm_clear_g_source (&cb_data->concheck.curl_timer); if (timeout_ms != -1) - priv->concheck.curl_timer = g_timeout_add (timeout_ms, _con_curl_timeout_cb, self); + cb_data->concheck.curl_timer = g_timeout_add (timeout_ms, _con_curl_timeout_cb, cb_data); return 0; } typedef struct { - NMConnectivity *self; + NMConnectivityCheckHandle *cb_data; GIOChannel *ch; /* this is a very simplistic weak-pointer. If ConCurlSockData gets @@ -385,8 +438,7 @@ static gboolean _con_curl_socketevent_cb (GIOChannel *ch, GIOCondition condition, gpointer user_data) { ConCurlSockData *fdp = user_data; - gs_unref_object NMConnectivity *self = g_object_ref (fdp->self); - NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); + NMConnectivityCheckHandle *cb_data = fdp->cb_data; int fd = g_io_channel_unix_get_fd (ch); int action = 0; gboolean fdp_destroyed = FALSE; @@ -402,7 +454,7 @@ _con_curl_socketevent_cb (GIOChannel *ch, GIOCondition condition, gpointer user_ nm_assert (!fdp->destroy_notify); fdp->destroy_notify = &fdp_destroyed; - success = _con_curl_check_connectivity (priv->concheck.curl_mhandle, fd, action); + success = _con_curl_check_connectivity (cb_data->concheck.curl_mhandle, fd, action); if (fdp_destroyed) { /* hups. fdp got invalidated during _con_curl_check_connectivity(). That's fine, @@ -414,7 +466,7 @@ _con_curl_socketevent_cb (GIOChannel *ch, GIOCondition condition, gpointer user_ fdp->ev = 0; } - _complete_queued (self); + _complete_queued (cb_data->self); return success ? G_SOURCE_CONTINUE : G_SOURCE_REMOVE; } @@ -422,8 +474,7 @@ _con_curl_socketevent_cb (GIOChannel *ch, GIOCondition condition, gpointer user_ static int multi_socket_cb (CURL *e_handle, curl_socket_t fd, int what, void *userdata, void *socketp) { - NMConnectivity *self = NM_CONNECTIVITY (userdata); - NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); + NMConnectivityCheckHandle *cb_data = userdata; ConCurlSockData *fdp = socketp; GIOCondition condition = 0; @@ -433,7 +484,7 @@ multi_socket_cb (CURL *e_handle, curl_socket_t fd, int what, void *userdata, voi if (fdp) { if (fdp->destroy_notify) *fdp->destroy_notify = TRUE; - curl_multi_assign (priv->concheck.curl_mhandle, fd, NULL); + curl_multi_assign (cb_data->concheck.curl_mhandle, fd, NULL); nm_clear_g_source (&fdp->ev); g_io_channel_unref (fdp->ch); g_slice_free (ConCurlSockData, fdp); @@ -441,9 +492,9 @@ multi_socket_cb (CURL *e_handle, curl_socket_t fd, int what, void *userdata, voi } else { if (!fdp) { fdp = g_slice_new0 (ConCurlSockData); - fdp->self = self; + fdp->cb_data = cb_data; fdp->ch = g_io_channel_unix_new (fd); - curl_multi_assign (priv->concheck.curl_mhandle, fd, fdp); + curl_multi_assign (cb_data->concheck.curl_mhandle, fd, fdp); } else nm_clear_g_source (&fdp->ev); @@ -501,7 +552,7 @@ easy_write_cb (void *buffer, size_t size, size_t nmemb, void *userdata) g_string_append_len (cb_data->concheck.recv_msg, buffer, len); - response = _check_handle_get_response (cb_data);; + response = _con_config_get_response (cb_data->concheck.con_config);; if ( response && cb_data->concheck.recv_msg->len >= strlen (response)) { /* We already have enough data -- check response */ @@ -551,66 +602,235 @@ _idle_cb (gpointer user_data) g_set_error (&error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT, "no interface specified for connectivity check"); cb_data_complete (cb_data, NM_CONNECTIVITY_ERROR, "missing interface"); + } else if (cb_data->fail_reason_no_dbus_connection) { + gs_free_error GError *error = NULL; + + g_set_error (&error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT, + "no D-Bus connection"); + cb_data_complete (cb_data, NM_CONNECTIVITY_ERROR, "no D-Bus connection"); } else cb_data_complete (cb_data, NM_CONNECTIVITY_FAKE, "fake result"); return G_SOURCE_REMOVE; } +static void +do_curl_request (NMConnectivityCheckHandle *cb_data) +{ + CURLM *mhandle; + CURL *ehandle; + long resolve; + + mhandle = curl_multi_init (); + if (!mhandle) { + cb_data_complete (cb_data, NM_CONNECTIVITY_ERROR, "curl error"); + return; + } + + ehandle = curl_easy_init (); + if (!ehandle) { + curl_multi_cleanup (mhandle); + cb_data_complete (cb_data, NM_CONNECTIVITY_ERROR, "curl error"); + return; + } + + cb_data->concheck.curl_mhandle = mhandle; + cb_data->concheck.curl_ehandle = ehandle; + cb_data->concheck.request_headers = curl_slist_append (NULL, "Connection: close"); + cb_data->timeout_id = g_timeout_add_seconds (20, _timeout_cb, cb_data); + + curl_multi_setopt (mhandle, CURLMOPT_SOCKETFUNCTION, multi_socket_cb); + curl_multi_setopt (mhandle, CURLMOPT_SOCKETDATA, cb_data); + curl_multi_setopt (mhandle, CURLMOPT_TIMERFUNCTION, multi_timer_cb); + curl_multi_setopt (mhandle, CURLMOPT_TIMERDATA, cb_data); + curl_multi_setopt (mhandle, CURLOPT_VERBOSE, 1); + + switch (cb_data->addr_family) { + case AF_INET: + resolve = CURL_IPRESOLVE_V4; + break; + case AF_INET6: + resolve = CURL_IPRESOLVE_V6; + break; + case AF_UNSPEC: + resolve = CURL_IPRESOLVE_WHATEVER; + break; + default: + resolve = CURL_IPRESOLVE_WHATEVER; + g_warn_if_reached (); + } + + curl_easy_setopt (ehandle, CURLOPT_URL, cb_data->concheck.con_config->uri); + curl_easy_setopt (ehandle, CURLOPT_WRITEFUNCTION, easy_write_cb); + curl_easy_setopt (ehandle, CURLOPT_WRITEDATA, cb_data); + curl_easy_setopt (ehandle, CURLOPT_HEADERFUNCTION, easy_header_cb); + curl_easy_setopt (ehandle, CURLOPT_HEADERDATA, cb_data); + curl_easy_setopt (ehandle, CURLOPT_PRIVATE, cb_data); + curl_easy_setopt (ehandle, CURLOPT_HTTPHEADER, cb_data->concheck.request_headers); + curl_easy_setopt (ehandle, CURLOPT_INTERFACE, cb_data->ifspec); + curl_easy_setopt (ehandle, CURLOPT_RESOLVE, cb_data->concheck.hosts); + curl_easy_setopt (ehandle, CURLOPT_IPRESOLVE, resolve); + + curl_multi_add_handle (mhandle, ehandle); +} + +static void +resolve_cb (GObject *object, GAsyncResult *res, gpointer user_data) +{ + NMConnectivityCheckHandle *cb_data; + gs_unref_variant GVariant *result = NULL; + gs_unref_variant GVariant *addresses = NULL; + gsize no_addresses; + int ifindex; + int addr_family; + gsize len = 0; + gsize i; + gs_free_error GError *error = NULL; + + result = g_dbus_connection_call_finish (G_DBUS_CONNECTION (object), res, &error); + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + cb_data = user_data; + + g_clear_object (&cb_data->concheck.resolve_cancellable); + + if (!result) { + /* Never mind. Just let do curl do its own resolving. */ + _LOG2D ("can't resolve a name via systemd-resolved: %s", error->message); + do_curl_request (cb_data); + return; + } + + addresses = g_variant_get_child_value (result, 0); + no_addresses = g_variant_n_children (addresses); + + for (i = 0; i < no_addresses; i++) { + gs_unref_variant GVariant *address = NULL; + char str_addr[NM_UTILS_INET_ADDRSTRLEN]; + gs_free char *host_entry = NULL; + const guchar *address_buf; + + g_variant_get_child (addresses, i, "(ii@ay)", &ifindex, &addr_family, &address); + + if ( cb_data->addr_family != AF_UNSPEC + && cb_data->addr_family != addr_family) + continue; + + address_buf = g_variant_get_fixed_array (address, &len, 1); + if ( (addr_family == AF_INET && len != sizeof (struct in_addr)) + || (addr_family == AF_INET6 && len != sizeof (struct in6_addr))) + continue; + + host_entry = g_strdup_printf ("%s:%s:%s", + cb_data->concheck.con_config->host, + cb_data->concheck.con_config->port ?: "80", + nm_utils_inet_ntop (addr_family, address_buf, str_addr)); + cb_data->concheck.hosts = curl_slist_append (cb_data->concheck.hosts, host_entry); + _LOG2T ("adding '%s' to curl resolve list", host_entry); + } + + do_curl_request (cb_data); +} + +#define SD_RESOLVED_DNS ((guint64) (1LL << 0)) + NMConnectivityCheckHandle * nm_connectivity_check_start (NMConnectivity *self, + int addr_family, + int ifindex, const char *iface, NMConnectivityCheckCallback callback, gpointer user_data) { NMConnectivityPrivate *priv; NMConnectivityCheckHandle *cb_data; + static guint64 request_counter = 0; g_return_val_if_fail (NM_IS_CONNECTIVITY (self), NULL); - g_return_val_if_fail (!iface || iface[0], NULL); g_return_val_if_fail (callback, NULL); priv = NM_CONNECTIVITY_GET_PRIVATE (self); cb_data = g_slice_new0 (NMConnectivityCheckHandle); cb_data->self = self; + cb_data->request_counter = ++request_counter; c_list_link_tail (&priv->handles_lst_head, &cb_data->handles_lst); cb_data->callback = callback; cb_data->user_data = user_data; cb_data->completed_state = NM_CONNECTIVITY_UNKNOWN; + cb_data->addr_family = addr_family; + cb_data->concheck.con_config = _con_config_ref (priv->con_config); if (iface) cb_data->ifspec = g_strdup_printf ("if!%s", iface); #if WITH_CONCHECK - if (iface) { - CURL *ehandle; - - if ( priv->enabled - && (ehandle = curl_easy_init ())) { - - cb_data->concheck.response = g_strdup (priv->response); - cb_data->concheck.curl_ehandle = ehandle; - cb_data->concheck.request_headers = curl_slist_append (NULL, "Connection: close"); - curl_easy_setopt (ehandle, CURLOPT_URL, priv->uri); - curl_easy_setopt (ehandle, CURLOPT_WRITEFUNCTION, easy_write_cb); - curl_easy_setopt (ehandle, CURLOPT_WRITEDATA, cb_data); - curl_easy_setopt (ehandle, CURLOPT_HEADERFUNCTION, easy_header_cb); - curl_easy_setopt (ehandle, CURLOPT_HEADERDATA, cb_data); - curl_easy_setopt (ehandle, CURLOPT_PRIVATE, cb_data); - curl_easy_setopt (ehandle, CURLOPT_HTTPHEADER, cb_data->concheck.request_headers); - curl_easy_setopt (ehandle, CURLOPT_INTERFACE, cb_data->ifspec); - curl_multi_add_handle (priv->concheck.curl_mhandle, ehandle); - - cb_data->timeout_id = g_timeout_add_seconds (20, _timeout_cb, cb_data); - - _LOG2D ("start request to '%s'", priv->uri); - return cb_data; + + if ( iface + && ifindex > 0 + && priv->enabled + && priv->uri_valid) { + gboolean has_systemd_resolved; + + cb_data->concheck.ch_ifindex = ifindex; + + /* note that we pick up support for systemd-resolved right away when we need it. + * We don't need to remember the setting, because we can (cheaply) check anew + * on each request. + * + * Yes, this makes NMConnectivity singleton dependent on NMDnsManager singleton. + * Well, not really: it makes connectivity-check-start dependent on NMDnsManager + * which merely means, not to start a connectivity check, late during shutdown. */ + has_systemd_resolved = nm_dns_manager_has_systemd_resolved (nm_dns_manager_get ()); + + if (has_systemd_resolved) { + GDBusConnection *dbus_connection; + + dbus_connection = nm_dbus_manager_get_dbus_connection (nm_dbus_manager_get ()); + if (!dbus_connection) { + /* we have no D-Bus connection? That might happen in configure and quit mode. + * + * Anyway, something is very odd, just fail connectivity check. */ + _LOG2D ("start fake request (fail due to no D-Bus connection)"); + cb_data->fail_reason_no_dbus_connection = TRUE; + cb_data->timeout_id = g_idle_add (_idle_cb, cb_data); + return cb_data; + } + + cb_data->concheck.resolve_cancellable = g_cancellable_new (); + + g_dbus_connection_call (nm_dbus_manager_get_dbus_connection (nm_dbus_manager_get ()), + "org.freedesktop.resolve1", + "/org/freedesktop/resolve1", + "org.freedesktop.resolve1.Manager", + "ResolveHostname", + g_variant_new ("(isit)", + (gint32) cb_data->concheck.ch_ifindex, + cb_data->concheck.con_config->host, + (gint32) cb_data->addr_family, + SD_RESOLVED_DNS), + G_VARIANT_TYPE ("(a(iiay)st)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + cb_data->concheck.resolve_cancellable, + resolve_cb, + cb_data); + _LOG2D ("start request to '%s' (try resolving '%s' using systemd-resolved)", + cb_data->concheck.con_config->uri, + cb_data->concheck.con_config->host); + } else { + _LOG2D ("start request to '%s' (systemd-resolved not available)", + cb_data->concheck.con_config->uri); + do_curl_request (cb_data); } + + return cb_data; } #endif _LOG2D ("start fake request"); cb_data->timeout_id = g_idle_add (_idle_cb, cb_data); + return cb_data; } @@ -646,42 +866,126 @@ nm_connectivity_get_interval (NMConnectivity *self) : 0; } +static gboolean +host_and_port_from_uri (const char *uri, char **host, char **port) +{ + const char *p = uri; + const char *host_begin = NULL; + size_t host_len = 0; + const char *port_begin = NULL; + size_t port_len = 0; + + /* scheme */ + while (*p != ':' && *p != '/') { + if (!*p++) + return FALSE; + } + + /* :// */ + if (*p++ != ':') + return FALSE; + if (*p++ != '/') + return FALSE; + if (*p++ != '/') + return FALSE; + /* host */ + if (*p == '[') + return FALSE; + host_begin = p; + while (*p && *p != ':' && *p != '/') { + host_len++; + p++; + } + if (host_len == 0) + return FALSE; + *host = g_strndup (host_begin, host_len); + + /* port */ + if (*p++ == ':') { + port_begin = p; + while (*p && *p != '/') { + port_len++; + p++; + } + if (port_len) + *port = g_strndup (port_begin, port_len); + } + + return TRUE; +} + static void update_config (NMConnectivity *self, NMConfigData *config_data) { NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); - const char *uri, *response; guint interval; gboolean enabled; gboolean changed = FALSE; - - /* Set the URI. */ - uri = nm_config_data_get_connectivity_uri (config_data); - if (uri && !*uri) - uri = NULL; - changed = g_strcmp0 (uri, priv->uri) != 0; - if (uri) { - char *scheme = g_uri_parse_scheme (uri); - - if (!scheme) { - _LOGE ("invalid URI '%s' for connectivity check.", uri); - uri = NULL; - } else if (strcasecmp (scheme, "https") == 0) { - _LOGW ("use of HTTPS for connectivity checking is not reliable and is discouraged (URI: %s)", uri); - } else if (strcasecmp (scheme, "http") != 0) { - _LOGE ("scheme of '%s' uri doesn't use a scheme that is allowed for connectivity check.", uri); - uri = NULL; + const char *cur_uri = priv->con_config ? priv->con_config->uri : NULL; + const char *cur_response = priv->con_config ? priv->con_config->response : NULL; + const char *new_response; + const char *new_uri; + gboolean new_uri_valid = priv->uri_valid; + gboolean new_host_port = FALSE; + gs_free char *new_host = NULL; + gs_free char *new_port = NULL; + + new_uri = nm_config_data_get_connectivity_uri (config_data); + if (!nm_streq0 (new_uri, cur_uri)) { + + new_uri_valid = (new_uri && *new_uri); + if (new_uri_valid) { + gs_free char *scheme = g_uri_parse_scheme (new_uri); + gboolean is_https = FALSE; + + if (!scheme) { + _LOGE ("invalid URI '%s' for connectivity check.", new_uri); + new_uri_valid = FALSE; + } else if (g_ascii_strcasecmp (scheme, "https") == 0) { + _LOGW ("use of HTTPS for connectivity checking is not reliable and is discouraged (URI: %s)", new_uri); + is_https = TRUE; + } else if (g_ascii_strcasecmp (scheme, "http") != 0) { + _LOGE ("scheme of '%s' uri doesn't use a scheme that is allowed for connectivity check.", new_uri); + new_uri_valid = FALSE; + } + if (new_uri_valid) { + new_host_port = TRUE; + if (!host_and_port_from_uri (new_uri, &new_host, &new_port)) { + _LOGE ("cannot parse host and port from '%s'", new_uri); + new_uri_valid = FALSE; + } else if (!new_port && is_https) + new_port = g_strdup ("443"); + } } - if (scheme) - g_free (scheme); + if ( new_uri_valid + || priv->uri_valid != new_uri_valid) + changed = TRUE; } - if (changed) { - g_free (priv->uri); - priv->uri = g_strdup (uri); + + new_response = nm_config_data_get_connectivity_response (config_data); + if (!nm_streq0 (new_response, cur_response)) + changed = TRUE; + + if ( !priv->con_config + || !nm_streq0 (new_uri, priv->con_config->uri) + || !nm_streq0 (new_response, priv->con_config->response)) { + if (!new_host_port) { + new_host = priv->con_config ? g_strdup (priv->con_config->host) : NULL; + new_port = priv->con_config ? g_strdup (priv->con_config->port) : NULL; + } + _con_config_unref (priv->con_config); + priv->con_config = g_slice_new (ConConfig); + *priv->con_config = (ConConfig) { + .ref_count = 1, + .uri = g_strdup (new_uri), + .response = g_strdup (new_response), + .host = g_steal_pointer (&new_host), + .port = g_steal_pointer (&new_port), + }; } + priv->uri_valid = new_uri_valid; - /* Set the interval. */ interval = nm_config_data_get_connectivity_interval (config_data); interval = MIN (interval, (7 * 24 * 3600)); if (priv->interval != interval) { @@ -691,11 +995,8 @@ update_config (NMConnectivity *self, NMConfigData *config_data) enabled = FALSE; #if WITH_CONCHECK - /* connectivity checking also requires a valid URI, interval and - * curl_mhandle */ - if ( priv->uri - && priv->interval - && priv->concheck.curl_mhandle) + if ( priv->uri_valid + && priv->interval) enabled = nm_config_data_get_connectivity_enabled (config_data); #endif @@ -704,16 +1005,6 @@ update_config (NMConnectivity *self, NMConfigData *config_data) changed = TRUE; } - /* Set the response. */ - response = nm_config_data_get_connectivity_response (config_data); - if (!nm_streq0 (response, priv->response)) { - /* a response %NULL means, NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE. Any other response - * (including "") is accepted. */ - g_free (priv->response); - priv->response = g_strdup (response); - changed = TRUE; - } - if (changed) g_signal_emit (self, signals[CONFIG_CHANGED], 0); } @@ -734,6 +1025,9 @@ static void nm_connectivity_init (NMConnectivity *self) { NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); +#if WITH_CONCHECK + CURLcode ret; +#endif c_list_init (&priv->handles_lst_head); c_list_init (&priv->completed_handles_lst_head); @@ -745,17 +1039,10 @@ nm_connectivity_init (NMConnectivity *self) self); #if WITH_CONCHECK - if (curl_global_init (CURL_GLOBAL_ALL) == CURLE_OK) - priv->concheck.curl_mhandle = curl_multi_init (); - - if (!priv->concheck.curl_mhandle) - _LOGE ("unable to init cURL, connectivity check will not work"); - else { - curl_multi_setopt (priv->concheck.curl_mhandle, CURLMOPT_SOCKETFUNCTION, multi_socket_cb); - curl_multi_setopt (priv->concheck.curl_mhandle, CURLMOPT_SOCKETDATA, self); - curl_multi_setopt (priv->concheck.curl_mhandle, CURLMOPT_TIMERFUNCTION, multi_timer_cb); - curl_multi_setopt (priv->concheck.curl_mhandle, CURLMOPT_TIMERDATA, self); - curl_multi_setopt (priv->concheck.curl_mhandle, CURLOPT_VERBOSE, 1); + ret = curl_global_init (CURL_GLOBAL_ALL); + if (ret != CURLE_OK) { + _LOGE ("unable to init cURL, connectivity check will not work: (%d) %s", + ret, curl_easy_strerror (ret)); } #endif @@ -776,13 +1063,9 @@ dispose (GObject *object) handles_lst))) cb_data_complete (cb_data, NM_CONNECTIVITY_DISPOSING, "shutting down"); - g_clear_pointer (&priv->uri, g_free); - g_clear_pointer (&priv->response, g_free); + nm_clear_pointer (&priv->con_config, _con_config_unref); #if WITH_CONCHECK - nm_clear_g_source (&priv->concheck.curl_timer); - - curl_multi_cleanup (priv->concheck.curl_mhandle); curl_global_cleanup (); #endif diff --git a/src/nm-connectivity.h b/src/nm-connectivity.h index 178f27ad..f262298a 100644 --- a/src/nm-connectivity.h +++ b/src/nm-connectivity.h @@ -24,6 +24,21 @@ #include "nm-dbus-interface.h" +/*****************************************************************************/ + +static inline int +nm_connectivity_state_cmp (NMConnectivityState a, NMConnectivityState b) +{ + if (a == NM_CONNECTIVITY_PORTAL && b == NM_CONNECTIVITY_LIMITED) + return 1; + if (b == NM_CONNECTIVITY_PORTAL && a == NM_CONNECTIVITY_LIMITED) + return -1; + NM_CMP_DIRECT (a, b); + return 0; +} + +/*****************************************************************************/ + #define NM_CONNECTIVITY_ERROR ((NMConnectivityState) -1) #define NM_CONNECTIVITY_FAKE ((NMConnectivityState) -2) #define NM_CONNECTIVITY_CANCELLED ((NMConnectivityState) -3) @@ -58,6 +73,8 @@ typedef void (*NMConnectivityCheckCallback) (NMConnectivity *self, gpointer user_data); NMConnectivityCheckHandle *nm_connectivity_check_start (NMConnectivity *self, + int family, + int ifindex, const char *iface, NMConnectivityCheckCallback callback, gpointer user_data); diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c index ca8c9526..3047ebc8 100644 --- a/src/nm-core-utils.c +++ b/src/nm-core-utils.c @@ -30,15 +30,19 @@ #include <unistd.h> #include <stdlib.h> #include <resolv.h> +#include <byteswap.h> #include <sys/types.h> #include <sys/wait.h> #include <sys/stat.h> #include <linux/if.h> #include <linux/if_infiniband.h> +#include <net/if_arp.h> #include <net/ethernet.h> #include "nm-utils/nm-random-utils.h" #include "nm-utils/nm-io-utils.h" +#include "nm-utils/unaligned.h" +#include "nm-utils/nm-secret-utils.h" #include "nm-utils.h" #include "nm-core-internal.h" #include "nm-setting-connection.h" @@ -47,7 +51,22 @@ #include "nm-setting-wireless.h" #include "nm-setting-wireless-security.h" +#ifdef __NM_SD_UTILS_H__ +#error "nm-core-utils.c should stay independent of systemd utils. Are you looking for NetworkMangerUtils.c? " +#endif + G_STATIC_ASSERT (sizeof (NMUtilsTestFlags) <= sizeof (int)); + +/* we read _nm_utils_testing without memory barrier. This is thread-safe, + * because the static variable is initialized to zero, and only reset + * once to a non-zero value (via g_atomic_int_compare_and_exchange()). + * + * Since there is only one integer that contains the data, there is no + * caching problem reading this (atomic int) variable without + * synchronization/memory-barrier. Contrary to a double-checked locking, + * where one needs a memory barrier to read the variable and ensure + * that also the related data is coherent in cache. Here there is no + * related data. */ static int _nm_utils_testing = 0; gboolean @@ -66,6 +85,7 @@ nm_utils_get_testing () { NMUtilsTestFlags flags; +again: flags = (NMUtilsTestFlags) _nm_utils_testing; if (flags != NM_UTILS_TEST_NONE) { /* Flags already initialized. Return them. */ @@ -78,12 +98,11 @@ nm_utils_get_testing () if (g_test_initialized ()) flags |= _NM_UTILS_TEST_GENERAL; - if (g_atomic_int_compare_and_exchange (&_nm_utils_testing, 0, (int) flags)) { - /* Done. We set it. */ - return flags & NM_UTILS_TEST_ALL; - } - /* It changed in the meantime (??). Re-read the value. */ - return ((NMUtilsTestFlags) _nm_utils_testing) & NM_UTILS_TEST_ALL; + g_atomic_int_compare_and_exchange (&_nm_utils_testing, 0, (int) flags); + + /* regardless of whether we won the race of initializing _nm_utils_testing, + * go back and read the value again. It must be non-zero by now. */ + goto again; } void @@ -708,7 +727,7 @@ _sleep_duration_convert_ms_to_us (guint32 sleep_duration_msec) * @log_domain: log debug information for this domain. Errors and warnings are logged both * as %LOGD_CORE and @log_domain. * @log_name: name of the process to kill for logging. - * @child_status: (out) (allow-none): return the exit status of the child, if no error occured. + * @child_status: (out) (allow-none): return the exit status of the child, if no error occurred. * @wait_before_kill_msec: Waittime in milliseconds before sending %SIGKILL signal. Set this value * to zero, not to send %SIGKILL. If @sig is already %SIGKILL, this parameter has not effect. * @sleep_duration_msec: the synchronous function sleeps repeatedly waiting for the child to terminate. @@ -717,7 +736,7 @@ _sleep_duration_convert_ms_to_us (guint32 sleep_duration_msec) * Kill a child process synchronously and wait. The function first checks if the child already terminated * and if it did, return the exit status. Otherwise send one @sig signal. @sig will always be * sent unless the child already exited. If the child does not exit within @wait_before_kill_msec milliseconds, - * the function will send %SIGKILL and waits for the child indefinitly. If @wait_before_kill_msec is zero, no + * the function will send %SIGKILL and waits for the child indefinitely. If @wait_before_kill_msec is zero, no * %SIGKILL signal will be sent. * * In case of error, errno is preserved to contain the last reason of failure. @@ -894,7 +913,7 @@ out: * @sleep_duration_msec: the synchronous function sleeps repeatedly waiting for the child to terminate. * Set to zero, to use the default (meaning 20 wakeups per seconds). * @max_wait_msec: if 0, waits indefinitely until the process is gone (or a zombie). Otherwise, this - * is the maxium wait time until returning. If @max_wait_msec is non-zero but smaller then @wait_before_kill_msec, + * is the maximum wait time until returning. If @max_wait_msec is non-zero but smaller then @wait_before_kill_msec, * we will not send a final %SIGKILL. * * Kill a non-child process synchronously and wait. This function will not return before the @@ -1114,13 +1133,17 @@ nm_utils_read_link_absolute (const char *link_file, GError **error) return ln; dirname = g_path_get_dirname (link_file); - if (!g_path_is_absolute (link_file)) { - gs_free char *dirname_rel = dirname; + if (!g_path_is_absolute (dirname)) { gs_free char *current_dir = g_get_current_dir (); - dirname = g_build_filename (current_dir, dirname_rel, NULL); - } - ln_abs = g_build_filename (dirname, ln, NULL); + /* @link_file argument was not an absolute path in the first place. + * That actually may be a bug, because the CWD is not well defined + * in most cases. Anyway, apparently we were able to load the file + * even from a relative path. So, when making the link absolute, we + * also need to prepend the CWD. */ + ln_abs = g_build_filename (current_dir, dirname, ln, NULL); + } else + ln_abs = g_build_filename (dirname, ln, NULL); g_free (dirname); g_free (ln); return ln_abs; @@ -1133,6 +1156,7 @@ nm_utils_read_link_absolute (const char *link_file, GError **error) #define DEVICE_TYPE_TAG "type:" #define DRIVER_TAG "driver:" #define SUBCHAN_TAG "s390-subchannels:" +#define DHCP_PLUGIN_TAG "dhcp-plugin:" #define EXCEPT_TAG "except:" #define MATCH_TAG_CONFIG_NM_VERSION "nm-version:" #define MATCH_TAG_CONFIG_NM_VERSION_MIN "nm-version-min:" @@ -1144,6 +1168,7 @@ typedef struct { const char *device_type; const char *driver; const char *driver_version; + const char *dhcp_plugin; struct { const char *value; gboolean is_parsed; @@ -1280,6 +1305,34 @@ match_device_hwaddr_eval (const char *spec_str, _has; \ }) +static NMMatchSpecMatchType +_match_result (gboolean has_except, + gboolean has_not_except, + gboolean has_match, + gboolean has_match_except) +{ + if ( has_except + && !has_not_except) { + /* a match spec that only consists of a list of except matches is treated specially. */ + nm_assert (!has_match); + if (has_match_except) { + /* one of the "except:" matches matched. The result is an explicit + * negative match. */ + return NM_MATCH_SPEC_NEG_MATCH; + } else { + /* none of the "except:" matches matched. The result is a positive match, + * despite there being no positive match. */ + return NM_MATCH_SPEC_MATCH; + } + } + + if (has_match_except) + return NM_MATCH_SPEC_NEG_MATCH; + if (has_match) + return NM_MATCH_SPEC_MATCH; + return NM_MATCH_SPEC_NO_MATCH; +} + static const char * match_except (const char *spec_str, gboolean *out_except) { @@ -1361,6 +1414,9 @@ match_device_eval (const char *spec_str, if (_MATCH_CHECK (spec_str, SUBCHAN_TAG)) return match_data_s390_subchannels_eval (spec_str, match_data); + if (_MATCH_CHECK (spec_str, DHCP_PLUGIN_TAG)) + return nm_streq0 (spec_str, match_data->dhcp_plugin); + if (allow_fuzzy) { if (match_device_hwaddr_eval (spec_str, match_data)) return TRUE; @@ -1379,17 +1435,21 @@ nm_match_spec_device (const GSList *specs, const char *driver, const char *driver_version, const char *hwaddr, - const char *s390_subchannels) + const char *s390_subchannels, + const char *dhcp_plugin) { const GSList *iter; - NMMatchSpecMatchType match; + gboolean has_match = FALSE; + gboolean has_match_except = FALSE; + gboolean has_except = FALSE; + gboolean has_not_except = FALSE; const char *spec_str; - gboolean except; MatchDeviceData match_data = { .interface_name = interface_name, .device_type = nm_str_not_empty (device_type), .driver = nm_str_not_empty (driver), .driver_version = nm_str_not_empty (driver_version), + .dhcp_plugin = nm_str_not_empty (dhcp_plugin), .hwaddr = { .value = hwaddr, }, @@ -1403,19 +1463,9 @@ nm_match_spec_device (const GSList *specs, if (!specs) return NM_MATCH_SPEC_NO_MATCH; - match = NM_MATCH_SPEC_NO_MATCH; - - /* pre-search for "*" */ for (iter = specs; iter; iter = iter->next) { - spec_str = iter->data; - - if (spec_str && spec_str[0] == '*' && spec_str[1] == '\0') { - match = NM_MATCH_SPEC_MATCH; - break; - } - } + gboolean except; - for (iter = specs; iter; iter = iter->next) { spec_str = iter->data; if (!spec_str || !*spec_str) @@ -1423,10 +1473,14 @@ nm_match_spec_device (const GSList *specs, spec_str = match_except (spec_str, &except); - if ( !except - && match == NM_MATCH_SPEC_MATCH) { - /* we have no "except-match" but already match. No need to evaluate - * the match, we cannot match stronger. */ + if (except) + has_except = TRUE; + else + has_not_except = TRUE; + + if ( ( except && has_match_except) + || (!except && has_match)) { + /* evaluating the match does not give new information. Skip it. */ continue; } @@ -1436,11 +1490,12 @@ nm_match_spec_device (const GSList *specs, continue; if (except) - return NM_MATCH_SPEC_NEG_MATCH; - match = NM_MATCH_SPEC_MATCH; + has_match_except = TRUE; + else + has_match = TRUE; } - return match; + return _match_result (has_except, has_not_except, has_match, has_match_except); } static gboolean @@ -1510,7 +1565,10 @@ NMMatchSpecMatchType nm_match_spec_config (const GSList *specs, guint cur_nm_version, const char *env) { const GSList *iter; - NMMatchSpecMatchType match = NM_MATCH_SPEC_NO_MATCH; + gboolean has_match = FALSE; + gboolean has_match_except = FALSE; + gboolean has_except = FALSE; + gboolean has_not_except = FALSE; if (!specs) return NM_MATCH_SPEC_NO_MATCH; @@ -1525,6 +1583,17 @@ nm_match_spec_config (const GSList *specs, guint cur_nm_version, const char *env spec_str = match_except (spec_str, &except); + if (except) + has_except = TRUE; + else + has_not_except = TRUE; + + if ( ( except && has_match_except) + || (!except && has_match)) { + /* evaluating the match does not give new information. Skip it. */ + continue; + } + if (_MATCH_CHECK (spec_str, MATCH_TAG_CONFIG_NM_VERSION)) v_match = match_config_eval (spec_str, MATCH_TAG_CONFIG_NM_VERSION, cur_nm_version); else if (_MATCH_CHECK (spec_str, MATCH_TAG_CONFIG_NM_VERSION_MIN)) @@ -1534,15 +1603,18 @@ nm_match_spec_config (const GSList *specs, guint cur_nm_version, const char *env else if (_MATCH_CHECK (spec_str, MATCH_TAG_CONFIG_ENV)) v_match = env && env[0] && !strcmp (spec_str, env); else + v_match = FALSE; + + if (!v_match) continue; - if (v_match) { - if (except) - return NM_MATCH_SPEC_NEG_MATCH; - match = NM_MATCH_SPEC_MATCH; - } + if (except) + has_match_except = TRUE; + else + has_match = TRUE; } - return match; + + return _match_result (has_except, has_not_except, has_match, has_match_except); } #undef _MATCH_CHECK @@ -1875,173 +1947,6 @@ nm_utils_cmp_connection_by_autoconnect_priority (NMConnection *a, NMConnection * /*****************************************************************************/ -static gint64 monotonic_timestamp_offset_sec; -static int monotonic_timestamp_clock_mode = 0; - -static void -monotonic_timestamp_get (struct timespec *tp) -{ - int clock_mode = 0; - int err = 0; - - switch (monotonic_timestamp_clock_mode) { - case 0: - /* the clock is not yet initialized (first run) */ - err = clock_gettime (CLOCK_BOOTTIME, tp); - if (err == -1 && errno == EINVAL) { - clock_mode = 2; - err = clock_gettime (CLOCK_MONOTONIC, tp); - } else - clock_mode = 1; - break; - case 1: - /* default, return CLOCK_BOOTTIME */ - err = clock_gettime (CLOCK_BOOTTIME, tp); - break; - case 2: - /* fallback, return CLOCK_MONOTONIC. Kernels prior to 2.6.39 - * (released on 18 May, 2011) don't support CLOCK_BOOTTIME. */ - err = clock_gettime (CLOCK_MONOTONIC, tp); - break; - } - - g_assert (err == 0); (void)err; - g_assert (tp->tv_nsec >= 0 && tp->tv_nsec < NM_UTILS_NS_PER_SECOND); - - if (G_LIKELY (clock_mode == 0)) - return; - - /* Calculate an offset for the time stamp. - * - * We always want positive values, because then we can initialize - * a timestamp with 0 and be sure, that it will be less then any - * value nm_utils_get_monotonic_timestamp_*() might return. - * For this to be true also for nm_utils_get_monotonic_timestamp_s() at - * early boot, we have to shift the timestamp to start counting at - * least from 1 second onward. - * - * Another advantage of shifting is, that this way we make use of the whole 31 bit - * range of signed int, before the time stamp for nm_utils_get_monotonic_timestamp_s() - * wraps (~68 years). - **/ - monotonic_timestamp_offset_sec = (- ((gint64) tp->tv_sec)) + 1; - monotonic_timestamp_clock_mode = clock_mode; - - if (nm_logging_enabled (LOGL_DEBUG, LOGD_CORE)) { - time_t now = time (NULL); - struct tm tm; - char s[255]; - - strftime (s, sizeof (s), "%Y-%m-%d %H:%M:%S", localtime_r (&now, &tm)); - nm_log_dbg (LOGD_CORE, "monotonic timestamp started counting 1.%09ld seconds ago with " - "an offset of %lld.0 seconds to %s (local time is %s)", - tp->tv_nsec, (long long) -monotonic_timestamp_offset_sec, - clock_mode == 1 ? "CLOCK_BOOTTIME" : "CLOCK_MONOTONIC", s); - } -} - -/** - * nm_utils_get_monotonic_timestamp_ns: - * - * Returns: a monotonically increasing time stamp in nanoseconds, - * starting at an unspecified offset. See clock_gettime(), %CLOCK_BOOTTIME. - * - * The returned value will start counting at an undefined point - * in the past and will always be positive. - * - * All the nm_utils_get_monotonic_timestamp_*s functions return the same - * timestamp but in different scales (nsec, usec, msec, sec). - **/ -gint64 -nm_utils_get_monotonic_timestamp_ns (void) -{ - struct timespec tp = { 0 }; - - monotonic_timestamp_get (&tp); - - /* Although the result will always be positive, we return a signed - * integer, which makes it easier to calculate time differences (when - * you want to subtract signed values). - **/ - return (((gint64) tp.tv_sec) + monotonic_timestamp_offset_sec) * NM_UTILS_NS_PER_SECOND + - tp.tv_nsec; -} - -/** - * nm_utils_get_monotonic_timestamp_us: - * - * Returns: a monotonically increasing time stamp in microseconds, - * starting at an unspecified offset. See clock_gettime(), %CLOCK_BOOTTIME. - * - * The returned value will start counting at an undefined point - * in the past and will always be positive. - * - * All the nm_utils_get_monotonic_timestamp_*s functions return the same - * timestamp but in different scales (nsec, usec, msec, sec). - **/ -gint64 -nm_utils_get_monotonic_timestamp_us (void) -{ - struct timespec tp = { 0 }; - - monotonic_timestamp_get (&tp); - - /* Although the result will always be positive, we return a signed - * integer, which makes it easier to calculate time differences (when - * you want to subtract signed values). - **/ - return (((gint64) tp.tv_sec) + monotonic_timestamp_offset_sec) * ((gint64) G_USEC_PER_SEC) + - (tp.tv_nsec / (NM_UTILS_NS_PER_SECOND/G_USEC_PER_SEC)); -} - -/** - * nm_utils_get_monotonic_timestamp_ms: - * - * Returns: a monotonically increasing time stamp in milliseconds, - * starting at an unspecified offset. See clock_gettime(), %CLOCK_BOOTTIME. - * - * The returned value will start counting at an undefined point - * in the past and will always be positive. - * - * All the nm_utils_get_monotonic_timestamp_*s functions return the same - * timestamp but in different scales (nsec, usec, msec, sec). - **/ -gint64 -nm_utils_get_monotonic_timestamp_ms (void) -{ - struct timespec tp = { 0 }; - - monotonic_timestamp_get (&tp); - - /* Although the result will always be positive, we return a signed - * integer, which makes it easier to calculate time differences (when - * you want to subtract signed values). - **/ - return (((gint64) tp.tv_sec) + monotonic_timestamp_offset_sec) * ((gint64) 1000) + - (tp.tv_nsec / (NM_UTILS_NS_PER_SECOND/1000)); -} - -/** - * nm_utils_get_monotonic_timestamp_s: - * - * Returns: nm_utils_get_monotonic_timestamp_ms() in seconds (throwing - * away sub second parts). The returned value will always be positive. - * - * This value wraps after roughly 68 years which should be fine for any - * practical purpose. - * - * All the nm_utils_get_monotonic_timestamp_*s functions return the same - * timestamp but in different scales (nsec, usec, msec, sec). - **/ -gint32 -nm_utils_get_monotonic_timestamp_s (void) -{ - struct timespec tp = { 0 }; - - monotonic_timestamp_get (&tp); - return (((gint64) tp.tv_sec) + monotonic_timestamp_offset_sec); -} - typedef struct { const char *name; @@ -2128,7 +2033,7 @@ _log_connection_get_property (NMSetting *setting, const char *name) return g_strdup ("****"); if (!_nm_setting_get_property (setting, name, &val)) - g_return_val_if_reached (FALSE); + return g_strdup ("<unknown>"); if (G_VALUE_HOLDS_STRING (&val)) { const char *val_s; @@ -2231,7 +2136,7 @@ nm_utils_log_connection_diff (NMConnection *connection, return; } - /* FIXME: it doesn't nicely show the content of NMSettingVpn, becuase nm_connection_diff() does not + /* FIXME: it doesn't nicely show the content of NMSettingVpn, because nm_connection_diff() does not * expand the hash values. */ sorted_hashes = _log_connection_sort_hashes (connection, diff_base, connection_diff); @@ -2321,49 +2226,6 @@ out: g_array_free (sorted_hashes, TRUE); } -/** - * nm_utils_monotonic_timestamp_as_boottime: - * @timestamp: the monotonic-timestamp that should be converted into CLOCK_BOOTTIME. - * @timestamp_ns_per_tick: How many nano seconds make one unit of @timestamp? E.g. if - * @timestamp is in unit seconds, pass %NM_UTILS_NS_PER_SECOND; @timestamp in nano - * seconds, pass 1; @timestamp in milli seconds, pass %NM_UTILS_NS_PER_SECOND/1000; etc. - * - * Returns: the monotonic-timestamp as CLOCK_BOOTTIME, as returned by clock_gettime(). - * The unit is the same as the passed in @timestamp basd on @timestamp_ns_per_tick. - * E.g. if you passed @timestamp in as seconds, it will return boottime in seconds. - * If @timestamp is a non-positive, it returns -1. Note that a (valid) monotonic-timestamp - * is always positive. - * - * On older kernels that don't support CLOCK_BOOTTIME, the returned time is instead CLOCK_MONOTONIC. - **/ -gint64 -nm_utils_monotonic_timestamp_as_boottime (gint64 timestamp, gint64 timestamp_ns_per_tick) -{ - gint64 offset; - - /* only support ns-per-tick being a multiple of 10. */ - g_return_val_if_fail (timestamp_ns_per_tick == 1 - || (timestamp_ns_per_tick > 0 && - timestamp_ns_per_tick <= NM_UTILS_NS_PER_SECOND && - timestamp_ns_per_tick % 10 == 0), - -1); - - /* Check that the timestamp is in a valid range. */ - g_return_val_if_fail (timestamp >= 0, -1); - - /* if the caller didn't yet ever fetch a monotonic-timestamp, he cannot pass any meaningful - * value (because he has no idea what these timestamps would be). That would be a bug. */ - g_return_val_if_fail (monotonic_timestamp_clock_mode != 0, -1); - - /* calculate the offset of monotonic-timestamp to boottime. offset_s is <= 1. */ - offset = monotonic_timestamp_offset_sec * (NM_UTILS_NS_PER_SECOND / timestamp_ns_per_tick); - - /* check for overflow. */ - g_return_val_if_fail (offset > 0 || timestamp < G_MAXINT64 + offset, G_MAXINT64); - - return timestamp - offset; -} - #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)); @@ -2503,209 +2365,549 @@ nm_utils_is_specific_hostname (const char *name) /*****************************************************************************/ -gboolean -nm_utils_machine_id_parse (const char *id_str, /*uuid_t*/ guchar *out_uuid) -{ - int i; - guint8 v0, v1; - - if (!id_str) - return FALSE; - - for (i = 0; i < 32; i++) { - if (!g_ascii_isxdigit (id_str[i])) - return FALSE; - } - if (id_str[i] != '\0') - return FALSE; +typedef struct { + NMUuid bin; + char _nul_sentinel; /* just for safety, if somebody accidentally uses the binary in a string context. */ - if (out_uuid) { - for (i = 0; i < 16; i++) { - v0 = g_ascii_xdigit_value (*(id_str++)); - v1 = g_ascii_xdigit_value (*(id_str++)); - out_uuid[i] = (v0 << 4) + v1; - } + /* depending on whether the string is packed or not (with/without hyphens), + * it's 32 or 36 characters long (plus the trailing NUL). + * + * The difference is that boot-id is a valid RFC 4211 UUID and represented + * as a 36 ascii string (with hyphens). The machine-id technically is not + * a UUID, but just a 32 byte sequence of hexchars. */ + char str[37]; + bool is_fake; +} UuidData; + +static UuidData * +_uuid_data_init (UuidData *uuid_data, + gboolean packed, + gboolean is_fake, + const NMUuid *uuid) +{ + nm_assert (uuid_data); + nm_assert (uuid); + + uuid_data->bin = *uuid; + uuid_data->_nul_sentinel = '\0'; + uuid_data->is_fake = is_fake; + if (packed) { + G_STATIC_ASSERT_EXPR (sizeof (uuid_data->str) >= (sizeof (*uuid) * 2 + 1)); + _nm_utils_bin2hexstr_full (uuid, + sizeof (*uuid), + '\0', + FALSE, + uuid_data->str); + } else { + G_STATIC_ASSERT_EXPR (sizeof (uuid_data->str) >= 37); + _nm_utils_uuid_unparse (uuid, uuid_data->str); } - return TRUE; + return uuid_data; } -char * -nm_utils_machine_id_read (void) +/*****************************************************************************/ + +static const UuidData * +_machine_id_get (gboolean allow_fake) { - gs_free char *contents = NULL; - int i; + static const UuidData *volatile p_uuid_data; + const UuidData *d; + +again: + d = g_atomic_pointer_get (&p_uuid_data); + if (G_UNLIKELY (!d)) { + static gsize lock; + static UuidData uuid_data; + gs_free char *content = NULL; + gboolean is_fake = TRUE; + const char *fake_type = NULL; + NMUuid uuid; + + /* Get the machine ID from /etc/machine-id; it's always in /etc no matter + * where our configured SYSCONFDIR is. Alternatively, it might be in + * LOCALSTATEDIR /lib/dbus/machine-id. + */ + if ( nm_utils_file_get_contents (-1, "/etc/machine-id", 100*1024, 0, &content, NULL, NULL) >= 0 + || nm_utils_file_get_contents (-1, LOCALSTATEDIR"/lib/dbus/machine-id", 100*1024, 0, &content, NULL, NULL) >= 0) { + g_strstrip (content); + if (_nm_utils_hexstr2bin_full (content, + FALSE, + FALSE, + NULL, + 16, + (guint8 *) &uuid, + sizeof (uuid), + NULL)) { + if (!nm_utils_uuid_is_null (&uuid)) { + /* an all-zero machine-id is not valid. */ + is_fake = FALSE; + } + } + } - /* Get the machine ID from /etc/machine-id; it's always in /etc no matter - * where our configured SYSCONFDIR is. Alternatively, it might be in - * LOCALSTATEDIR /lib/dbus/machine-id. - */ - if ( !g_file_get_contents ("/etc/machine-id", &contents, NULL, NULL) - && !g_file_get_contents (LOCALSTATEDIR "/lib/dbus/machine-id", &contents, NULL, NULL)) - return NULL; + if (is_fake) { + const guint8 *seed_bin; + const char *hash_seed; + gsize seed_len; - contents = g_strstrip (contents); + if (!allow_fake) { + /* we don't allow generating (and memoizing) a fake key. + * Signal that no valid machine-id exists. */ + return NULL; + } - for (i = 0; i < 32; i++) { - if (!g_ascii_isxdigit (contents[i])) - return NULL; - if (contents[i] >= 'A' && contents[i] <= 'F') { - /* canonicalize to lower-case */ - contents[i] = 'a' + (contents[i] - 'A'); + if (nm_utils_host_id_get (&seed_bin, &seed_len)) { + /* we have no valid machine-id. Generate a fake one by hashing + * the secret-key. This key is commonly persisted, so it should be + * stable accross reboots (despite having a broken system without + * proper machine-id). */ + fake_type = "secret-key"; + hash_seed = "ab085f06-b629-46d1-a553-84eeba5683b6"; + } else { + /* the secret-key is not valid/persistent either. That happens when we fail + * to read/write the secret-key to disk. Fallback to boot-id. The boot-id + * itself may be fake and randomly generated ad-hoc, but that is as best + * as it gets. */ + seed_bin = (const guint8 *) nm_utils_boot_id_bin (); + seed_len = sizeof (NMUuid); + fake_type = "boot-id"; + hash_seed = "7ff0c8f5-5399-4901-ab63-61bf594abe8b"; + } + + /* the fake machine-id is based on secret-key/boot-id, but we hash it + * again, so that they are not literally the same. */ + nm_utils_uuid_generate_from_string_bin (&uuid, + (const char *) seed_bin, + seed_len, + NM_UTILS_UUID_TYPE_VERSION5, + (gpointer) hash_seed); } + + if (!g_once_init_enter (&lock)) + goto again; + + d = _uuid_data_init (&uuid_data, TRUE, is_fake, &uuid); + g_atomic_pointer_set (&p_uuid_data, d); + g_once_init_leave (&lock, 1); + + if (is_fake) { + nm_log_err (LOGD_CORE, + "/etc/machine-id: no valid machine-id. Use fake one based on %s: %s", + fake_type, + d->str); + } else + nm_log_dbg (LOGD_CORE, "/etc/machine-id: %s", d->str); } - if (contents[i] != '\0') - return NULL; - return g_steal_pointer (&contents); + return d; +} + +const char * +nm_utils_machine_id_str (void) +{ + return _machine_id_get (TRUE)->str; +} + +const NMUuid * +nm_utils_machine_id_bin (void) +{ + return &_machine_id_get (TRUE)->bin; +} + +gboolean +nm_utils_machine_id_is_fake (void) +{ + return _machine_id_get (TRUE)->is_fake; } /*****************************************************************************/ +/* prefix for version2 secret key. The secret key is hashed with /etc/machine-id. */ +#define SECRET_KEY_V2_PREFIX "nm-v2:" +#define SECRET_KEY_FILE NMSTATEDIR"/secret_key" + static gboolean -_secret_key_read (guint8 **out_secret_key, - gsize *out_key_len) +_host_id_read_timestamp (gboolean use_secret_key_file, + const guint8 *host_id, + gsize host_id_len, + gint64 *out_timestamp_ns) { - guint8 *secret_key; - gboolean success = TRUE; - gsize key_len; - gs_free_error GError *error = NULL; + struct stat st; + gint64 now; + guint64 v; + + if ( use_secret_key_file + && stat (SECRET_KEY_FILE, &st) == 0) { + /* don't check for overflow or timestamps in the future. We get whatever + * (bogus) date is on the file. */ + *out_timestamp_ns = (st.st_mtim.tv_sec * NM_UTILS_NS_PER_SECOND) + st.st_mtim.tv_nsec; + return TRUE; + } - /* Let's try to load a saved secret key first. */ - if (g_file_get_contents (NMSTATEDIR "/secret_key", (char **) &secret_key, &key_len, &error)) { - if (key_len >= 16) - goto out; + /* generate a fake timestamp based on the host-id. + * + * This really should never happen under normal circumstances. We already + * are in a code path, where the system has a problem (unable to get good randomness + * and/or can't access the secret_key). In such a scenario, a fake timestamp is the + * least of our problems. + * + * At least, generate something sensible so we don't have to worry about the + * timestamp. It is wrong to worry about using a fake timestamp (which is tied to + * the secret_key) if we are unable to access the secret_key file in the first place. + * + * Pick a random timestamp from the past two years. Yes, this timestamp + * is not stable accross restarts, but apparently neither is the host-id + * nor the secret_key itself. */ - /* the secret key is borked. Log a warning, but proceed below to generate - * a new one. */ - nm_log_warn (LOGD_CORE, "secret-key: too short secret key in \"%s\" (generate new key)", NMSTATEDIR "/secret_key"); - nm_clear_g_free (&secret_key); - } else { - if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) { +#define EPOCH_TWO_YEARS (G_GINT64_CONSTANT (2 * 365 * 24 * 3600) * NM_UTILS_NS_PER_SECOND) + + v = nm_hash_siphash42 (1156657133u, host_id, host_id_len); + + now = time (NULL); + *out_timestamp_ns = NM_MAX ((gint64) 1, + (now * NM_UTILS_NS_PER_SECOND) - ((gint64) (v % ((guint64) (EPOCH_TWO_YEARS))))); + return FALSE; +} + +static const guint8 * +_host_id_hash_v2 (const guint8 *seed_arr, + gsize seed_len, + guint8 *out_digest /* 32 bytes (NM_UTILS_CHECKSUM_LENGTH_SHA256) */) +{ + nm_auto_free_checksum GChecksum *sum = g_checksum_new (G_CHECKSUM_SHA256); + const UuidData *machine_id_data; + char slen[100]; + + /* + (stat -c '%s' /var/lib/NetworkManager/secret_key; + echo -n ' '; + cat /var/lib/NetworkManager/secret_key; + cat /etc/machine-id | tr -d '\n' | sed -n 's/[a-f0-9-]/\0/pg') | sha256sum + */ + + nm_sprintf_buf (slen, "%"G_GSIZE_FORMAT" ", seed_len); + g_checksum_update (sum, (const guchar *) slen, strlen (slen)); + + g_checksum_update (sum, (const guchar *) seed_arr, seed_len); + + machine_id_data = _machine_id_get (FALSE); + if ( machine_id_data + && !machine_id_data->is_fake) + g_checksum_update (sum, (const guchar *) machine_id_data->str, strlen (machine_id_data->str)); + + nm_utils_checksum_get_digest_len (sum, out_digest, NM_UTILS_CHECKSUM_LENGTH_SHA256); + return out_digest; +} + +static gboolean +_host_id_read (guint8 **out_host_id, + gsize *out_host_id_len) +{ +#define SECRET_KEY_LEN 32u + guint8 sha256_digest[NM_UTILS_CHECKSUM_LENGTH_SHA256]; + nm_auto_clear_secret_ptr NMSecretPtr file_content = { 0 }; + const guint8 *secret_arr; + gsize secret_len; + GError *error = NULL; + gboolean success; + + if (nm_utils_file_get_contents (-1, + SECRET_KEY_FILE, + 10*1024, + NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET, + (char **) &file_content.str, + &file_content.len, + &error) < 0) { + if (!nm_utils_error_is_notfound (error)) { nm_log_warn (LOGD_CORE, "secret-key: failure reading secret key in \"%s\": %s (generate new key)", - NMSTATEDIR "/secret_key", error->message); + SECRET_KEY_FILE, error->message); } g_clear_error (&error); - } - - /* RFC7217 mandates the key SHOULD be at least 128 bits. - * Let's use twice as much. */ - key_len = 32; - secret_key = g_malloc (key_len + 1); - - /* the secret-key is binary. Still, ensure that it's NULL terminated, just like - * g_file_set_contents() does. */ - secret_key[32] = '\0'; + } else if ( file_content.len >= NM_STRLEN (SECRET_KEY_V2_PREFIX) + SECRET_KEY_LEN + && memcmp (file_content.bin, SECRET_KEY_V2_PREFIX, NM_STRLEN (SECRET_KEY_V2_PREFIX)) == 0) { + /* for this type of secret key, we require a prefix followed at least SECRET_KEY_LEN (32) bytes. We + * (also) do that, because older versions of NetworkManager wrote exactly 32 bytes without + * prefix, so we won't wrongly interpret such legacy keys as v2 (if they accidentally have + * a SECRET_KEY_V2_PREFIX prefix, they'll still have the wrong size). + * + * Note that below we generate the random seed in base64 encoding. But that is only done + * to write an ASCII file. There is no base64 decoding and the ASCII is hashed as-is. + * We would accept any binary data just as well (provided a suitable prefix and at least + * 32 bytes). + * + * Note that when hashing the v2 content, we also hash the prefix. There is no strong reason, + * except that it seems simpler not to distinguish between the v2 prefix and the content. + * It's all just part of the seed. */ - if (!nm_utils_random_bytes (secret_key, key_len)) { - nm_log_warn (LOGD_CORE, "secret-key: failure to generate good random data for secret-key (use non-persistent key)"); - success = FALSE; + secret_arr = _host_id_hash_v2 (file_content.bin, file_content.len, sha256_digest); + secret_len = NM_UTILS_CHECKSUM_LENGTH_SHA256; + success = TRUE; goto out; + } else if (file_content.len >= 16) { + secret_arr = file_content.bin; + secret_len = file_content.len; + success = TRUE; + goto out; + } else { + /* the secret key is borked. Log a warning, but proceed below to generate + * a new one. */ + nm_log_warn (LOGD_CORE, "secret-key: too short secret key in \"%s\" (generate new key)", SECRET_KEY_FILE); } - if (!nm_utils_file_set_contents (NMSTATEDIR "/secret_key", (char *) secret_key, key_len, 0077, &error)) { - nm_log_warn (LOGD_CORE, "secret-key: failure to persist secret key in \"%s\" (%s) (use non-persistent key)", - NMSTATEDIR "/secret_key", error->message); - success = FALSE; - goto out; + /* generate and persist new key */ + { +#define SECRET_KEY_LEN_BASE64 ((((SECRET_KEY_LEN / 3) + 1) * 4) + 4) + guint8 rnd_buf[SECRET_KEY_LEN]; + guint8 new_content[NM_STRLEN (SECRET_KEY_V2_PREFIX) + SECRET_KEY_LEN_BASE64]; + int base64_state = 0; + int base64_save = 0; + gsize len; + + success = nm_utils_random_bytes (rnd_buf, sizeof (rnd_buf)); + + /* Our key is really binary data. But since we anyway generate a random seed + * (with 32 random bytes), don't write it in binary, but instead create + * an pure ASCII (base64) representation. Note that the ASCII will still be taken + * as-is (no base64 decoding is done). The sole purpose is to write a ASCII file + * instead of a binary. The content is gibberish either way. */ + memcpy (new_content, SECRET_KEY_V2_PREFIX, NM_STRLEN (SECRET_KEY_V2_PREFIX)); + len = NM_STRLEN (SECRET_KEY_V2_PREFIX); + len += g_base64_encode_step (rnd_buf, + sizeof (rnd_buf), + FALSE, + (char *) &new_content[len], + &base64_state, + &base64_save); + len += g_base64_encode_close (FALSE, + (char *) &new_content[len], + &base64_state, + &base64_save); + nm_assert (len <= sizeof (new_content)); + + secret_arr = _host_id_hash_v2 (new_content, len, sha256_digest); + secret_len = NM_UTILS_CHECKSUM_LENGTH_SHA256; + + if (!success) + nm_log_warn (LOGD_CORE, "secret-key: failure to generate good random data for secret-key (use non-persistent key)"); + else if (nm_utils_get_testing ()) { + /* for test code, we don't write the generated secret-key to disk. */ + } else if (!nm_utils_file_set_contents (SECRET_KEY_FILE, + (const char *) new_content, + len, + 0077, + &error)) { + nm_log_warn (LOGD_CORE, "secret-key: failure to persist secret key in \"%s\" (%s) (use non-persistent key)", + SECRET_KEY_FILE, error->message); + g_clear_error (&error); + success = FALSE; + } else + nm_log_dbg (LOGD_CORE, "secret-key: persist new secret key to \"%s\"", SECRET_KEY_FILE); + + nm_explicit_bzero (rnd_buf, sizeof (rnd_buf)); + nm_explicit_bzero (new_content, sizeof (new_content)); } out: - /* regardless of success or failue, we always return a secret-key. The - * caller may choose to ignore the error and proceed. */ - *out_key_len = key_len; - *out_secret_key = secret_key; + *out_host_id_len = secret_len; + *out_host_id = nm_memdup (secret_arr, secret_len); return success; } typedef struct { - const guint8 *secret_key; - gsize key_len; + guint8 *host_id; + gsize host_id_len; + gint64 timestamp_ns; bool is_good:1; -} SecretKeyData; + bool timestamp_is_good:1; +} HostIdData; -gboolean -nm_utils_secret_key_get (const guint8 **out_secret_key, - gsize *out_key_len) +static const HostIdData * +_host_id_get (void) { - static volatile const SecretKeyData *secret_key_static; - const SecretKeyData *secret_key; + static const HostIdData *volatile host_id_static; + const HostIdData *host_id; - secret_key = g_atomic_pointer_get (&secret_key_static); - if (G_UNLIKELY (!secret_key)) { +again: + host_id = g_atomic_pointer_get (&host_id_static); + if (G_UNLIKELY (!host_id)) { + static HostIdData host_id_data; static gsize init_value = 0; - static SecretKeyData secret_key_data; - gboolean tmp_success; - gs_free guint8 *tmp_secret_key = NULL; - gsize tmp_key_len; - - tmp_success = _secret_key_read (&tmp_secret_key, &tmp_key_len); - if (g_once_init_enter (&init_value)) { - secret_key_data.secret_key = tmp_secret_key; - secret_key_data.key_len = tmp_key_len; - secret_key_data.is_good = tmp_success; - - if (g_atomic_pointer_compare_and_exchange (&secret_key_static, NULL, &secret_key_data)) { - g_steal_pointer (&tmp_secret_key); - secret_key = &secret_key_data; - } - g_once_init_leave (&init_value, 1); - } - if (!secret_key) - secret_key = g_atomic_pointer_get (&secret_key_static); + if (!g_once_init_enter (&init_value)) + goto again; + + host_id_data.is_good = _host_id_read (&host_id_data.host_id, + &host_id_data.host_id_len); + + host_id_data.timestamp_is_good = _host_id_read_timestamp (host_id_data.is_good, + host_id_data.host_id, + host_id_data.host_id_len, + &host_id_data.timestamp_ns); + if ( !host_id_data.timestamp_is_good + && host_id_data.is_good) + nm_log_warn (LOGD_CORE, "secret-key: failure reading host timestamp (use fake one)"); + + host_id = &host_id_data; + g_atomic_pointer_set (&host_id_static, host_id); + g_once_init_leave (&init_value, 1); } - *out_secret_key = secret_key->secret_key; - *out_key_len = secret_key->key_len; - return secret_key->is_good; + return host_id; } -gint64 -nm_utils_secret_key_get_timestamp (void) +/** + * nm_utils_host_id_get: + * @out_host_id: (out) (transfer none): the binary host key + * @out_host_id_len: the length of the host key. + * + * This returns a per-host key that depends on /var/lib/NetworkManage/secret_key + * and (depending on the version) on /etc/machine-id. If /var/lib/NetworkManage/secret_key + * does not exist, it will be generated and persisted for next boot. + * + * Returns: %TRUE, if the host key is "good". Note that this function + * will always succeed to return a host-key, and that this key + * won't change during the run of the program (no matter what). + * A %FALSE return possibly means, that the secret_key is not persisted + * to disk, and/or that it was generated with bad randomness. + */ +gboolean +nm_utils_host_id_get (const guint8 **out_host_id, + gsize *out_host_id_len) { - struct stat stat_buf; - const guint8 *key; - gsize key_len; + const HostIdData *host_id; - if (!nm_utils_secret_key_get (&key, &key_len)) - return 0; - - if (stat (NMSTATEDIR "/secret_key", &stat_buf) != 0) - return 0; + host_id = _host_id_get (); + *out_host_id = host_id->host_id; + *out_host_id_len = host_id->host_id_len; + return host_id->is_good; +} - return stat_buf.st_mtim.tv_sec; +gint64 +nm_utils_host_id_get_timestamp_ns (void) +{ + return _host_id_get ()->timestamp_ns; } /*****************************************************************************/ -const char * -nm_utils_get_boot_id (void) +static const UuidData * +_boot_id_get (void) { - static const char *boot_id; - - if (G_UNLIKELY (!boot_id)) { + static const UuidData *volatile p_boot_id; + const UuidData *d; + +again: + d = g_atomic_pointer_get (&p_boot_id); + if (G_UNLIKELY (!d)) { + static gsize lock; + static UuidData boot_id; gs_free char *contents = NULL; + NMUuid uuid; + gboolean is_fake = FALSE; nm_utils_file_get_contents (-1, "/proc/sys/kernel/random/boot_id", 0, NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE, &contents, NULL, NULL); - if (contents) { - g_strstrip (contents); - if (contents[0]) { - /* clone @contents because we keep @boot_id until the program - * ends. - * nm_utils_file_get_contents() likely allocated a larger - * buffer chunk initially and (although using realloc to shrink - * the buffer) it might not be best to keep this memory - * around. */ - boot_id = g_strdup (contents); - } + if ( !contents + || !_nm_utils_uuid_parse (nm_strstrip (contents), &uuid)) { + /* generate a random UUID instead. */ + is_fake = TRUE; + _nm_utils_uuid_generate_random (&uuid); } - if (!boot_id) - boot_id = nm_utils_uuid_generate (); + + if (!g_once_init_enter (&lock)) + goto again; + + d = _uuid_data_init (&boot_id, FALSE, is_fake, &uuid); + g_atomic_pointer_set (&p_boot_id, d); + g_once_init_leave (&lock, 1); } - return boot_id; + return d; +} + +const char * +nm_utils_boot_id_str (void) +{ + return _boot_id_get ()->str; +} + +const NMUuid * +nm_utils_boot_id_bin (void) +{ + return &_boot_id_get ()->bin; +} + +/*****************************************************************************/ + +/** + * nm_utils_arp_type_detect_from_hwaddrlen: + * @hwaddr_len: the length of the hardware address in bytes. + * + * Detects the arp-type based on the length of the MAC address. + * On success, this returns a (positive) value in uint16_t range, + * like ARPHRD_ETHER or ARPHRD_INFINIBAND. + * + * On failure, returns a negative error code. + * + * Returns: the arp-type or negative value on error. */ +int +nm_utils_arp_type_detect_from_hwaddrlen (gsize hwaddr_len) +{ + switch (hwaddr_len) { + case ETH_ALEN: + return ARPHRD_ETHER; + case INFINIBAND_ALEN: + return ARPHRD_INFINIBAND; + default: + /* Note: if you ever support anything but ethernet and infiniband, + * make sure to look at all callers. They assert that it's one of + * these two. */ + return -EINVAL; + } +} + +gboolean +nm_utils_arp_type_validate_hwaddr (int arp_type, + const guint8 *hwaddr, + gsize hwaddr_len) +{ + + if (!hwaddr) + return FALSE; + + if (arp_type == ARPHRD_ETHER) { + G_STATIC_ASSERT (ARPHRD_ETHER >= 0 && ARPHRD_ETHER <= 0xFF); + if (hwaddr_len != ETH_ALEN) + return FALSE; + } else if (arp_type == ARPHRD_INFINIBAND) { + G_STATIC_ASSERT (ARPHRD_INFINIBAND >= 0 && ARPHRD_INFINIBAND <= 0xFF); + if (hwaddr_len != INFINIBAND_ALEN) + return FALSE; + } else + return FALSE; + + nm_assert (arp_type == nm_utils_arp_type_detect_from_hwaddrlen (hwaddr_len)); + return TRUE; +} + +gboolean +nm_utils_arp_type_get_hwaddr_relevant_part (int arp_type, + const guint8 **hwaddr, + gsize *hwaddr_len) +{ + g_return_val_if_fail ( hwaddr + && hwaddr_len + && nm_utils_arp_type_validate_hwaddr (arp_type, *hwaddr, *hwaddr_len), + FALSE); + + /* for infiniband, we only consider the last 8 bytes. */ + if (arp_type == ARPHRD_INFINIBAND) { + *hwaddr += (INFINIBAND_ALEN - 8); + *hwaddr_len = 8; + } + + return TRUE; } /*****************************************************************************/ @@ -2888,14 +3090,12 @@ nm_utils_ipv6_interface_identifier_get_from_token (NMUtilsIPv6IfaceId *iid, /** * nm_utils_inet6_interface_identifier_to_token: * @iid: %NMUtilsIPv6IfaceId interface identifier - * @buf: the destination buffer or %NULL + * @buf: the destination buffer of at least %NM_UTILS_INET_ADDRSTRLEN + * bytes. * * Converts the interface identifier to a string token. - * If the destination buffer it set, set it is used to store the - * resulting token, otherwise an internal static buffer is used. - * The buffer needs to be %NM_UTILS_INET_ADDRSTRLEN characters long. * - * Returns: a statically allocated array. Do not g_free(). + * Returns: the input buffer filled with the id as string. */ const char * nm_utils_inet6_interface_identifier_to_token (NMUtilsIPv6IfaceId iid, char *buf) @@ -2920,9 +3120,8 @@ nm_utils_stable_id_random (void) char * nm_utils_stable_id_generated_complete (const char *stable_id_generated) { - guint8 buf[20]; - GChecksum *sum; - gsize buf_size; + nm_auto_free_checksum GChecksum *sum = NULL; + guint8 buf[NM_UTILS_CHECKSUM_LENGTH_SHA1]; char *base64; /* for NM_UTILS_STABLE_TYPE_GENERATED we genererate a possibly long string @@ -2933,15 +3132,8 @@ nm_utils_stable_id_generated_complete (const char *stable_id_generated) g_return_val_if_fail (stable_id_generated, NULL); sum = g_checksum_new (G_CHECKSUM_SHA1); - nm_assert (sum); - g_checksum_update (sum, (guchar *) stable_id_generated, strlen (stable_id_generated)); - - buf_size = sizeof (buf); - g_checksum_get_digest (sum, buf, &buf_size); - nm_assert (buf_size == sizeof (buf)); - - g_checksum_free (sum); + nm_utils_checksum_get_digest (sum, buf); /* we don't care to use the sha1 sum in common hex representation. * Use instead base64, it's 27 chars (stripping the padding) vs. @@ -2967,6 +3159,7 @@ _stable_id_append (GString *str, NMUtilsStableType nm_utils_stable_id_parse (const char *stable_id, const char *deviceid, + const char *hwaddr, const char *bootid, const char *uuid, char **out_generated) @@ -3042,12 +3235,14 @@ nm_utils_stable_id_parse (const char *stable_id, if (CHECK_PREFIX ("${CONNECTION}")) _stable_id_append (str, uuid); else if (CHECK_PREFIX ("${BOOT}")) - _stable_id_append (str, bootid ?: nm_utils_get_boot_id ()); + _stable_id_append (str, bootid); else if (CHECK_PREFIX ("${DEVICE}")) _stable_id_append (str, deviceid); + else if (CHECK_PREFIX ("${MAC}")) + _stable_id_append (str, hwaddr); else if (g_str_has_prefix (&stable_id[i], "${RANDOM}")) { /* RANDOM makes not so much sense for cloned-mac-address - * as the result is simmilar to specyifing "cloned-mac-address=random". + * as the result is similar to specyifing "cloned-mac-address=random". * It makes however sense for RFC 7217 Stable Privacy IPv6 addresses * where this is effectively the only way to generate a different * (random) host identifier for each connect. @@ -3121,28 +3316,20 @@ _set_stable_privacy (NMUtilsStableType stable_type, const char *ifname, const char *network_id, guint32 dad_counter, - const guint8 *secret_key, - gsize key_len, + const guint8 *host_id, + gsize host_id_len, GError **error) { - GChecksum *sum; - guint8 digest[32]; + nm_auto_free_checksum GChecksum *sum = NULL; + guint8 digest[NM_UTILS_CHECKSUM_LENGTH_SHA256]; guint32 tmp[2]; - gsize len = sizeof (digest); - nm_assert (key_len); + nm_assert (host_id_len); nm_assert (network_id); - /* Documentation suggests that this can fail. - * Maybe in case of a missing algorithm in crypto library? */ sum = g_checksum_new (G_CHECKSUM_SHA256); - if (!sum) { - g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - "Can't create a SHA256 hash"); - return FALSE; - } - key_len = MIN (key_len, G_MAXUINT32); + host_id_len = MIN (host_id_len, G_MAXUINT32); if (stable_type != NM_UTILS_STABLE_TYPE_UUID) { guint8 stable_type_uint8; @@ -3155,7 +3342,7 @@ _set_stable_privacy (NMUtilsStableType stable_type, * * That is no real problem and it is still impossible to * force a collision here, because of how the remaining - * fields are hashed. That is, as we also hash @key_len + * fields are hashed. That is, as we also hash @host_id_len * and the terminating '\0' of @network_id, it is unambigiously * possible to revert the process and deduce the @stable_type. */ @@ -3166,27 +3353,20 @@ _set_stable_privacy (NMUtilsStableType stable_type, g_checksum_update (sum, (const guchar *) ifname, strlen (ifname) + 1); g_checksum_update (sum, (const guchar *) network_id, strlen (network_id) + 1); tmp[0] = htonl (dad_counter); - tmp[1] = htonl (key_len); + tmp[1] = htonl (host_id_len); g_checksum_update (sum, (const guchar *) tmp, sizeof (tmp)); - g_checksum_update (sum, (const guchar *) secret_key, key_len); - - g_checksum_get_digest (sum, digest, &len); - - nm_assert (len == sizeof (digest)); + g_checksum_update (sum, (const guchar *) host_id, host_id_len); + nm_utils_checksum_get_digest (sum, digest); while (_is_reserved_ipv6_iid (digest)) { g_checksum_reset (sum); tmp[0] = htonl (++dad_counter); - g_checksum_update (sum, digest, len); + g_checksum_update (sum, digest, sizeof (digest)); g_checksum_update (sum, (const guchar *) &tmp[0], sizeof (tmp[0])); - g_checksum_get_digest (sum, digest, &len); - nm_assert (len == sizeof (digest)); + nm_utils_checksum_get_digest (sum, digest); } - g_checksum_free (sum); - memcpy (addr->s6_addr + 8, &digest[0], 8); - return TRUE; } @@ -3196,11 +3376,11 @@ nm_utils_ipv6_addr_set_stable_privacy_impl (NMUtilsStableType stable_type, const char *ifname, const char *network_id, guint32 dad_counter, - guint8 *secret_key, - gsize key_len, + guint8 *host_id, + gsize host_id_len, GError **error) { - return _set_stable_privacy (stable_type, addr, ifname, network_id, dad_counter, secret_key, key_len, error); + return _set_stable_privacy (stable_type, addr, ifname, network_id, dad_counter, host_id, host_id_len, error); } #define RFC7217_IDGEN_RETRIES 3 @@ -3220,8 +3400,8 @@ nm_utils_ipv6_addr_set_stable_privacy (NMUtilsStableType stable_type, guint32 dad_counter, GError **error) { - const guint8 *secret_key; - gsize key_len; + const guint8 *host_id; + gsize host_id_len; g_return_val_if_fail (network_id, FALSE); @@ -3231,10 +3411,10 @@ nm_utils_ipv6_addr_set_stable_privacy (NMUtilsStableType stable_type, return FALSE; } - nm_utils_secret_key_get (&secret_key, &key_len); + nm_utils_host_id_get (&host_id, &host_id_len); return _set_stable_privacy (stable_type, addr, ifname, network_id, dad_counter, - secret_key, key_len, error); + host_id, host_id_len, error); } /*****************************************************************************/ @@ -3306,42 +3486,36 @@ nm_utils_hw_addr_gen_random_eth (const char *current_mac_address, static char * _hw_addr_gen_stable_eth (NMUtilsStableType stable_type, const char *stable_id, - const guint8 *secret_key, - gsize key_len, + const guint8 *host_id, + gsize host_id_len, const char *ifname, const char *current_mac_address, const char *generate_mac_address_mask) { - GChecksum *sum; + nm_auto_free_checksum GChecksum *sum = NULL; guint32 tmp; - guint8 digest[32]; - gsize len = sizeof (digest); + guint8 digest[NM_UTILS_CHECKSUM_LENGTH_SHA256]; struct ether_addr bin_addr; guint8 stable_type_uint8; nm_assert (stable_id); - nm_assert (secret_key); + nm_assert (host_id); sum = g_checksum_new (G_CHECKSUM_SHA256); - if (!sum) - return NULL; - key_len = MIN (key_len, G_MAXUINT32); + host_id_len = MIN (host_id_len, G_MAXUINT32); nm_assert (stable_type < (NMUtilsStableType) 255); stable_type_uint8 = stable_type; g_checksum_update (sum, (const guchar *) &stable_type_uint8, sizeof (stable_type_uint8)); - tmp = htonl ((guint32) key_len); + tmp = htonl ((guint32) host_id_len); g_checksum_update (sum, (const guchar *) &tmp, sizeof (tmp)); - g_checksum_update (sum, (const guchar *) secret_key, key_len); + g_checksum_update (sum, (const guchar *) host_id, host_id_len); g_checksum_update (sum, (const guchar *) (ifname ?: ""), ifname ? (strlen (ifname) + 1) : 1); g_checksum_update (sum, (const guchar *) stable_id, strlen (stable_id) + 1); - g_checksum_get_digest (sum, digest, &len); - g_checksum_free (sum); - - g_return_val_if_fail (len == 32, NULL); + nm_utils_checksum_get_digest (sum, digest); memcpy (&bin_addr, digest, ETH_ALEN); _hw_addr_eth_complete (&bin_addr, current_mac_address, generate_mac_address_mask); @@ -3351,13 +3525,13 @@ _hw_addr_gen_stable_eth (NMUtilsStableType stable_type, char * nm_utils_hw_addr_gen_stable_eth_impl (NMUtilsStableType stable_type, const char *stable_id, - const guint8 *secret_key, - gsize key_len, + const guint8 *host_id, + gsize host_id_len, const char *ifname, const char *current_mac_address, const char *generate_mac_address_mask) { - return _hw_addr_gen_stable_eth (stable_type, stable_id, secret_key, key_len, ifname, current_mac_address, generate_mac_address_mask); + return _hw_addr_gen_stable_eth (stable_type, stable_id, host_id, host_id_len, ifname, current_mac_address, generate_mac_address_mask); } char * @@ -3367,17 +3541,17 @@ nm_utils_hw_addr_gen_stable_eth (NMUtilsStableType stable_type, const char *current_mac_address, const char *generate_mac_address_mask) { - const guint8 *secret_key; - gsize key_len; + const guint8 *host_id; + gsize host_id_len; g_return_val_if_fail (stable_id, NULL); - nm_utils_secret_key_get (&secret_key, &key_len); + nm_utils_host_id_get (&host_id, &host_id_len); return _hw_addr_gen_stable_eth (stable_type, stable_id, - secret_key, - key_len, + host_id, + host_id_len, ifname, current_mac_address, generate_mac_address_mask); @@ -3385,6 +3559,150 @@ nm_utils_hw_addr_gen_stable_eth (NMUtilsStableType stable_type, /*****************************************************************************/ +GBytes * +nm_utils_dhcp_client_id_mac (int arp_type, + const guint8 *hwaddr, + gsize hwaddr_len) +{ + guint8 *client_id_buf; + const guint8 hwaddr_type = arp_type; + + if (!nm_utils_arp_type_get_hwaddr_relevant_part (arp_type, &hwaddr, &hwaddr_len)) + g_return_val_if_reached (NULL); + + client_id_buf = g_malloc (hwaddr_len + 1); + client_id_buf[0] = hwaddr_type; + memcpy (&client_id_buf[1], hwaddr, hwaddr_len); + return g_bytes_new_take (client_id_buf, hwaddr_len + 1); +} + +#define HASH_KEY ((const guint8[16]) { 0x80, 0x11, 0x8c, 0xc2, 0xfe, 0x4a, 0x03, 0xee, 0x3e, 0xd6, 0x0c, 0x6f, 0x36, 0x39, 0x14, 0x09 }) + +/** + * nm_utils_create_dhcp_iaid: + * @legacy_unstable_byteorder: legacy behavior is to generate a u32 iaid which + * is endianness dependant. This is to preserve backward compatibility. + * For non-legacy behavior, the returned integer is in stable endianness, + * and corresponds to legacy behavior on little endian systems. + * @interface_id: the seed for hashing when generating the ID. Usually, + * this is the interface name. + * @interface_id_len: length of @interface_id + * + * This corresponds to systemd's dhcp_identifier_set_iaid() for generating + * a IAID for the interface. + * + * Returns: the IAID in host byte order. */ +guint32 +nm_utils_create_dhcp_iaid (gboolean legacy_unstable_byteorder, + const guint8 *interface_id, + gsize interface_id_len) +{ + guint64 u64; + guint32 u32; + + u64 = c_siphash_hash (HASH_KEY, interface_id, interface_id_len); + u32 = (u64 & 0xffffffffu) ^ (u64 >> 32); + if (legacy_unstable_byteorder) { + /* legacy systemd code dhcp_identifier_set_iaid() generates the iaid + * dependent on the host endianness. Since this function returns the IAID + * in native-byte order, we need to account for that. + * + * On little endian systems, we want the legacy-behavior is identical to + * the endianness-agnostic behavior. So, we need to swap the bytes on + * big-endian systems. + * + * (https://github.com/systemd/systemd/pull/10614). */ + return htole32 (u32); + } else { + /* we return the value as-is, in native byte order. */ + return u32; + } +} + +/** + * nm_utils_dhcp_client_id_systemd_node_specific_full: + * @legacy_unstable_byteorder: historically, the code would generate a iaid + * dependent on host endianness. This is undesirable, if backward compatibility + * are not a concern, generate stable endianness. + * @interface_id: a binary identifer that is hashed into the DUID. + * Comonly this is the interface-name, but it may be the MAC address. + * @interface_id_len: the length of @interface_id. + * @machine_id: the binary identifier for the machine. It is hashed + * into the DUID. It commonly is /etc/machine-id (parsed in binary as NMUuid). + * @machine_id_len: the length of the @machine_id. + * + * Systemd's sd_dhcp_client generates a default client ID (type 255, node-specific, + * RFC 4361) if no explicit client-id is set. This function duplicates that + * implementation and exposes it as (internal) API. + * + * Returns: a %GBytes of generated client-id. This function cannot fail. + */ +GBytes * +nm_utils_dhcp_client_id_systemd_node_specific_full (gboolean legacy_unstable_byteorder, + const guint8 *interface_id, + gsize interface_id_len, + const guint8 *machine_id, + gsize machine_id_len) +{ + const guint16 DUID_TYPE_EN = 2; + const guint32 SYSTEMD_PEN = 43793; + struct _nm_packed { + guint8 type; + guint32 iaid; + struct _nm_packed { + guint16 type; + union { + struct _nm_packed { + /* DUID_TYPE_EN */ + guint32 pen; + uint8_t id[8]; + } en; + }; + } duid; + } *client_id; + guint64 u64; + guint32 u32; + + g_return_val_if_fail (interface_id, NULL); + g_return_val_if_fail (interface_id_len > 0, NULL); + g_return_val_if_fail (machine_id, NULL); + g_return_val_if_fail (machine_id_len > 0, NULL); + + client_id = g_malloc (sizeof (*client_id)); + + client_id->type = 255; + + u32 = nm_utils_create_dhcp_iaid (legacy_unstable_byteorder, + interface_id, + interface_id_len); + unaligned_write_be32 (&client_id->iaid, u32); + + unaligned_write_be16 (&client_id->duid.type, DUID_TYPE_EN); + + unaligned_write_be32 (&client_id->duid.en.pen, SYSTEMD_PEN); + + u64 = htole64 (c_siphash_hash (HASH_KEY, machine_id, machine_id_len)); + memcpy(client_id->duid.en.id, &u64, sizeof (client_id->duid.en.id)); + + G_STATIC_ASSERT_EXPR (sizeof (*client_id) == 19); + return g_bytes_new_take (client_id, 19); +} + +GBytes * +nm_utils_dhcp_client_id_systemd_node_specific (gboolean legacy_unstable_byteorder, + const char *ifname) +{ + g_return_val_if_fail (ifname && ifname[0], NULL); + + return nm_utils_dhcp_client_id_systemd_node_specific_full (legacy_unstable_byteorder, + (const guint8 *) ifname, + strlen (ifname), + (const guint8 *) nm_utils_machine_id_bin (), + sizeof (NMUuid)); +} + +/*****************************************************************************/ + /** * nm_utils_setpgid: * @unused: unused diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h index 30d1360a..a93854a4 100644 --- a/src/nm-core-utils.h +++ b/src/nm-core-utils.h @@ -27,6 +27,8 @@ #include "nm-connection.h" +#include "nm-utils/nm-time-utils.h" + /*****************************************************************************/ #define NM_PLATFORM_LIFETIME_PERMANENT G_MAXUINT32 @@ -61,20 +63,35 @@ void _nm_singleton_instance_register_destruction (GObject *instance); #define NM_DEFINE_SINGLETON_GETTER(TYPE, GETTER, GTYPE, ...) \ NM_DEFINE_SINGLETON_INSTANCE (TYPE); \ NM_DEFINE_SINGLETON_REGISTER (TYPE); \ +static char _already_created_##GETTER = FALSE; \ TYPE * \ GETTER (void) \ { \ if (G_UNLIKELY (!singleton_instance)) { \ - static char _already_created = FALSE; \ -\ - g_assert (!_already_created || (NM_DEFINE_SINGLETON_ALLOW_MULTIPLE)); \ - _already_created = TRUE;\ + g_assert (!(_already_created_##GETTER) || (NM_DEFINE_SINGLETON_ALLOW_MULTIPLE)); \ + (_already_created_##GETTER) = TRUE;\ singleton_instance = (g_object_new (GTYPE, ##__VA_ARGS__, NULL)); \ g_assert (singleton_instance); \ nm_singleton_instance_register (); \ nm_log_dbg (LOGD_CORE, "create %s singleton (%p)", G_STRINGIFY (TYPE), singleton_instance); \ } \ return singleton_instance; \ +} \ +_nm_unused static void \ +_nmtst_##GETTER##_reset (TYPE *instance) \ +{ \ + /* usually, the singleton can only be created once (and further instantiations + * are guarded by an assert). For testing, we need to reset the singleton to + * allow multiple instantiations. */ \ + g_assert (G_IS_OBJECT (instance)); \ + g_assert (instance == singleton_instance); \ + g_assert (_already_created_##GETTER); \ + g_object_unref (instance); \ + \ + /* require that the last unref also destroyed the singleton. If this fails, + * somebody still keeps a reference. Fix your test! */ \ + g_assert (!singleton_instance); \ + _already_created_##GETTER = FALSE; \ } /* attach @instance to the data or @owner. @owner owns a reference @@ -208,7 +225,8 @@ NMMatchSpecMatchType nm_match_spec_device (const GSList *specs, const char *driver, const char *driver_version, const char *hwaddr, - const char *s390_subchannels); + const char *s390_subchannels, + const char *dhcp_plugin); NMMatchSpecMatchType nm_match_spec_config (const GSList *specs, guint nm_version, const char *env); @@ -221,9 +239,6 @@ gboolean nm_wildcard_match_check (const char *str, /*****************************************************************************/ -const char *nm_utils_get_ip_config_method (NMConnection *connection, - GType ip_setting_type); - gboolean nm_utils_connection_has_default_route (NMConnection *connection, int addr_family, gboolean *out_is_never_default); @@ -240,19 +255,6 @@ void nm_utils_log_connection_diff (NMConnection *connection, const char *prefix, const char *dbus_path); -gint64 nm_utils_get_monotonic_timestamp_ns (void); -gint64 nm_utils_get_monotonic_timestamp_us (void); -gint64 nm_utils_get_monotonic_timestamp_ms (void); -gint32 nm_utils_get_monotonic_timestamp_s (void); -gint64 nm_utils_monotonic_timestamp_as_boottime (gint64 timestamp, gint64 timestamp_ticks_per_ns); - -static inline gint64 -nm_utils_get_monotonic_timestamp_ns_cached (gint64 *cache_now) -{ - return (*cache_now) - ?: (*cache_now = nm_utils_get_monotonic_timestamp_ns ()); -} - gboolean nm_utils_is_valid_path_component (const char *name); const char *NM_ASSERT_VALID_PATH_COMPONENT (const char *name); @@ -264,14 +266,32 @@ gboolean nm_utils_sysctl_ip_conf_is_path (int addr_family, const char *path, con gboolean nm_utils_is_specific_hostname (const char *name); -char *nm_utils_machine_id_read (void); -gboolean nm_utils_machine_id_parse (const char *id_str, /*uuid_t*/ guchar *out_uuid); +struct _NMUuid; + +const char *nm_utils_machine_id_str (void); +const struct _NMUuid *nm_utils_machine_id_bin (void); +gboolean nm_utils_machine_id_is_fake (void); + +const char *nm_utils_boot_id_str (void); +const struct _NMUuid *nm_utils_boot_id_bin (void); + +gboolean nm_utils_host_id_get (const guint8 **out_host_id, + gsize *out_host_id_len); +gint64 nm_utils_host_id_get_timestamp_ns (void); + +/*****************************************************************************/ + +int nm_utils_arp_type_detect_from_hwaddrlen (gsize hwaddr_len); + +gboolean nm_utils_arp_type_validate_hwaddr (int arp_type, + const guint8 *hwaddr, + gsize hwaddr_len); -gboolean nm_utils_secret_key_get (const guint8 **out_secret_key, - gsize *out_key_len); -gint64 nm_utils_secret_key_get_timestamp (void); +gboolean nm_utils_arp_type_get_hwaddr_relevant_part (int arp_type, + const guint8 **hwaddr, + gsize *hwaddr_len); -const char *nm_utils_get_boot_id (void); +/*****************************************************************************/ /* IPv6 Interface Identifier helpers */ @@ -326,6 +346,7 @@ typedef enum { NMUtilsStableType nm_utils_stable_id_parse (const char *stable_id, const char *deviceid, + const char *hwaddr, const char *bootid, const char *uuid, char **out_generated); @@ -338,8 +359,8 @@ gboolean nm_utils_ipv6_addr_set_stable_privacy_impl (NMUtilsStableType stable_ty const char *ifname, const char *network_id, guint32 dad_counter, - guint8 *secret_key, - gsize key_len, + guint8 *host_id, + gsize host_id_len, GError **error); gboolean nm_utils_ipv6_addr_set_stable_privacy (NMUtilsStableType id_type, @@ -353,8 +374,8 @@ char *nm_utils_hw_addr_gen_random_eth (const char *current_mac_address, const char *generate_mac_address_mask); char *nm_utils_hw_addr_gen_stable_eth_impl (NMUtilsStableType stable_type, const char *stable_id, - const guint8 *secret_key, - gsize key_len, + const guint8 *host_id, + gsize host_id_len, const char *ifname, const char *current_mac_address, const char *generate_mac_address_mask); @@ -364,6 +385,27 @@ char *nm_utils_hw_addr_gen_stable_eth (NMUtilsStableType stable_type, const char *current_mac_address, const char *generate_mac_address_mask); +/*****************************************************************************/ + +GBytes *nm_utils_dhcp_client_id_mac (int arp_type, + const guint8 *hwaddr, + gsize hwaddr_len); + +guint32 nm_utils_create_dhcp_iaid (gboolean legacy_unstable_byteorder, + const guint8 *interface_id, + gsize interface_id_len); + +GBytes *nm_utils_dhcp_client_id_systemd_node_specific_full (gboolean legacy_unstable_byteorder, + const guint8 *interface_id, + gsize interface_id_len, + const guint8 *machine_id, + gsize machine_id_len); + +GBytes *nm_utils_dhcp_client_id_systemd_node_specific (gboolean legacy_unstable_byteorder, + const char *ifname); + +/*****************************************************************************/ + void nm_utils_array_remove_at_indexes (GArray *array, const guint *indexes_to_delete, gsize len); void nm_utils_setpgid (gpointer unused); diff --git a/src/nm-dbus-manager.c b/src/nm-dbus-manager.c index a5c7c12b..9c8d6570 100644 --- a/src/nm-dbus-manager.c +++ b/src/nm-dbus-manager.c @@ -1182,7 +1182,7 @@ _nm_dbus_manager_obj_notify (NMDBusObject *obj, priv = NM_DBUS_MANAGER_GET_PRIVATE (self); /* do a naive search for the matching NMDBusPropertyInfoExtended infos. Since the number of - * (interaces x properties) is static and possibly small, this naive search is effectively + * (interfaces x properties) is static and possibly small, this naive search is effectively * O(1). We might wanna introduce some index to lookup the properties in question faster. * * The nice part of this implementation is however, that the order in which properties @@ -1470,6 +1470,14 @@ static const GDBusInterfaceInfo interface_info_objmgr = NM_DEFINE_GDBUS_INTERFAC /*****************************************************************************/ +GDBusConnection * +nm_dbus_manager_get_dbus_connection (NMDBusManager *self) +{ + g_return_val_if_fail (NM_IS_DBUS_MANAGER (self), NULL); + + return NM_DBUS_MANAGER_GET_PRIVATE (self)->connection; +} + void nm_dbus_manager_start (NMDBusManager *self, NMDBusManagerSetPropertyHandler set_property_handler, diff --git a/src/nm-dbus-manager.h b/src/nm-dbus-manager.h index 04c42bb0..89acd7c8 100644 --- a/src/nm-dbus-manager.h +++ b/src/nm-dbus-manager.h @@ -51,6 +51,8 @@ typedef void (*NMDBusManagerSetPropertyHandler) (NMDBusObject *obj, gboolean nm_dbus_manager_acquire_bus (NMDBusManager *self); +GDBusConnection *nm_dbus_manager_get_dbus_connection (NMDBusManager *self); + void nm_dbus_manager_start (NMDBusManager *self, NMDBusManagerSetPropertyHandler set_property_handler, gpointer set_property_handler_data); diff --git a/src/nm-iface-helper.c b/src/nm-iface-helper.c index 6ae2b9d2..1229ad35 100644 --- a/src/nm-iface-helper.c +++ b/src/nm-iface-helper.c @@ -223,14 +223,14 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in } if (changed & NM_NDISC_CONFIG_HOP_LIMIT) - nm_platform_sysctl_set_ip6_hop_limit_safe (NM_PLATFORM_GET, global_opt.ifname, rdata->hop_limit); + nm_platform_sysctl_ip_conf_set_ipv6_hop_limit_safe (NM_PLATFORM_GET, global_opt.ifname, rdata->hop_limit); if (changed & NM_NDISC_CONFIG_MTU) { - char val[16]; - char sysctl_path_buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; - - g_snprintf (val, sizeof (val), "%d", rdata->mtu); - nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET6, sysctl_path_buf, global_opt.ifname, "mtu")), val); + nm_platform_sysctl_ip_conf_set_int64 (NM_PLATFORM_GET, + AF_INET6, + global_opt.ifname, + "mtu", + rdata->mtu); } nm_ip6_config_merge (existing, ndisc_config, NM_IP_CONFIG_MERGE_DEFAULT, 0); @@ -389,7 +389,6 @@ main (int argc, char *argv[]) gs_unref_bytes GBytes *client_id = NULL; gs_free NMUtilsIPv6IfaceId *iid = NULL; guint sd_id; - char sysctl_path_buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; c_list_init (&gl.dad_failed_lst_head); @@ -500,7 +499,11 @@ main (int argc, char *argv[]) } if (global_opt.dhcp4_address) { - nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET, sysctl_path_buf, global_opt.ifname, "promote_secondaries")), "1"); + nm_platform_sysctl_ip_conf_set (NM_PLATFORM_GET, + AF_INET, + global_opt.ifname, + "promote_secondaries", + "1"); dhcp4_client = nm_dhcp_manager_start_ip4 (nm_dhcp_manager_get (), nm_platform_get_multi_idx (NM_PLATFORM_GET), @@ -552,10 +555,10 @@ main (int argc, char *argv[]) if (iid) nm_ndisc_set_iid (ndisc, *iid); - nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET6, sysctl_path_buf, global_opt.ifname, "accept_ra")), "1"); - nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET6, sysctl_path_buf, global_opt.ifname, "accept_ra_defrtr")), "0"); - nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET6, sysctl_path_buf, global_opt.ifname, "accept_ra_pinfo")), "0"); - nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET6, sysctl_path_buf, global_opt.ifname, "accept_ra_rtr_pref")), "0"); + nm_platform_sysctl_ip_conf_set (NM_PLATFORM_GET, AF_INET6, global_opt.ifname, "accept_ra", "1"); + nm_platform_sysctl_ip_conf_set (NM_PLATFORM_GET, AF_INET6, global_opt.ifname, "accept_ra_defrtr", "0"); + nm_platform_sysctl_ip_conf_set (NM_PLATFORM_GET, AF_INET6, global_opt.ifname, "accept_ra_pinfo", "0"); + nm_platform_sysctl_ip_conf_set (NM_PLATFORM_GET, AF_INET6, global_opt.ifname, "accept_ra_rtr_pref", "0"); g_signal_connect (NM_PLATFORM_GET, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c index 6604711c..1179a77f 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -1051,6 +1051,7 @@ nm_ip4_config_create_setting (const NMIP4Config *self) NMDedupMultiIter ipconf_iter; const NMPlatformIP4Address *address; const NMPlatformIP4Route *route; + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; s_ip4 = NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new ()); @@ -1095,7 +1096,7 @@ nm_ip4_config_create_setting (const NMIP4Config *self) g_object_set (s_ip4, NM_SETTING_IP_CONFIG_GATEWAY, nm_utils_inet4_ntop (NMP_OBJECT_CAST_IP4_ROUTE (priv->best_default_route)->gateway, - NULL), + sbuf), NULL); } @@ -1130,7 +1131,7 @@ nm_ip4_config_create_setting (const NMIP4Config *self) for (i = 0; i < nnameservers; i++) { guint32 nameserver = nm_ip4_config_get_nameserver (self, i); - nm_setting_ip_config_add_dns (s_ip4, nm_utils_inet4_ntop (nameserver, NULL)); + nm_setting_ip_config_add_dns (s_ip4, nm_utils_inet4_ntop (nameserver, sbuf)); } for (i = 0; i < nsearches; i++) { const char *search = nm_ip4_config_get_search (self, i); @@ -2039,9 +2040,11 @@ nm_ip_config_dump (const NMIPConfig *self, } for (i = 0; i < nm_ip_config_get_num_nameservers (self); i++) { + char buf[NM_UTILS_INET_ADDRSTRLEN]; + ptr = nm_ip_config_get_nameserver (self, i); nm_log (level, domain, NULL, NULL, " dns : %s", - nm_utils_inet_ntop (addr_family, ptr, NULL)); + nm_utils_inet_ntop (addr_family, ptr, buf)); } for (i = 0; i < nm_ip_config_get_num_domains (self); i++) @@ -2110,7 +2113,7 @@ nm_ip4_config_add_address (NMIP4Config *self, const NMPlatformIP4Address *new) { g_return_if_fail (self); g_return_if_fail (new); - g_return_if_fail (new->plen > 0 && new->plen <= 32); + g_return_if_fail (new->plen <= 32); g_return_if_fail (NM_IP4_CONFIG_GET_PRIVATE (self)->ifindex > 0); _add_address (self, NULL, new); @@ -2996,29 +2999,19 @@ nm_ip4_config_hash (const NMIP4Config *self, GChecksum *sum, gboolean dns_only) gboolean nm_ip4_config_equal (const NMIP4Config *a, const NMIP4Config *b) { - GChecksum *a_checksum = g_checksum_new (G_CHECKSUM_SHA1); - GChecksum *b_checksum = g_checksum_new (G_CHECKSUM_SHA1); - guchar a_data[20], b_data[20]; - gsize a_len = sizeof (a_data); - gsize b_len = sizeof (b_data); - gboolean equal; + nm_auto_free_checksum GChecksum *a_checksum = g_checksum_new (G_CHECKSUM_SHA1); + nm_auto_free_checksum GChecksum *b_checksum = g_checksum_new (G_CHECKSUM_SHA1); + guint8 a_data[NM_UTILS_CHECKSUM_LENGTH_SHA1]; + guint8 b_data[NM_UTILS_CHECKSUM_LENGTH_SHA1]; if (a) nm_ip4_config_hash (a, a_checksum, FALSE); if (b) nm_ip4_config_hash (b, b_checksum, FALSE); - g_checksum_get_digest (a_checksum, a_data, &a_len); - g_checksum_get_digest (b_checksum, b_data, &b_len); - - nm_assert (a_len == sizeof (a_data)); - nm_assert (b_len == sizeof (b_data)); - equal = !memcmp (a_data, b_data, a_len); - - g_checksum_free (a_checksum); - g_checksum_free (b_checksum); - - return equal; + nm_utils_checksum_get_digest (a_checksum, a_data); + nm_utils_checksum_get_digest (b_checksum, b_data); + return !memcmp (a_data, b_data, sizeof (a_data)); } /*****************************************************************************/ @@ -3034,6 +3027,7 @@ get_property (GObject *object, guint prop_id, const NMPlatformIP4Route *route; GVariantBuilder builder_data, builder_legacy; guint i; + char addr_str[NM_UTILS_INET_ADDRSTRLEN]; switch (prop_id) { case PROP_IFINDEX: @@ -3071,14 +3065,14 @@ get_property (GObject *object, guint prop_id, g_variant_builder_init (&addr_builder, G_VARIANT_TYPE ("a{sv}")); g_variant_builder_add (&addr_builder, "{sv}", "address", - g_variant_new_string (nm_utils_inet4_ntop (address->address, NULL))); + g_variant_new_string (nm_utils_inet4_ntop (address->address, addr_str))); g_variant_builder_add (&addr_builder, "{sv}", "prefix", g_variant_new_uint32 (address->plen)); if (address->peer_address != address->address) { g_variant_builder_add (&addr_builder, "{sv}", "peer", - g_variant_new_string (nm_utils_inet4_ntop (address->peer_address, NULL))); + g_variant_new_string (nm_utils_inet4_ntop (address->peer_address, addr_str))); } if (*address->label) { @@ -3133,14 +3127,14 @@ out_addresses_cached: g_variant_builder_init (&route_builder, G_VARIANT_TYPE ("a{sv}")); g_variant_builder_add (&route_builder, "{sv}", "dest", - g_variant_new_string (nm_utils_inet4_ntop (route->network, NULL))); + g_variant_new_string (nm_utils_inet4_ntop (route->network, addr_str))); g_variant_builder_add (&route_builder, "{sv}", "prefix", g_variant_new_uint32 (route->plen)); if (route->gateway) { g_variant_builder_add (&route_builder, "{sv}", "next-hop", - g_variant_new_string (nm_utils_inet4_ntop (route->gateway, NULL))); + g_variant_new_string (nm_utils_inet4_ntop (route->gateway, addr_str))); } g_variant_builder_add (&route_builder, "{sv}", "metric", @@ -3182,9 +3176,8 @@ out_routes_cached: break; case PROP_GATEWAY: if (priv->best_default_route) { - g_value_set_string (value, - nm_utils_inet4_ntop (NMP_OBJECT_CAST_IP4_ROUTE (priv->best_default_route)->gateway, - NULL)); + g_value_take_string (value, + nm_utils_inet4_ntop_dup (NMP_OBJECT_CAST_IP4_ROUTE (priv->best_default_route)->gateway)); } else g_value_set_string (value, NULL); break; @@ -3193,7 +3186,6 @@ out_routes_cached: for (i = 0; i < priv->nameservers->len; i++) { GVariantBuilder nested_builder; - char addr_str[NM_UTILS_INET_ADDRSTRLEN]; nm_utils_inet4_ntop (g_array_index (priv->nameservers, in_addr_t, i), addr_str); @@ -3230,8 +3222,6 @@ out_routes_cached: case PROP_WINS_SERVER_DATA: g_variant_builder_init (&builder_data, G_VARIANT_TYPE ("as")); for (i = 0; i < priv->wins->len; i++) { - char addr_str[NM_UTILS_INET_ADDRSTRLEN]; - g_variant_builder_add (&builder_data, "s", nm_utils_inet4_ntop (g_array_index (priv->wins, in_addr_t, i), diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c index a8c4aecd..6d54809e 100644 --- a/src/nm-ip6-config.c +++ b/src/nm-ip6-config.c @@ -485,6 +485,8 @@ nm_ip6_config_add_dependent_routes (NMIP6Config *self, if (NM_FLAGS_HAS (my_addr->n_ifa_flags, IFA_F_NOPREFIXROUTE)) continue; + if (my_addr->plen == 0) + continue; has_peer = !IN6_IS_ADDR_UNSPECIFIED (&my_addr->peer_address); @@ -713,6 +715,7 @@ nm_ip6_config_create_setting (const NMIP6Config *self) NMSettingIPConfig *s_ip6; guint nnameservers, nsearches, noptions; const char *method = NULL; + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; int i; NMDedupMultiIter ipconf_iter; const NMPlatformIP6Address *address; @@ -765,7 +768,7 @@ nm_ip6_config_create_setting (const NMIP6Config *self) g_object_set (s_ip6, NM_SETTING_IP_CONFIG_GATEWAY, nm_utils_inet6_ntop (&NMP_OBJECT_CAST_IP6_ROUTE (priv->best_default_route)->gateway, - NULL), + sbuf), NULL); } @@ -804,7 +807,7 @@ nm_ip6_config_create_setting (const NMIP6Config *self) for (i = 0; i < nnameservers; i++) { const struct in6_addr *nameserver = nm_ip6_config_get_nameserver (self, i); - nm_setting_ip_config_add_dns (s_ip6, nm_utils_inet6_ntop (nameserver, NULL)); + nm_setting_ip_config_add_dns (s_ip6, nm_utils_inet6_ntop (nameserver, sbuf)); } for (i = 0; i < nsearches; i++) { const char *search = nm_ip6_config_get_search (self, i); @@ -1592,7 +1595,7 @@ nm_ip6_config_add_address (NMIP6Config *self, const NMPlatformIP6Address *new) { g_return_if_fail (self); g_return_if_fail (new); - g_return_if_fail (new->plen > 0 && new->plen <= 128); + g_return_if_fail (new->plen <= 128); g_return_if_fail (NM_IP6_CONFIG_GET_PRIVATE (self)->ifindex > 0); _add_address (self, NULL, new); @@ -2426,29 +2429,19 @@ nm_ip6_config_hash (const NMIP6Config *self, GChecksum *sum, gboolean dns_only) gboolean nm_ip6_config_equal (const NMIP6Config *a, const NMIP6Config *b) { - GChecksum *a_checksum = g_checksum_new (G_CHECKSUM_SHA1); - GChecksum *b_checksum = g_checksum_new (G_CHECKSUM_SHA1); - guchar a_data[20], b_data[20]; - gsize a_len = sizeof (a_data); - gsize b_len = sizeof (b_data); - gboolean equal; + nm_auto_free_checksum GChecksum *a_checksum = g_checksum_new (G_CHECKSUM_SHA1); + nm_auto_free_checksum GChecksum *b_checksum = g_checksum_new (G_CHECKSUM_SHA1); + guint8 a_data[NM_UTILS_CHECKSUM_LENGTH_SHA1]; + guint8 b_data[NM_UTILS_CHECKSUM_LENGTH_SHA1]; if (a) nm_ip6_config_hash (a, a_checksum, FALSE); if (b) nm_ip6_config_hash (b, b_checksum, FALSE); - g_checksum_get_digest (a_checksum, a_data, &a_len); - g_checksum_get_digest (b_checksum, b_data, &b_len); - - nm_assert (a_len == sizeof (a_data)); - nm_assert (b_len == sizeof (b_data)); - equal = !memcmp (a_data, b_data, a_len); - - g_checksum_free (a_checksum); - g_checksum_free (b_checksum); - - return equal; + nm_utils_checksum_get_digest (a_checksum, a_data); + nm_utils_checksum_get_digest (b_checksum, b_data); + return !memcmp (a_data, b_data, sizeof (a_data)); } /*****************************************************************************/ @@ -2483,6 +2476,7 @@ get_property (GObject *object, guint prop_id, NMDedupMultiIter ipconf_iter; const NMPlatformIP6Route *route; GVariantBuilder builder_data, builder_legacy; + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; switch (prop_id) { case PROP_IFINDEX: @@ -2519,7 +2513,7 @@ get_property (GObject *object, guint prop_id, g_variant_builder_init (&addr_builder, G_VARIANT_TYPE ("a{sv}")); g_variant_builder_add (&addr_builder, "{sv}", "address", - g_variant_new_string (nm_utils_inet6_ntop (&address->address, NULL))); + g_variant_new_string (nm_utils_inet6_ntop (&address->address, sbuf))); g_variant_builder_add (&addr_builder, "{sv}", "prefix", g_variant_new_uint32 (address->plen)); @@ -2527,7 +2521,7 @@ get_property (GObject *object, guint prop_id, && !IN6_ARE_ADDR_EQUAL (&address->peer_address, &address->address)) { g_variant_builder_add (&addr_builder, "{sv}", "peer", - g_variant_new_string (nm_utils_inet6_ntop (&address->peer_address, NULL))); + g_variant_new_string (nm_utils_inet6_ntop (&address->peer_address, sbuf))); } g_variant_builder_add (&builder_data, "a{sv}", &addr_builder); @@ -2572,14 +2566,14 @@ out_addresses_cached: g_variant_builder_init (&route_builder, G_VARIANT_TYPE ("a{sv}")); g_variant_builder_add (&route_builder, "{sv}", "dest", - g_variant_new_string (nm_utils_inet6_ntop (&route->network, NULL))); + g_variant_new_string (nm_utils_inet6_ntop (&route->network, sbuf))); g_variant_builder_add (&route_builder, "{sv}", "prefix", g_variant_new_uint32 (route->plen)); if (!IN6_IS_ADDR_UNSPECIFIED (&route->gateway)) { g_variant_builder_add (&route_builder, "{sv}", "next-hop", - g_variant_new_string (nm_utils_inet6_ntop (&route->gateway, NULL))); + g_variant_new_string (nm_utils_inet6_ntop (&route->gateway, sbuf))); } g_variant_builder_add (&route_builder, "{sv}", @@ -2617,9 +2611,8 @@ out_routes_cached: break; case PROP_GATEWAY: if (priv->best_default_route) { - g_value_set_string (value, - nm_utils_inet6_ntop (&NMP_OBJECT_CAST_IP6_ROUTE (priv->best_default_route)->gateway, - NULL)); + g_value_take_string (value, + nm_utils_inet6_ntop_dup (&NMP_OBJECT_CAST_IP6_ROUTE (priv->best_default_route)->gateway)); } else g_value_set_string (value, NULL); break; diff --git a/src/nm-keep-alive.c b/src/nm-keep-alive.c new file mode 100644 index 00000000..e601483b --- /dev/null +++ b/src/nm-keep-alive.c @@ -0,0 +1,528 @@ +/* + * NetworkManager -- Inhibition management + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2018 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-keep-alive.h" + +#include <string.h> + +#include "settings/nm-settings-connection.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE (NMKeepAlive, + PROP_ALIVE, +); + +typedef struct { + GObject *owner; + + NMSettingsConnection *connection; + GDBusConnection *dbus_connection; + char *dbus_client; + + GCancellable *dbus_client_confirm_cancellable; + guint subscription_id; + + bool armed:1; + bool disarmed:1; + + bool alive:1; + bool dbus_client_confirmed:1; + bool dbus_client_watching:1; + bool connection_was_visible:1; +} NMKeepAlivePrivate; + +struct _NMKeepAlive { + GObject parent; + NMKeepAlivePrivate _priv; +}; + +struct _NMKeepAliveClass { + GObjectClass parent; +}; + +G_DEFINE_TYPE (NMKeepAlive, nm_keep_alive, G_TYPE_OBJECT) + +#define NM_KEEP_ALIVE_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMKeepAlive, NM_IS_KEEP_ALIVE) + +/*****************************************************************************/ + +#define _NMLOG_DOMAIN LOGD_CORE +#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "keep-alive", __VA_ARGS__) + +/*****************************************************************************/ + +static gboolean _is_alive_dbus_client (NMKeepAlive *self); +static void cleanup_dbus_watch (NMKeepAlive *self); + +/*****************************************************************************/ + +static gboolean +_is_alive (NMKeepAlive *self) +{ + NMKeepAlivePrivate *priv = NM_KEEP_ALIVE_GET_PRIVATE (self); + + nm_assert (!priv->disarmed); + + if (!priv->armed) { + /* before arming, the instance is always alive. */ + return TRUE; + } + + if (priv->dbus_client_watching) { + if (_is_alive_dbus_client (self)) { + /* no matter what, the keep-alive is alive, because there is a D-Bus client + * still around keeping it alive. */ + return TRUE; + } + /* the D-Bus client is gone. The only other binding (below) for the connection's + * visibility cannot keep the instance alive. + * + * As such, a D-Bus client watch is authorative and overrules other conditions (that + * we have so far). */ + return FALSE; + } + + if ( priv->connection + && priv->connection_was_visible + && !NM_FLAGS_HAS (nm_settings_connection_get_flags (priv->connection), + NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE)) { + /* note that we only declare the keep-alive as dead due to invisible + * connection, if + * (1) we monitor a connection, obviously + * (2) the connection was visible earlier and is no longer. It was + * was invisible all the time, it does not suffice. + */ + return FALSE; + } + + /* by default, the instance is alive. */ + return TRUE; +} + +static void +_notify_alive (NMKeepAlive *self) +{ + NMKeepAlivePrivate *priv = NM_KEEP_ALIVE_GET_PRIVATE (self); + + if (priv->disarmed) { + /* once disarmed, the alive state is frozen. */ + return; + } + + if (priv->alive == _is_alive (self)) + return; + priv->alive = !priv->alive; + _LOGD ("instance is now %s", priv->alive ? "alive" : "dead"); + _notify (self, PROP_ALIVE); +} + +gboolean +nm_keep_alive_is_alive (NMKeepAlive *self) +{ + return NM_KEEP_ALIVE_GET_PRIVATE (self)->alive; +} + +/*****************************************************************************/ + +static void +connection_flags_changed (NMSettingsConnection *connection, + NMKeepAlive *self) +{ + NMKeepAlivePrivate *priv = NM_KEEP_ALIVE_GET_PRIVATE (self); + + if ( !priv->connection_was_visible + && NM_FLAGS_HAS (nm_settings_connection_get_flags (priv->connection), + NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE)) { + /* the profile was never visible but now it becomes visible. + * Remember that. + * + * Before this happens (that is, if the device was invisible all along), + * the keep alive instance is considered alive (w.r.t. watching the connection). + * + * The reason is to allow a user to manually activate an invisible profile and keep + * it alive. At least, as long until the user logs out the first time (which is the + * first time, the profiles changes from visible to invisible). + * + * Yes, that is odd. How to improve? */ + priv->connection_was_visible = TRUE; + } + _notify_alive (self); +} + +static void +_set_settings_connection_watch_visible (NMKeepAlive *self, + NMSettingsConnection *connection, + gboolean emit_signal) +{ + NMKeepAlivePrivate *priv = NM_KEEP_ALIVE_GET_PRIVATE (self); + gs_unref_object NMSettingsConnection *old_connection = NULL; + + if (priv->connection == connection) + return; + + if (priv->connection) { + g_signal_handlers_disconnect_by_func (priv->connection, + G_CALLBACK (connection_flags_changed), + self); + old_connection = g_steal_pointer (&priv->connection); + } + + if ( connection + && !priv->disarmed) { + priv->connection = g_object_ref (connection); + priv->connection_was_visible = NM_FLAGS_HAS (nm_settings_connection_get_flags (priv->connection), + NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE); + g_signal_connect (priv->connection, + NM_SETTINGS_CONNECTION_FLAGS_CHANGED, + G_CALLBACK (connection_flags_changed), + self); + } + + if (emit_signal) + _notify_alive (self); +} + +void +nm_keep_alive_set_settings_connection_watch_visible (NMKeepAlive *self, + NMSettingsConnection *connection) +{ + _set_settings_connection_watch_visible (self, connection, TRUE); +} + +/*****************************************************************************/ + +static void +get_name_owner_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + NMKeepAlive *self = user_data; + NMKeepAlivePrivate *priv; + gs_free_error GError *error = NULL; + gs_unref_variant GVariant *result = NULL; + const char *name_owner; + + result = g_dbus_connection_call_finish ((GDBusConnection *) source_object, + res, + &error); + if ( !result + && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + if (result) { + g_variant_get (result, "(&s)", &name_owner); + + priv = NM_KEEP_ALIVE_GET_PRIVATE (self); + + if (nm_streq (name_owner, priv->dbus_client)) { + /* all good, the name is confirmed. */ + return; + } + } + + _LOGD ("DBus client for keep alive is not on the bus"); + cleanup_dbus_watch (self); + _notify_alive (self); +} + +static gboolean +_is_alive_dbus_client (NMKeepAlive *self) +{ + NMKeepAlivePrivate *priv = NM_KEEP_ALIVE_GET_PRIVATE (self); + + if (!priv->dbus_client) + return FALSE; + + if (!priv->dbus_client_confirmed) { + /* it's unconfirmed that the D-Bus client is really alive. + * It looks like it is, but as we are claiming that to be + * the case, issue an async GetNameOwner call to make sure. */ + priv->dbus_client_confirmed = TRUE; + priv->dbus_client_confirm_cancellable = g_cancellable_new (); + + g_dbus_connection_call (priv->dbus_connection, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "GetNameOwner", + g_variant_new ("(s)", priv->dbus_client), + G_VARIANT_TYPE ("(s)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->dbus_client_confirm_cancellable, + get_name_owner_cb, + self); + } + return TRUE; +} + +static void +cleanup_dbus_watch (NMKeepAlive *self) +{ + NMKeepAlivePrivate *priv = NM_KEEP_ALIVE_GET_PRIVATE (self); + + if (!priv->dbus_client) + return; + + _LOGD ("Cleanup DBus client watch"); + + nm_clear_g_cancellable (&priv->dbus_client_confirm_cancellable); + nm_clear_g_free (&priv->dbus_client); + if (priv->dbus_connection) { + g_dbus_connection_signal_unsubscribe (priv->dbus_connection, + priv->subscription_id); + g_clear_object (&priv->dbus_connection); + } +} + +static void +name_owner_changed_cb (GDBusConnection *connection, + const char *sender_name, + const char *object_path, + const char *interface_name, + const char *signal_name, + GVariant *parameters, + gpointer user_data) +{ + NMKeepAlive *self = NM_KEEP_ALIVE (user_data); + const char *old_owner; + const char *new_owner; + + g_variant_get (parameters, "(&s&s&s)", NULL, &old_owner, &new_owner); + + if (!nm_streq0 (new_owner, "")) + return; + + _LOGD ("DBus client for keep alive disappeared from bus"); + cleanup_dbus_watch (self); + _notify_alive (self); +} + +void +nm_keep_alive_set_dbus_client_watch (NMKeepAlive *self, + GDBusConnection *connection, + const char *client_address) +{ + NMKeepAlivePrivate *priv = NM_KEEP_ALIVE_GET_PRIVATE (self); + + if (priv->disarmed) + return; + + cleanup_dbus_watch (self); + + if (client_address) { + _LOGD ("Registering dbus client watch for keep alive"); + + priv->dbus_client = g_strdup (client_address); + priv->dbus_client_watching = TRUE; + priv->dbus_client_confirmed = FALSE; + priv->dbus_connection = g_object_ref (connection); + priv->subscription_id = g_dbus_connection_signal_subscribe (connection, + "org.freedesktop.DBus", + "org.freedesktop.DBus", + "NameOwnerChanged", + "/org/freedesktop/DBus", + priv->dbus_client, + G_DBUS_SIGNAL_FLAGS_NONE, + name_owner_changed_cb, + self, + NULL); + } else + priv->dbus_client_watching = FALSE; + + _notify_alive (self); +} + +/*****************************************************************************/ + +/** + * nm_keep_alive_arm: + * @self: the #NMKeepAlive + * + * A #NMKeepAlive instance is unarmed by default. That means, it's + * alive and stays alive until being armed. Arming means, that the conditions + * start to be actively evaluated, that the alive state may change, and + * that property changed signals are emitted. + * + * The opposite is nm_keep_alive_disarm() which freezes the alive state + * for good. Once disarmed, the instance cannot be armed again. Arming an + * instance multiple times has no effect. Arming an already disarmed instance + * also has no effect. */ +void +nm_keep_alive_arm (NMKeepAlive *self) +{ + NMKeepAlivePrivate *priv = NM_KEEP_ALIVE_GET_PRIVATE (self); + + if (!priv->armed) { + priv->armed = TRUE; + _notify_alive (self); + } +} + +/** + * nm_keep_alive_disarm: + * @self: the #NMKeepAlive instance + * + * Once the instance is disarmed, it will not change its alive state + * anymore and will not emit anymore property changed signals about + * alive state changed. + * + * As such, it will also free internal resources (since they no longer + * affect the externally visible state). + * + * Once disarmed, the instance is frozen and cannot change anymore. + */ +void +nm_keep_alive_disarm (NMKeepAlive *self) +{ + NMKeepAlivePrivate *priv = NM_KEEP_ALIVE_GET_PRIVATE (self); + + priv->disarmed = TRUE; + + /* release internal data. */ + _set_settings_connection_watch_visible (self, NULL, FALSE); + cleanup_dbus_watch (self); +} + +/*****************************************************************************/ + +static void +get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NMKeepAlive *self = NM_KEEP_ALIVE (object); + + switch (prop_id) { + case PROP_ALIVE: + g_value_set_boolean (value, nm_keep_alive_is_alive (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + +/** + * nm_keep_alive_get_owner: + * @self: the #NMKeepAlive + * + * Returns: the owner instance associated with this @self. This commonly + * is set to be the target instance, which @self guards for being alive. + * Returns a gpointer, but of course it's some GObject instance. */ +gpointer /* GObject * */ +nm_keep_alive_get_owner (NMKeepAlive *self) +{ + NMKeepAlivePrivate *priv = NM_KEEP_ALIVE_GET_PRIVATE (self); + + nm_assert (!priv->owner || G_IS_OBJECT (priv->owner)); + + return priv->owner; +} + +/** + * _nm_keep_alive_set_owner: + * @self: the #NMKeepAlive + * @owner: the owner to set or unset. + * + * Sets or unsets the owner instance. Think of the owner the target + * instance that is guarded by @self. It's the responsibility of the + * owner to set and properly unset this pointer. As the owner also + * controls the lifetime of the NMKeepAlive instance. + * + * This API is not to be called by everybody, but only the owner of + * @self. + */ +void +_nm_keep_alive_set_owner (NMKeepAlive *self, + GObject *owner) +{ + NMKeepAlivePrivate *priv = NM_KEEP_ALIVE_GET_PRIVATE (self); + + nm_assert (!owner || G_IS_OBJECT (owner)); + + /* it's bad style to reset the owner object. You are supposed to + * set it once, and clear it once. That's it. */ + nm_assert (!owner || !priv->owner); + + /* optimally, we would take a reference to @owner. But the + * owner already owns a refrence to the keep-alive, so we cannot + * just own a reference back. + * + * We could register a weak-pointer here. But instead, declare that + * owner is required to set itself as owner when creating the + * keep-alive instance, and unset itself when it lets go of the + * keep-alive instance (at latest, when the owner itself gets destroyed). + */ + priv->owner = owner; +} + +/*****************************************************************************/ + +static void +nm_keep_alive_init (NMKeepAlive *self) +{ + NMKeepAlivePrivate *priv = NM_KEEP_ALIVE_GET_PRIVATE (self); + + priv->alive = TRUE; + + nm_assert (priv->alive == _is_alive (self)); +} + +NMKeepAlive * +nm_keep_alive_new (void) +{ + return g_object_new (NM_TYPE_KEEP_ALIVE, NULL); +} + +static void +dispose (GObject *object) +{ + NMKeepAlive *self = NM_KEEP_ALIVE (object); + + nm_assert (!NM_KEEP_ALIVE_GET_PRIVATE (self)->owner); + + /* disarm also happens to free all resources. */ + nm_keep_alive_disarm (self); +} + +static void +nm_keep_alive_class_init (NMKeepAliveClass *keep_alive_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (keep_alive_class); + + object_class->get_property = get_property; + object_class->dispose = dispose; + + obj_properties[PROP_ALIVE] = + g_param_spec_string (NM_KEEP_ALIVE_ALIVE, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); +} diff --git a/src/nm-keep-alive.h b/src/nm-keep-alive.h new file mode 100644 index 00000000..160b2adb --- /dev/null +++ b/src/nm-keep-alive.h @@ -0,0 +1,59 @@ +/* + * NetworkManager -- Inhibition management + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2018 Red Hat, Inc. + */ + +#ifndef __NETWORKMANAGER_KEEP_ALIVE_H__ +#define __NETWORKMANAGER_KEEP_ALIVE_H__ + +#define NM_TYPE_KEEP_ALIVE (nm_keep_alive_get_type ()) +#define NM_KEEP_ALIVE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), NM_TYPE_KEEP_ALIVE, NMKeepAlive)) +#define NM_KEEP_ALIVE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), NM_TYPE_KEEP_ALIVE, NMKeepAliveClass)) +#define NM_KEEP_ALIVE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), NM_TYPE_KEEP_ALIVE, NMKeepAliveClass)) +#define NM_IS_KEEP_ALIVE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), NM_TYPE_KEEP_ALIVE)) +#define NM_IS_KEEP_ALIVE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), NM_TYPE_KEEP_ALIVE)) + + +#define NM_KEEP_ALIVE_ALIVE "alive" + +typedef struct _NMKeepAliveClass NMKeepAliveClass; + +GType nm_keep_alive_get_type (void) G_GNUC_CONST; + +NMKeepAlive* nm_keep_alive_new (void); + +gboolean nm_keep_alive_is_alive (NMKeepAlive *self); + +void nm_keep_alive_arm (NMKeepAlive *self); +void nm_keep_alive_disarm (NMKeepAlive *self); + +void nm_keep_alive_set_settings_connection_watch_visible (NMKeepAlive *self, + NMSettingsConnection *connection); + +void nm_keep_alive_set_dbus_client_watch (NMKeepAlive *self, + GDBusConnection *connection, + const char *client_address); + +gpointer /* GObject * */ nm_keep_alive_get_owner (NMKeepAlive *self); + +/* _nm_keep_alive_set_owner() is reserved for the owner to set/unset itself. */ +void _nm_keep_alive_set_owner (NMKeepAlive *self, + GObject *owner); + +#endif /* __NETWORKMANAGER_KEEP_ALIVE_H__ */ diff --git a/src/nm-logging.c b/src/nm-logging.c index 9e7d3892..6f912f58 100644 --- a/src/nm-logging.c +++ b/src/nm-logging.c @@ -21,6 +21,8 @@ #include "nm-default.h" +#include "nm-logging.h" + #include <dlfcn.h> #include <syslog.h> #include <stdio.h> @@ -37,19 +39,8 @@ #include <systemd/sd-journal.h> #endif +#include "nm-utils/nm-time-utils.h" #include "nm-errors.h" -#include "nm-core-utils.h" - -/* often we have some static string where we need to know the maximum length. - * _MAX_LEN() returns @max but adds a debugging assertion that @str is indeed - * shorter then @mac. */ -#define _MAX_LEN(max, str) \ - ({ \ - const char *const _str = (str); \ - \ - nm_assert (_str && strlen (str) < (max)); \ - (max); \ - }) void (*_nm_logging_clear_platform_logging_cache) (void); @@ -548,6 +539,13 @@ nm_logging_get_level (NMLogDomain domain) return sl; } +gboolean +_nm_log_enabled (NMLogLevel level, + NMLogDomain domain) +{ + return nm_logging_enabled (level, domain); +} + #if SYSTEMD_JOURNAL static void _iovec_set (struct iovec *iov, const void *str, gsize len) @@ -583,14 +581,25 @@ _iovec_set_format (struct iovec *iov, gpointer *iov_free, const char *format, .. char *const _buf = g_alloca (_size); \ int _len; \ \ + G_STATIC_ASSERT_EXPR ((reserve_extra) + (NM_STRLEN (format) + 3) <= 96); \ + \ _len = g_snprintf (_buf, _size, ""format"", ##__VA_ARGS__);\ \ nm_assert (_len >= 0); \ - nm_assert (_len <= _size); \ + nm_assert (_len < _size); \ nm_assert (_len == strlen (_buf)); \ \ _iovec_set ((iov), _buf, _len); \ } G_STMT_END + +#define _iovec_set_format_str_a(iov, max_str_len, format, str_arg) \ + G_STMT_START { \ + const char *_str_arg = (str_arg); \ + \ + nm_assert (_str_arg && strlen (_str_arg) < (max_str_len)); \ + _iovec_set_format_a ((iov), (max_str_len), format, str_arg); \ + } G_STMT_END + #endif void @@ -690,7 +699,7 @@ _nm_log_impl (const char *file, if (NM_FLAGS_ANY (dom, diter->num)) { if (i_domain > 0) { /* SYSLOG_FACILITY is specified multiple times for each domain that is actually enabled. */ - _iovec_set_format_a (iov++, _MAX_LEN (30, diter->name), "SYSLOG_FACILITY=%s", diter->name); + _iovec_set_format_str_a (iov++, 30, "SYSLOG_FACILITY=%s", diter->name); i_domain--; } dom &= ~diter->num; @@ -701,9 +710,9 @@ _nm_log_impl (const char *file, if (s_domain_all) _iovec_set (iov++, s_domain_all->str, s_domain_all->len); else - _iovec_set_format_a (iov++, _MAX_LEN (30, s_domain_1), "NM_LOG_DOMAINS=%s", s_domain_1); + _iovec_set_format_str_a (iov++, 30, "NM_LOG_DOMAINS=%s", s_domain_1); } - _iovec_set_format_a (iov++, _MAX_LEN (15, global.level_desc[level].name), "NM_LOG_LEVEL=%s", global.level_desc[level].name); + _iovec_set_format_str_a (iov++, 15, "NM_LOG_LEVEL=%s", global.level_desc[level].name); if (func) _iovec_set_format (iov++, iov_free++, "CODE_FUNC=%s", func); _iovec_set_format (iov++, iov_free++, "CODE_FILE=%s", file ?: ""); @@ -744,6 +753,27 @@ _nm_log_impl (const char *file, /*****************************************************************************/ +void +_nm_utils_monotonic_timestamp_initialized (const struct timespec *tp, + gint64 offset_sec, + gboolean is_boottime) +{ + if (nm_logging_enabled (LOGL_DEBUG, LOGD_CORE)) { + time_t now = time (NULL); + struct tm tm; + char s[255]; + + strftime (s, sizeof (s), "%Y-%m-%d %H:%M:%S", localtime_r (&now, &tm)); + nm_log_dbg (LOGD_CORE, "monotonic timestamp started counting 1.%09ld seconds ago with " + "an offset of %lld.0 seconds to %s (local time is %s)", + tp->tv_nsec, + (long long) -offset_sec, + is_boottime ? "CLOCK_BOOTTIME" : "CLOCK_MONOTONIC", s); + } +} + +/*****************************************************************************/ + static void nm_log_handler (const char *log_domain, GLogLevelFlags level, diff --git a/src/nm-logging.h b/src/nm-logging.h index 0737bbd6..21263c7b 100644 --- a/src/nm-logging.h +++ b/src/nm-logging.h @@ -22,71 +22,16 @@ #ifndef __NETWORKMANAGER_LOGGING_H__ #define __NETWORKMANAGER_LOGGING_H__ -#include "nm-core-types.h" - #ifdef __NM_TEST_UTILS_H__ #error nm-test-utils.h must be included as last header #endif +#include "nm-utils/nm-logging-fwd.h" + #define NM_LOG_CONFIG_BACKEND_DEBUG "debug" #define NM_LOG_CONFIG_BACKEND_SYSLOG "syslog" #define NM_LOG_CONFIG_BACKEND_JOURNAL "journal" -/* Log domains */ -typedef enum { /*< skip >*/ - LOGD_NONE = 0LL, - LOGD_PLATFORM = (1LL << 0), /* Platform services */ - LOGD_RFKILL = (1LL << 1), - LOGD_ETHER = (1LL << 2), - LOGD_WIFI = (1LL << 3), - LOGD_BT = (1LL << 4), - LOGD_MB = (1LL << 5), /* mobile broadband */ - LOGD_DHCP4 = (1LL << 6), - LOGD_DHCP6 = (1LL << 7), - LOGD_PPP = (1LL << 8), - LOGD_WIFI_SCAN = (1LL << 9), - LOGD_IP4 = (1LL << 10), - LOGD_IP6 = (1LL << 11), - LOGD_AUTOIP4 = (1LL << 12), - LOGD_DNS = (1LL << 13), - LOGD_VPN = (1LL << 14), - LOGD_SHARING = (1LL << 15), /* Connection sharing/dnsmasq */ - LOGD_SUPPLICANT = (1LL << 16), /* WiFi and 802.1x */ - LOGD_AGENTS = (1LL << 17), /* Secret agents */ - LOGD_SETTINGS = (1LL << 18), /* Settings */ - LOGD_SUSPEND = (1LL << 19), /* Suspend/Resume */ - LOGD_CORE = (1LL << 20), /* Core daemon and policy stuff */ - LOGD_DEVICE = (1LL << 21), /* Device state and activation */ - LOGD_OLPC = (1LL << 22), - LOGD_INFINIBAND = (1LL << 23), - LOGD_FIREWALL = (1LL << 24), - LOGD_ADSL = (1LL << 25), - LOGD_BOND = (1LL << 26), - LOGD_VLAN = (1LL << 27), - LOGD_BRIDGE = (1LL << 28), - LOGD_DBUS_PROPS = (1LL << 29), - LOGD_TEAM = (1LL << 30), - LOGD_CONCHECK = (1LL << 31), - LOGD_DCB = (1LL << 32), /* Data Center Bridging */ - LOGD_DISPATCH = (1LL << 33), - LOGD_AUDIT = (1LL << 34), - LOGD_SYSTEMD = (1LL << 35), - LOGD_VPN_PLUGIN = (1LL << 36), - LOGD_PROXY = (1LL << 37), - - __LOGD_MAX, - LOGD_ALL = (((__LOGD_MAX - 1LL) << 1) - 1LL), - LOGD_DEFAULT = LOGD_ALL & ~( - LOGD_DBUS_PROPS | - LOGD_WIFI_SCAN | - LOGD_VPN_PLUGIN | - 0), - - /* aliases: */ - LOGD_DHCP = LOGD_DHCP4 | LOGD_DHCP6, - LOGD_IP = LOGD_IP4 | LOGD_IP6, -} NMLogDomain; - static inline NMLogDomain LOGD_IP_from_af (int addr_family) { @@ -97,22 +42,6 @@ LOGD_IP_from_af (int addr_family) g_return_val_if_reached (LOGD_NONE); } -/* Log levels */ -typedef enum { /*< skip >*/ - LOGL_TRACE, - LOGL_DEBUG, - LOGL_INFO, - LOGL_WARN, - LOGL_ERR, - - _LOGL_N_REAL, /* the number of actual logging levels */ - - _LOGL_OFF = _LOGL_N_REAL, /* special logging level that is always disabled. */ - _LOGL_KEEP, /* special logging level to indicate that the logging level should not be changed. */ - - _LOGL_N, /* the number of logging levels including "OFF" */ -} NMLogLevel; - #define nm_log_err(domain, ...) nm_log (LOGL_ERR, (domain), NULL, NULL, __VA_ARGS__) #define nm_log_warn(domain, ...) nm_log (LOGL_WARN, (domain), NULL, NULL, __VA_ARGS__) #define nm_log_info(domain, ...) nm_log (LOGL_INFO, (domain), NULL, NULL, __VA_ARGS__) @@ -206,17 +135,6 @@ _nm_log_ptr_is_debug (NMLogLevel level) prefix, \ __VA_ARGS__) -void _nm_log_impl (const char *file, - guint line, - const char *func, - NMLogLevel level, - NMLogDomain domain, - int error, - const char *ifname, - const char *con_uuid, - const char *fmt, - ...) _nm_printf (9, 10); - const char *nm_logging_level_to_string (void); const char *nm_logging_domains_to_string (void); diff --git a/src/nm-manager.c b/src/nm-manager.c index 7598995d..127d47a3 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -39,6 +39,7 @@ #include "platform/nm-platform.h" #include "platform/nmp-object.h" #include "nm-hostname-manager.h" +#include "nm-keep-alive.h" #include "nm-rfkill-manager.h" #include "dhcp/nm-dhcp-manager.h" #include "settings/nm-settings.h" @@ -79,6 +80,7 @@ typedef enum { ASYNC_OP_TYPE_AC_AUTH_ACTIVATE_INTERNAL, ASYNC_OP_TYPE_AC_AUTH_ACTIVATE_USER, ASYNC_OP_TYPE_AC_AUTH_ADD_AND_ACTIVATE, + ASYNC_OP_TYPE_AC_AUTH_ADD_AND_ACTIVATE2, } AsyncOpType; typedef struct { @@ -95,6 +97,7 @@ typedef struct { struct { GDBusMethodInvocation *invocation; NMConnection *connection; + NMSettingsConnectionPersistMode persist; } add_and_activate; }; } ac_auth; @@ -318,6 +321,7 @@ static NMActiveConnection *_new_active_connection (NMManager *self, NMAuthSubject *subject, NMActivationType activation_type, NMActivationReason activation_reason, + NMActivationStateFlags initial_state_flags, GError **error); static void policy_activating_ac_changed (GObject *object, GParamSpec *pspec, gpointer user_data); @@ -367,9 +371,11 @@ static void _internal_activation_auth_done (NMManager *self, gboolean success, const char *error_desc); static void _add_and_activate_auth_done (NMManager *self, + AsyncOpType async_op_type, NMActiveConnection *active, NMConnection *connection, GDBusMethodInvocation *invocation, + NMSettingsConnectionPersistMode persist, gboolean success, const char *error_desc); static void _activation_auth_done (NMManager *self, @@ -395,7 +401,7 @@ _connection_is_vpn (NMConnection *connection) /* we have an incomplete (invalid) connection at hand. That can only * happen during AddAndActivate. Determine whether it's VPN type based - * on the existance of a [vpn] section. */ + * on the existence of a [vpn] section. */ return !!nm_connection_get_setting_vpn (connection); } @@ -482,18 +488,24 @@ _async_op_data_new_ac_auth_activate_user (NMManager *self, static AsyncOpData * _async_op_data_new_ac_auth_add_and_activate (NMManager *self, + AsyncOpType async_op_type, NMActiveConnection *active_take, GDBusMethodInvocation *invocation_take, - NMConnection *connection_take) + NMConnection *connection_take, + NMSettingsConnectionPersistMode persist) { AsyncOpData *async_op_data; + nm_assert (NM_IN_SET (async_op_type, ASYNC_OP_TYPE_AC_AUTH_ADD_AND_ACTIVATE, + ASYNC_OP_TYPE_AC_AUTH_ADD_AND_ACTIVATE2)); + async_op_data = g_slice_new0 (AsyncOpData); - async_op_data->async_op_type = ASYNC_OP_TYPE_AC_AUTH_ADD_AND_ACTIVATE; + async_op_data->async_op_type = async_op_type; async_op_data->self = g_object_ref (self); async_op_data->ac_auth.active = active_take; async_op_data->ac_auth.add_and_activate.invocation = invocation_take; async_op_data->ac_auth.add_and_activate.connection = connection_take; + async_op_data->ac_auth.add_and_activate.persist = persist; c_list_link_tail (&NM_MANAGER_GET_PRIVATE (self)->async_op_lst_head, &async_op_data->async_op_lst); return async_op_data; } @@ -529,10 +541,13 @@ _async_op_complete_ac_auth_cb (NMActiveConnection *active, error_desc); break; case ASYNC_OP_TYPE_AC_AUTH_ADD_AND_ACTIVATE: + case ASYNC_OP_TYPE_AC_AUTH_ADD_AND_ACTIVATE2: _add_and_activate_auth_done (async_op_data->self, + async_op_data->async_op_type, async_op_data->ac_auth.active, async_op_data->ac_auth.add_and_activate.connection, async_op_data->ac_auth.add_and_activate.invocation, + async_op_data->ac_auth.add_and_activate.persist, success, error_desc); g_object_unref (async_op_data->ac_auth.add_and_activate.connection); @@ -1176,7 +1191,7 @@ _reload_auth_cb (NMAuthChain *chain, goto out; } - nm_config_reload (priv->config, reload_type); + nm_config_reload (priv->config, reload_type, TRUE); g_dbus_method_invocation_return_value (context, NULL); out: @@ -1440,6 +1455,14 @@ nm_manager_update_metered (NMManager *self) } } +NMMetered +nm_manager_get_metered (NMManager *self) +{ + g_return_val_if_fail (NM_IS_MANAGER (self), NM_METERED_UNKNOWN); + + return NM_MANAGER_GET_PRIVATE (self)->metered; +} + static void nm_manager_update_state (NMManager *self) { @@ -1518,6 +1541,7 @@ check_if_startup_complete (NMManager *self) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); NMDevice *device; + const char *reason; if (!priv->startup) return; @@ -1525,15 +1549,19 @@ check_if_startup_complete (NMManager *self) if (!priv->devices_inited) return; - if (!nm_settings_get_startup_complete (priv->settings)) { - _LOGD (LOGD_CORE, "check_if_startup_complete returns FALSE because of NMSettings"); + reason = nm_settings_get_startup_complete_blocked_reason (priv->settings); + if (reason) { + _LOGD (LOGD_CORE, "startup complete is waiting for connection (%s)", + reason); return; } c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { - if (nm_device_has_pending_action (device)) { - _LOGD (LOGD_CORE, "check_if_startup_complete returns FALSE because of %s", - nm_device_get_iface (device)); + reason = nm_device_has_pending_action_reason (device); + if (reason) { + _LOGD (LOGD_CORE, "startup complete is waiting for device '%s' (%s)", + nm_device_get_iface (device), + reason); return; } } @@ -1596,7 +1624,12 @@ again: static gboolean device_is_wake_on_lan (NMPlatform *platform, NMDevice *device) { - return nm_platform_link_get_wake_on_lan (platform, nm_device_get_ip_ifindex (device)); + int ifindex; + + ifindex = nm_device_get_ip_ifindex (device); + if (ifindex <= 0) + return FALSE; + return nm_platform_link_get_wake_on_lan (platform, ifindex); } static void @@ -1728,7 +1761,7 @@ find_parent_device_for_connection (NMManager *self, NMConnection *connection, NM if (!parent_connection) return NULL; - /* Check if the parent connection is currently activated or is comaptible + /* Check if the parent connection is currently activated or is compatible * with some known device. */ c_list_for_each_entry (candidate, &priv->devices_lst_head, devices_lst) { @@ -2674,6 +2707,18 @@ recheck_assume_connection (NMManager *self, GError *error = NULL; subject = nm_auth_subject_new_internal (); + + /* Note: the lifetime of the activation connection is always bound to the profiles visiblity + * via NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY. + * + * This only makes a difference, if the profile actually has "connection.permissions" + * set to limit visibility (which is not the case for externally managed, generated profiles). + * + * If we assume a previously active connection whose lifetime was unbound, we now bind it + * after restart. That is not correct, and can mean that the profile becomes subject to + * deactivation after restart (if the user logs out). + * + * This should be improved, but it's unclear how. */ active = _new_active_connection (self, FALSE, sett_conn, @@ -2684,6 +2729,7 @@ recheck_assume_connection (NMManager *self, subject, generated ? NM_ACTIVATION_TYPE_EXTERNAL : NM_ACTIVATION_TYPE_ASSUME, generated ? NM_ACTIVATION_REASON_EXTERNAL : NM_ACTIVATION_REASON_ASSUME, + NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY, &error); if (!active) { @@ -2805,40 +2851,91 @@ device_realized (NMDevice *device, _emit_device_added_removed (self, device, nm_device_is_real (device)); } -static void -device_connectivity_changed (NMDevice *device, - NMManager *self) +static NMConnectivityState +_get_best_connectivity (NMManager *self, int addr_family) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - NMConnectivityState best_state = NM_CONNECTIVITY_UNKNOWN; - NMConnectivityState state; + NMConnectivityState best_state; NMDevice *dev; + gint64 best_metric; + + if (addr_family == AF_UNSPEC) { + best_state = _get_best_connectivity (self, AF_INET); + if (nm_connectivity_state_cmp (best_state, NM_CONNECTIVITY_FULL) >= 0) { + /* already FULL IPv4 connectivity. No need to check IPv6, it doesn't get + * better. */ + return best_state; + } + return NM_MAX_WITH_CMP (nm_connectivity_state_cmp, + best_state, + _get_best_connectivity (self, AF_INET6)); + } - best_state = nm_device_get_connectivity_state (device); - if (best_state < NM_CONNECTIVITY_FULL) { - c_list_for_each_entry (dev, &priv->devices_lst_head, devices_lst) { - state = nm_device_get_connectivity_state (dev); - if (state <= best_state) - continue; + nm_assert_addr_family (addr_family); + + best_state = NM_CONNECTIVITY_UNKNOWN; + best_metric = G_MAXINT64; + c_list_for_each_entry (dev, &priv->devices_lst_head, devices_lst) { + const NMPObject *r; + NMConnectivityState state; + gint64 metric; + + r = nm_device_get_best_default_route (dev, addr_family); + if (r) { + metric = nm_utils_ip_route_metric_normalize (addr_family, + NMP_OBJECT_CAST_IP_ROUTE (r)->metric); + } else { + /* if all devices have no default-route, we still include the best + * of all connectivity state of all the devices. */ + metric = G_MAXINT64; + } + + if (metric > best_metric) { + /* we already have a default route with better metric. The connectivity state + * of this device is irreleavnt. */ + continue; + } + + state = nm_device_get_connectivity_state (dev, addr_family); + if (metric < best_metric) { + /* this device has a better default route. It wins. */ + best_metric = metric; best_state = state; - if (best_state >= NM_CONNECTIVITY_FULL) { - /* it doesn't get better than this. */ - break; - } + } else { + best_state = NM_MAX_WITH_CMP (nm_connectivity_state_cmp, + best_state, + state); + } + + if (nm_connectivity_state_cmp (best_state, NM_CONNECTIVITY_FULL) >= 0) { + /* it doesn't get better than FULL. We are done. */ + break; } } - nm_assert (best_state <= NM_CONNECTIVITY_FULL); - if (best_state != priv->connectivity_state) { - priv->connectivity_state = best_state; + return best_state; +} + +static void +device_connectivity_changed (NMDevice *device, + GParamSpec *pspec, + NMManager *self) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMConnectivityState best_state; + + best_state = _get_best_connectivity (self, AF_UNSPEC); + if (best_state == priv->connectivity_state) + return; - _LOGD (LOGD_CORE, "connectivity checking indicates %s", - nm_connectivity_state_to_string (priv->connectivity_state)); + priv->connectivity_state = best_state; - nm_manager_update_state (self); - _notify (self, PROP_CONNECTIVITY); - nm_dispatcher_call_connectivity (priv->connectivity_state, NULL, NULL, NULL); - } + _LOGD (LOGD_CORE, "connectivity checking indicates %s", + nm_connectivity_state_to_string (priv->connectivity_state)); + + nm_manager_update_state (self); + _notify (self, PROP_CONNECTIVITY); + nm_dispatcher_call_connectivity (priv->connectivity_state, NULL, NULL, NULL); } static void @@ -2949,7 +3046,10 @@ add_device (NMManager *self, NMDevice *device, GError **error) G_CALLBACK (device_realized), self); - g_signal_connect (device, NM_DEVICE_CONNECTIVITY_CHANGED, + g_signal_connect (device, "notify::" NM_DEVICE_IP4_CONNECTIVITY, + G_CALLBACK (device_connectivity_changed), + self); + g_signal_connect (device, "notify::" NM_DEVICE_IP6_CONNECTIVITY, G_CALLBACK (device_connectivity_changed), self); @@ -3864,11 +3964,16 @@ ensure_master_active_connection (NMManager *self, GError **error) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMActiveConnection *ac; NMActiveConnection *master_ac = NULL; NMDeviceState master_state; + gboolean bind_lifetime_to_profile_visibility; + + g_return_val_if_fail (connection, NULL); + g_return_val_if_fail (master_connection || master_device, FALSE); - g_assert (connection); - g_assert (master_connection || master_device); + bind_lifetime_to_profile_visibility = NM_FLAGS_HAS (nm_device_get_activation_state_flags (device), + NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY); /* If the master device isn't activated then we need to activate it using * compatible connection. If it's already activating we can just proceed. @@ -3893,8 +3998,16 @@ ensure_master_active_connection (NMManager *self, if ( (master_state == NM_DEVICE_STATE_ACTIVATED) || nm_device_is_activating (master_device)) { /* Device already using master_connection */ - g_assert (device_connection); - return NM_ACTIVE_CONNECTION (nm_device_get_act_request (master_device)); + ac = NM_ACTIVE_CONNECTION (nm_device_get_act_request (master_device)); + g_return_val_if_fail (device_connection, ac); + + if (!bind_lifetime_to_profile_visibility) { + /* unbind the lifetime. */ + nm_active_connection_set_state_flags_clear (ac, + NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY); + } + + return ac; } /* If the device is disconnected, find a compatible connection and @@ -3931,6 +4044,9 @@ ensure_master_active_connection (NMManager *self, subject, NM_ACTIVATION_TYPE_MANAGED, activation_reason, + bind_lifetime_to_profile_visibility + ? NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY + : NM_ACTIVATION_STATE_FLAG_NONE, error); return master_ac; } @@ -3979,6 +4095,9 @@ ensure_master_active_connection (NMManager *self, subject, NM_ACTIVATION_TYPE_MANAGED, activation_reason, + bind_lifetime_to_profile_visibility + ? NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY + : NM_ACTIVATION_STATE_FLAG_NONE, error); return master_ac; } @@ -4096,7 +4215,7 @@ should_connect_slaves (NMConnection *connection, NMDevice *device) goto out; val = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA, - "connection.autoconnect-slaves", + NM_CON_DEFAULT ("connection.autoconnect-slaves"), device, 0, 1, -1); @@ -4140,6 +4259,7 @@ autoconnect_slaves (NMManager *self, master_device)) { gs_free SlaveConnectionInfo *slaves = NULL; guint i, n_slaves = 0; + gboolean bind_lifetime_to_profile_visibility; slaves = find_slaves (self, master_connection, master_device, &n_slaves); if (n_slaves > 1) { @@ -4154,6 +4274,10 @@ autoconnect_slaves (NMManager *self, GINT_TO_POINTER (!nm_streq0 (value, "index"))); } + bind_lifetime_to_profile_visibility = n_slaves > 0 + && NM_FLAGS_HAS (nm_device_get_activation_state_flags (master_device), + NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY); + for (i = 0; i < n_slaves; i++) { SlaveConnectionInfo *slave = &slaves[i]; const char *uuid; @@ -4208,6 +4332,9 @@ autoconnect_slaves (NMManager *self, subject, NM_ACTIVATION_TYPE_MANAGED, NM_ACTIVATION_REASON_AUTOCONNECT_SLAVES, + bind_lifetime_to_profile_visibility + ? NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY + : NM_ACTIVATION_STATE_FLAG_NONE, &local_err); if (local_err) { _LOGW (LOGD_CORE, "Slave connection activation failed: %s", local_err->message); @@ -4268,6 +4395,40 @@ unmanaged_to_disconnected (NMDevice *device) } } +static NMActivationStateFlags +_activation_bind_lifetime_to_profile_visibility (NMAuthSubject *subject) +{ + if ( nm_auth_subject_is_internal (subject) + || nm_auth_subject_get_unix_process_uid (subject) == 0) { + /* internal requests and requests from root are always unbound. */ + return NM_ACTIVATION_STATE_FLAG_NONE; + } + + /* if the activation was not done by internal decision nor root, there + * are the following cases: + * + * - the connection has "connection.permissions" unset and the profile + * is not restricted to a user and commonly always visible. It does + * not hurt to bind the lifetime, because we expect the profile to be + * visible at the moment. If the profile changes (while still being active), + * we want to pick-up changes to the visibility and possibly disconnect. + * + * - the connection has "connection.permissions" set, and the current user + * is the owner: + * + * - Usually, we would expect that the profile is visible at the moment, + * and of course we want to bind the lifetime. The moment the user + * logs out, the connection becomes invisible and disconnects. + * + * - the profile at this time could already be invisible (e.g. if the + * user didn't ceate a proper session (sudo) and manually activates + * an invisible profile. In this case, we still want to bind the + * lifetime, and it will disconnect after the user logs in and logs + * out again. NMKeepAlive takes care of that. + */ + return NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY; +} + /* The parent connection is ready; we can proceed realizing the device and * progressing the device to disconencted state. */ @@ -4397,6 +4558,8 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError * subject, NM_ACTIVATION_TYPE_MANAGED, nm_active_connection_get_activation_reason (active), + nm_active_connection_get_state_flags (active) + & NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY, error); if (!parent_ac) { g_prefix_error (error, "%s failed to activate parent: ", nm_device_get_iface (device)); @@ -4522,7 +4685,9 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError * for (i = 0; i < n_all; i++) { nm_device_disconnect_active_connection ( all_ac_arr ? all_ac_arr->pdata[i] - : ac); + : ac, + NM_DEVICE_STATE_REASON_NEW_ACTIVATION, + NM_ACTIVE_CONNECTION_STATE_REASON_UNKNOWN); } } } @@ -4595,6 +4760,7 @@ _new_active_connection (NMManager *self, NMAuthSubject *subject, NMActivationType activation_type, NMActivationReason activation_reason, + NMActivationStateFlags initial_state_flags, GError **error) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); @@ -4666,6 +4832,7 @@ _new_active_connection (NMManager *self, parent_device, nm_dbus_object_get_path (NM_DBUS_OBJECT (parent)), activation_reason, + initial_state_flags, subject); } @@ -4675,6 +4842,7 @@ _new_active_connection (NMManager *self, subject, activation_type, activation_reason, + initial_state_flags, device); } @@ -4738,6 +4906,7 @@ fail: * @activation_type: whether to assume the connection. That is, take over gracefully, * non-destructible. * @activation_reason: the reason for activation + * @initial_state_flags: the inital state flags for the activation. * @error: return location for an error * * Begins a new internally-initiated activation of @sett_conn on @device. @@ -4759,6 +4928,7 @@ nm_manager_activate_connection (NMManager *self, NMAuthSubject *subject, NMActivationType activation_type, NMActivationReason activation_reason, + NMActivationStateFlags initial_state_flags, GError **error) { NMManagerPrivate *priv; @@ -4812,6 +4982,7 @@ nm_manager_activate_connection (NMManager *self, subject, activation_type, activation_reason, + initial_state_flags, error); if (!active) return NULL; @@ -4832,7 +5003,7 @@ nm_manager_activate_connection (NMManager *self, * is only a partial activation. * @connection: the partial #NMConnection to be activated (if @sett_conn is unspecified) * @device_path: the object path of the device to be activated, or NULL - * @out_device: on successful reutrn, the #NMDevice to be activated with @connection + * @out_device: on successful return, the #NMDevice to be activated with @connection * The caller may pass in a device which shortcuts the lookup by path. * In this case, the passed in device must have the matching @device_path * already. @@ -5071,6 +5242,7 @@ impl_manager_activate_connection (NMDBusObject *obj, subject, NM_ACTIVATION_TYPE_MANAGED, NM_ACTIVATION_REASON_USER_REQUEST, + _activation_bind_lifetime_to_profile_visibility (subject), &error); if (!active) goto error; @@ -5108,35 +5280,54 @@ activation_add_done (NMSettings *settings, NMManager *self; gs_unref_object NMActiveConnection *active = NULL; gs_free_error GError *local = NULL; + gpointer persist_ptr; + NMSettingsConnectionPersistMode persist; + gpointer async_op_type_ptr; + AsyncOpType async_op_type; + GVariant *result_floating; - nm_utils_user_data_unpack (user_data, &self, &active); - - if (!error) { - nm_active_connection_set_settings_connection (active, new_connection); - - if (_internal_activate_generic (self, active, &local)) { - nm_settings_connection_update (new_connection, - NULL, - NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK, - NM_SETTINGS_CONNECTION_COMMIT_REASON_USER_ACTION | NM_SETTINGS_CONNECTION_COMMIT_REASON_ID_CHANGED, - "add-and-activate", - NULL); - g_dbus_method_invocation_return_value ( - context, - g_variant_new ("(oo)", - nm_dbus_object_get_path (NM_DBUS_OBJECT (new_connection)), - nm_dbus_object_get_path (NM_DBUS_OBJECT (active)))); - nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD_ACTIVATE, - nm_active_connection_get_settings_connection (active), - TRUE, - NULL, - nm_active_connection_get_subject (active), - NULL); - return; - } + nm_utils_user_data_unpack (user_data, &self, &active, &persist_ptr, &async_op_type_ptr); + persist = GPOINTER_TO_INT (persist_ptr); + async_op_type = GPOINTER_TO_INT (async_op_type_ptr); + + if (error) + goto fail; + + nm_active_connection_set_settings_connection (active, new_connection); + + if (!_internal_activate_generic (self, active, &local)) { error = local; + goto fail; + } + + nm_settings_connection_update (new_connection, + NULL, + persist, + NM_SETTINGS_CONNECTION_COMMIT_REASON_USER_ACTION | NM_SETTINGS_CONNECTION_COMMIT_REASON_ID_CHANGED, + "add-and-activate", + NULL); + + if (async_op_type == ASYNC_OP_TYPE_AC_AUTH_ADD_AND_ACTIVATE) { + result_floating = g_variant_new ("(oo)", + nm_dbus_object_get_path (NM_DBUS_OBJECT (new_connection)), + nm_dbus_object_get_path (NM_DBUS_OBJECT (active))); + } else { + result_floating = g_variant_new ("(ooa{sv})", + nm_dbus_object_get_path (NM_DBUS_OBJECT (new_connection)), + nm_dbus_object_get_path (NM_DBUS_OBJECT (active)), + g_variant_new_array (G_VARIANT_TYPE ("a{sv}"), NULL, 0)); } + g_dbus_method_invocation_return_value (context, result_floating); + + nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD_ACTIVATE, + nm_active_connection_get_settings_connection (active), + TRUE, + NULL, + nm_active_connection_get_subject (active), + NULL); + return; +fail: nm_assert (error); nm_active_connection_set_state_fail (active, @@ -5155,9 +5346,11 @@ activation_add_done (NMSettings *settings, static void _add_and_activate_auth_done (NMManager *self, + AsyncOpType async_op_type, NMActiveConnection *active, NMConnection *connection, GDBusMethodInvocation *invocation, + NMSettingsConnectionPersistMode persist, gboolean success, const char *error_desc) { @@ -5190,7 +5383,9 @@ _add_and_activate_auth_done (NMManager *self, invocation, activation_add_done, nm_utils_user_data_pack (self, - g_object_ref (active))); + g_object_ref (active), + GINT_TO_POINTER (persist), + GINT_TO_POINTER (async_op_type))); } static void @@ -5205,17 +5400,79 @@ impl_manager_add_and_activate_connection (NMDBusObject *obj, NMManager *self = NM_MANAGER (obj); NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); gs_unref_object NMConnection *incompl_conn = NULL; - NMActiveConnection *active = NULL; + gs_unref_object NMActiveConnection *active = NULL; gs_unref_object NMAuthSubject *subject = NULL; GError *error = NULL; NMDevice *device = NULL; gboolean is_vpn = FALSE; gs_unref_variant GVariant *settings = NULL; + gs_unref_variant GVariant *options = NULL; const char *device_path; const char *specific_object_path; gs_free NMConnection **conns = NULL; + NMSettingsConnectionPersistMode persist = NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK; + gboolean bind_dbus_client = FALSE; + AsyncOpType async_op_type; - g_variant_get (parameters, "(@a{sa{sv}}&o&o)", &settings, &device_path, &specific_object_path); + if (nm_streq (method_info->parent.name, "AddAndActivateConnection2")) { + async_op_type = ASYNC_OP_TYPE_AC_AUTH_ADD_AND_ACTIVATE2; + g_variant_get (parameters, "(@a{sa{sv}}&o&o@a{sv})", &settings, &device_path, &specific_object_path, &options); + } else { + nm_assert (nm_streq (method_info->parent.name, "AddAndActivateConnection")); + async_op_type = ASYNC_OP_TYPE_AC_AUTH_ADD_AND_ACTIVATE; + g_variant_get (parameters, "(@a{sa{sv}}&o&o)", &settings, &device_path, &specific_object_path); + } + + if (options) { + GVariantIter iter; + const char *option_name; + GVariant *option_value; + + g_variant_iter_init (&iter, options); + while (g_variant_iter_next (&iter, "{&sv}", &option_name, &option_value)) { + gs_unref_variant GVariant *option_value_free = NULL; + const char *s; + + option_value_free = option_value; + + if ( nm_streq (option_name, "persist") + && g_variant_is_of_type (option_value, G_VARIANT_TYPE_STRING)) { + s = g_variant_get_string (option_value, NULL); + + if (nm_streq (s, "volatile")) + persist = NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_ONLY; + else if (nm_streq (s, "memory")) + persist = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY; + else if (nm_streq (s, "disk")) + persist = NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK; + else { + error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_INVALID_ARGUMENTS, + "Option \"persist\" must be one of \"volatile\", \"memory\" or \"disk\""); + goto error; + } + } else if ( nm_streq (option_name, "bind-activation") + && g_variant_is_of_type (option_value, G_VARIANT_TYPE_STRING)) { + s = g_variant_get_string (option_value, NULL); + + if (nm_streq (s, "dbus-client")) + bind_dbus_client = TRUE; + else if (nm_streq (s, "none")) + bind_dbus_client = FALSE; + else { + error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_INVALID_ARGUMENTS, + "Option \"bind-activation\" must be one of \"dbus-client\" or \"none\""); + goto error; + } + } else { + error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_INVALID_ARGUMENTS, + "Unknown extra option passed"); + goto error; + } + } + } specific_object_path = nm_utils_dbus_normalize_object_path (specific_object_path); device_path = nm_utils_dbus_normalize_object_path (device_path); @@ -5283,17 +5540,28 @@ impl_manager_add_and_activate_connection (NMDBusObject *obj, subject, NM_ACTIVATION_TYPE_MANAGED, NM_ACTIVATION_REASON_USER_REQUEST, + _activation_bind_lifetime_to_profile_visibility (subject), &error); if (!active) goto error; + if (bind_dbus_client) { + NMKeepAlive *keep_alive; + + keep_alive = nm_active_connection_get_keep_alive (active); + nm_keep_alive_set_dbus_client_watch (keep_alive, dbus_connection, sender); + nm_keep_alive_arm (keep_alive); + } + nm_active_connection_authorize (active, incompl_conn, _async_op_complete_ac_auth_cb, _async_op_data_new_ac_auth_add_and_activate (self, + async_op_type, active, invocation, - incompl_conn)); + incompl_conn, + persist)); /* we passed the pointers on to _async_op_data_new_ac_auth_add_and_activate() */ g_steal_pointer (&incompl_conn); @@ -5313,31 +5581,26 @@ nm_manager_deactivate_connection (NMManager *manager, NMDeviceStateReason reason, GError **error) { - gboolean success = FALSE; - if (NM_IS_VPN_CONNECTION (active)) { NMActiveConnectionStateReason vpn_reason = NM_ACTIVE_CONNECTION_STATE_REASON_USER_DISCONNECTED; if (nm_device_state_reason_check (reason) == NM_DEVICE_STATE_REASON_CONNECTION_REMOVED) vpn_reason = NM_ACTIVE_CONNECTION_STATE_REASON_CONNECTION_REMOVED; - if (nm_vpn_connection_deactivate (NM_VPN_CONNECTION (active), vpn_reason, FALSE)) - success = TRUE; - else + if (!nm_vpn_connection_deactivate (NM_VPN_CONNECTION (active), vpn_reason, FALSE)) { g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE, "The VPN connection was not active."); + return FALSE; + } } else { - g_assert (NM_IS_ACT_REQUEST (active)); - nm_device_state_changed (nm_active_connection_get_device (active), - NM_DEVICE_STATE_DEACTIVATING, - reason); - success = TRUE; + nm_assert (NM_IS_ACT_REQUEST (active)); + nm_device_disconnect_active_connection (active, + reason, + NM_ACTIVE_CONNECTION_STATE_REASON_UNKNOWN); } - if (success) - _notify (manager, PROP_ACTIVE_CONNECTIONS); - - return success; + _notify (manager, PROP_ACTIVE_CONNECTIONS); + return TRUE; } static void @@ -6163,6 +6426,12 @@ check_connectivity_auth_done_cb (NMAuthChain *chain, c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { if (nm_device_check_connectivity (device, + AF_INET, + device_connectivity_done, + data)) + data->remaining++; + if (nm_device_check_connectivity (device, + AF_INET6, device_connectivity_done, data)) data->remaining++; @@ -6565,6 +6834,7 @@ _dbus_set_property_auth_cb (NMAuthChain *chain, gs_unref_object NMManager *self = handle_data->self; NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); NMAuthCallResult result; + gs_free_error GError *local = NULL; const char *error_name = NULL; const char *error_message = NULL; GValue gvalue; @@ -6602,7 +6872,10 @@ _dbus_set_property_auth_cb (NMAuthChain *chain, } g_dbus_gvariant_to_gvalue (value, &gvalue); - g_object_set_property (G_OBJECT (obj), property_info->property_name, &gvalue); + if (!nm_g_object_set_property (G_OBJECT (obj), property_info->property_name, &gvalue, &local)) { + error_name = "org.freedesktop.DBus.Error.InvalidArgs"; + error_message = local->message; + } g_value_unset (&gvalue); out: @@ -6947,14 +7220,14 @@ rfkill_change (NMManager *self, const char *desc, RfKillType rtype, gboolean ena len = write (fd, &event, sizeof (event)); if (len < 0) { - _LOGW (LOGD_RFKILL, "rfkill: (%s): failed to change WiFi killswitch state: (%d) %s", + _LOGW (LOGD_RFKILL, "rfkill: (%s): failed to change Wi-Fi killswitch state: (%d) %s", desc, errno, g_strerror (errno)); } else if (len == sizeof (event)) { _LOGI (LOGD_RFKILL, "rfkill: %s hardware radio set %s", desc, enabled ? "enabled" : "disabled"); } else { /* Failed to write full structure */ - _LOGW (LOGD_RFKILL, "rfkill: (%s): failed to change WiFi killswitch state", desc); + _LOGW (LOGD_RFKILL, "rfkill: (%s): failed to change Wi-Fi killswitch state", desc); } nm_close (fd); @@ -7151,7 +7424,7 @@ constructed (GObject *object) G_CALLBACK (rfkill_manager_rfkill_changed_cb), self); - /* Force kernel WiFi/WWAN rfkill state to follow NM saved WiFi/WWAN state + /* Force kernel Wi-Fi/WWAN rfkill state to follow NM saved Wi-Fi/WWAN state * in case the BIOS doesn't save rfkill state, and to be consistent with user * changes to the WirelessEnabled/WWANEnabled properties which toggle kernel * rfkill. @@ -7184,7 +7457,7 @@ nm_manager_init (NMManager *self) priv->radio_states[RFKILL_TYPE_WLAN].key = NM_CONFIG_STATE_PROPERTY_WIFI_ENABLED; priv->radio_states[RFKILL_TYPE_WLAN].prop = NM_MANAGER_WIRELESS_ENABLED; priv->radio_states[RFKILL_TYPE_WLAN].hw_prop = NM_MANAGER_WIRELESS_HARDWARE_ENABLED; - priv->radio_states[RFKILL_TYPE_WLAN].desc = "WiFi"; + priv->radio_states[RFKILL_TYPE_WLAN].desc = "Wi-Fi"; priv->radio_states[RFKILL_TYPE_WLAN].rtype = RFKILL_TYPE_WLAN; priv->radio_states[RFKILL_TYPE_WWAN].user_enabled = TRUE; @@ -7380,7 +7653,7 @@ set_property (GObject *object, guint prop_id, g_value_get_boolean (value)); break; case PROP_WIMAX_ENABLED: - /* WIMAX is depreacted. This does nothing. */ + /* WIMAX is deprecated. This does nothing. */ break; case PROP_CONNECTIVITY_CHECK_ENABLED: nm_config_set_connectivity_check_enabled (priv->config, @@ -7636,6 +7909,23 @@ static const NMDBusInterfaceInfoExtended interface_info_manager = { ), NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "AddAndActivateConnection2", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("connection", "a{sa{sv}}"), + NM_DEFINE_GDBUS_ARG_INFO ("device", "o"), + NM_DEFINE_GDBUS_ARG_INFO ("specific_object", "o"), + NM_DEFINE_GDBUS_ARG_INFO ("options", "a{sv}"), + ), + .out_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("path", "o"), + NM_DEFINE_GDBUS_ARG_INFO ("active_connection", "o"), + NM_DEFINE_GDBUS_ARG_INFO ("result", "a{sv}"), + ), + ), + .handle = impl_manager_add_and_activate_connection, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( "DeactivateConnection", .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( NM_DEFINE_GDBUS_ARG_INFO ("active_connection", "o"), diff --git a/src/nm-manager.h b/src/nm-manager.h index 11cba1a5..ecb4b017 100644 --- a/src/nm-manager.h +++ b/src/nm-manager.h @@ -97,6 +97,19 @@ const CList * nm_manager_get_active_connections (NMManager *manager); }); \ iter = c_list_entry (iter->active_connections_lst.next, NMActiveConnection, active_connections_lst)) +#define nm_manager_for_each_active_connection_safe(manager, iter, tmp_list, iter_safe) \ + for (tmp_list = nm_manager_get_active_connections (manager), \ + iter_safe = tmp_list->next; \ + ({ \ + if (iter_safe != tmp_list) { \ + iter = c_list_entry (iter_safe, NMActiveConnection, active_connections_lst); \ + iter_safe = iter_safe->next; \ + } else \ + iter = NULL; \ + (iter != NULL); \ + }); \ + ) + NMSettingsConnection **nm_manager_get_activatable_connections (NMManager *manager, gboolean for_auto_activation, gboolean sort, @@ -121,6 +134,19 @@ const CList * nm_manager_get_devices (NMManager *manager); }); \ iter = c_list_entry (iter->devices_lst.next, NMDevice, devices_lst)) +#define nm_manager_for_each_device_safe(manager, iter, tmp_list, iter_safe) \ + for (tmp_list = nm_manager_get_devices (manager), \ + iter_safe = tmp_list->next; \ + ({ \ + if (iter_safe != tmp_list) { \ + iter = c_list_entry (iter_safe, NMDevice, devices_lst); \ + iter_safe = iter_safe->next; \ + } else \ + iter = NULL; \ + (iter != NULL); \ + }); \ + ) + NMDevice * nm_manager_get_device_by_ifindex (NMManager *manager, int ifindex); NMDevice * nm_manager_get_device_by_path (NMManager *manager, @@ -149,6 +175,7 @@ NMActiveConnection *nm_manager_activate_connection (NMManager *manager, NMAuthSubject *subject, NMActivationType activation_type, NMActivationReason activation_reason, + NMActivationStateFlags initial_state_flags, GError **error); gboolean nm_manager_deactivate_connection (NMManager *manager, @@ -174,4 +201,6 @@ void nm_manager_dbus_set_property_handle (NMDBusObject *obj, GVariant *value, gpointer user_data); +NMMetered nm_manager_get_metered (NMManager *self); + #endif /* __NETWORKMANAGER_MANAGER_H__ */ diff --git a/src/nm-pacrunner-manager.c b/src/nm-pacrunner-manager.c index af43edbe..b9881f76 100644 --- a/src/nm-pacrunner-manager.c +++ b/src/nm-pacrunner-manager.c @@ -173,6 +173,7 @@ get_ip4_domains (GPtrArray *domains, NMIP4Config *ip4) const NMPlatformIP4Address *address; const NMPlatformIP4Route *routes; guint i; + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; /* Extract searches */ for (i = 0; i < nm_ip4_config_get_num_searches (ip4); i++) @@ -186,7 +187,7 @@ get_ip4_domains (GPtrArray *domains, NMIP4Config *ip4) nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, ip4, &address) { cidr = g_strdup_printf ("%s/%u", - nm_utils_inet4_ntop (address->address, NULL), + nm_utils_inet4_ntop (address->address, sbuf), address->plen); g_ptr_array_add (domains, cidr); } @@ -195,7 +196,7 @@ get_ip4_domains (GPtrArray *domains, NMIP4Config *ip4) if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (routes)) continue; cidr = g_strdup_printf ("%s/%u", - nm_utils_inet4_ntop (routes->network, NULL), + nm_utils_inet4_ntop (routes->network, sbuf), routes->plen); g_ptr_array_add (domains, cidr); } @@ -209,6 +210,7 @@ get_ip6_domains (GPtrArray *domains, NMIP6Config *ip6) const NMPlatformIP6Address *address; const NMPlatformIP6Route *routes; guint i; + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; /* Extract searches */ for (i = 0; i < nm_ip6_config_get_num_searches (ip6); i++) @@ -221,7 +223,7 @@ get_ip6_domains (GPtrArray *domains, NMIP6Config *ip6) /* Add addresses and routes in CIDR form */ nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, ip6, &address) { cidr = g_strdup_printf ("%s/%u", - nm_utils_inet6_ntop (&address->address, NULL), + nm_utils_inet6_ntop (&address->address, sbuf), address->plen); g_ptr_array_add (domains, cidr); } @@ -230,7 +232,7 @@ get_ip6_domains (GPtrArray *domains, NMIP6Config *ip6) if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (routes)) continue; cidr = g_strdup_printf ("%s/%u", - nm_utils_inet6_ntop (&routes->network, NULL), + nm_utils_inet6_ntop (&routes->network, sbuf), routes->plen); g_ptr_array_add (domains, cidr); } @@ -271,7 +273,7 @@ pacrunner_send_done (GObject *source, GAsyncResult *res, gpointer user_data) g_variant_get (variant, "(&o)", &path); if (c_list_is_empty (&config->lst)) { - _LOG2D (config, "sent (%s), but destory it right away", path); + _LOG2D (config, "sent (%s), but destroy it right away", path); g_dbus_proxy_call (priv->pacrunner, "DestroyProxyConfiguration", g_variant_new ("(o)", path), diff --git a/src/nm-policy.c b/src/nm-policy.c index 7f8c665c..77fc929c 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -30,6 +30,7 @@ #include "NetworkManagerUtils.h" #include "nm-act-request.h" +#include "nm-keep-alive.h" #include "devices/nm-device.h" #include "nm-setting-ip4-config.h" #include "nm-setting-connection.h" @@ -178,9 +179,10 @@ static void clear_ip6_prefix_delegation (gpointer data) { IP6PrefixDelegation *delegation = data; + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; _LOGD (LOGD_IP6, "ipv6-pd: undelegating prefix %s/%d", - nm_utils_inet6_ntop (&delegation->prefix.address, NULL), + nm_utils_inet6_ntop (&delegation->prefix.address, sbuf), delegation->prefix.plen); g_hash_table_foreach (delegation->subnets, _clear_ip6_subnet, NULL); @@ -214,13 +216,14 @@ ip6_subnet_from_delegation (IP6PrefixDelegation *delegation, NMDevice *device) { NMPlatformIP6Address *subnet; int ifindex = nm_device_get_ifindex (device); + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; subnet = g_hash_table_lookup (delegation->subnets, GINT_TO_POINTER (ifindex)); if (!subnet) { /* Check for out-of-prefixes condition. */ if (delegation->next_subnet >= (1 << (64 - delegation->prefix.plen))) { _LOGD (LOGD_IP6, "ipv6-pd: no more prefixes in %s/%d", - nm_utils_inet6_ntop (&delegation->prefix.address, NULL), + nm_utils_inet6_ntop (&delegation->prefix.address, sbuf), delegation->prefix.plen); return FALSE; } @@ -248,7 +251,7 @@ ip6_subnet_from_delegation (IP6PrefixDelegation *delegation, NMDevice *device) subnet->preferred = delegation->prefix.preferred; _LOGD (LOGD_IP6, "ipv6-pd: %s allocated from a /%d prefix on %s", - nm_utils_inet6_ntop (&subnet->address, NULL), + nm_utils_inet6_ntop (&subnet->address, sbuf), delegation->prefix.plen, nm_device_get_iface (device)); @@ -319,9 +322,10 @@ device_ip6_prefix_delegated (NMDevice *device, guint i; const CList *tmp_list; NMActiveConnection *ac; + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; _LOGI (LOGD_IP6, "ipv6-pd: received a prefix %s/%d from %s", - nm_utils_inet6_ntop (&prefix->address, NULL), + nm_utils_inet6_ntop (&prefix->address, sbuf), prefix->plen, nm_device_get_iface (device)); @@ -1283,6 +1287,7 @@ auto_activate_device (NMPolicy *self, subject, NM_ACTIVATION_TYPE_MANAGED, NM_ACTIVATION_REASON_AUTOCONNECT, + NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY, &error); if (!ac) { _LOGI (LOGD_DEVICE, "connection '%s' auto-activation failed: %s", @@ -1673,9 +1678,14 @@ activate_secondary_connections (NMPolicy *self, GError *error = NULL; guint32 i; gboolean success = TRUE; + NMActivationStateFlags initial_state_flags; s_con = nm_connection_get_setting_connection (connection); - nm_assert (s_con); + nm_assert (NM_IS_SETTING_CONNECTION (s_con)); + + /* we propagate the activation's state flags. */ + initial_state_flags = nm_device_get_activation_state_flags (device) + & NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY; for (i = 0; i < nm_setting_connection_get_num_secondaries (s_con); i++) { NMSettingsConnection *sett_conn; @@ -1699,7 +1709,6 @@ activate_secondary_connections (NMPolicy *self, } req = nm_device_get_act_request (device); - g_assert (req); _LOGD (LOGD_DEVICE, "activating secondary connection '%s (%s)' for base connection '%s (%s)'", nm_settings_connection_get_id (sett_conn), sec_uuid, @@ -1712,6 +1721,7 @@ activate_secondary_connections (NMPolicy *self, nm_active_connection_get_subject (NM_ACTIVE_CONNECTION (req)), NM_ACTIVATION_TYPE_MANAGED, nm_active_connection_get_activation_reason (NM_ACTIVE_CONNECTION (req)), + initial_state_flags, &error); if (ac) secondary_ac_list = g_slist_append (secondary_ac_list, g_object_ref (ac)); @@ -1751,17 +1761,14 @@ device_state_changed (NMDevice *device, NMSettingConnection *s_con = NULL; switch (nm_device_state_reason_check (reason)) { - case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED: - case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING: - case NM_DEVICE_STATE_REASON_GSM_SIM_NOT_INSERTED: case NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED: case NM_DEVICE_STATE_REASON_GSM_SIM_PUK_REQUIRED: - case NM_DEVICE_STATE_REASON_GSM_SIM_WRONG: case NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT: - case NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED: case NM_DEVICE_STATE_REASON_GSM_APN_FAILED: - /* Block autoconnect of the just-failed connection for situations - * where a retry attempt would just fail again. + /* Block autoconnection at settings level if there is any settings-specific + * error reported by the modem (e.g. wrong SIM-PIN or wrong APN). Do not block + * autoconnection at settings level for errors in the device domain (e.g. + * a missing SIM or wrong modem initialization). */ if (sett_conn) { nm_settings_connection_autoconnect_blocked_reason_set (sett_conn, @@ -1875,8 +1882,8 @@ device_state_changed (NMDevice *device, if (blocked_reason != NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NONE) { _LOGD (LOGD_DEVICE, "blocking autoconnect of connection '%s': %s", nm_settings_connection_get_id (sett_conn), - NM_UTILS_LOOKUP_STR (nm_device_state_reason_to_str, - nm_device_state_reason_check (reason))); + NM_UTILS_LOOKUP_STR_A (nm_device_state_reason_to_str, + nm_device_state_reason_check (reason))); nm_settings_connection_autoconnect_blocked_reason_set (sett_conn, blocked_reason, TRUE); } } @@ -1969,7 +1976,7 @@ device_ip_config_changed (NMDevice *device, /* We catch already all the IP events registering on the device state changes but * the ones where the IP changes but the device state keep stable (i.e., activated): * ignore IP config changes but when the device is in activated state. - * Prevents unecessary changes to DNS information. + * Prevents unnecessary changes to DNS information. */ if (nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED) { if (old_config != new_config) { @@ -2161,6 +2168,8 @@ vpn_connection_retry_after_failure (NMVpnConnection *vpn, NMPolicy *self) nm_active_connection_get_subject (ac), NM_ACTIVATION_TYPE_MANAGED, nm_active_connection_get_activation_reason (ac), + ( nm_active_connection_get_state_flags (ac) + & NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY), &error)) { _LOGW (LOGD_DEVICE, "VPN '%s' reconnect failed: %s", nm_settings_connection_get_id (connection), @@ -2183,12 +2192,47 @@ active_connection_state_changed (NMActiveConnection *active, } static void +active_connection_keep_alive_changed (NMKeepAlive *keep_alive, + GParamSpec *pspec, + NMPolicy *self) +{ + NMPolicyPrivate *priv; + NMActiveConnection *ac; + GError *error = NULL; + + nm_assert (NM_IS_POLICY (self)); + nm_assert (NM_IS_KEEP_ALIVE (keep_alive)); + nm_assert (NM_IS_ACTIVE_CONNECTION (nm_keep_alive_get_owner (keep_alive))); + + if (nm_keep_alive_is_alive (keep_alive)) + return; + + ac = nm_keep_alive_get_owner (keep_alive); + + if (nm_active_connection_get_state (ac) > NM_ACTIVE_CONNECTION_STATE_ACTIVATED) + return; + + priv = NM_POLICY_GET_PRIVATE (self); + + if (!nm_manager_deactivate_connection (priv->manager, + ac, + NM_DEVICE_STATE_REASON_CONNECTION_REMOVED, + &error)) { + _LOGW (LOGD_DEVICE, "connection '%s' is no longer kept alive, but error deactivating it: %s", + nm_active_connection_get_settings_connection_id (ac), + error->message); + g_clear_error (&error); + } +} + +static void active_connection_added (NMManager *manager, NMActiveConnection *active, gpointer user_data) { NMPolicyPrivate *priv = user_data; NMPolicy *self = _PRIV_TO_SELF (priv); + NMKeepAlive *keep_alive; if (NM_IS_VPN_CONNECTION (active)) { g_signal_connect (active, NM_VPN_CONNECTION_INTERNAL_STATE_CHANGED, @@ -2199,9 +2243,18 @@ active_connection_added (NMManager *manager, self); } + keep_alive = nm_active_connection_get_keep_alive (active); + + nm_keep_alive_arm (keep_alive); + g_signal_connect (active, "notify::" NM_ACTIVE_CONNECTION_STATE, G_CALLBACK (active_connection_state_changed), self); + g_signal_connect (keep_alive, + "notify::" NM_KEEP_ALIVE_ALIVE, + G_CALLBACK (active_connection_keep_alive_changed), + self); + active_connection_keep_alive_changed (keep_alive, NULL, self); } static void @@ -2221,6 +2274,9 @@ active_connection_removed (NMManager *manager, g_signal_handlers_disconnect_by_func (active, active_connection_state_changed, self); + g_signal_handlers_disconnect_by_func (nm_active_connection_get_keep_alive (active), + active_connection_keep_alive_changed, + self); } /*****************************************************************************/ @@ -2358,12 +2414,12 @@ _deactivate_if_active (NMPolicy *self, NMSettingsConnection *connection) { NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); NMActiveConnection *ac; - const CList *tmp_list; + const CList *tmp_list, *tmp_safe; GError *error = NULL; nm_assert (NM_IS_SETTINGS_CONNECTION (connection)); - nm_manager_for_each_active_connection (priv->manager, ac, tmp_list) { + nm_manager_for_each_active_connection_safe (priv->manager, ac, tmp_list, tmp_safe) { if ( nm_active_connection_get_settings_connection (ac) == connection && (nm_active_connection_get_state (ac) <= NM_ACTIVE_CONNECTION_STATE_ACTIVATED)) { @@ -2404,8 +2460,7 @@ connection_flags_changed (NMSettings *settings, NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE)) { if (!nm_settings_connection_autoconnect_is_blocked (connection)) schedule_activate_all (self); - } else - _deactivate_if_active (self, connection); + } } static void diff --git a/src/nm-rfkill-manager.c b/src/nm-rfkill-manager.c index a7bc694a..557f4cd9 100644 --- a/src/nm-rfkill-manager.c +++ b/src/nm-rfkill-manager.c @@ -82,7 +82,7 @@ static const char * rfkill_type_to_desc (RfKillType rtype) { if (rtype == 0) - return "WiFi"; + return "Wi-Fi"; else if (rtype == 1) return "WWAN"; else if (rtype == 2) diff --git a/src/nm-test-utils-core.h b/src/nm-test-utils-core.h index 5e89cb39..dbfe71ad 100644 --- a/src/nm-test-utils-core.h +++ b/src/nm-test-utils-core.h @@ -88,8 +88,8 @@ nmtst_platform_ip4_address_full (const char *address, const char *peer_address, { NMPlatformIP4Address *addr = nmtst_platform_ip4_address (address, peer_address, plen); - G_STATIC_ASSERT (IFNAMSIZ == sizeof (addr->label)); - g_assert (!label || strlen (label) < IFNAMSIZ); + G_STATIC_ASSERT (NMP_IFNAMSIZ == sizeof (addr->label)); + g_assert (!label || strlen (label) < NMP_IFNAMSIZ); addr->ifindex = ifindex; addr->addr_source = source; diff --git a/src/nm-types.h b/src/nm-types.h index a0a7f620..277e0f6c 100644 --- a/src/nm-types.h +++ b/src/nm-types.h @@ -37,7 +37,6 @@ typedef struct _NMAuthSubject NMAuthSubject; typedef struct _NMDBusManager NMDBusManager; typedef struct _NMConfig NMConfig; typedef struct _NMConfigData NMConfigData; -typedef struct _NMAcdManager NMAcdManager; typedef struct _NMConnectivity NMConnectivity; typedef struct _NMDevice NMDevice; typedef struct _NMDhcp4Config NMDhcp4Config; @@ -52,6 +51,7 @@ typedef struct _NMPolicy NMPolicy; typedef struct _NMRfkillManager NMRfkillManager; typedef struct _NMPacrunnerManager NMPacrunnerManager; typedef struct _NMSessionMonitor NMSessionMonitor; +typedef struct _NMKeepAlive NMKeepAlive; typedef struct _NMSleepMonitor NMSleepMonitor; typedef struct _NMLldpListener NMLldpListener; typedef struct _NMConfigDeviceStateData NMConfigDeviceStateData; diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index 82f9e2fb..814d9cea 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -26,6 +26,7 @@ #include <unistd.h> #include <netinet/icmp6.h> #include <netinet/in.h> +#include <linux/if.h> #include <linux/rtnetlink.h> #include "nm-utils.h" @@ -194,7 +195,7 @@ link_add_prepare (NMPlatform *platform, { gboolean connected; - /* we must clear the driver, because platform cache want's to set it */ + /* we must clear the driver, because platform cache wants to set it */ g_assert (obj_tmp->link.driver == g_intern_string (obj_tmp->link.driver)); obj_tmp->link.driver = NULL; @@ -282,7 +283,7 @@ link_add_pre (NMPlatform *platform, return device; } -static gboolean +static int link_add (NMPlatform *platform, const char *name, NMLinkType type, @@ -334,7 +335,7 @@ link_add (NMPlatform *platform, if (veth_peer) link_changed (platform, device_veth, cache_op_veth, NULL); - return TRUE; + return 0; } static NMFakePlatformLink * @@ -562,7 +563,7 @@ link_set_noarp (NMPlatform *platform, int ifindex) return TRUE; } -static NMPlatformError +static int link_set_address (NMPlatform *platform, int ifindex, gconstpointer addr, size_t len) { NMFakePlatformLink *device = link_get (platform, ifindex); @@ -571,10 +572,10 @@ link_set_address (NMPlatform *platform, int ifindex, gconstpointer addr, size_t if ( len == 0 || len > NM_UTILS_HWADDR_LEN_MAX || !addr) - g_return_val_if_reached (NM_PLATFORM_ERROR_BUG); + g_return_val_if_reached (-NME_BUG); if (!device) - return NM_PLATFORM_ERROR_EXISTS; + return -NME_PL_EXISTS; obj_tmp = nmp_object_clone (device->obj, FALSE); obj_tmp->link.addr.len = len; @@ -582,10 +583,10 @@ link_set_address (NMPlatform *platform, int ifindex, gconstpointer addr, size_t memcpy (obj_tmp->link.addr.data, addr, len); link_set_obj (platform, device, obj_tmp); - return NM_PLATFORM_ERROR_SUCCESS; + return 0; } -static NMPlatformError +static int link_set_mtu (NMPlatform *platform, int ifindex, guint32 mtu) { NMFakePlatformLink *device = link_get (platform, ifindex); @@ -593,13 +594,13 @@ link_set_mtu (NMPlatform *platform, int ifindex, guint32 mtu) if (!device) { _LOGE ("failure changing link: netlink error (No such device)"); - return NM_PLATFORM_ERROR_EXISTS; + return -NME_PL_EXISTS; } obj_tmp = nmp_object_clone (device->obj, FALSE); obj_tmp->link.mtu = mtu; link_set_obj (platform, device, obj_tmp); - return NM_PLATFORM_ERROR_SUCCESS; + return 0; } static const char * @@ -1186,7 +1187,7 @@ object_delete (NMPlatform *platform, const NMPObject *obj) return ipx_route_delete (platform, AF_UNSPEC, -1, obj); } -static NMPlatformError +static int ip_route_add (NMPlatform *platform, NMPNlmFlags flags, int addr_family, @@ -1266,14 +1267,16 @@ ip_route_add (NMPlatform *platform, } } if (!has_route_to_gw) { + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + if (addr_family == AF_INET) { nm_log_warn (LOGD_PLATFORM, "Fake platform: failure adding ip4-route '%d: %s/%d %d': Network Unreachable", - r->ifindex, nm_utils_inet4_ntop (r4->network, NULL), r->plen, r->metric); + r->ifindex, nm_utils_inet4_ntop (r4->network, sbuf), r->plen, r->metric); } else { nm_log_warn (LOGD_PLATFORM, "Fake platform: failure adding ip6-route '%d: %s/%d %d': Network Unreachable", - r->ifindex, nm_utils_inet6_ntop (&r6->network, NULL), r->plen, r->metric); + r->ifindex, nm_utils_inet6_ntop (&r6->network, sbuf), r->plen, r->metric); } - return NM_PLATFORM_ERROR_UNSPECIFIED; + return -NME_UNSPEC; } } @@ -1335,7 +1338,7 @@ ip_route_add (NMPlatform *platform, } } - return NM_PLATFORM_ERROR_SUCCESS; + return 0; } /*****************************************************************************/ diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index e73d5d8c..85214ec8 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -44,6 +44,7 @@ #include "nm-core-internal.h" #include "nm-setting-vlan.h" +#include "nm-utils/nm-errno.h" #include "nm-utils/nm-secret-utils.h" #include "nm-netlink.h" #include "nm-core-utils.h" @@ -186,6 +187,12 @@ G_STATIC_ASSERT (RTA_MAX == (__RTA_MAX - 1)); #define WG_CMD_GET_DEVICE 0 #define WG_CMD_SET_DEVICE 1 +#define WGDEVICE_F_REPLACE_PEERS ((guint32) (1U << 0)) + +#define WGPEER_F_REMOVE_ME ((guint32) (1U << 0)) +#define WGPEER_F_REPLACE_ALLOWEDIPS ((guint32) (1U << 1)) + + #define WGDEVICE_A_UNSPEC 0 #define WGDEVICE_A_IFINDEX 1 #define WGDEVICE_A_IFNAME 2 @@ -447,7 +454,7 @@ G_DEFINE_TYPE (NMLinuxPlatform, nm_linux_platform, NM_TYPE_PLATFORM) if (nm_logging_enabled (__level, __domain)) { \ int __errsv = (errsv); \ \ - /* The %m format specifier (GNU extension) would alread allow you to specify the error + /* The %m format specifier (GNU extension) would already allow you to specify the error * message conveniently (and nm_log would get that right too). But we don't want to depend * on that, so instead append the message at the end. * Currently users are expected not to use %m in the format string. */ \ @@ -474,14 +481,14 @@ static struct nl_sock *_genl_sock (NMLinuxPlatform *platform); /*****************************************************************************/ -static NMPlatformError -wait_for_nl_response_to_plerr (WaitForNlResponseResult seq_result) +static int +wait_for_nl_response_to_nmerr (WaitForNlResponseResult seq_result) { if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) - return NM_PLATFORM_ERROR_SUCCESS; + return 0; if (seq_result < 0) - return (NMPlatformError) seq_result; - return NM_PLATFORM_ERROR_NETLINK; + return (int) seq_result; + return -NME_PL_NETLINK; } static const char * @@ -1942,9 +1949,10 @@ _wireguard_update_from_allowed_ips_nla (NMPWireGuardAllowedIP *allowed_ip, _check_addr_or_return_val (tb, WGALLOWEDIP_A_IPADDR, addr_len, FALSE); - memset (allowed_ip, 0, sizeof (NMPWireGuardAllowedIP)); + *allowed_ip = (NMPWireGuardAllowedIP) { + .family = family, + }; - allowed_ip->family = family; nm_assert ((int) allowed_ip->family == family); if (tb[WGALLOWEDIP_A_IPADDR]) @@ -2006,30 +2014,11 @@ _wireguard_update_from_peers_nla (CList *peers, nm_explicit_bzero (nla_data (tb[WGPEER_A_PRESHARED_KEY]), nla_len (tb[WGPEER_A_PRESHARED_KEY])); } - if (tb[WGPEER_A_ENDPOINT]) { - const struct sockaddr *addr = nla_data (tb[WGPEER_A_ENDPOINT]); - unsigned short family; - - G_STATIC_ASSERT (sizeof (addr->sa_family) == sizeof (family)); - memcpy (&family, &addr->sa_family, sizeof (addr->sa_family)); - - if ( family == AF_INET - && nla_len (tb[WGPEER_A_ENDPOINT]) == sizeof (struct sockaddr_in)) { - const struct sockaddr_in *addr4 = (const struct sockaddr_in *) addr; - - peer_c->data.endpoint_family = AF_INET; - peer_c->data.endpoint_port = unaligned_read_be16 (&addr4->sin_port); - peer_c->data.endpoint_addr.addr4 = unaligned_read_ne32 (&addr4->sin_addr.s_addr); - memcpy (&peer_c->data.endpoint_addr.addr4, &addr4->sin_addr.s_addr, 4); - } else if ( family == AF_INET6 - && nla_len (tb[WGPEER_A_ENDPOINT]) == sizeof (struct sockaddr_in6)) { - const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *) addr; - - peer_c->data.endpoint_family = AF_INET6; - peer_c->data.endpoint_port = unaligned_read_be16 (&addr6->sin6_port); - memcpy (&peer_c->data.endpoint_addr.addr6, &addr6->sin6_addr, 16); - } - } + + nm_sock_addr_union_cpy_untrusted (&peer_c->data.endpoint, + tb[WGPEER_A_ENDPOINT] ? nla_data (tb[WGPEER_A_ENDPOINT]) : NULL, + tb[WGPEER_A_ENDPOINT] ? nla_len (tb[WGPEER_A_ENDPOINT]) : 0); + if (tb[WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL]) peer_c->data.persistent_keepalive_interval = nla_get_u64 (tb[WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL]); if (tb[WGPEER_A_LAST_HANDSHAKE_TIME]) @@ -2157,9 +2146,9 @@ _wireguard_get_device_cb (struct nl_msg *msg, void *arg) static const NMPObject * _wireguard_read_info (NMPlatform *platform /* used only as logging context */, - struct nl_sock *genl, - int wireguard_family_id, - int ifindex) + struct nl_sock *genl, + int wireguard_family_id, + int ifindex) { nm_auto_nlmsg struct nl_msg *msg = NULL; NMPObject *obj = NULL; @@ -2175,6 +2164,8 @@ _wireguard_read_info (NMPlatform *platform /* used only as logging context */, nm_assert (wireguard_family_id >= 0); nm_assert (ifindex > 0); + _LOGT ("wireguard: fetching infomation for ifindex %d (genl-id %d)...", ifindex, wireguard_family_id); + msg = nlmsg_alloc (); if (!genlmsg_put (msg, @@ -2227,7 +2218,7 @@ _wireguard_read_info (NMPlatform *platform /* used only as logging context */, * there. The realloc/resize of the GArray is fine there. However, * while we build the GArray, we don't yet have the final pointers. * Hence, while constructing, we track the indexes with peer->_construct_idx_* - * fields. These indexes must be convered to actual pointers blow. + * fields. These indexes must be converted to actual pointers blow. * * This is all done during parsing. In the final NMPObjectLnkWireGuard we * don't want the CList anymore and repackage the NMPObject tightly. The @@ -2283,6 +2274,336 @@ nla_put_failure: g_return_val_if_reached (NULL); } +static int +_wireguard_get_family_id (NMPlatform *platform, int ifindex_try) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + int wireguard_family_id = -1; + + if (ifindex_try > 0) { + const NMPlatformLink *plink; + + if (nm_platform_link_get_lnk_wireguard (platform, ifindex_try, &plink)) + wireguard_family_id = NMP_OBJECT_UP_CAST (plink)->_link.wireguard_family_id; + } + if (wireguard_family_id < 0) + wireguard_family_id = genl_ctrl_resolve (priv->genl, "wireguard"); + return wireguard_family_id; +} + +static const NMPObject * +_wireguard_refresh_link (NMPlatform *platform, + int wireguard_family_id, + int ifindex) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + nm_auto_nmpobj const NMPObject *obj_old = NULL; + nm_auto_nmpobj const NMPObject *obj_new = NULL; + nm_auto_nmpobj const NMPObject *lnk_new = NULL; + NMPCacheOpsType cache_op; + const NMPObject *plink = NULL; + nm_auto_nmpobj NMPObject *obj = NULL; + + nm_assert (wireguard_family_id >= 0); + nm_assert (ifindex > 0); + + nm_platform_process_events (platform); + + plink = nm_platform_link_get_obj (platform, ifindex, TRUE); + + if ( !plink + || plink->link.type != NM_LINK_TYPE_WIREGUARD) { + nm_platform_link_refresh (platform, ifindex); + plink = nm_platform_link_get_obj (platform, ifindex, TRUE); + if ( !plink + || plink->link.type != NM_LINK_TYPE_WIREGUARD) + return NULL; + if (NMP_OBJECT_GET_TYPE (plink->_link.netlink.lnk) == NMP_OBJECT_TYPE_LNK_WIREGUARD) + lnk_new = nmp_object_ref (plink->_link.netlink.lnk); + } else { + lnk_new = _wireguard_read_info (platform, + priv->genl, + wireguard_family_id, + ifindex); + if (!lnk_new) { + if (NMP_OBJECT_GET_TYPE (plink->_link.netlink.lnk) == NMP_OBJECT_TYPE_LNK_WIREGUARD) + lnk_new = nmp_object_ref (plink->_link.netlink.lnk); + } else if (nmp_object_equal (plink->_link.netlink.lnk, lnk_new)) { + nmp_object_unref (lnk_new); + lnk_new = nmp_object_ref (plink->_link.netlink.lnk); + } + } + + if ( plink->_link.wireguard_family_id == wireguard_family_id + && plink->_link.netlink.lnk == lnk_new) + return plink; + + /* we use nmp_cache_update_netlink() to re-inject the new object into the cache. + * For that, we need to clone it, and tweak it so that it's suitable. It's a bit + * of a hack, in particular that we need to clear driver and udev-device. */ + obj = nmp_object_clone (plink, FALSE); + obj->_link.wireguard_family_id = wireguard_family_id; + nmp_object_unref (obj->_link.netlink.lnk); + obj->_link.netlink.lnk = g_steal_pointer (&lnk_new); + obj->link.driver = NULL; + nm_clear_pointer (&obj->_link.udev.device, udev_device_unref); + + cache_op = nmp_cache_update_netlink (nm_platform_get_cache (platform), + obj, + FALSE, + &obj_old, + &obj_new); + nm_assert (NM_IN_SET (cache_op, NMP_CACHE_OPS_UPDATED)); + if (cache_op != NMP_CACHE_OPS_UNCHANGED) { + cache_on_change (platform, cache_op, obj_old, obj_new); + nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new); + } + + nm_assert ( !obj_new + || ( NMP_OBJECT_GET_TYPE (obj_new) == NMP_OBJECT_TYPE_LINK + && obj_new->link.type == NM_LINK_TYPE_WIREGUARD + && ( !obj_new->_link.netlink.lnk + || NMP_OBJECT_GET_TYPE (obj_new->_link.netlink.lnk) == NMP_OBJECT_TYPE_LNK_WIREGUARD))); + return obj_new; +} + +static int +_wireguard_create_change_nlmsgs (NMPlatform *platform, + int ifindex, + int wireguard_family_id, + const NMPlatformLnkWireGuard *lnk_wireguard, + const NMPWireGuardPeer *peers, + guint peers_len, + gboolean replace_peers, + GPtrArray **out_msgs) +{ + gs_unref_ptrarray GPtrArray *msgs = NULL; + nm_auto_nlmsg struct nl_msg *msg = NULL; + const guint IDX_NIL = G_MAXUINT; + guint idx_peer_curr; + guint idx_allowed_ips_curr; + struct nlattr *nest_peers; + struct nlattr *nest_curr_peer; + struct nlattr *nest_allowed_ips; + struct nlattr *nest_curr_allowed_ip; + +#define _nla_nest_end(msg, nest_start) \ + G_STMT_START { \ + if (nla_nest_end ((msg), (nest_start)) < 0) \ + g_return_val_if_reached (-NME_BUG); \ + } G_STMT_END + + /* Adapted from LGPL-2.1+ code [1]. + * + * [1] https://git.zx2c4.com/WireGuard/tree/contrib/examples/embeddable-wg-library/wireguard.c?id=5e99a6d43fe2351adf36c786f5ea2086a8fe7ab8#n1073 */ + + idx_peer_curr = IDX_NIL; + idx_allowed_ips_curr = IDX_NIL; + + /* TODO: for the moment, we always reset all peers and allowed-ips (WGDEVICE_F_REPLACE_PEERS, WGPEER_F_REPLACE_ALLOWEDIPS). + * The platform API should be extended to also support partial updates. In particular, configuring the same configuration + * multiple times, should not clear and re-add all settings, but rather sync the existing settings with the desired configuration. */ + +again: + + msg = nlmsg_alloc (); + if (!genlmsg_put (msg, + NL_AUTO_PORT, + NL_AUTO_SEQ, + wireguard_family_id, + 0, + NLM_F_REQUEST, + WG_CMD_SET_DEVICE, + 1)) + g_return_val_if_reached (-NME_BUG); + + NLA_PUT_U32 (msg, WGDEVICE_A_IFINDEX, (guint32) ifindex); + + if (idx_peer_curr == IDX_NIL) { + NLA_PUT (msg, WGDEVICE_A_PRIVATE_KEY, sizeof (lnk_wireguard->private_key), lnk_wireguard->private_key); + NLA_PUT_U16 (msg, WGDEVICE_A_LISTEN_PORT, lnk_wireguard->listen_port); + NLA_PUT_U32 (msg, WGDEVICE_A_FWMARK, lnk_wireguard->fwmark); + + NLA_PUT_U32 (msg, WGDEVICE_A_FLAGS, + replace_peers ? WGDEVICE_F_REPLACE_PEERS : ((guint32) 0u)); + } + + if (peers_len == 0) + goto send; + + nest_curr_peer = NULL; + nest_allowed_ips = NULL; + nest_curr_allowed_ip = NULL; + + nest_peers = nla_nest_start (msg, WGDEVICE_A_PEERS); + if (!nest_peers) + g_return_val_if_reached (-NME_BUG); + + if (idx_peer_curr == IDX_NIL) + idx_peer_curr = 0; + for (; idx_peer_curr < peers_len; idx_peer_curr++) { + const NMPWireGuardPeer *p = &peers[idx_peer_curr]; + + nest_curr_peer = nla_nest_start (msg, 0); + if (!nest_curr_peer) + goto toobig_peers; + + if (nla_put (msg, WGPEER_A_PUBLIC_KEY, NMP_WIREGUARD_PUBLIC_KEY_LEN, p->public_key) < 0) + goto toobig_peers; + + if (idx_allowed_ips_curr == IDX_NIL) { + + if (nla_put (msg, WGPEER_A_PRESHARED_KEY, sizeof (p->preshared_key), p->preshared_key) < 0) + goto toobig_peers; + + if (nla_put_uint16 (msg, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, p->persistent_keepalive_interval) < 0) + goto toobig_peers; + + if (nla_put_uint32 (msg, WGPEER_A_FLAGS, WGPEER_F_REPLACE_ALLOWEDIPS) < 0) + goto toobig_peers; + + if (NM_IN_SET (p->endpoint.sa.sa_family, AF_INET, AF_INET6)) { + if (nla_put (msg, + WGPEER_A_ENDPOINT, + p->endpoint.sa.sa_family == AF_INET + ? sizeof (p->endpoint.in) + : sizeof (p->endpoint.in6), + &p->endpoint) < 0) + goto toobig_peers; + } else + nm_assert (p->endpoint.sa.sa_family == AF_UNSPEC); + } + + if (p->allowed_ips_len > 0) { + if (idx_allowed_ips_curr == IDX_NIL) + idx_allowed_ips_curr = 0; + + nest_allowed_ips = nla_nest_start (msg, WGPEER_A_ALLOWEDIPS); + if (!nest_allowed_ips) + goto toobig_allowedips; + + for (; idx_allowed_ips_curr < p->allowed_ips_len; idx_allowed_ips_curr++) { + const NMPWireGuardAllowedIP *aip = &p->allowed_ips[idx_allowed_ips_curr]; + + nest_curr_allowed_ip = nla_nest_start (msg, 0); + if (!nest_curr_allowed_ip) + goto toobig_allowedips; + + g_return_val_if_fail (NM_IN_SET (aip->family, AF_INET, AF_INET6), -NME_BUG); + + if (nla_put_uint16 (msg, WGALLOWEDIP_A_FAMILY, aip->family) < 0) + goto toobig_allowedips; + if (nla_put (msg, + WGALLOWEDIP_A_IPADDR, + nm_utils_addr_family_to_size (aip->family), + &aip->addr) < 0) + goto toobig_allowedips; + if (nla_put_uint8 (msg, WGALLOWEDIP_A_CIDR_MASK, aip->mask) < 0) + goto toobig_allowedips; + + _nla_nest_end (msg, nest_curr_allowed_ip); + nest_curr_allowed_ip = NULL; + } + idx_allowed_ips_curr = IDX_NIL; + + _nla_nest_end (msg, nest_allowed_ips); + nest_allowed_ips = NULL; + } + + _nla_nest_end (msg, nest_curr_peer); + nest_curr_peer = NULL; + } + + _nla_nest_end (msg, nest_peers); + goto send; + +toobig_allowedips: + if (nest_curr_allowed_ip) + nla_nest_cancel (msg, nest_curr_allowed_ip); + if (nest_allowed_ips) + nla_nest_cancel (msg, nest_allowed_ips); + _nla_nest_end (msg, nest_curr_peer); + _nla_nest_end (msg, nest_peers); + goto send; + +toobig_peers: + if (nest_curr_peer) + nla_nest_cancel (msg, nest_curr_peer); + _nla_nest_end (msg, nest_peers); + goto send; + +send: + if (!msgs) + msgs = g_ptr_array_new_with_free_func ((GDestroyNotify) nlmsg_free); + g_ptr_array_add (msgs, g_steal_pointer (&msg)); + + if ( idx_peer_curr != IDX_NIL + && idx_peer_curr < peers_len) + goto again; + + NM_SET_OUT (out_msgs, g_steal_pointer (&msgs)); + return 0; + +nla_put_failure: + g_return_val_if_reached (-NME_BUG); + +#undef _nla_nest_end +} + +static int +link_wireguard_change (NMPlatform *platform, + int ifindex, + const NMPlatformLnkWireGuard *lnk_wireguard, + const NMPWireGuardPeer *peers, + guint peers_len, + gboolean replace_peers) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + gs_unref_ptrarray GPtrArray *msgs = NULL; + int wireguard_family_id; + guint i; + int r; + + wireguard_family_id = _wireguard_get_family_id (platform, ifindex); + if (wireguard_family_id < 0) + return -NME_PL_NO_FIRMWARE; + + r = _wireguard_create_change_nlmsgs (platform, + ifindex, + wireguard_family_id, + lnk_wireguard, + peers, + peers_len, + replace_peers, + &msgs); + if (r < 0) { + _LOGW ("wireguard: set-device, cannot construct netlink message: %s", nm_strerror (r)); + return r; + } + + for (i = 0; i < msgs->len; i++) { + r = nl_send_auto (priv->genl, msgs->pdata[i]); + if (r < 0) { + _LOGW ("wireguard: set-device, send netlink message #%u failed: %s", i, nm_strerror (r)); + return r; + } + + do { + r = nl_recvmsgs (priv->genl, NULL); + } while (r == -EAGAIN); + if (r < 0) { + _LOGW ("wireguard: set-device, message #%u was rejected: %s", i, nm_strerror (r)); + return r; + } + + _LOGT ("wireguard: set-device, message #%u sent and confirmed", i); + } + + _wireguard_refresh_link (platform, wireguard_family_id, ifindex); + + return 0; +} + /*****************************************************************************/ /* Copied and heavily modified from libnl3's link_msg_parser(). */ @@ -4745,7 +5066,7 @@ _nl_send_nlmsg (NMPlatform *platform, nle = nl_send_auto (priv->nlh, nlmsg); if (nle < 0) { - _LOGD ("netlink: nl-send-nlmsg: failed sending message: %s (%d)", nl_geterror (nle), nle); + _LOGD ("netlink: nl-send-nlmsg: failed sending message: %s (%d)", nm_strerror (nle), nle); return nle; } @@ -4791,7 +5112,7 @@ do_request_link_no_delayed_actions (NMPlatform *platform, int ifindex, const cha if (nle < 0) { _LOGE ("do-request-link: %d %s: failed sending netlink request \"%s\" (%d)", ifindex, name ?: "", - nl_geterror (nle), -nle); + nm_strerror (nle), -nle); return; } } @@ -5121,7 +5442,7 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event /*****************************************************************************/ -static gboolean +static int do_add_link_with_lookup (NMPlatform *platform, NMLinkType link_type, const char *name, @@ -5142,9 +5463,9 @@ do_add_link_with_lookup (NMPlatform *platform, _LOGE ("do-add-link[%s/%s]: failed sending netlink request \"%s\" (%d)", name, nm_link_type_to_string (link_type), - nl_geterror (nle), -nle); + nm_strerror (nle), -nle); NM_SET_OUT (out_link, NULL); - return FALSE; + return nle; } delayed_action_handle_all (platform, FALSE); @@ -5164,10 +5485,10 @@ do_add_link_with_lookup (NMPlatform *platform, *out_link = NMP_OBJECT_CAST_LINK (obj); } - return seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK; + return wait_for_nl_response_to_nmerr (seq_result); } -static NMPlatformError +static int do_add_addrroute (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *nlmsg, @@ -5189,8 +5510,8 @@ do_add_addrroute (NMPlatform *platform, _LOGE ("do-add-%s[%s]: failure sending netlink request \"%s\" (%d)", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), - nl_geterror (nle), -nle); - return NM_PLATFORM_ERROR_NETLINK; + nm_strerror (nle), -nle); + return -NME_PL_NETLINK; } delayed_action_handle_all (platform, FALSE); @@ -5219,7 +5540,7 @@ do_add_addrroute (NMPlatform *platform, do_request_one_type (platform, NMP_OBJECT_GET_TYPE (obj_id)); } - return wait_for_nl_response_to_plerr (seq_result); + return wait_for_nl_response_to_nmerr (seq_result); } static gboolean @@ -5239,7 +5560,7 @@ do_delete_object (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg * _LOGE ("do-delete-%s[%s]: failure sending netlink request \"%s\" (%d)", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), - nl_geterror (nle), -nle); + nm_strerror (nle), -nle); return FALSE; } @@ -5287,7 +5608,7 @@ do_delete_object (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg * return success; } -static NMPlatformError +static int do_change_link (NMPlatform *platform, ChangeLinkType change_link_type, int ifindex, @@ -5299,7 +5620,7 @@ do_change_link (NMPlatform *platform, WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; gs_free char *errmsg = NULL; char s_buf[256]; - NMPlatformError result = NM_PLATFORM_ERROR_SUCCESS; + int result = 0; NMLogLevel log_level = LOGL_DEBUG; const char *log_result = "failure"; const char *log_detail = ""; @@ -5317,7 +5638,7 @@ retry: if (nle < 0) { log_level = LOGL_ERR; log_detail_free = g_strdup_printf (", failure sending netlink request: %s (%d)", - nl_geterror (nle), -nle); + nm_strerror (nle), -nle); log_detail = log_detail_free; goto out; } @@ -5342,11 +5663,11 @@ retry: /* */ } else if (NM_IN_SET (-((int) seq_result), ESRCH, ENOENT)) { log_detail = ", firmware not found"; - result = NM_PLATFORM_ERROR_NO_FIRMWARE; + result = -NME_PL_NO_FIRMWARE; } else if ( NM_IN_SET (-((int) seq_result), ERANGE) && change_link_type == CHANGE_LINK_TYPE_SET_MTU) { log_detail = ", setting MTU to requested size is not possible"; - result = NM_PLATFORM_ERROR_CANT_SET_MTU; + result = -NME_PL_CANT_SET_MTU; } else if ( NM_IN_SET (-((int) seq_result), ENFILE) && change_link_type == CHANGE_LINK_TYPE_SET_ADDRESS && (obj_cache = nmp_cache_lookup_link (nm_platform_get_cache (platform), ifindex)) @@ -5356,16 +5677,16 @@ retry: * If the MAC address is as expected, assume success? */ log_result = "success"; log_detail = " (assume success changing address)"; - result = NM_PLATFORM_ERROR_SUCCESS; + result = 0; } else if (NM_IN_SET (-((int) seq_result), ENODEV)) { log_level = LOGL_DEBUG; - result = NM_PLATFORM_ERROR_NOT_FOUND; + result = -NME_PL_NOT_FOUND; } else if (-((int) seq_result) == EAFNOSUPPORT) { log_level = LOGL_DEBUG; - result = NM_PLATFORM_ERROR_OPNOTSUPP; + result = -NME_PL_OPNOTSUPP; } else { log_level = LOGL_WARN; - result = NM_PLATFORM_ERROR_UNSPECIFIED; + result = -NME_UNSPEC; } out: @@ -5378,7 +5699,7 @@ out: return result; } -static gboolean +static int link_add (NMPlatform *platform, const char *name, NMLinkType type, @@ -5408,17 +5729,17 @@ link_add (NMPlatform *platform, 0, 0); if (!nlmsg) - return FALSE; + return -NME_UNSPEC; if (address && address_len) NLA_PUT (nlmsg, IFLA_ADDRESS, address_len, address); if (!_nl_msg_new_link_set_linkinfo (nlmsg, type, veth_peer)) - return FALSE; + return -NME_UNSPEC; return do_add_link_with_lookup (platform, type, name, nlmsg, out_link); nla_put_failure: - g_return_val_if_reached (FALSE); + g_return_val_if_reached (-NME_BUG); } static gboolean @@ -5473,13 +5794,13 @@ link_set_netns (NMPlatform *platform, return FALSE; NLA_PUT (nlmsg, IFLA_NET_NS_FD, 4, &netns_fd); - return do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) == NM_PLATFORM_ERROR_SUCCESS; + return (do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) >= 0); nla_put_failure: g_return_val_if_reached (FALSE); } -static NMPlatformError +static int link_change_flags (NMPlatform *platform, int ifindex, unsigned flags_mask, @@ -5502,37 +5823,36 @@ link_change_flags (NMPlatform *platform, flags_mask, flags_set); if (!nlmsg) - return NM_PLATFORM_ERROR_UNSPECIFIED; + return -NME_UNSPEC; return do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL); } static gboolean link_set_up (NMPlatform *platform, int ifindex, gboolean *out_no_firmware) { - NMPlatformError plerr; + int r; - plerr = link_change_flags (platform, ifindex, IFF_UP, IFF_UP); - if (out_no_firmware) - *out_no_firmware = plerr == NM_PLATFORM_ERROR_NO_FIRMWARE; - return plerr == NM_PLATFORM_ERROR_SUCCESS; + r = link_change_flags (platform, ifindex, IFF_UP, IFF_UP); + NM_SET_OUT (out_no_firmware, (r == -NME_PL_NO_FIRMWARE)); + return r >= 0; } static gboolean link_set_down (NMPlatform *platform, int ifindex) { - return link_change_flags (platform, ifindex, IFF_UP, 0) == NM_PLATFORM_ERROR_SUCCESS; + return (link_change_flags (platform, ifindex, IFF_UP, 0) >= 0); } static gboolean link_set_arp (NMPlatform *platform, int ifindex) { - return link_change_flags (platform, ifindex, IFF_NOARP, 0) == NM_PLATFORM_ERROR_SUCCESS; + return (link_change_flags (platform, ifindex, IFF_NOARP, 0) >= 0); } static gboolean link_set_noarp (NMPlatform *platform, int ifindex) { - return link_change_flags (platform, ifindex, IFF_NOARP, IFF_NOARP) == NM_PLATFORM_ERROR_SUCCESS; + return (link_change_flags (platform, ifindex, IFF_NOARP, IFF_NOARP) >= 0); } static const char * @@ -5547,7 +5867,7 @@ link_get_udi (NMPlatform *platform, int ifindex) return udev_device_get_syspath (obj->_link.udev.device); } -static NMPlatformError +static int link_set_user_ipv6ll_enabled (NMPlatform *platform, int ifindex, gboolean enabled) { nm_auto_nlmsg struct nl_msg *nlmsg = NULL; @@ -5559,7 +5879,7 @@ link_set_user_ipv6ll_enabled (NMPlatform *platform, int ifindex, gboolean enable if (!_support_user_ipv6ll_get ()) { _LOGD ("link: change %d: user-ipv6ll: not supported", ifindex); - return NM_PLATFORM_ERROR_OPNOTSUPP; + return -NME_PL_OPNOTSUPP; } nlmsg = _nl_msg_new_link (RTM_NEWLINK, @@ -5570,7 +5890,7 @@ link_set_user_ipv6ll_enabled (NMPlatform *platform, int ifindex, gboolean enable 0); if ( !nlmsg || !_nl_msg_new_link_set_afspec (nlmsg, mode, NULL)) - g_return_val_if_reached (NM_PLATFORM_ERROR_BUG); + g_return_val_if_reached (-NME_BUG); return do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL); } @@ -5579,15 +5899,16 @@ static gboolean link_set_token (NMPlatform *platform, int ifindex, NMUtilsIPv6IfaceId iid) { nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; _LOGD ("link: change %d: token: set IPv6 address generation token to %s", - ifindex, nm_utils_inet6_interface_identifier_to_token (iid, NULL)); + ifindex, nm_utils_inet6_interface_identifier_to_token (iid, sbuf)); nlmsg = _nl_msg_new_link (RTM_NEWLINK, 0, ifindex, NULL, 0, 0); if (!nlmsg || !_nl_msg_new_link_set_afspec (nlmsg, -1, &iid)) g_return_val_if_reached (FALSE); - return do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) == NM_PLATFORM_ERROR_SUCCESS; + return (do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) >= 0); } static gboolean @@ -5647,7 +5968,7 @@ link_supports_sriov (NMPlatform *platform, int ifindex) return total > 0; } -static NMPlatformError +static int link_set_address (NMPlatform *platform, int ifindex, gconstpointer address, size_t length) { nm_auto_nlmsg struct nl_msg *nlmsg = NULL; @@ -5659,7 +5980,7 @@ link_set_address (NMPlatform *platform, int ifindex, gconstpointer address, size }; if (!address || !length) - g_return_val_if_reached (NM_PLATFORM_ERROR_BUG); + g_return_val_if_reached (-NME_BUG); nlmsg = _nl_msg_new_link (RTM_NEWLINK, 0, @@ -5668,16 +5989,16 @@ link_set_address (NMPlatform *platform, int ifindex, gconstpointer address, size 0, 0); if (!nlmsg) - g_return_val_if_reached (NM_PLATFORM_ERROR_UNSPECIFIED); + g_return_val_if_reached (-NME_BUG); NLA_PUT (nlmsg, IFLA_ADDRESS, length, address); return do_change_link (platform, CHANGE_LINK_TYPE_SET_ADDRESS, ifindex, nlmsg, &d); nla_put_failure: - g_return_val_if_reached (NM_PLATFORM_ERROR_UNSPECIFIED); + g_return_val_if_reached (-NME_BUG); } -static NMPlatformError +static int link_set_name (NMPlatform *platform, int ifindex, const char *name) { nm_auto_nlmsg struct nl_msg *nlmsg = NULL; @@ -5689,11 +6010,11 @@ link_set_name (NMPlatform *platform, int ifindex, const char *name) 0, 0); if (!nlmsg) - g_return_val_if_reached (NM_PLATFORM_ERROR_UNSPECIFIED); + g_return_val_if_reached (-NME_BUG); NLA_PUT (nlmsg, IFLA_IFNAME, strlen (name) + 1, name); - return do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) == NM_PLATFORM_ERROR_SUCCESS; + return (do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) >= 0); nla_put_failure: g_return_val_if_reached (FALSE); } @@ -5712,7 +6033,7 @@ link_get_permanent_address (NMPlatform *platform, return nmp_utils_ethtool_get_permanent_address (ifindex, buf, length); } -static NMPlatformError +static int link_set_mtu (NMPlatform *platform, int ifindex, guint32 mtu) { nm_auto_nlmsg struct nl_msg *nlmsg = NULL; @@ -5737,12 +6058,13 @@ static gboolean link_set_sriov_params (NMPlatform *platform, int ifindex, guint num_vfs, - int autoprobe) + NMTernary autoprobe) { nm_auto_pop_netns NMPNetns *netns = NULL; nm_auto_close int dirfd = -1; - gboolean current_autoprobe; - guint total, current_num; + int current_autoprobe; + guint total; + gint64 current_num; char ifname[IFNAMSIZ]; char buf[64]; @@ -5775,14 +6097,14 @@ link_set_sriov_params (NMPlatform *platform, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname, "device/sriov_numvfs"), - 10, 0, G_MAXUINT, 0); + 10, 0, G_MAXUINT, -1); current_autoprobe = nm_platform_sysctl_get_int_checked (platform, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname, "device/sriov_drivers_autoprobe"), - 10, 0, G_MAXUINT, 0); + 10, 0, 1, -1); if ( current_num == num_vfs - && (autoprobe == -1 || current_autoprobe == autoprobe)) + && (autoprobe == NM_TERNARY_DEFAULT || current_autoprobe == autoprobe)) return TRUE; if (current_num != 0) { @@ -5800,14 +6122,14 @@ link_set_sriov_params (NMPlatform *platform, if (num_vfs == 0) return TRUE; - if ( autoprobe >= 0 + if ( NM_IN_SET (autoprobe, NM_TERNARY_TRUE, NM_TERNARY_FALSE) && current_autoprobe != autoprobe && !nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname, "device/sriov_drivers_autoprobe"), - nm_sprintf_buf (buf, "%d", autoprobe))) { - _LOGW ("link: couldn't set SR-IOV drivers-autoprobe to %d: %s", autoprobe, strerror (errno)); + nm_sprintf_buf (buf, "%d", (int) autoprobe))) { + _LOGW ("link: couldn't set SR-IOV drivers-autoprobe to %d: %s", (int) autoprobe, strerror (errno)); return FALSE; } @@ -5837,7 +6159,7 @@ link_set_sriov_vfs (NMPlatform *platform, int ifindex, const NMPlatformVF *const 0, 0); if (!nlmsg) - g_return_val_if_reached (NM_PLATFORM_ERROR_UNSPECIFIED); + g_return_val_if_reached (-NME_BUG); if (!(list = nla_nest_start (nlmsg, IFLA_VFINFO_LIST))) goto nla_put_failure; @@ -5913,7 +6235,7 @@ link_set_sriov_vfs (NMPlatform *platform, int ifindex, const NMPlatformVF *const } nla_nest_end (nlmsg, list); - return do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) == NM_PLATFORM_ERROR_SUCCESS; + return (do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) >= 0); nla_put_failure: g_return_val_if_reached (FALSE); } @@ -5981,7 +6303,7 @@ vlan_add (NMPlatform *platform, 0)) return FALSE; - return do_add_link_with_lookup (platform, NM_LINK_TYPE_VLAN, name, nlmsg, out_link); + return (do_add_link_with_lookup (platform, NM_LINK_TYPE_VLAN, name, nlmsg, out_link) >= 0); nla_put_failure: g_return_val_if_reached (FALSE); } @@ -6028,9 +6350,9 @@ link_gre_add (NMPlatform *platform, nla_nest_end (nlmsg, data); nla_nest_end (nlmsg, info); - return do_add_link_with_lookup (platform, - props->is_tap ? NM_LINK_TYPE_GRETAP : NM_LINK_TYPE_GRE, - name, nlmsg, out_link); + return (do_add_link_with_lookup (platform, + props->is_tap ? NM_LINK_TYPE_GRETAP : NM_LINK_TYPE_GRE, + name, nlmsg, out_link) >= 0); nla_put_failure: g_return_val_if_reached (FALSE); } @@ -6086,7 +6408,7 @@ link_ip6tnl_add (NMPlatform *platform, nla_nest_end (nlmsg, data); nla_nest_end (nlmsg, info); - return do_add_link_with_lookup (platform, NM_LINK_TYPE_IP6TNL, name, nlmsg, out_link); + return (do_add_link_with_lookup (platform, NM_LINK_TYPE_IP6TNL, name, nlmsg, out_link) >= 0); nla_put_failure: g_return_val_if_reached (FALSE); } @@ -6146,9 +6468,9 @@ link_ip6gre_add (NMPlatform *platform, nla_nest_end (nlmsg, data); nla_nest_end (nlmsg, info); - return do_add_link_with_lookup (platform, - props->is_tap ? NM_LINK_TYPE_IP6GRETAP : NM_LINK_TYPE_IP6GRE, - name, nlmsg, out_link); + return (do_add_link_with_lookup (platform, + props->is_tap ? NM_LINK_TYPE_IP6GRETAP : NM_LINK_TYPE_IP6GRE, + name, nlmsg, out_link) >= 0); nla_put_failure: g_return_val_if_reached (FALSE); } @@ -6191,7 +6513,7 @@ link_ipip_add (NMPlatform *platform, nla_nest_end (nlmsg, data); nla_nest_end (nlmsg, info); - return do_add_link_with_lookup (platform, NM_LINK_TYPE_IPIP, name, nlmsg, out_link); + return (do_add_link_with_lookup (platform, NM_LINK_TYPE_IPIP, name, nlmsg, out_link) >= 0); nla_put_failure: g_return_val_if_reached (FALSE); } @@ -6246,9 +6568,9 @@ link_macsec_add (NMPlatform *platform, nla_nest_end (nlmsg, data); nla_nest_end (nlmsg, info); - return do_add_link_with_lookup (platform, - NM_LINK_TYPE_MACSEC, - name, nlmsg, out_link); + return (do_add_link_with_lookup (platform, + NM_LINK_TYPE_MACSEC, + name, nlmsg, out_link) >= 0); nla_put_failure: g_return_val_if_reached (FALSE); } @@ -6289,9 +6611,9 @@ link_macvlan_add (NMPlatform *platform, nla_nest_end (nlmsg, data); nla_nest_end (nlmsg, info); - return do_add_link_with_lookup (platform, - props->tap ? NM_LINK_TYPE_MACVTAP : NM_LINK_TYPE_MACVLAN, - name, nlmsg, out_link); + return (do_add_link_with_lookup (platform, + props->tap ? NM_LINK_TYPE_MACVTAP : NM_LINK_TYPE_MACVLAN, + name, nlmsg, out_link) >= 0); nla_put_failure: g_return_val_if_reached (FALSE); } @@ -6334,7 +6656,7 @@ link_sit_add (NMPlatform *platform, nla_nest_end (nlmsg, data); nla_nest_end (nlmsg, info); - return do_add_link_with_lookup (platform, NM_LINK_TYPE_SIT, name, nlmsg, out_link); + return (do_add_link_with_lookup (platform, NM_LINK_TYPE_SIT, name, nlmsg, out_link) >= 0); nla_put_failure: g_return_val_if_reached (FALSE); } @@ -6460,7 +6782,7 @@ link_vxlan_add (NMPlatform *platform, nla_nest_end (nlmsg, data); nla_nest_end (nlmsg, info); - return do_add_link_with_lookup (platform, NM_LINK_TYPE_VXLAN, name, nlmsg, out_link); + return (do_add_link_with_lookup (platform, NM_LINK_TYPE_VXLAN, name, nlmsg, out_link) >= 0); nla_put_failure: g_return_val_if_reached (FALSE); } @@ -6492,9 +6814,9 @@ link_6lowpan_add (NMPlatform *platform, nla_nest_end (nlmsg, info); - return do_add_link_with_lookup (platform, - NM_LINK_TYPE_6LOWPAN, - name, nlmsg, out_link); + return (do_add_link_with_lookup (platform, + NM_LINK_TYPE_6LOWPAN, + name, nlmsg, out_link) >= 0); nla_put_failure: g_return_val_if_reached (FALSE); } @@ -6641,7 +6963,7 @@ link_vlan_change (NMPlatform *platform, new_n_egress_map)) g_return_val_if_reached (FALSE); - return do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) == NM_PLATFORM_ERROR_SUCCESS; + return (do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) >= 0); } static gboolean @@ -6661,7 +6983,7 @@ link_enslave (NMPlatform *platform, int master, int slave) NLA_PUT_U32 (nlmsg, IFLA_MASTER, master); - return do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) == NM_PLATFORM_ERROR_SUCCESS; + return (do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) >= 0); nla_put_failure: g_return_val_if_reached (FALSE); } @@ -6947,6 +7269,13 @@ wpan_set_short_addr (NMPlatform *platform, int ifindex, guint16 short_addr) return nm_wpan_utils_set_short_addr (wpan_data, short_addr); } +static gboolean +wpan_set_channel (NMPlatform *platform, int ifindex, guint8 page, guint8 channel) +{ + WPAN_GET_WPAN_DATA (wpan_data, platform, ifindex, FALSE); + return nm_wpan_utils_set_channel (wpan_data, page, channel); +} + /*****************************************************************************/ static gboolean @@ -7022,7 +7351,7 @@ ip4_address_add (NMPlatform *platform, label); nmp_object_stackinit_id_ip4_address (&obj_id, ifindex, addr, plen, peer_addr); - return do_add_addrroute (platform, &obj_id, nlmsg, FALSE) == NM_PLATFORM_ERROR_SUCCESS; + return (do_add_addrroute (platform, &obj_id, nlmsg, FALSE) >= 0); } static gboolean @@ -7052,7 +7381,7 @@ ip6_address_add (NMPlatform *platform, NULL); nmp_object_stackinit_id_ip6_address (&obj_id, ifindex, &addr); - return do_add_addrroute (platform, &obj_id, nlmsg, FALSE) == NM_PLATFORM_ERROR_SUCCESS; + return (do_add_addrroute (platform, &obj_id, nlmsg, FALSE) >= 0); } static gboolean @@ -7107,7 +7436,7 @@ ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, gui /*****************************************************************************/ -static NMPlatformError +static int ip_route_add (NMPlatform *platform, NMPNlmFlags flags, int addr_family, @@ -7131,7 +7460,7 @@ ip_route_add (NMPlatform *platform, nlmsg = _nl_msg_new_route (RTM_NEWROUTE, flags & NMP_NLM_FLAG_FMASK, &obj); if (!nlmsg) - g_return_val_if_reached (NM_PLATFORM_ERROR_BUG); + g_return_val_if_reached (-NME_BUG); return do_add_addrroute (platform, &obj, nlmsg, @@ -7170,7 +7499,7 @@ object_delete (NMPlatform *platform, /*****************************************************************************/ -static NMPlatformError +static int ip_route_get (NMPlatform *platform, int addr_family, gconstpointer address, @@ -7220,7 +7549,7 @@ ip_route_get (NMPlatform *platform, if (nle < 0) { _LOGE ("get-route: failure sending netlink request \"%s\" (%d)", g_strerror (-nle), -nle); - return NM_PLATFORM_ERROR_UNSPECIFIED; + return -NME_UNSPEC; } delayed_action_handle_all (platform, FALSE); @@ -7232,24 +7561,24 @@ ip_route_get (NMPlatform *platform, if (seq_result < 0) { /* negative seq_result is an errno from kernel. Map it to negative - * NMPlatformError (which are also errno). */ - return (NMPlatformError) seq_result; + * int (which are also errno). */ + return (int) seq_result; } if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) { if (route) { NM_SET_OUT (out_route, g_steal_pointer (&route)); - return NM_PLATFORM_ERROR_SUCCESS; + return 0; } seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN; } - return NM_PLATFORM_ERROR_UNSPECIFIED; + return -NME_UNSPEC; } /*****************************************************************************/ -static NMPlatformError +static int qdisc_add (NMPlatform *platform, NMPNlmFlags flags, const NMPlatformQdisc *qdisc) @@ -7267,8 +7596,8 @@ qdisc_add (NMPlatform *platform, nle = _nl_send_nlmsg (platform, msg, &seq_result, &errmsg, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); if (nle < 0) { _LOGE ("do-add-qdisc: failed sending netlink request \"%s\" (%d)", - nl_geterror (nle), -nle); - return NM_PLATFORM_ERROR_NETLINK; + nm_strerror (nle), -nle); + return -NME_PL_NETLINK; } delayed_action_handle_all (platform, FALSE); @@ -7282,14 +7611,14 @@ qdisc_add (NMPlatform *platform, wait_for_nl_response_to_string (seq_result, errmsg, s_buf, sizeof (s_buf))); if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) - return NM_PLATFORM_ERROR_SUCCESS; + return 0; - return NM_PLATFORM_ERROR_UNSPECIFIED; + return -NME_UNSPEC; } /*****************************************************************************/ -static NMPlatformError +static int tfilter_add (NMPlatform *platform, NMPNlmFlags flags, const NMPlatformTfilter *tfilter) @@ -7307,8 +7636,8 @@ tfilter_add (NMPlatform *platform, nle = _nl_send_nlmsg (platform, msg, &seq_result, &errmsg, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); if (nle < 0) { _LOGE ("do-add-tfilter: failed sending netlink request \"%s\" (%d)", - nl_geterror (nle), -nle); - return NM_PLATFORM_ERROR_NETLINK; + nm_strerror (nle), -nle); + return -NME_PL_NETLINK; } delayed_action_handle_all (platform, FALSE); @@ -7322,9 +7651,9 @@ tfilter_add (NMPlatform *platform, wait_for_nl_response_to_string (seq_result, errmsg, s_buf, sizeof (s_buf))); if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) - return NM_PLATFORM_ERROR_SUCCESS; + return 0; - return NM_PLATFORM_ERROR_UNSPECIFIED; + return -NME_UNSPEC; } /*****************************************************************************/ @@ -7357,17 +7686,17 @@ event_handler_recvmsgs (NMPlatform *platform, gboolean handle_events) struct nlmsghdr *hdr; WaitForNlResponseResult seq_result; struct sockaddr_nl nla = {0}; - nm_auto_free struct ucred *creds = NULL; + struct ucred creds; + gboolean creds_has; nm_auto_free unsigned char *buf = NULL; continue_reading: g_clear_pointer (&buf, free); - g_clear_pointer (&creds, free); - n = nl_recv (sk, &nla, &buf, &creds); + n = nl_recv (sk, &nla, &buf, &creds, &creds_has); if (n <= 0) { - if (n == -NLE_MSG_TRUNC) { + if (n == -NME_NL_MSG_TRUNC) { int buf_size; /* the message receive buffer was too small. We lost one message, which @@ -7400,11 +7729,11 @@ continue_reading: nlmsg_set_proto (msg, NETLINK_ROUTE); nlmsg_set_src (msg, &nla); - if (!creds || creds->pid) { - if (creds) - _LOGT ("netlink: recvmsg: received non-kernel message (pid %d)", creds->pid); - else + if (!creds_has || creds.pid) { + if (!creds_has) _LOGT ("netlink: recvmsg: received message without credentials"); + else + _LOGT ("netlink: recvmsg: received non-kernel message (pid %d)", creds.pid); err = 0; goto stop; } @@ -7412,8 +7741,7 @@ continue_reading: _LOGt ("netlink: recvmsg: new message %s", nl_nlmsghdr_to_str (hdr, buf_nlmsghdr, sizeof (buf_nlmsghdr))); - if (creds) - nlmsg_set_creds (msg, creds); + nlmsg_set_creds (msg, &creds); if (hdr->nlmsg_flags & NLM_F_MULTI) multipart = TRUE; @@ -7450,7 +7778,7 @@ continue_reading: /* Data got lost, report back to user. The default action is to * quit parsing. The user may overrule this action by retuning * NL_SKIP or NL_PROCEED (dangerous) */ - err = -NLE_MSG_OVERFLOW; + err = -NME_NL_MSG_OVERFLOW; abort_parsing = TRUE; } else if (hdr->nlmsg_type == NLMSG_ERROR) { /* Message carries a nlmsgerr */ @@ -7461,7 +7789,7 @@ continue_reading: * is to stop parsing. The user may overrule * this action by returning NL_SKIP or * NL_PROCEED (dangerous) */ - err = -NLE_MSG_TRUNC; + err = -NME_NL_MSG_TRUNC; abort_parsing = TRUE; } else if (e->error) { int errsv = e->error > 0 ? e->error : -e->error; @@ -7538,7 +7866,7 @@ stop: } if (interrupted) - return -NLE_DUMP_INTR; + return -NME_NL_DUMP_INTR; return err; } @@ -7575,16 +7903,16 @@ event_handler_read_netlink (NMPlatform *platform, gboolean wait_for_acks) switch (nle) { case -EAGAIN: goto after_read; - case -NLE_DUMP_INTR: - _LOGD ("netlink: read: uncritical failure to retrieve incoming events: %s (%d)", nl_geterror (nle), nle); + case -NME_NL_DUMP_INTR: + _LOGD ("netlink: read: uncritical failure to retrieve incoming events: %s (%d)", nm_strerror (nle), nle); break; - case -NLE_MSG_TRUNC: + case -NME_NL_MSG_TRUNC: case -ENOBUFS: _LOGI ("netlink: read: %s. Need to resynchronize platform cache", ({ const char *_reason = "unknown"; switch (nle) { - case -NLE_MSG_TRUNC: _reason = "message truncated"; break; + case -NME_NL_MSG_TRUNC: _reason = "message truncated"; break; case -ENOBUFS: _reason = "too many netlink events"; break; } _reason; @@ -7604,7 +7932,7 @@ event_handler_read_netlink (NMPlatform *platform, gboolean wait_for_acks) NULL); break; default: - _LOGE ("netlink: read: failed to retrieve incoming events: %s (%d)", nl_geterror (nle), nle); + _LOGE ("netlink: read: failed to retrieve incoming events: %s (%d)", nm_strerror (nle), nle); break; } } @@ -7832,7 +8160,7 @@ constructed (GObject *_object) nle = nl_connect (priv->genl, NETLINK_GENERIC); if (nle) { _LOGE ("unable to connect the generic netlink socket \"%s\" (%d)", - nl_geterror (nle), -nle); + nm_strerror (nle), -nle); nl_socket_free (priv->genl); priv->genl = NULL; } @@ -7858,7 +8186,7 @@ constructed (GObject *_object) _LOGD ("could not enable extended acks on netlink socket"); /* explicitly set the msg buffer size and disable MSG_PEEK. - * If we later encounter NLE_MSG_TRUNC, we will adjust the buffer size. */ + * If we later encounter NME_NL_MSG_TRUNC, we will adjust the buffer size. */ nl_socket_disable_msg_peek (priv->nlh); nle = nl_socket_set_msg_buf_size (priv->nlh, 32 * 1024); g_assert (!nle); @@ -8041,6 +8369,7 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->vlan_add = vlan_add; platform_class->link_vlan_change = link_vlan_change; + platform_class->link_wireguard_change = link_wireguard_change; platform_class->link_vxlan_add = link_vxlan_add; platform_class->infiniband_partition_add = infiniband_partition_add; @@ -8067,6 +8396,7 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->wpan_set_pan_id = wpan_set_pan_id; platform_class->wpan_get_short_addr = wpan_get_short_addr; platform_class->wpan_set_short_addr = wpan_set_short_addr; + platform_class->wpan_set_channel = wpan_set_channel; platform_class->link_gre_add = link_gre_add; platform_class->link_ip6tnl_add = link_ip6tnl_add; diff --git a/src/platform/nm-netlink.c b/src/platform/nm-netlink.c index 3e2ad911..6abc1c3a 100644 --- a/src/platform/nm-netlink.c +++ b/src/platform/nm-netlink.c @@ -25,6 +25,8 @@ #include <unistd.h> #include <fcntl.h> +#include "nm-utils/nm-errno.h" + /*****************************************************************************/ #ifndef SOL_NETLINK @@ -42,16 +44,14 @@ #define NETLINK_EXT_ACK 11 #endif -#define NL_MSG_CRED_PRESENT 1 - struct nl_msg { int nm_protocol; - int nm_flags; struct sockaddr_nl nm_src; struct sockaddr_nl nm_dst; struct ucred nm_creds; struct nlmsghdr * nm_nlh; size_t nm_size; + bool nm_creds_has:1; }; struct nl_sock { @@ -67,38 +67,6 @@ struct nl_sock { /*****************************************************************************/ -NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_geterror, int, - NM_UTILS_LOOKUP_DEFAULT (NULL), - NM_UTILS_LOOKUP_ITEM (NLE_UNSPEC, "NLE_UNSPEC"), - NM_UTILS_LOOKUP_ITEM (NLE_BUG, "NLE_BUG"), - NM_UTILS_LOOKUP_ITEM (NLE_NATIVE_ERRNO, "NLE_NATIVE_ERRNO"), - - NM_UTILS_LOOKUP_ITEM (NLE_ATTRSIZE, "NLE_ATTRSIZE"), - NM_UTILS_LOOKUP_ITEM (NLE_BAD_SOCK, "NLE_BAD_SOCK"), - NM_UTILS_LOOKUP_ITEM (NLE_DUMP_INTR, "NLE_DUMP_INTR"), - NM_UTILS_LOOKUP_ITEM (NLE_MSG_OVERFLOW, "NLE_MSG_OVERFLOW"), - NM_UTILS_LOOKUP_ITEM (NLE_MSG_TOOSHORT, "NLE_MSG_TOOSHORT"), - NM_UTILS_LOOKUP_ITEM (NLE_MSG_TRUNC, "NLE_MSG_TRUNC"), - NM_UTILS_LOOKUP_ITEM (NLE_SEQ_MISMATCH, "NLE_SEQ_MISMATCH"), -) - -const char * -nl_geterror (int nlerr) -{ - const char *s; - - nlerr = nl_errno (nlerr); - - if (nlerr >= _NLE_BASE) { - s = _geterror (nlerr); - if (s) - return s; - } - return g_strerror (nlerr); -} - -/*****************************************************************************/ - NM_UTILS_ENUM2STR_DEFINE (nl_nlmsgtype2str, int, NM_UTILS_ENUM2STR (NLMSG_NOOP, "NOOP"), NM_UTILS_ENUM2STR (NLMSG_ERROR, "ERROR"), @@ -285,20 +253,6 @@ nla_reserve (struct nl_msg *msg, int attrtype, int attrlen) /*****************************************************************************/ -static int -get_default_page_size (void) -{ - static int val = 0; - int v; - - if (G_UNLIKELY (val == 0)) { - v = getpagesize (); - g_assert (v > 0); - val = v; - } - return val; -} - struct nl_msg * nlmsg_alloc_size (size_t len) { @@ -328,7 +282,7 @@ nlmsg_alloc_size (size_t len) struct nl_msg * nlmsg_alloc (void) { - return nlmsg_alloc_size (get_default_page_size ()); + return nlmsg_alloc_size (nm_utils_getpagesize ()); } struct nl_msg * @@ -385,7 +339,7 @@ nlmsg_parse (struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], int maxtype, const struct nla_policy *policy) { if (!nlmsg_valid_hdr (nlh, hdrlen)) - return -NLE_MSG_TOOSHORT; + return -NME_NL_MSG_TOOSHORT; return nla_parse (tb, maxtype, nlmsg_attrdata (nlh, hdrlen), nlmsg_attrlen (nlh, hdrlen), policy); @@ -465,7 +419,7 @@ nla_put (struct nl_msg *msg, int attrtype, int datalen, const void *data) nla = nla_reserve (msg, attrtype, datalen); if (!nla) { if (datalen < 0) - g_return_val_if_reached (-NLE_BUG); + g_return_val_if_reached (-NME_BUG); return -ENOMEM; } @@ -531,7 +485,7 @@ _nest_end (struct nl_msg *msg, struct nlattr *start, int keep_empty) nla_nest_cancel (msg, start); /* Return error only if nlattr size was exceeded */ - return (len == NLA_HDRLEN) ? 0 : -NLE_ATTRSIZE; + return (len == NLA_HDRLEN) ? 0 : -NME_NL_ATTRSIZE; } start->nla_len = len; @@ -539,13 +493,13 @@ _nest_end (struct nl_msg *msg, struct nlattr *start, int keep_empty) pad = NLMSG_ALIGN (msg->nm_nlh->nlmsg_len) - msg->nm_nlh->nlmsg_len; if (pad > 0) { /* - * Data inside attribute does not end at a alignment boundry. + * Data inside attribute does not end at a alignment boundary. * Pad accordingly and accoun for the additional space in * the message. nlmsg_reserve() may never fail in this situation, * the allocate message buffer must be a multiple of NLMSG_ALIGNTO. */ if (!nlmsg_reserve (msg, pad, 0)) - g_return_val_if_reached (-NLE_BUG); + g_return_val_if_reached (-NME_BUG); } return 0; @@ -580,7 +534,7 @@ validate_nla (const struct nlattr *nla, int maxtype, pt = &policy[type]; if (pt->type > NLA_TYPE_MAX) - g_return_val_if_reached (-NLE_BUG); + g_return_val_if_reached (-NME_BUG); if (pt->minlen) minlen = pt->minlen; @@ -588,15 +542,15 @@ validate_nla (const struct nlattr *nla, int maxtype, minlen = nla_attr_minlen[pt->type]; if (nla_len (nla) < minlen) - return -NLE_UNSPEC; + return -NME_UNSPEC; if (pt->maxlen && nla_len (nla) > pt->maxlen) - return -NLE_UNSPEC; + return -NME_UNSPEC; if (pt->type == NLA_STRING) { const char *data = nla_data (nla); if (data[nla_len (nla) - 1] != '\0') - return -NLE_UNSPEC; + return -NME_UNSPEC; } return 0; @@ -607,7 +561,7 @@ nla_parse (struct nlattr *tb[], int maxtype, struct nlattr *head, int len, const struct nla_policy *policy) { struct nlattr *nla; - int rem, nlerr; + int rem, nmerr; memset (tb, 0, sizeof (struct nlattr *) * (maxtype + 1)); @@ -618,17 +572,17 @@ nla_parse (struct nlattr *tb[], int maxtype, struct nlattr *head, int len, continue; if (policy) { - nlerr = validate_nla (nla, maxtype, policy); - if (nlerr < 0) + nmerr = validate_nla (nla, maxtype, policy); + if (nmerr < 0) goto errout; } tb[type] = nla; } - nlerr = 0; + nmerr = 0; errout: - return nlerr; + return nmerr; } /*****************************************************************************/ @@ -654,7 +608,7 @@ nlmsg_set_src (struct nl_msg *msg, struct sockaddr_nl *addr) struct ucred * nlmsg_get_creds (struct nl_msg *msg) { - if (msg->nm_flags & NL_MSG_CRED_PRESENT) + if (msg->nm_creds_has) return &msg->nm_creds; return NULL; } @@ -662,8 +616,11 @@ nlmsg_get_creds (struct nl_msg *msg) void nlmsg_set_creds (struct nl_msg *msg, struct ucred *creds) { - memcpy (&msg->nm_creds, creds, sizeof (*creds)); - msg->nm_flags |= NL_MSG_CRED_PRESENT; + if (creds) { + memcpy (&msg->nm_creds, creds, sizeof (*creds)); + msg->nm_creds_has = TRUE; + } else + msg->nm_creds_has = FALSE; } /*****************************************************************************/ @@ -754,7 +711,7 @@ genlmsg_parse (struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], struct genlmsghdr *ghdr; if (!genlmsg_valid_hdr (nlh, hdrlen)) - return -NLE_MSG_TOOSHORT; + return -NME_NL_MSG_TOOSHORT; ghdr = nlmsg_data (nlh); return nla_parse (tb, maxtype, genlmsg_attrdata (ghdr, hdrlen), @@ -791,7 +748,7 @@ int genl_ctrl_resolve (struct nl_sock *sk, const char *name) { nm_auto_nlmsg struct nl_msg *msg = NULL; - int nlerr; + int nmerr; gint32 response_data = -1; const struct nl_cb cb = { .valid_cb = _genl_parse_getfamily, @@ -804,25 +761,25 @@ genl_ctrl_resolve (struct nl_sock *sk, const char *name) 0, 0, CTRL_CMD_GETFAMILY, 1)) return -ENOMEM; - nlerr = nla_put_string (msg, CTRL_ATTR_FAMILY_NAME, name); - if (nlerr < 0) - return nlerr; + nmerr = nla_put_string (msg, CTRL_ATTR_FAMILY_NAME, name); + if (nmerr < 0) + return nmerr; - nlerr = nl_send_auto (sk, msg); - if (nlerr < 0) - return nlerr; + nmerr = nl_send_auto (sk, msg); + if (nmerr < 0) + return nmerr; - nlerr = nl_recvmsgs (sk, &cb); - if (nlerr < 0) - return nlerr; + nmerr = nl_recvmsgs (sk, &cb); + if (nmerr < 0) + return nmerr; /* If search was successful, request may be ACKed after data */ - nlerr = nl_wait_for_ack (sk, NULL); - if (nlerr < 0) - return nlerr; + nmerr = nl_wait_for_ack (sk, NULL); + if (nmerr < 0) + return nmerr; if (response_data < 0) - return -NLE_UNSPEC; + return -NME_UNSPEC; return response_data; } @@ -879,12 +836,12 @@ nl_socket_set_passcred (struct nl_sock *sk, int state) int err; if (sk->s_fd == -1) - return -NLE_BAD_SOCK; + return -NME_NL_BAD_SOCK; err = setsockopt (sk->s_fd, SOL_SOCKET, SO_PASSCRED, &state, sizeof (state)); if (err < 0) - return -nl_syserr2nlerr (errno); + return -nm_errno_from_native (errno); if (state) sk->s_flags |= NL_SOCK_PASSCRED; @@ -912,10 +869,10 @@ int nl_socket_set_nonblocking (const struct nl_sock *sk) { if (sk->s_fd == -1) - return -NLE_BAD_SOCK; + return -NME_NL_BAD_SOCK; if (fcntl (sk->s_fd, F_SETFL, O_NONBLOCK) < 0) - return -nl_syserr2nlerr (errno); + return -nm_errno_from_native (errno); return 0; } @@ -932,18 +889,18 @@ nl_socket_set_buffer_size (struct nl_sock *sk, int rxbuf, int txbuf) txbuf = 32768; if (sk->s_fd == -1) - return -NLE_BAD_SOCK; + return -NME_NL_BAD_SOCK; err = setsockopt (sk->s_fd, SOL_SOCKET, SO_SNDBUF, &txbuf, sizeof (txbuf)); if (err < 0) { - return -nl_syserr2nlerr (errno); + return -nm_errno_from_native (errno); } err = setsockopt (sk->s_fd, SOL_SOCKET, SO_RCVBUF, &rxbuf, sizeof (rxbuf)); if (err < 0) { - return -nl_syserr2nlerr (errno); + return -nm_errno_from_native (errno); } return 0; @@ -956,14 +913,14 @@ nl_socket_add_memberships (struct nl_sock *sk, int group, ...) va_list ap; if (sk->s_fd == -1) - return -NLE_BAD_SOCK; + return -NME_NL_BAD_SOCK; va_start (ap, group); while (group != 0) { if (group < 0) { va_end (ap); - g_return_val_if_reached (-NLE_BUG); + g_return_val_if_reached (-NME_BUG); } err = setsockopt (sk->s_fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, @@ -972,7 +929,7 @@ nl_socket_add_memberships (struct nl_sock *sk, int group, ...) int errsv = errno; va_end (ap); - return -nl_syserr2nlerr (errsv); + return -nm_errno_from_native (errsv); } group = va_arg (ap, int); @@ -989,12 +946,12 @@ nl_socket_set_ext_ack (struct nl_sock *sk, gboolean enable) int err, val; if (sk->s_fd == -1) - return -NLE_BAD_SOCK; + return -NME_NL_BAD_SOCK; val = !!enable; err = setsockopt (sk->s_fd, SOL_NETLINK, NETLINK_EXT_ACK, &val, sizeof (val)); if (err < 0) - return -nl_syserr2nlerr (errno); + return -nm_errno_from_native (errno); return 0; } @@ -1008,21 +965,21 @@ void nl_socket_disable_msg_peek (struct nl_sock *sk) int nl_connect (struct nl_sock *sk, int protocol) { - int err, nlerr; + int err, nmerr; socklen_t addrlen; struct sockaddr_nl local = { 0 }; if (sk->s_fd != -1) - return -NLE_BAD_SOCK; + return -NME_NL_BAD_SOCK; sk->s_fd = socket (AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, protocol); if (sk->s_fd < 0) { - nlerr = -nl_syserr2nlerr (errno); + nmerr = -nm_errno_from_native (errno); goto errout; } - nlerr = nl_socket_set_buffer_size (sk, 0, 0); - if (nlerr < 0) + nmerr = nl_socket_set_buffer_size (sk, 0, 0); + if (nmerr < 0) goto errout; nm_assert (sk->s_local.nl_pid == 0); @@ -1030,7 +987,7 @@ nl_connect (struct nl_sock *sk, int protocol) err = bind (sk->s_fd, (struct sockaddr*) &sk->s_local, sizeof (sk->s_local)); if (err != 0) { - nlerr = -nl_syserr2nlerr (errno); + nmerr = -nm_errno_from_native (errno); goto errout; } @@ -1038,17 +995,17 @@ nl_connect (struct nl_sock *sk, int protocol) err = getsockname (sk->s_fd, (struct sockaddr *) &local, &addrlen); if (err < 0) { - nlerr = -nl_syserr2nlerr (errno); + nmerr = -nm_errno_from_native (errno); goto errout; } if (addrlen != sizeof (local)) { - nlerr = -NLE_UNSPEC; + nmerr = -NME_UNSPEC; goto errout; } if (local.nl_family != AF_NETLINK) { - nlerr = -NLE_UNSPEC; + nmerr = -NME_UNSPEC; goto errout; } @@ -1062,7 +1019,7 @@ errout: close (sk->s_fd); sk->s_fd = -1; } - return nlerr; + return nmerr; } /*****************************************************************************/ @@ -1098,22 +1055,22 @@ nl_wait_for_ack (struct nl_sock *sk, do { \ const struct nl_cb *_cb = (cb); \ \ - if (_cb->type##_cb) { \ + if (_cb && _cb->type##_cb) { \ /* the returned value here must be either a negative * netlink error number, or one of NL_SKIP, NL_STOP, NL_OK. */ \ - nlerr = _cb->type##_cb ((msg), _cb->type##_arg); \ - switch (nlerr) { \ + nmerr = _cb->type##_cb ((msg), _cb->type##_arg); \ + switch (nmerr) { \ case NL_OK: \ - nlerr = 0; \ + nmerr = 0; \ break; \ case NL_SKIP: \ goto skip; \ case NL_STOP: \ goto stop; \ default: \ - if (nlerr >= 0) { \ + if (nmerr >= 0) { \ nm_assert_not_reached (); \ - nlerr = -NLE_BUG; \ + nmerr = -NME_BUG; \ } \ goto out; \ } \ @@ -1123,14 +1080,15 @@ do { \ int nl_recvmsgs (struct nl_sock *sk, const struct nl_cb *cb) { - int n, nlerr = 0, multipart = 0, interrupted = 0, nrecv = 0; + int n, nmerr = 0, multipart = 0, interrupted = 0, nrecv = 0; gs_free unsigned char *buf = NULL; struct nlmsghdr *hdr; struct sockaddr_nl nla = { 0 }; - gs_free struct ucred *creds = NULL; + struct ucred creds; + gboolean creds_has; continue_reading: - n = nl_recv (sk, &nla, &buf, &creds); + n = nl_recv (sk, &nla, &buf, &creds, &creds_has); if (n <= 0) return n; @@ -1142,15 +1100,14 @@ continue_reading: nlmsg_set_proto (msg, sk->s_proto); nlmsg_set_src (msg, &nla); - if (creds) - nlmsg_set_creds (msg, creds); + nlmsg_set_creds (msg, creds_has ? &creds : NULL); nrecv++; /* Only do sequence checking if auto-ack mode is enabled */ if (! (sk->s_flags & NL_NO_AUTO_ACK)) { if (hdr->nlmsg_seq != sk->s_seq_expect) { - nlerr = -NLE_SEQ_MISMATCH; + nmerr = -NME_NL_SEQ_MISMATCH; goto out; } } @@ -1196,7 +1153,7 @@ continue_reading: * quit parsing. The user may overrule this action by retuning * NL_SKIP or NL_PROCEED (dangerous) */ else if (hdr->nlmsg_type == NLMSG_OVERRUN) { - nlerr = -NLE_MSG_OVERFLOW; + nmerr = -NME_NL_MSG_OVERFLOW; goto out; } @@ -1209,27 +1166,27 @@ continue_reading: * is to stop parsing. The user may overrule * this action by returning NL_SKIP or * NL_PROCEED (dangerous) */ - nlerr = -NLE_MSG_TRUNC; + nmerr = -NME_NL_MSG_TRUNC; goto out; } if (e->error) { /* Error message reported back from kernel. */ - if (cb->err_cb) { + if (cb && cb->err_cb) { /* the returned value here must be either a negative * netlink error number, or one of NL_SKIP, NL_STOP, NL_OK. */ - nlerr = cb->err_cb (&nla, e, + nmerr = cb->err_cb (&nla, e, cb->err_arg); - if (nlerr < 0) + if (nmerr < 0) goto out; - else if (nlerr == NL_SKIP) + else if (nmerr == NL_SKIP) goto skip; - else if (nlerr == NL_STOP) { - nlerr = -nl_syserr2nlerr (e->error); + else if (nmerr == NL_STOP) { + nmerr = -nm_errno_from_native (e->error); goto out; } - nm_assert (nlerr == NL_OK); + nm_assert (nmerr == NL_OK); } else { - nlerr = -nl_syserr2nlerr (e->error); + nmerr = -nm_errno_from_native (e->error); goto out; } } else @@ -1241,27 +1198,26 @@ continue_reading: NL_CB_CALL (cb, valid, msg); } skip: - nlerr = 0; + nmerr = 0; hdr = nlmsg_next (hdr, &n); } if (multipart) { /* Multipart message not yet complete, continue reading */ - nm_clear_g_free (&creds); nm_clear_g_free (&buf); goto continue_reading; } stop: - nlerr = 0; + nmerr = 0; out: if (interrupted) - nlerr = -NLE_DUMP_INTR; + nmerr = -NME_NL_DUMP_INTR; - nm_assert (nlerr <= 0); - return nlerr ?: nrecv; + nm_assert (nmerr <= 0); + return nmerr ?: nrecv; } int @@ -1270,13 +1226,13 @@ nl_sendmsg (struct nl_sock *sk, struct nl_msg *msg, struct msghdr *hdr) int ret; if (sk->s_fd < 0) - return -NLE_BAD_SOCK; + return -NME_NL_BAD_SOCK; nlmsg_set_src (msg, &sk->s_local); ret = sendmsg (sk->s_fd, hdr, 0); if (ret < 0) - return -nl_syserr2nlerr (errno); + return -nm_errno_from_native (errno); return ret; } @@ -1359,12 +1315,14 @@ int nl_send_auto (struct nl_sock *sk, struct nl_msg *msg) } int -nl_recv (struct nl_sock *sk, struct sockaddr_nl *nla, - unsigned char **buf, struct ucred **creds) +nl_recv (struct nl_sock *sk, + struct sockaddr_nl *nla, + unsigned char **buf, + struct ucred *out_creds, + gboolean *out_creds_has) { ssize_t n; int flags = 0; - static int page_size = 0; struct iovec iov; struct msghdr msg = { .msg_name = (void *) nla, @@ -1372,25 +1330,24 @@ nl_recv (struct nl_sock *sk, struct sockaddr_nl *nla, .msg_iov = &iov, .msg_iovlen = 1, }; - gs_free struct ucred* tmpcreds = NULL; + struct ucred tmpcreds; + gboolean tmpcreds_has = FALSE; int retval; nm_assert (nla); nm_assert (buf && !*buf); - nm_assert (!creds || !*creds); + nm_assert (!out_creds_has == !out_creds); if ( (sk->s_flags & NL_MSG_PEEK) || ( !(sk->s_flags & NL_MSG_PEEK_EXPLICIT) && sk->s_bufsize == 0)) flags |= MSG_PEEK | MSG_TRUNC; - if (page_size == 0) - page_size = getpagesize () * 4; - - iov.iov_len = sk->s_bufsize ?: page_size; + iov.iov_len = sk->s_bufsize + ?: (((size_t) nm_utils_getpagesize ()) * 4u); iov.iov_base = g_malloc (iov.iov_len); - if ( creds + if ( out_creds && (sk->s_flags & NL_SOCK_PASSCRED)) { msg.msg_controllen = CMSG_SPACE (sizeof (struct ucred)); msg.msg_control = g_malloc (msg.msg_controllen); @@ -1407,13 +1364,13 @@ retry: if (errno == EINTR) goto retry; - retval = -nl_syserr2nlerr (errno); + retval = -nm_errno_from_native (errno); goto abort; } if (msg.msg_flags & MSG_CTRUNC) { if (msg.msg_controllen == 0) { - retval = -NLE_MSG_TRUNC; + retval = -NME_NL_MSG_TRUNC; goto abort; } @@ -1426,7 +1383,7 @@ retry: || (msg.msg_flags & MSG_TRUNC)) { /* respond with error to an incomplete message */ if (flags == 0) { - retval = -NLE_MSG_TRUNC; + retval = -NME_NL_MSG_TRUNC; goto abort; } @@ -1446,11 +1403,11 @@ retry: } if (msg.msg_namelen != sizeof (struct sockaddr_nl)) { - retval = -NLE_UNSPEC; + retval = -NME_UNSPEC; goto abort; } - if (creds && (sk->s_flags & NL_SOCK_PASSCRED)) { + if (out_creds && (sk->s_flags & NL_SOCK_PASSCRED)) { struct cmsghdr *cmsg; for (cmsg = CMSG_FIRSTHDR (&msg); cmsg; cmsg = CMSG_NXTHDR (&msg, cmsg)) { @@ -1458,7 +1415,8 @@ retry: continue; if (cmsg->cmsg_type != SCM_CREDENTIALS) continue; - tmpcreds = nm_memdup (CMSG_DATA (cmsg), sizeof (*tmpcreds)); + memcpy (&tmpcreds, CMSG_DATA (cmsg), sizeof (tmpcreds)); + tmpcreds_has = TRUE; break; } } @@ -1474,6 +1432,8 @@ abort: } *buf = iov.iov_base; - NM_SET_OUT (creds, g_steal_pointer (&tmpcreds)); + if (out_creds && tmpcreds_has) + *out_creds = tmpcreds; + NM_SET_OUT (out_creds_has, tmpcreds_has); return retval; } diff --git a/src/platform/nm-netlink.h b/src/platform/nm-netlink.h index d5df7ab9..84bbe27b 100644 --- a/src/platform/nm-netlink.h +++ b/src/platform/nm-netlink.h @@ -26,20 +26,6 @@ #include <linux/genetlink.h> /*****************************************************************************/ -#define _NLE_BASE 100000 -#define NLE_UNSPEC (_NLE_BASE + 0) -#define NLE_BUG (_NLE_BASE + 1) -#define NLE_NATIVE_ERRNO (_NLE_BASE + 2) -#define NLE_SEQ_MISMATCH (_NLE_BASE + 3) -#define NLE_MSG_TRUNC (_NLE_BASE + 4) -#define NLE_MSG_TOOSHORT (_NLE_BASE + 5) -#define NLE_DUMP_INTR (_NLE_BASE + 6) -#define NLE_ATTRSIZE (_NLE_BASE + 7) -#define NLE_BAD_SOCK (_NLE_BASE + 8) -#define NLE_NOADDR (_NLE_BASE + 9) -#define NLE_MSG_OVERFLOW (_NLE_BASE + 10) - -#define _NLE_BASE_END (_NLE_BASE + 11) #define NLMSGERR_ATTR_UNUSED 0 #define NLMSGERR_ATTR_MSG 1 @@ -51,50 +37,6 @@ #define NLM_F_ACK_TLVS 0x200 #endif -static inline int -nl_errno (int nlerr) -{ - /* Normalizes an netlink error to be positive. Various API returns negative - * error codes, and this function converts the negative value to its - * positive. - * - * It's very similar to nm_errno(), but not exactly. The difference is that - * nm_errno() is for plain errno, while nl_errno() is for netlink error numbers. - * Yes, netlink error number are ~almost~ the same as errno, except that a particular - * range (_NLE_BASE, _NLE_BASE_END) is reserved. The difference between the two - * functions is only how G_MININT is mapped. - * - * See also nl_syserr2nlerr() below. */ - return nlerr >= 0 - ? nlerr - : ((nlerr == G_MININT) ? NLE_BUG : -nlerr); -} - -static inline int -nl_syserr2nlerr (int errsv) -{ - /* this maps a native errno to a (always non-negative) netlink error number. - * - * Note that netlink error numbers are embedded into the range of regular - * errno. The only difference is, that netlink error numbers reserve a - * range (_NLE_BASE, _NLE_BASE_END) for their own purpose. - * - * That means, converting an errno to netlink error number means in - * most cases just returning itself (negative values are normalized - * to be positive). Only values G_MININT and [_NLE_BASE, _NLE_BASE_END] - * are coerced to the special value NLE_NATIVE_ERRNO, as they cannot - * otherwise be represented in netlink error number domain. */ - if (errsv == G_MININT) - return NLE_NATIVE_ERRNO; - if (errsv < 0) - errsv = -errsv; - return (errsv >= _NLE_BASE && errsv < _NLE_BASE_END) - ? NLE_NATIVE_ERRNO - : errsv; -} - -const char *nl_geterror (int nlerr); - /*****************************************************************************/ /* Basic attribute data types */ @@ -245,6 +187,24 @@ nla_put_string (struct nl_msg *msg, int attrtype, const char *str) return nla_put(msg, attrtype, strlen(str) + 1, str); } +static inline int +nla_put_uint8 (struct nl_msg *msg, int attrtype, uint8_t val) +{ + return nla_put (msg, attrtype, sizeof (val), &val); +} + +static inline int +nla_put_uint16 (struct nl_msg *msg, int attrtype, uint16_t val) +{ + return nla_put (msg, attrtype, sizeof (val), &val); +} + +static inline int +nla_put_uint32 (struct nl_msg *msg, int attrtype, uint32_t val) +{ + return nla_put (msg, attrtype, sizeof (val), &val); +} + #define NLA_PUT(msg, attrtype, attrlen, data) \ do { \ if (nla_put(msg, attrtype, attrlen, data) < 0) \ @@ -474,8 +434,11 @@ int nl_socket_add_memberships (struct nl_sock *sk, int group, ...); int nl_connect (struct nl_sock *sk, int protocol); -int nl_recv (struct nl_sock *sk, struct sockaddr_nl *nla, - unsigned char **buf, struct ucred **creds); +int nl_recv (struct nl_sock *sk, + struct sockaddr_nl *nla, + unsigned char **buf, + struct ucred *out_creds, + gboolean *out_creds_has); int nl_send (struct nl_sock *sk, struct nl_msg *msg); diff --git a/src/platform/nm-platform-utils.c b/src/platform/nm-platform-utils.c index 216b1547..772d4667 100644 --- a/src/platform/nm-platform-utils.c +++ b/src/platform/nm-platform-utils.c @@ -29,6 +29,7 @@ #include <linux/ethtool.h> #include <linux/sockios.h> #include <linux/mii.h> +#include <linux/if.h> #include <linux/version.h> #include <linux/rtnetlink.h> #include <fcntl.h> @@ -664,7 +665,7 @@ nmp_utils_ethtool_set_features (int ifindex, "set-features", success ? "successfully setting features" - : "at least some of the features were not successfuly set"); + : "at least some of the features were not successfully set"); return success; } @@ -1247,7 +1248,7 @@ nmp_utils_ip_config_source_to_string (NMIPConfigSource source, char *buf, gsize * @ifindex: the ifindex for which to open "/sys/class/net/%s" * @ifname_guess: (allow-none): optional argument, if present used as initial * guess as the current name for @ifindex. If guessed right, - * it saves an addtional if_indextoname() call. + * it saves an additional if_indextoname() call. * @out_ifname: (allow-none): if present, must be at least IFNAMSIZ * characters. On success, this will contain the actual ifname * found while opening the directory. @@ -1276,7 +1277,6 @@ nmp_utils_sysctl_open_netdir (int ifindex, for (try_count = 0; try_count < 10; try_count++, ifname = NULL) { nm_auto_close int fd_dir = -1; nm_auto_close int fd_ifindex = -1; - int fd; if (!ifname) { ifname = nmp_utils_if_indextoname (ifindex, ifname_buf); @@ -1309,15 +1309,13 @@ nmp_utils_sysctl_open_netdir (int ifindex, continue; fd_buf[nn] = '\0'; - if (ifindex != _nm_utils_ascii_str_to_int64 (fd_buf, 10, 1, G_MAXINT, -1)) + if (ifindex != (int) _nm_utils_ascii_str_to_int64 (fd_buf, 10, 1, G_MAXINT, -1)) continue; if (out_ifname) strcpy (out_ifname, ifname); - fd = fd_dir; - fd_dir = -1; - return fd; + return nm_steal_fd (&fd_dir); } return -1; diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index c73631a9..4bb31b17 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -31,6 +31,7 @@ #include <netdb.h> #include <string.h> #include <linux/ip.h> +#include <linux/if.h> #include <linux/if_tun.h> #include <linux/if_tunnel.h> #include <linux/rtnetlink.h> @@ -40,6 +41,7 @@ #include "nm-core-internal.h" #include "nm-utils/nm-dedup-multi.h" #include "nm-utils/nm-udev-utils.h" +#include "nm-utils/nm-errno.h" #include "nm-core-utils.h" #include "nm-platform-utils.h" @@ -57,22 +59,48 @@ G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPRoute, network_ptr) == G_STRUCT_OF #define _NMLOG_DOMAIN LOGD_PLATFORM #define _NMLOG_PREFIX_NAME "platform" + + +#define NMLOG_COMMON(level, name, ...) \ + char __prefix[32]; \ + const char *__p_prefix = _NMLOG_PREFIX_NAME; \ + const NMPlatform *const __self = (self); \ + const char *__name = name; \ + \ + if (__self && NM_PLATFORM_GET_PRIVATE (__self)->log_with_ptr) { \ + g_snprintf (__prefix, sizeof (__prefix), "%s[%p]", _NMLOG_PREFIX_NAME, __self); \ + __p_prefix = __prefix; \ + } \ + _nm_log (__level, _NMLOG_DOMAIN, 0, __name, NULL, \ + "%s: %s%s%s" _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ + __p_prefix, \ + NM_PRINT_FMT_QUOTED (__name, "(", __name, ") ", "") \ + _NM_UTILS_MACRO_REST (__VA_ARGS__)); + #define _NMLOG(level, ...) \ G_STMT_START { \ const NMLogLevel __level = (level); \ \ if (nm_logging_enabled (__level, _NMLOG_DOMAIN)) { \ - char __prefix[32]; \ - const char *__p_prefix = _NMLOG_PREFIX_NAME; \ - const NMPlatform *const __self = (self); \ - \ - if (__self && NM_PLATFORM_GET_PRIVATE (__self)->log_with_ptr) { \ - g_snprintf (__prefix, sizeof (__prefix), "%s[%p]", _NMLOG_PREFIX_NAME, __self); \ - __p_prefix = __prefix; \ - } \ - _nm_log (__level, _NMLOG_DOMAIN, 0, NULL, NULL, \ - "%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ - __p_prefix _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ + NMLOG_COMMON(level, NULL, __VA_ARGS__); \ + } \ + } G_STMT_END + +#define _NMLOG2(level, ...) \ + G_STMT_START { \ + const NMLogLevel __level = (level); \ + \ + if (nm_logging_enabled (__level, _NMLOG_DOMAIN)) { \ + NMLOG_COMMON(level, name, __VA_ARGS__); \ + } \ + } G_STMT_END + +#define _NMLOG3(level, ...) \ + G_STMT_START { \ + const NMLogLevel __level = (level); \ + \ + if (nm_logging_enabled (__level, _NMLOG_DOMAIN)) { \ + NMLOG_COMMON(level, ifindex > 0 ? nm_platform_link_get_name (self, ifindex) : NULL, __VA_ARGS__); \ } \ } G_STMT_END @@ -228,58 +256,6 @@ nm_platform_get_multi_idx (NMPlatform *self) /*****************************************************************************/ -NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_nm_platform_error_to_string, NMPlatformError, - NM_UTILS_LOOKUP_DEFAULT (NULL), - NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_SUCCESS, "success"), - NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_BUG, "bug"), - NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_UNSPECIFIED, "unspecified"), - NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_NOT_FOUND, "not-found"), - NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_EXISTS, "exists"), - NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_WRONG_TYPE, "wrong-type"), - NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_NOT_SLAVE, "not-slave"), - NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_NO_FIRMWARE, "no-firmware"), - NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_OPNOTSUPP, "not-supported"), - NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_NETLINK, "netlink"), - NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_CANT_SET_MTU, "cant-set-mtu"), - NM_UTILS_LOOKUP_ITEM_IGNORE (_NM_PLATFORM_ERROR_MININT), -); - -/** - * nm_platform_error_to_string: - * @error_code: the error code to stringify. - * @buf: (allow-none): buffer - * @buf_len: size of buffer - * - * Returns: A string representation of the error. - * For negative numbers, this function interprets - * the code as -errno. - * For invalid (positive) numbers it returns NULL. - */ -const char * -nm_platform_error_to_string (NMPlatformError error_code, char *buf, gsize buf_len) -{ - const char *s; - - if (error_code < 0) { - int errsv = -((int) error_code); - - nm_utils_to_string_buffer_init (&buf, &buf_len); - g_snprintf (buf, buf_len, "%s (%d)", g_strerror (errsv), errsv); - } else { - s = _nm_platform_error_to_string (error_code); - if (s) { - if (!buf) - return s; - g_strlcpy (buf, s, buf_len); - } else { - nm_utils_to_string_buffer_init (&buf, &buf_len); - g_snprintf (buf, buf_len, "(%d)", (int) error_code); - } - } - - return buf; -} - NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_nmp_nlm_flag_to_string_lookup, NMPNlmFlags, NM_UTILS_LOOKUP_DEFAULT (NULL), NM_UTILS_LOOKUP_ITEM (NMP_NLM_FLAG_ADD, "add"), @@ -449,7 +425,9 @@ nm_platform_sysctl_set (NMPlatform *self, const char *pathid, int dirfd, const c } gboolean -nm_platform_sysctl_set_ip6_hop_limit_safe (NMPlatform *self, const char *iface, int value) +nm_platform_sysctl_ip_conf_set_ipv6_hop_limit_safe (NMPlatform *self, + const char *iface, + int value) { const char *path; gint64 cur; @@ -543,7 +521,14 @@ nm_platform_sysctl_get_int32 (NMPlatform *self, const char *pathid, int dirfd, c * (inclusive) or @fallback. */ gint64 -nm_platform_sysctl_get_int_checked (NMPlatform *self, const char *pathid, int dirfd, const char *path, guint base, gint64 min, gint64 max, gint64 fallback) +nm_platform_sysctl_get_int_checked (NMPlatform *self, + const char *pathid, + int dirfd, + const char *path, + guint base, + gint64 min, + gint64 max, + gint64 fallback) { char *value = NULL; gint32 ret; @@ -570,6 +555,81 @@ nm_platform_sysctl_get_int_checked (NMPlatform *self, const char *pathid, int di /*****************************************************************************/ +char * +nm_platform_sysctl_ip_conf_get (NMPlatform *platform, + int addr_family, + const char *ifname, + const char *property) +{ + char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; + + return nm_platform_sysctl_get (platform, + NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (addr_family, + buf, + ifname, + property))); +} + +gint64 +nm_platform_sysctl_ip_conf_get_int_checked (NMPlatform *platform, + int addr_family, + const char *ifname, + const char *property, + guint base, + gint64 min, + gint64 max, + gint64 fallback) +{ + char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; + + return nm_platform_sysctl_get_int_checked (platform, + NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (addr_family, + buf, + ifname, + property)), + base, + min, + max, + fallback); +} + +gboolean +nm_platform_sysctl_ip_conf_set (NMPlatform *platform, + int addr_family, + const char *ifname, + const char *property, + const char *value) +{ + char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; + + return nm_platform_sysctl_set (platform, + NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (addr_family, + buf, + ifname, + property)), + value); +} + +gboolean +nm_platform_sysctl_ip_conf_set_int64 (NMPlatform *platform, + int addr_family, + const char *ifname, + const char *property, + gint64 value) +{ + char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; + char s[64]; + + return nm_platform_sysctl_set (platform, + NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (addr_family, + buf, + ifname, + property)), + nm_sprintf_buf (s, "%"G_GINT64_FORMAT, value)); +} + +/*****************************************************************************/ + static int _link_get_all_presort (gconstpointer p_a, gconstpointer p_b, @@ -727,6 +787,8 @@ nm_platform_link_get_obj (NMPlatform *self, { const NMPObject *obj_cache; + _CHECK_SELF (self, klass, NULL); + obj_cache = nmp_cache_lookup_link (nm_platform_get_cache (self), ifindex); if ( !obj_cache || ( visible_only @@ -752,15 +814,7 @@ nm_platform_link_get_obj (NMPlatform *self, const NMPlatformLink * nm_platform_link_get (NMPlatform *self, int ifindex) { - const NMPObject *obj; - - _CHECK_SELF (self, klass, NULL); - - if (ifindex <= 0) - return NULL; - - obj = nm_platform_link_get_obj (self, ifindex, TRUE); - return NMP_OBJECT_CAST_LINK (obj); + return NMP_OBJECT_CAST_LINK (nm_platform_link_get_obj (self, ifindex, TRUE)); } /** @@ -833,7 +887,7 @@ nm_platform_link_get_by_address (NMPlatform *self, return NMP_OBJECT_CAST_LINK (obj); } -static NMPlatformError +static int _link_add_check_existing (NMPlatform *self, const char *name, NMLinkType type, const NMPlatformLink **out_link) { const NMPlatformLink *pllink; @@ -843,20 +897,19 @@ _link_add_check_existing (NMPlatform *self, const char *name, NMLinkType type, c gboolean wrong_type; wrong_type = type != NM_LINK_TYPE_NONE && pllink->type != type; - _LOGD ("link: skip adding link due to existing interface '%s' of type %s%s%s", - name, - nm_link_type_to_string (pllink->type), - wrong_type ? ", expected " : "", - wrong_type ? nm_link_type_to_string (type) : ""); + _LOG2D ("link: skip adding link due to existing interface of type %s%s%s", + nm_link_type_to_string (pllink->type), + wrong_type ? ", expected " : "", + wrong_type ? nm_link_type_to_string (type) : ""); if (out_link) *out_link = pllink; if (wrong_type) - return NM_PLATFORM_ERROR_WRONG_TYPE; - return NM_PLATFORM_ERROR_EXISTS; + return -NME_PL_WRONG_TYPE; + return -NME_PL_EXISTS; } if (out_link) *out_link = NULL; - return NM_PLATFORM_ERROR_SUCCESS; + return 0; } /** @@ -870,16 +923,16 @@ _link_add_check_existing (NMPlatform *self, const char *name, NMLinkType type, c * @out_link: on success, the link object * * Add a software interface. If the interface already exists and is of type - * @type, return NM_PLATFORM_ERROR_EXISTS and returns the link + * @type, return -NME_PL_EXISTS and returns the link * in @out_link. If the interface already exists and is not of type @type, - * return NM_PLATFORM_ERROR_WRONG_TYPE. + * return -NME_PL_WRONG_TYPE. * * Any link-changed ADDED signal will be emitted directly, before this * function finishes. * - * Returns: the error reason or NM_PLATFORM_ERROR_SUCCESS. + * Returns: the negative nm-error on failure. */ -static NMPlatformError +static int nm_platform_link_add (NMPlatform *self, const char *name, NMLinkType type, @@ -888,38 +941,35 @@ nm_platform_link_add (NMPlatform *self, size_t address_len, const NMPlatformLink **out_link) { - NMPlatformError plerr; + int r; char addr_buf[NM_UTILS_HWADDR_LEN_MAX * 3]; - _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); + _CHECK_SELF (self, klass, -NME_BUG); - g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG); - g_return_val_if_fail ((address != NULL) ^ (address_len == 0) , NM_PLATFORM_ERROR_BUG); - g_return_val_if_fail (address_len <= NM_UTILS_HWADDR_LEN_MAX, NM_PLATFORM_ERROR_BUG); - g_return_val_if_fail ((!!veth_peer) == (type == NM_LINK_TYPE_VETH), NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (name, -NME_BUG); + g_return_val_if_fail ((address != NULL) ^ (address_len == 0) , -NME_BUG); + g_return_val_if_fail (address_len <= NM_UTILS_HWADDR_LEN_MAX, -NME_BUG); + g_return_val_if_fail ((!!veth_peer) == (type == NM_LINK_TYPE_VETH), -NME_BUG); - plerr = _link_add_check_existing (self, name, type, out_link); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) - return plerr; + r = _link_add_check_existing (self, name, type, out_link); + if (r < 0) + return r; - _LOGD ("link: adding link '%s': %s (%d)" - "%s%s" /* address */ - "%s%s" /* veth peer */ - "", - name, - nm_link_type_to_string (type), - (int) type, - address ? ", address: " : "", - address ? nm_utils_hwaddr_ntoa_buf (address, address_len, FALSE, addr_buf, sizeof (addr_buf)) : "", - veth_peer ? ", veth-peer: " : "", - veth_peer ?: ""); + _LOG2D ("link: adding link: %s (%d)" + "%s%s" /* address */ + "%s%s" /* veth peer */ + "", + nm_link_type_to_string (type), + (int) type, + address ? ", address: " : "", + address ? nm_utils_hwaddr_ntoa_buf (address, address_len, FALSE, addr_buf, sizeof (addr_buf)) : "", + veth_peer ? ", veth-peer: " : "", + veth_peer ?: ""); - if (!klass->link_add (self, name, type, veth_peer, address, address_len, out_link)) - return NM_PLATFORM_ERROR_UNSPECIFIED; - return NM_PLATFORM_ERROR_SUCCESS; + return klass->link_add (self, name, type, veth_peer, address, address_len, out_link); } -NMPlatformError +int nm_platform_link_veth_add (NMPlatform *self, const char *name, const char *peer, @@ -936,7 +986,7 @@ nm_platform_link_veth_add (NMPlatform *self, * * Create a software ethernet-like interface */ -NMPlatformError +int nm_platform_link_dummy_add (NMPlatform *self, const char *name, const NMPlatformLink **out_link) @@ -952,15 +1002,11 @@ nm_platform_link_dummy_add (NMPlatform *self, gboolean nm_platform_link_delete (NMPlatform *self, int ifindex) { - const NMPlatformLink *pllink; - _CHECK_SELF (self, klass, FALSE); - pllink = nm_platform_link_get (self, ifindex); - if (!pllink) - return FALSE; + g_return_val_if_fail (ifindex > 0, FALSE); - _LOGD ("link: deleting '%s' (%d)", pllink->name, ifindex); + _LOG3D ("link: deleting"); return klass->link_delete (self, ifindex); } @@ -975,18 +1021,12 @@ nm_platform_link_delete (NMPlatform *self, int ifindex) gboolean nm_platform_link_set_netns (NMPlatform *self, int ifindex, int netns_fd) { - const NMPlatformLink *pllink; - _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (netns_fd > 0, FALSE); - pllink = nm_platform_link_get (self, ifindex); - if (!pllink) - return FALSE; - - _LOGD ("link: move link %d to network namespace with fd %d", ifindex, netns_fd); + _LOG3D ("link: move link to network namespace with fd %d", netns_fd); return klass->link_set_netns (self, ifindex, netns_fd); } @@ -996,7 +1036,7 @@ nm_platform_link_set_netns (NMPlatform *self, int ifindex, int netns_fd) * @name: Interface name * * Returns: The interface index corresponding to the given interface name - * or 0. Inteface name is owned by #NMPlatform, don't free it. + * or 0. Interface name is owned by #NMPlatform, don't free it. */ int nm_platform_link_get_ifindex (NMPlatform *self, const char *name) @@ -1036,8 +1076,6 @@ nm_platform_link_get_name (NMPlatform *self, int ifindex) { const NMPlatformLink *pllink; - _CHECK_SELF (self, klass, NULL); - pllink = nm_platform_link_get (self, ifindex); return pllink ? pllink->name : NULL; } @@ -1055,8 +1093,6 @@ nm_platform_link_get_type (NMPlatform *self, int ifindex) { const NMPlatformLink *pllink; - _CHECK_SELF (self, klass, NM_LINK_TYPE_NONE); - pllink = nm_platform_link_get (self, ifindex); return pllink ? pllink->type : NM_LINK_TYPE_NONE; } @@ -1075,16 +1111,13 @@ nm_platform_link_get_type_name (NMPlatform *self, int ifindex) { const NMPObject *obj; - _CHECK_SELF (self, klass, NULL); - obj = nm_platform_link_get_obj (self, ifindex, TRUE); - if (!obj) return NULL; if (obj->link.type != NM_LINK_TYPE_UNKNOWN) { /* We could detect the @link_type. In this case the function returns - * our internel module names, which differs from rtnl_link_get_type(): + * our internal module names, which differs from rtnl_link_get_type(): * - NM_LINK_TYPE_INFINIBAND (gives "infiniband", instead of "ipoib") * - NM_LINK_TYPE_TAP (gives "tap", instead of "tun"). * Note that this functions is only used by NMDeviceGeneric to @@ -1199,11 +1232,6 @@ nm_platform_link_get_ifi_flags (NMPlatform *self, { const NMPlatformLink *pllink; - _CHECK_SELF (self, klass, -EINVAL); - - if (ifindex <= 0) - return -EINVAL; - /* include invisible links (only in netlink, not udev). */ pllink = NMP_OBJECT_CAST_LINK (nm_platform_link_get_obj (self, ifindex, FALSE)); if (!pllink) @@ -1242,8 +1270,6 @@ nm_platform_link_is_connected (NMPlatform *self, int ifindex) { const NMPlatformLink *pllink; - _CHECK_SELF (self, klass, FALSE); - pllink = nm_platform_link_get (self, ifindex); return pllink ? pllink->connected : FALSE; } @@ -1309,10 +1335,6 @@ nm_platform_link_get_udev_device (NMPlatform *self, int ifindex) { const NMPObject *obj_cache; - _CHECK_SELF (self, klass, FALSE); - - g_return_val_if_fail (ifindex >= 0, NULL); - obj_cache = nm_platform_link_get_obj (self, ifindex, FALSE); return obj_cache ? obj_cache->_link.udev.device : NULL; } @@ -1333,10 +1355,6 @@ nm_platform_link_get_user_ipv6ll_enabled (NMPlatform *self, int ifindex) { const NMPlatformLink *pllink; - _CHECK_SELF (self, klass, FALSE); - - g_return_val_if_fail (ifindex >= 0, FALSE); - pllink = nm_platform_link_get (self, ifindex); if (pllink && pllink->inet6_addr_gen_mode_inv) return _nm_platform_uint8_inv (pllink->inet6_addr_gen_mode_inv) == NM_IN6_ADDR_GEN_MODE_NONE; @@ -1352,14 +1370,14 @@ nm_platform_link_get_user_ipv6ll_enabled (NMPlatform *self, int ifindex) * platform or OS doesn't support changing the IPv6LL address mode, this call * will fail and return %FALSE. * - * Returns: %NM_PLATFORM_ERROR_SUCCESS if the operation was successful or an error code otherwise. + * Returns: the negative nm-error on failure. */ -NMPlatformError +int nm_platform_link_set_user_ipv6ll_enabled (NMPlatform *self, int ifindex, gboolean enabled) { - _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); + _CHECK_SELF (self, klass, -NME_BUG); - g_return_val_if_fail (ifindex > 0, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (ifindex > 0, -NME_BUG); return klass->link_set_user_ipv6ll_enabled (self, ifindex, enabled); } @@ -1372,21 +1390,19 @@ nm_platform_link_set_user_ipv6ll_enabled (NMPlatform *self, int ifindex, gboolea * * Set interface MAC address. */ -NMPlatformError +int nm_platform_link_set_address (NMPlatform *self, int ifindex, gconstpointer address, size_t length) { gs_free char *mac = NULL; - _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); + _CHECK_SELF (self, klass, -NME_BUG); - g_return_val_if_fail (ifindex > 0, NM_PLATFORM_ERROR_BUG); - g_return_val_if_fail (address, NM_PLATFORM_ERROR_BUG); - g_return_val_if_fail (length > 0, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (ifindex > 0, -NME_BUG); + g_return_val_if_fail (address, -NME_BUG); + g_return_val_if_fail (length > 0, -NME_BUG); - _LOGD ("link: setting %s (%d) hardware address to %s", - nm_strquote_a (20, nm_platform_link_get_name (self, ifindex)), - ifindex, - (mac = nm_utils_hwaddr_ntoa (address, length))); + _LOG3D ("link: setting hardware address to %s", + (mac = nm_utils_hwaddr_ntoa (address, length))); return klass->link_set_address (self, ifindex, address, length); } @@ -1404,12 +1420,7 @@ nm_platform_link_get_address (NMPlatform *self, int ifindex, size_t *length) { const NMPlatformLink *pllink; - _CHECK_SELF (self, klass, NULL); - - g_return_val_if_fail (ifindex > 0, NULL); - pllink = nm_platform_link_get (self, ifindex); - if ( !pllink || pllink->addr.len <= 0) { NM_SET_OUT (length, 0); @@ -1488,25 +1499,20 @@ nm_platform_link_supports_sriov (NMPlatform *self, int ifindex) * @self: platform instance * @ifindex: the index of the interface to change * @num_vfs: the number of VFs to create - * @autoprobe: -1 to keep the current autoprobe-drivers value, - * or {0,1} to set a new value + * @autoprobe: the new autoprobe-drivers value (pass + * %NM_TERNARY_DEFAULT to keep current value) */ gboolean nm_platform_link_set_sriov_params (NMPlatform *self, int ifindex, guint num_vfs, - int autoprobe) + NMTernary autoprobe) { _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); - g_return_val_if_fail (NM_IN_SET (autoprobe, -1, 0, 1), FALSE); - _LOGD ("link: setting %u total VFs and autoprobe %d for %s (%d)", - num_vfs, - autoprobe, - nm_strquote_a (25, nm_platform_link_get_name (self, ifindex)), - ifindex); + _LOG3D ("link: setting %u total VFs and autoprobe %d", num_vfs, (int) autoprobe); return klass->link_set_sriov_params (self, ifindex, num_vfs, autoprobe); } @@ -1518,14 +1524,11 @@ nm_platform_link_set_sriov_vfs (NMPlatform *self, int ifindex, const NMPlatformV g_return_val_if_fail (ifindex > 0, FALSE); - _LOGD ("link: setting VFs for \"%s\" (%d):", - nm_platform_link_get_name (self, ifindex), - ifindex); - + _LOG3D ("link: setting VFs"); for (i = 0; vfs[i]; i++) { const NMPlatformVF *vf = vfs[i]; - _LOGD ("link: VF %s", nm_platform_vf_to_string (vf, NULL, 0)); + _LOG3D ("link: VF %s", nm_platform_vf_to_string (vf, NULL, 0)); } return klass->link_set_sriov_vfs (self, ifindex, vfs); @@ -1546,7 +1549,7 @@ nm_platform_link_set_up (NMPlatform *self, int ifindex, gboolean *out_no_firmwar g_return_val_if_fail (ifindex > 0, FALSE); - _LOGD ("link: setting up %s (%d)", nm_strquote_a (25, nm_platform_link_get_name (self, ifindex)), ifindex); + _LOG3D ("link: setting up"); return klass->link_set_up (self, ifindex, out_no_firmware); } @@ -1564,7 +1567,7 @@ nm_platform_link_set_down (NMPlatform *self, int ifindex) g_return_val_if_fail (ifindex > 0, FALSE); - _LOGD ("link: setting down %s (%d)", nm_strquote_a (25, nm_platform_link_get_name (self, ifindex)), ifindex); + _LOG3D ("link: setting down"); return klass->link_set_down (self, ifindex); } @@ -1582,7 +1585,7 @@ nm_platform_link_set_arp (NMPlatform *self, int ifindex) g_return_val_if_fail (ifindex >= 0, FALSE); - _LOGD ("link: setting arp %s (%d)", nm_strquote_a (25, nm_platform_link_get_name (self, ifindex)), ifindex); + _LOG3D ("link: setting arp"); return klass->link_set_arp (self, ifindex); } @@ -1600,7 +1603,7 @@ nm_platform_link_set_noarp (NMPlatform *self, int ifindex) g_return_val_if_fail (ifindex >= 0, FALSE); - _LOGD ("link: setting noarp '%s' (%d)", nm_platform_link_get_name (self, ifindex), ifindex); + _LOG3D ("link: setting noarp"); return klass->link_set_noarp (self, ifindex); } @@ -1612,7 +1615,7 @@ nm_platform_link_set_noarp (NMPlatform *self, int ifindex) * * Set interface MTU. */ -NMPlatformError +int nm_platform_link_set_mtu (NMPlatform *self, int ifindex, guint32 mtu) { _CHECK_SELF (self, klass, FALSE); @@ -1620,7 +1623,7 @@ nm_platform_link_set_mtu (NMPlatform *self, int ifindex, guint32 mtu) g_return_val_if_fail (ifindex >= 0, FALSE); g_return_val_if_fail (mtu > 0, FALSE); - _LOGD ("link: setting '%s' (%d) mtu %"G_GUINT32_FORMAT, nm_platform_link_get_name (self, ifindex), ifindex, mtu); + _LOG3D ("link: setting mtu %"G_GUINT32_FORMAT, mtu); return klass->link_set_mtu (self, ifindex, mtu); } @@ -1636,8 +1639,6 @@ nm_platform_link_get_mtu (NMPlatform *self, int ifindex) { const NMPlatformLink *pllink; - _CHECK_SELF (self, klass, 0); - pllink = nm_platform_link_get (self, ifindex); return pllink ? pllink->mtu : 0; } @@ -1658,7 +1659,7 @@ nm_platform_link_set_name (NMPlatform *self, int ifindex, const char *name) g_return_val_if_fail (ifindex >= 0, FALSE); g_return_val_if_fail (name, FALSE); - _LOGD ("link: setting '%s' (%d) name %s", nm_platform_link_get_name (self, ifindex), ifindex, name); + _LOG3D ("link: setting name %s", name); if (strlen (name) + 1 > IFNAMSIZ) return FALSE; @@ -1769,47 +1770,43 @@ nm_platform_link_get_driver_info (NMPlatform *self, * nm_platform_link_enslave: * @self: platform instance * @master: Interface index of the master - * @slave: Interface index of the slave + * @ifindex: Interface index of the slave * - * Enslave @slave to @master. + * Enslave @ifindex to @master. */ gboolean -nm_platform_link_enslave (NMPlatform *self, int master, int slave) +nm_platform_link_enslave (NMPlatform *self, int master, int ifindex) { _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (master > 0, FALSE); - g_return_val_if_fail (slave> 0, FALSE); + g_return_val_if_fail (ifindex > 0, FALSE); - _LOGD ("link: enslaving '%s' (%d) to master '%s' (%d)", - nm_platform_link_get_name (self, slave), slave, - nm_platform_link_get_name (self, master), master); - return klass->link_enslave (self, master, slave); + _LOG3D ("link: enslaving to master '%s'", nm_platform_link_get_name (self, master)); + return klass->link_enslave (self, master, ifindex); } /** * nm_platform_link_release: * @self: platform instance * @master: Interface index of the master - * @slave: Interface index of the slave + * @ifindex: Interface index of the slave * * Release @slave from @master. */ gboolean -nm_platform_link_release (NMPlatform *self, int master, int slave) +nm_platform_link_release (NMPlatform *self, int master, int ifindex) { _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (master > 0, FALSE); - g_return_val_if_fail (slave > 0, FALSE); + g_return_val_if_fail (ifindex > 0, FALSE); - if (nm_platform_link_get_master (self, slave) != master) + if (nm_platform_link_get_master (self, ifindex) != master) return FALSE; - _LOGD ("link: releasing '%s' (%d) from master '%s' (%d)", - nm_platform_link_get_name (self, slave), slave, - nm_platform_link_get_name (self, master), master); - return klass->link_release (self, master, slave); + _LOG3D ("link: releasing from master '%s'", nm_platform_link_get_name (self, master)); + return klass->link_release (self, master, ifindex); } /** @@ -1824,10 +1821,6 @@ nm_platform_link_get_master (NMPlatform *self, int slave) { const NMPlatformLink *pllink; - _CHECK_SELF (self, klass, 0); - - g_return_val_if_fail (slave >= 0, FALSE); - pllink = nm_platform_link_get (self, slave); return pllink ? pllink->master : 0; } @@ -1865,7 +1858,7 @@ nm_platform_link_can_assume (NMPlatform *self, int ifindex) * Returns: the internal link lnk object. The returned object * is owned by the platform cache and must not be modified. Note * however, that the object is guaranteed to be immutable, so - * you can savely take a reference and keep it for yourself + * you can safely take a reference and keep it for yourself * (but don't modify it). */ const NMPObject * @@ -1873,15 +1866,11 @@ nm_platform_link_get_lnk (NMPlatform *self, int ifindex, NMLinkType link_type, c { const NMPObject *obj; - _CHECK_SELF (self, klass, FALSE); - - NM_SET_OUT (out_link, NULL); - - g_return_val_if_fail (ifindex > 0, NULL); - obj = nm_platform_link_get_obj (self, ifindex, TRUE); - if (!obj) + if (!obj) { + NM_SET_OUT (out_link, NULL); return NULL; + } NM_SET_OUT (out_link, &obj->link); @@ -1996,6 +1985,64 @@ nm_platform_link_get_lnk_wireguard (NMPlatform *self, int ifindex, const NMPlatf /*****************************************************************************/ +int +nm_platform_link_wireguard_add (NMPlatform *self, + const char *name, + const NMPlatformLink **out_link) +{ + return nm_platform_link_add (self, name, NM_LINK_TYPE_WIREGUARD, NULL, NULL, 0, out_link); +} + +int +nm_platform_link_wireguard_change (NMPlatform *self, + int ifindex, + const NMPlatformLnkWireGuard *lnk_wireguard, + const NMPWireGuardPeer *peers, + guint peers_len, + gboolean replace_peers) +{ + _CHECK_SELF (self, klass, -NME_BUG); + + nm_assert (klass->link_wireguard_change); + + if (_LOGD_ENABLED ()) { + char buf_lnk[256]; + char buf_peers[512]; + + buf_peers[0] = '\0'; + if (peers_len > 0) { + char *b = buf_peers; + gsize len = sizeof (buf_peers); + guint i; + + nm_utils_strbuf_append_str (&b, &len, " { "); + for (i = 0; i < peers_len; i++) { + nm_utils_strbuf_append_str (&b, &len, " { "); + nm_platform_wireguard_peer_to_string (&peers[i], b, len); + nm_utils_strbuf_seek_end (&b, &len); + nm_utils_strbuf_append_str (&b, &len, " } "); + } + nm_utils_strbuf_append_str (&b, &len, "}"); + } + + _LOG3D ("link: change wireguard ifindex %d, %s, %u peers%s%s", + ifindex, + nm_platform_lnk_wireguard_to_string (lnk_wireguard, buf_lnk, sizeof (buf_lnk)), + peers_len, + buf_peers, + replace_peers ? " (replace-peers)" : " (update-peers)"); + } + + return klass->link_wireguard_change (self, + ifindex, + lnk_wireguard, + peers, + peers_len, + replace_peers); +} + +/*****************************************************************************/ + /** * nm_platform_link_bridge_add: * @self: platform instance @@ -2006,7 +2053,7 @@ nm_platform_link_get_lnk_wireguard (NMPlatform *self, int ifindex, const NMPlatf * * Create a software bridge. */ -NMPlatformError +int nm_platform_link_bridge_add (NMPlatform *self, const char *name, const void *address, @@ -2024,7 +2071,7 @@ nm_platform_link_bridge_add (NMPlatform *self, * * Create a software bonding device. */ -NMPlatformError +int nm_platform_link_bond_add (NMPlatform *self, const char *name, const NMPlatformLink **out_link) @@ -2040,7 +2087,7 @@ nm_platform_link_bond_add (NMPlatform *self, * * Create a software teaming device. */ -NMPlatformError +int nm_platform_link_team_add (NMPlatform *self, const char *name, const NMPlatformLink **out_link) @@ -2058,7 +2105,7 @@ nm_platform_link_team_add (NMPlatform *self, * * Create a software VLAN device. */ -NMPlatformError +int nm_platform_link_vlan_add (NMPlatform *self, const char *name, int parent, @@ -2066,24 +2113,24 @@ nm_platform_link_vlan_add (NMPlatform *self, guint32 vlanflags, const NMPlatformLink **out_link) { - NMPlatformError plerr; + int r; - _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); + _CHECK_SELF (self, klass, -NME_BUG); - g_return_val_if_fail (parent >= 0, NM_PLATFORM_ERROR_BUG); - g_return_val_if_fail (vlanid >= 0, NM_PLATFORM_ERROR_BUG); - g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (parent >= 0, -NME_BUG); + g_return_val_if_fail (vlanid >= 0, -NME_BUG); + g_return_val_if_fail (name, -NME_BUG); - plerr = _link_add_check_existing (self, name, NM_LINK_TYPE_VLAN, out_link); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) - return plerr; + r = _link_add_check_existing (self, name, NM_LINK_TYPE_VLAN, out_link); + if (r < 0) + return r; - _LOGD ("link: adding link '%s': vlan parent %d vlanid %d vlanflags %x", - name, parent, vlanid, vlanflags); + _LOG2D ("link: adding link vlan parent %d vlanid %d vlanflags %x", + parent, vlanid, vlanflags); if (!klass->vlan_add (self, name, parent, vlanid, vlanflags, out_link)) - return NM_PLATFORM_ERROR_UNSPECIFIED; - return NM_PLATFORM_ERROR_SUCCESS; + return -NME_UNSPEC; + return 0; } /** @@ -2095,28 +2142,27 @@ nm_platform_link_vlan_add (NMPlatform *self, * * Create a VXLAN device. */ -NMPlatformError +int nm_platform_link_vxlan_add (NMPlatform *self, const char *name, const NMPlatformLnkVxlan *props, const NMPlatformLink **out_link) { - NMPlatformError plerr; + int r; - _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); + _CHECK_SELF (self, klass, -NME_BUG); - g_return_val_if_fail (props, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (props, -NME_BUG); - plerr = _link_add_check_existing (self, name, NM_LINK_TYPE_VXLAN, out_link); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) - return plerr; + r = _link_add_check_existing (self, name, NM_LINK_TYPE_VXLAN, out_link); + if (r < 0) + return r; - _LOGD ("link: adding link '%s': %s", - name, nm_platform_lnk_vxlan_to_string (props, NULL, 0)); + _LOG2D ("link: adding link %s", nm_platform_lnk_vxlan_to_string (props, NULL, 0)); if (!klass->link_vxlan_add (self, name, props, out_link)) - return NM_PLATFORM_ERROR_UNSPECIFIED; - return NM_PLATFORM_ERROR_SUCCESS; + return -NME_UNSPEC; + return 0; } /** @@ -2138,7 +2184,7 @@ nm_platform_link_vxlan_add (NMPlatform *self, * * Create a TUN or TAP interface. */ -NMPlatformError +int nm_platform_link_tun_add (NMPlatform *self, const char *name, const NMPlatformLnkTun *props, @@ -2146,30 +2192,29 @@ nm_platform_link_tun_add (NMPlatform *self, int *out_fd) { char b[255]; - NMPlatformError plerr; + int r; - _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); + _CHECK_SELF (self, klass, -NME_BUG); - g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG); - g_return_val_if_fail (props, NM_PLATFORM_ERROR_BUG); - g_return_val_if_fail (NM_IN_SET (props->type, IFF_TUN, IFF_TAP), NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (name, -NME_BUG); + g_return_val_if_fail (props, -NME_BUG); + g_return_val_if_fail (NM_IN_SET (props->type, IFF_TUN, IFF_TAP), -NME_BUG); /* creating a non-persistant device requires that the caller handles * the file descriptor. */ - g_return_val_if_fail (props->persist || out_fd, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (props->persist || out_fd, -NME_BUG); NM_SET_OUT (out_fd, -1); - plerr = _link_add_check_existing (self, name, NM_LINK_TYPE_TUN, out_link); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) - return plerr; + r = _link_add_check_existing (self, name, NM_LINK_TYPE_TUN, out_link); + if (r < 0) + return r; - _LOGD ("link: adding link '%s': %s", - name, nm_platform_lnk_tun_to_string (props, b, sizeof (b))); + _LOG2D ("link: adding link %s", nm_platform_lnk_tun_to_string (props, b, sizeof (b))); if (!klass->link_tun_add (self, name, props, out_link, out_fd)) - return NM_PLATFORM_ERROR_UNSPECIFIED; - return NM_PLATFORM_ERROR_SUCCESS; + return -NME_UNSPEC; + return 0; } /** @@ -2181,39 +2226,38 @@ nm_platform_link_tun_add (NMPlatform *self, * * Create a 6LoWPAN interface. */ -NMPlatformError +int nm_platform_link_6lowpan_add (NMPlatform *self, const char *name, int parent, const NMPlatformLink **out_link) { - NMPlatformError plerr; + int r; - _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); + _CHECK_SELF (self, klass, -NME_BUG); - g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (name, -NME_BUG); - plerr = _link_add_check_existing (self, name, NM_LINK_TYPE_6LOWPAN, out_link); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) - return plerr; + r = _link_add_check_existing (self, name, NM_LINK_TYPE_6LOWPAN, out_link); + if (r < 0) + return r; - _LOGD ("adding link '%s': 6lowpan parent %u", name, parent); + _LOG2D ("adding link 6lowpan parent %u", parent); if (!klass->link_6lowpan_add (self, name, parent, out_link)) - return NM_PLATFORM_ERROR_UNSPECIFIED; - return NM_PLATFORM_ERROR_SUCCESS; + return -NME_UNSPEC; + return 0; } gboolean nm_platform_link_6lowpan_get_properties (NMPlatform *self, int ifindex, int *out_parent) { const NMPlatformLink *plink; - _CHECK_SELF (self, klass, FALSE); plink = nm_platform_link_get (self, ifindex); - if (!plink) return FALSE; + if (plink->type != NM_LINK_TYPE_6LOWPAN) return FALSE; @@ -2252,9 +2296,10 @@ link_set_option (NMPlatform *self, int ifindex, const char *category, const char if (dirfd < 0) return FALSE; - path = nm_sprintf_bufa (strlen (category) + strlen (option) + 2, - "%s/%s", - category, option); + path = nm_sprintf_buf_unsafe_a (strlen (category) + strlen (option) + 2, + "%s/%s", + category, + option); return nm_platform_sysctl_set (self, NMP_SYSCTL_PATHID_NETDIR_unsafe (dirfd, ifname_verified, path), value); } @@ -2272,9 +2317,9 @@ link_get_option (NMPlatform *self, int ifindex, const char *category, const char if (dirfd < 0) return NULL; - path = nm_sprintf_bufa (strlen (category) + strlen (option) + 2, - "%s/%s", - category, option); + path = nm_sprintf_buf_unsafe_a (strlen (category) + strlen (option) + 2, + "%s/%s", + category, option); return nm_platform_sysctl_get (self, NMP_SYSCTL_PATHID_NETDIR_unsafe (dirfd, ifname_verified, path)); } @@ -2407,7 +2452,7 @@ nm_platform_link_vlan_change (NMPlatform *self, nm_utils_strbuf_append_str (&b, &len, " (reset-all)"); } - _LOGD ("link: change vlan %d:%s", ifindex, buf); + _LOG3D ("link: change vlan %s", buf); } return klass->link_vlan_change (self, ifindex, @@ -2452,81 +2497,78 @@ nm_platform_link_vlan_set_egress_map (NMPlatform *self, int ifindex, int from, i * * Create a software GRE device. */ -NMPlatformError +int nm_platform_link_gre_add (NMPlatform *self, const char *name, const NMPlatformLnkGre *props, const NMPlatformLink **out_link) { - NMPlatformError plerr; + int r; - _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); + _CHECK_SELF (self, klass, -NME_BUG); - g_return_val_if_fail (props, NM_PLATFORM_ERROR_BUG); - g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (props, -NME_BUG); + g_return_val_if_fail (name, -NME_BUG); - plerr = _link_add_check_existing (self, name, props->is_tap ? NM_LINK_TYPE_GRETAP : NM_LINK_TYPE_GRE, out_link); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) - return plerr; + r = _link_add_check_existing (self, name, props->is_tap ? NM_LINK_TYPE_GRETAP : NM_LINK_TYPE_GRE, out_link); + if (r < 0) + return r; - _LOGD ("adding link '%s': %s", - name, nm_platform_lnk_gre_to_string (props, NULL, 0)); + _LOG2D ("adding link %s", nm_platform_lnk_gre_to_string (props, NULL, 0)); if (!klass->link_gre_add (self, name, props, out_link)) - return NM_PLATFORM_ERROR_UNSPECIFIED; - return NM_PLATFORM_ERROR_SUCCESS; + return -NME_UNSPEC; + return 0; } -static NMPlatformError +static int _infiniband_add_add_or_delete (NMPlatform *self, - int parent, + int ifindex, int p_key, gboolean add, const NMPlatformLink **out_link) { char name[IFNAMSIZ]; const NMPlatformLink *parent_link; - NMPlatformError plerr; + int r; - _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); + _CHECK_SELF (self, klass, -NME_BUG); - g_return_val_if_fail (parent >= 0, NM_PLATFORM_ERROR_BUG); - g_return_val_if_fail (p_key >= 0 && p_key <= 0xffff, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (ifindex >= 0, -NME_BUG); + g_return_val_if_fail (p_key >= 0 && p_key <= 0xffff, -NME_BUG); /* the special keys 0x0000 and 0x8000 are not allowed. */ if (NM_IN_SET (p_key, 0, 0x8000)) - return NM_PLATFORM_ERROR_UNSPECIFIED; + return -NME_UNSPEC; - parent_link = nm_platform_link_get (self, parent); + parent_link = nm_platform_link_get (self, ifindex); if (!parent_link) - return NM_PLATFORM_ERROR_NOT_FOUND; + return -NME_PL_NOT_FOUND; if (parent_link->type != NM_LINK_TYPE_INFINIBAND) - return NM_PLATFORM_ERROR_WRONG_TYPE; + return -NME_PL_WRONG_TYPE; nm_utils_new_infiniband_name (name, parent_link->name, p_key); if (add) { - plerr = _link_add_check_existing (self, name, NM_LINK_TYPE_INFINIBAND, out_link); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) - return plerr; - - _LOGD ("link: adding infiniband partition %s for parent '%s' (%d), key %d", - name, parent_link->name, parent, p_key); - if (!klass->infiniband_partition_add (self, parent, p_key, out_link)) - return NM_PLATFORM_ERROR_UNSPECIFIED; + r = _link_add_check_existing (self, name, NM_LINK_TYPE_INFINIBAND, out_link); + if (r < 0) + return r; + + _LOG3D ("link: adding infiniband partition %s, key %d", name, p_key); + if (!klass->infiniband_partition_add (self, ifindex, p_key, out_link)) + return -NME_UNSPEC; } else { - _LOGD ("link: deleting infiniband partition %s for parent '%s' (%d), key %d", - name, parent_link->name, parent, p_key); + _LOG3D ("link: deleting infiniband partition %s, key %d", name, p_key); - if (!klass->infiniband_partition_delete (self, parent, p_key)) - return NM_PLATFORM_ERROR_UNSPECIFIED; + if (!klass->infiniband_partition_delete (self, ifindex, p_key)) + return -NME_UNSPEC; } - return NM_PLATFORM_ERROR_SUCCESS; + return 0; } -NMPlatformError +int nm_platform_link_infiniband_add (NMPlatform *self, int parent, int p_key, @@ -2535,7 +2577,7 @@ nm_platform_link_infiniband_add (NMPlatform *self, return _infiniband_add_add_or_delete (self, parent, p_key, TRUE, out_link); } -NMPlatformError +int nm_platform_link_infiniband_delete (NMPlatform *self, int parent, int p_key) @@ -2612,30 +2654,29 @@ nm_platform_link_infiniband_get_properties (NMPlatform *self, * * Create an IPv6 tunnel. */ -NMPlatformError +int nm_platform_link_ip6tnl_add (NMPlatform *self, const char *name, const NMPlatformLnkIp6Tnl *props, const NMPlatformLink **out_link) { - NMPlatformError plerr; + int r; - _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); + _CHECK_SELF (self, klass, -NME_BUG); - g_return_val_if_fail (props, NM_PLATFORM_ERROR_BUG); - g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG); - g_return_val_if_fail (!props->is_gre, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (props, -NME_BUG); + g_return_val_if_fail (name, -NME_BUG); + g_return_val_if_fail (!props->is_gre, -NME_BUG); - plerr = _link_add_check_existing (self, name, NM_LINK_TYPE_IP6TNL, out_link); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) - return plerr; + r = _link_add_check_existing (self, name, NM_LINK_TYPE_IP6TNL, out_link); + if (r < 0) + return r; - _LOGD ("adding link '%s': %s", - name, nm_platform_lnk_ip6tnl_to_string (props, NULL, 0)); + _LOG2D ("adding link %s", nm_platform_lnk_ip6tnl_to_string (props, NULL, 0)); if (!klass->link_ip6tnl_add (self, name, props, out_link)) - return NM_PLATFORM_ERROR_UNSPECIFIED; - return NM_PLATFORM_ERROR_SUCCESS; + return -NME_UNSPEC; + return 0; } /** @@ -2647,35 +2688,34 @@ nm_platform_link_ip6tnl_add (NMPlatform *self, * * Create an IPv6 GRE/GRETAP tunnel. */ -NMPlatformError +int nm_platform_link_ip6gre_add (NMPlatform *self, const char *name, const NMPlatformLnkIp6Tnl *props, const NMPlatformLink **out_link) { - NMPlatformError plerr; + int r; - _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); + _CHECK_SELF (self, klass, -NME_BUG); - g_return_val_if_fail (props, NM_PLATFORM_ERROR_BUG); - g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG); - g_return_val_if_fail (props->is_gre, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (props, -NME_BUG); + g_return_val_if_fail (name, -NME_BUG); + g_return_val_if_fail (props->is_gre, -NME_BUG); - plerr = _link_add_check_existing (self, - name, - props->is_tap - ? NM_LINK_TYPE_IP6GRETAP - : NM_LINK_TYPE_IP6GRE, - out_link); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) - return plerr; + r = _link_add_check_existing (self, + name, + props->is_tap + ? NM_LINK_TYPE_IP6GRETAP + : NM_LINK_TYPE_IP6GRE, + out_link); + if (r < 0) + return r; - _LOGD ("adding link '%s': %s", - name, nm_platform_lnk_ip6tnl_to_string (props, NULL, 0)); + _LOG2D ("adding link %s", nm_platform_lnk_ip6tnl_to_string (props, NULL, 0)); if (!klass->link_ip6gre_add (self, name, props, out_link)) - return NM_PLATFORM_ERROR_UNSPECIFIED; - return NM_PLATFORM_ERROR_SUCCESS; + return -NME_UNSPEC; + return 0; } /** @@ -2687,29 +2727,28 @@ nm_platform_link_ip6gre_add (NMPlatform *self, * * Create an IPIP tunnel. */ -NMPlatformError +int nm_platform_link_ipip_add (NMPlatform *self, const char *name, const NMPlatformLnkIpIp *props, const NMPlatformLink **out_link) { - NMPlatformError plerr; + int r; - _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); + _CHECK_SELF (self, klass, -NME_BUG); - g_return_val_if_fail (props, NM_PLATFORM_ERROR_BUG); - g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (props, -NME_BUG); + g_return_val_if_fail (name, -NME_BUG); - plerr = _link_add_check_existing (self, name, NM_LINK_TYPE_IPIP, out_link); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) - return plerr; + r = _link_add_check_existing (self, name, NM_LINK_TYPE_IPIP, out_link); + if (r < 0) + return r; - _LOGD ("adding link '%s': %s", - name, nm_platform_lnk_ipip_to_string (props, NULL, 0)); + _LOG2D ("adding link %s", nm_platform_lnk_ipip_to_string (props, NULL, 0)); if (!klass->link_ipip_add (self, name, props, out_link)) - return NM_PLATFORM_ERROR_UNSPECIFIED; - return NM_PLATFORM_ERROR_SUCCESS; + return -NME_UNSPEC; + return 0; } /** @@ -2722,30 +2761,29 @@ nm_platform_link_ipip_add (NMPlatform *self, * * Create a MACsec interface. */ -NMPlatformError +int nm_platform_link_macsec_add (NMPlatform *self, const char *name, int parent, const NMPlatformLnkMacsec *props, const NMPlatformLink **out_link) { - NMPlatformError plerr; + int r; - _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); + _CHECK_SELF (self, klass, -NME_BUG); - g_return_val_if_fail (props, NM_PLATFORM_ERROR_BUG); - g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (props, -NME_BUG); + g_return_val_if_fail (name, -NME_BUG); - plerr = _link_add_check_existing (self, name, NM_LINK_TYPE_MACSEC, out_link); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) - return plerr; + r = _link_add_check_existing (self, name, NM_LINK_TYPE_MACSEC, out_link); + if (r < 0) + return r; - _LOGD ("adding link '%s': %s", - name, nm_platform_lnk_macsec_to_string (props, NULL, 0)); + _LOG2D ("adding link %s", nm_platform_lnk_macsec_to_string (props, NULL, 0)); if (!klass->link_macsec_add (self, name, parent, props, out_link)) - return NM_PLATFORM_ERROR_UNSPECIFIED; - return NM_PLATFORM_ERROR_SUCCESS; + return -NME_UNSPEC; + return 0; } /** @@ -2757,33 +2795,32 @@ nm_platform_link_macsec_add (NMPlatform *self, * * Create a MACVLAN or MACVTAP device. */ -NMPlatformError +int nm_platform_link_macvlan_add (NMPlatform *self, const char *name, int parent, const NMPlatformLnkMacvlan *props, const NMPlatformLink **out_link) { - NMPlatformError plerr; + int r; NMLinkType type; - _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); + _CHECK_SELF (self, klass, -NME_BUG); - g_return_val_if_fail (props, NM_PLATFORM_ERROR_BUG); - g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (props, -NME_BUG); + g_return_val_if_fail (name, -NME_BUG); type = props->tap ? NM_LINK_TYPE_MACVTAP : NM_LINK_TYPE_MACVLAN; - plerr = _link_add_check_existing (self, name, type, out_link); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) - return plerr; + r = _link_add_check_existing (self, name, type, out_link); + if (r < 0) + return r; - _LOGD ("adding link '%s': %s", - name, nm_platform_lnk_macvlan_to_string (props, NULL, 0)); + _LOG2D ("adding link %s", nm_platform_lnk_macvlan_to_string (props, NULL, 0)); if (!klass->link_macvlan_add (self, name, parent, props, out_link)) - return NM_PLATFORM_ERROR_UNSPECIFIED; - return NM_PLATFORM_ERROR_SUCCESS; + return -NME_UNSPEC; + return 0; } /** @@ -2795,29 +2832,28 @@ nm_platform_link_macvlan_add (NMPlatform *self, * * Create a software SIT device. */ -NMPlatformError +int nm_platform_link_sit_add (NMPlatform *self, const char *name, const NMPlatformLnkSit *props, const NMPlatformLink **out_link) { - NMPlatformError plerr; + int r; - _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); + _CHECK_SELF (self, klass, -NME_BUG); - g_return_val_if_fail (props, NM_PLATFORM_ERROR_BUG); - g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (props, -NME_BUG); + g_return_val_if_fail (name, -NME_BUG); - plerr = _link_add_check_existing (self, name, NM_LINK_TYPE_SIT, out_link); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) - return plerr; + r = _link_add_check_existing (self, name, NM_LINK_TYPE_SIT, out_link); + if (r < 0) + return r; - _LOGD ("adding link '%s': %s", - name, nm_platform_lnk_sit_to_string (props, NULL, 0)); + _LOG2D ("adding link %s", nm_platform_lnk_sit_to_string (props, NULL, 0)); if (!klass->link_sit_add (self, name, props, out_link)) - return NM_PLATFORM_ERROR_UNSPECIFIED; - return NM_PLATFORM_ERROR_SUCCESS; + return -NME_UNSPEC; + return 0; } gboolean @@ -2825,12 +2861,11 @@ nm_platform_link_veth_get_properties (NMPlatform *self, int ifindex, int *out_pe { const NMPlatformLink *plink; int peer_ifindex; - _CHECK_SELF (self, klass, FALSE); plink = nm_platform_link_get (self, ifindex); - if (!plink) return FALSE; + if (plink->type != NM_LINK_TYPE_VETH) return FALSE; @@ -2888,14 +2923,11 @@ nm_platform_link_tun_get_properties (NMPlatform *self, gint64 group; gint64 flags; - _CHECK_SELF (self, klass, FALSE); - - g_return_val_if_fail (ifindex > 0, FALSE); - /* we consider also invisible links (those that are not yet in udev). */ plobj = nm_platform_link_get_obj (self, ifindex, FALSE); if (!plobj) return FALSE; + if (NMP_OBJECT_CAST_LINK (plobj)->type != NM_LINK_TYPE_TUN) return FALSE; @@ -3148,6 +3180,16 @@ nm_platform_wpan_set_short_addr (NMPlatform *self, int ifindex, guint16 short_ad return klass->wpan_set_short_addr (self, ifindex, short_addr); } +gboolean +nm_platform_wpan_set_channel (NMPlatform *self, int ifindex, guint8 page, guint8 channel) +{ + _CHECK_SELF (self, klass, FALSE); + + g_return_val_if_fail (ifindex > 0, FALSE); + + return klass->wpan_set_channel (self, ifindex, page, channel); +} + #define TO_STRING_DEV_BUF_SIZE (5+15+1) static const char * _to_string_dev (NMPlatform *self, int ifindex, char *buf, size_t size) @@ -3387,7 +3429,7 @@ nm_platform_ip4_address_add (NMPlatform *self, if (label) g_strlcpy (addr.label, label, sizeof (addr.label)); - _LOGD ("address: adding or updating IPv4 address: %s", nm_platform_ip4_address_to_string (&addr, NULL, 0)); + _LOG3D ("address: adding or updating IPv4 address: %s", nm_platform_ip4_address_to_string (&addr, NULL, 0)); } return klass->ip4_address_add (self, ifindex, address, plen, peer_address, lifetime, preferred, flags, label); } @@ -3421,7 +3463,7 @@ nm_platform_ip6_address_add (NMPlatform *self, addr.preferred = preferred; addr.n_ifa_flags = flags; - _LOGD ("address: adding or updating IPv6 address: %s", nm_platform_ip6_address_to_string (&addr, NULL, 0)); + _LOG3D ("address: adding or updating IPv6 address: %s", nm_platform_ip6_address_to_string (&addr, NULL, 0)); } return klass->ip6_address_add (self, ifindex, address, plen, peer_address, lifetime, preferred, flags); } @@ -3430,20 +3472,24 @@ gboolean nm_platform_ip4_address_delete (NMPlatform *self, int ifindex, in_addr_t address, guint8 plen, in_addr_t peer_address) { char str_dev[TO_STRING_DEV_BUF_SIZE]; - char str_peer2[NM_UTILS_INET_ADDRSTRLEN]; - char str_peer[100]; + char b1[NM_UTILS_INET_ADDRSTRLEN]; + char b2[NM_UTILS_INET_ADDRSTRLEN]; + char str_peer[INET_ADDRSTRLEN + 50]; _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (plen <= 32, FALSE); - _LOGD ("address: deleting IPv4 address %s/%d, %sifindex %d%s", - nm_utils_inet4_ntop (address, NULL), plen, - peer_address != address - ? nm_sprintf_buf (str_peer, "peer %s, ", nm_utils_inet4_ntop (peer_address, str_peer2)) : "", - ifindex, - _to_string_dev (self, ifindex, str_dev, sizeof (str_dev))); + _LOG3D ("address: deleting IPv4 address %s/%d, %s%s", + nm_utils_inet4_ntop (address, b1), + plen, + peer_address != address + ? nm_sprintf_buf (str_peer, + "peer %s, ", + nm_utils_inet4_ntop (peer_address, b2)) + : "", + _to_string_dev (self, ifindex, str_dev, sizeof (str_dev))); return klass->ip4_address_delete (self, ifindex, address, plen, peer_address); } @@ -3451,15 +3497,16 @@ gboolean nm_platform_ip6_address_delete (NMPlatform *self, int ifindex, struct in6_addr address, guint8 plen) { char str_dev[TO_STRING_DEV_BUF_SIZE]; + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (plen <= 128, FALSE); - _LOGD ("address: deleting IPv6 address %s/%d, ifindex %d%s", - nm_utils_inet6_ntop (&address, NULL), plen, ifindex, - _to_string_dev (self, ifindex, str_dev, sizeof (str_dev))); + _LOG3D ("address: deleting IPv6 address %s/%d, %s", + nm_utils_inet6_ntop (&address, sbuf), plen, + _to_string_dev (self, ifindex, str_dev, sizeof (str_dev))); return klass->ip6_address_delete (self, ifindex, address, plen); } @@ -3711,7 +3758,7 @@ ip4_addr_subnets_is_secondary (const NMPObject *address, * That means, expired addresses and addresses that could not be added * will be dropped. * Hence, the input argument @known_addresses is also an output argument - * telling which addresses were succesfully added. + * telling which addresses were successfully added. * Addresses are removed by unrefing the instance via nmp_object_unref() * and leaving a NULL tombstone. * @@ -3885,7 +3932,7 @@ ip6_address_scope_cmp (gconstpointer a, gconstpointer b) * That means, expired addresses and addresses that could not be added * will be dropped. * Hence, the input argument @known_addresses is also an output argument - * telling which addresses were succesfully added. + * telling which addresses were successfully added. * Addresses are removed by unrefing the instance via nmp_object_unref() * and leaving a NULL tombstone. * @full_sync: Also remove link-local and temporary addresses. @@ -4193,7 +4240,6 @@ nm_platform_ip_route_sync (NMPlatform *self, gboolean success = TRUE; char sbuf1[sizeof (_nm_utils_to_string_buffer)]; char sbuf2[sizeof (_nm_utils_to_string_buffer)]; - char sbuf_err[60]; nm_assert (NM_IS_PLATFORM (self)); nm_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); @@ -4205,7 +4251,7 @@ nm_platform_ip_route_sync (NMPlatform *self, for (i_type = 0; routes && i_type < 2; i_type++) { for (i = 0; i < routes->len; i++) { - NMPlatformError plerr, plerr2; + int r, r2; gboolean gateway_route_added = FALSE; conf_o = routes->pdata[i]; @@ -4227,8 +4273,8 @@ nm_platform_ip_route_sync (NMPlatform *self, (GEqualFunc) nmp_object_id_equal); } if (!g_hash_table_insert (routes_idx, (gpointer) conf_o, (gpointer) conf_o)) { - _LOGD ("route-sync: skip adding duplicate route %s", - nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1))); + _LOG3D ("route-sync: skip adding duplicate route %s", + nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1))); continue; } @@ -4245,7 +4291,7 @@ nm_platform_ip_route_sync (NMPlatform *self, NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) == 0) continue; - /* we need to replace the existing route with a (slightly) differnt + /* we need to replace the existing route with a (slightly) different * one. Delete it first. */ if (!nm_platform_object_delete (self, plat_o)) { /* ignore error. */ @@ -4253,12 +4299,12 @@ nm_platform_ip_route_sync (NMPlatform *self, } sync_route_add: - plerr = nm_platform_ip_route_add (self, - NMP_NLM_FLAG_APPEND - | NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE, - conf_o); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) { - if (-((int) plerr) == EEXIST) { + r = nm_platform_ip_route_add (self, + NMP_NLM_FLAG_APPEND + | NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE, + conf_o); + if (r < 0) { + if (r == -EEXIST) { /* Don't fail for EEXIST. It's not clear that the existing route * is identical to the one that we were about to add. However, * above we should have deleted conflicting (non-identical) routes. */ @@ -4267,92 +4313,92 @@ sync_route_add: NMP_CACHE_ID_TYPE_OBJECT_TYPE, conf_o); if (!plat_entry) { - _LOGD ("route-sync: adding route %s failed with EEXIST, however we cannot find such a route", - nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1))); + _LOG3D ("route-sync: adding route %s failed with EEXIST, however we cannot find such a route", + nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1))); } else if (vt->route_cmp (NMP_OBJECT_CAST_IPX_ROUTE (conf_o), NMP_OBJECT_CAST_IPX_ROUTE (plat_entry->obj), NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) != 0) { - _LOGD ("route-sync: adding route %s failed due to existing (different!) route %s", - nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1)), - nmp_object_to_string (plat_entry->obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf2, sizeof (sbuf2))); + _LOG3D ("route-sync: adding route %s failed due to existing (different!) route %s", + nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1)), + nmp_object_to_string (plat_entry->obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf2, sizeof (sbuf2))); } } } else if (NMP_OBJECT_CAST_IP_ROUTE (conf_o)->rt_source < NM_IP_CONFIG_SOURCE_USER) { - _LOGD ("route-sync: ignore failure to add IPv%c route: %s: %s", + _LOG3D ("route-sync: ignore failure to add IPv%c route: %s: %s", vt->is_ip4 ? '4' : '6', nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1)), - nm_platform_error_to_string (plerr, sbuf_err, sizeof (sbuf_err))); - } else if ( -((int) plerr) == EINVAL + nm_strerror (r)); + } else if ( r == -EINVAL && out_temporary_not_available && _err_inval_due_to_ipv6_tentative_pref_src (self, conf_o)) { - _LOGD ("route-sync: ignore failure to add IPv6 route with tentative IPv6 pref-src: %s: %s", - nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1)), - nm_platform_error_to_string (plerr, sbuf_err, sizeof (sbuf_err))); + _LOG3D ("route-sync: ignore failure to add IPv6 route with tentative IPv6 pref-src: %s: %s", + nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1)), + nm_strerror (r)); if (!*out_temporary_not_available) *out_temporary_not_available = g_ptr_array_new_full (0, (GDestroyNotify) nmp_object_unref); g_ptr_array_add (*out_temporary_not_available, (gpointer) nmp_object_ref (conf_o)); } else if ( !gateway_route_added - && ( ( -((int) plerr) == ENETUNREACH + && ( ( r == -ENETUNREACH && vt->is_ip4 && !!NMP_OBJECT_CAST_IP4_ROUTE (conf_o)->gateway) - || ( -((int) plerr) == EHOSTUNREACH + || ( r == -EHOSTUNREACH && !vt->is_ip4 && !IN6_IS_ADDR_UNSPECIFIED (&NMP_OBJECT_CAST_IP6_ROUTE (conf_o)->gateway)))) { NMPObject oo; if (vt->is_ip4) { - const NMPlatformIP4Route *r = NMP_OBJECT_CAST_IP4_ROUTE (conf_o); + const NMPlatformIP4Route *rt = NMP_OBJECT_CAST_IP4_ROUTE (conf_o); nmp_object_stackinit (&oo, NMP_OBJECT_TYPE_IP4_ROUTE, &((NMPlatformIP4Route) { - .ifindex = r->ifindex, - .network = r->gateway, + .ifindex = rt->ifindex, + .network = rt->gateway, .plen = 32, - .metric = r->metric, - .rt_source = r->rt_source, - .table_coerced = r->table_coerced, + .metric = rt->metric, + .rt_source = rt->rt_source, + .table_coerced = rt->table_coerced, })); } else { - const NMPlatformIP6Route *r = NMP_OBJECT_CAST_IP6_ROUTE (conf_o); + const NMPlatformIP6Route *rt = NMP_OBJECT_CAST_IP6_ROUTE (conf_o); nmp_object_stackinit (&oo, NMP_OBJECT_TYPE_IP6_ROUTE, &((NMPlatformIP6Route) { - .ifindex = r->ifindex, - .network = r->gateway, + .ifindex = rt->ifindex, + .network = rt->gateway, .plen = 128, - .metric = r->metric, - .rt_source = r->rt_source, - .table_coerced = r->table_coerced, + .metric = rt->metric, + .rt_source = rt->rt_source, + .table_coerced = rt->table_coerced, })); } - _LOGD ("route-sync: failure to add IPv%c route: %s: %s; try adding direct route to gateway %s", - vt->is_ip4 ? '4' : '6', - nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1)), - nm_platform_error_to_string (plerr, sbuf_err, sizeof (sbuf_err)), - nmp_object_to_string (&oo, NMP_OBJECT_TO_STRING_PUBLIC, sbuf2, sizeof (sbuf2))); - - plerr2 = nm_platform_ip_route_add (self, - NMP_NLM_FLAG_APPEND - | NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE, - &oo); - - if (plerr2 != NM_PLATFORM_ERROR_SUCCESS) { - _LOGD ("route-sync: failure to add gateway IPv%c route: %s: %s", - vt->is_ip4 ? '4' : '6', - nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1)), - nm_platform_error_to_string (plerr, sbuf_err, sizeof (sbuf_err))); + _LOG3D ("route-sync: failure to add IPv%c route: %s: %s; try adding direct route to gateway %s", + vt->is_ip4 ? '4' : '6', + nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1)), + nm_strerror (r), + nmp_object_to_string (&oo, NMP_OBJECT_TO_STRING_PUBLIC, sbuf2, sizeof (sbuf2))); + + r2 = nm_platform_ip_route_add (self, + NMP_NLM_FLAG_APPEND + | NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE, + &oo); + + if (r2 < 0) { + _LOG3D ("route-sync: failure to add gateway IPv%c route: %s: %s", + vt->is_ip4 ? '4' : '6', + nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1)), + nm_strerror (r2)); } gateway_route_added = TRUE; goto sync_route_add; } else { - _LOGW ("route-sync: failure to add IPv%c route: %s: %s", + _LOG3W ("route-sync: failure to add IPv%c route: %s: %s", vt->is_ip4 ? '4' : '6', nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1)), - nm_platform_error_to_string (plerr, sbuf_err, sizeof (sbuf_err))); + nm_strerror (r)); success = FALSE; } } @@ -4494,30 +4540,32 @@ nm_platform_ip_route_normalize (int addr_family, } } -static NMPlatformError +static int _ip_route_add (NMPlatform *self, NMPNlmFlags flags, int addr_family, gconstpointer route) { char sbuf[sizeof (_nm_utils_to_string_buffer)]; + int ifindex; _CHECK_SELF (self, klass, FALSE); nm_assert (route); nm_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); - _LOGD ("route: %-10s IPv%c route: %s", - _nmp_nlm_flag_to_string (flags & NMP_NLM_FLAG_FMASK), - nm_utils_addr_family_to_char (addr_family), - addr_family == AF_INET - ? nm_platform_ip4_route_to_string (route, sbuf, sizeof (sbuf)) - : nm_platform_ip6_route_to_string (route, sbuf, sizeof (sbuf))); + ifindex = ((NMPlatformObject *)route)->ifindex; + _LOG3D ("route: %-10s IPv%c route: %s", + _nmp_nlm_flag_to_string (flags & NMP_NLM_FLAG_FMASK), + nm_utils_addr_family_to_char (addr_family), + addr_family == AF_INET + ? nm_platform_ip4_route_to_string (route, sbuf, sizeof (sbuf)) + : nm_platform_ip6_route_to_string (route, sbuf, sizeof (sbuf))); return klass->ip_route_add (self, flags, addr_family, route); } -NMPlatformError +int nm_platform_ip_route_add (NMPlatform *self, NMPNlmFlags flags, const NMPObject *route) @@ -4538,7 +4586,7 @@ nm_platform_ip_route_add (NMPlatform *self, return _ip_route_add (self, flags, addr_family, NMP_OBJECT_CAST_IP_ROUTE (route)); } -NMPlatformError +int nm_platform_ip4_route_add (NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP4Route *route) @@ -4546,7 +4594,7 @@ nm_platform_ip4_route_add (NMPlatform *self, return _ip_route_add (self, flags, AF_INET, route); } -NMPlatformError +int nm_platform_ip6_route_add (NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP6Route *route) @@ -4558,6 +4606,7 @@ gboolean nm_platform_object_delete (NMPlatform *self, const NMPObject *obj) { + int ifindex = obj->object.ifindex; _CHECK_SELF (self, klass, FALSE); if (!NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), NMP_OBJECT_TYPE_IP4_ROUTE, @@ -4566,16 +4615,16 @@ nm_platform_object_delete (NMPlatform *self, NMP_OBJECT_TYPE_TFILTER)) g_return_val_if_reached (FALSE); - _LOGD ("%s: delete %s", - NMP_OBJECT_GET_CLASS (obj)->obj_type_name, - nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + _LOG3D ("%s: delete %s", + NMP_OBJECT_GET_CLASS (obj)->obj_type_name, + nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); return klass->object_delete (self, obj); } /*****************************************************************************/ -NMPlatformError +int nm_platform_ip_route_get (NMPlatform *self, int addr_family, gconstpointer address /* in_addr_t or struct in6_addr */, @@ -4583,16 +4632,15 @@ nm_platform_ip_route_get (NMPlatform *self, NMPObject **out_route) { nm_auto_nmpobj NMPObject *route = NULL; - NMPlatformError result; + int result; char buf[NM_UTILS_INET_ADDRSTRLEN]; - char buf_err[200]; char buf_oif[64]; _CHECK_SELF (self, klass, FALSE); - g_return_val_if_fail (address, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (address, -NME_BUG); g_return_val_if_fail (NM_IN_SET (addr_family, AF_INET, - AF_INET6), NM_PLATFORM_ERROR_BUG); + AF_INET6), -NME_BUG); _LOGT ("route: get IPv%c route for: %s%s", nm_utils_addr_family_to_char (addr_family), @@ -4600,7 +4648,7 @@ nm_platform_ip_route_get (NMPlatform *self, oif_ifindex > 0 ? nm_sprintf_buf (buf_oif, " oif %d", oif_ifindex) : ""); if (!klass->ip_route_get) - result = NM_PLATFORM_ERROR_OPNOTSUPP; + result = -NME_PL_OPNOTSUPP; else { result = klass->ip_route_get (self, addr_family, @@ -4609,12 +4657,12 @@ nm_platform_ip_route_get (NMPlatform *self, &route); } - if (result != NM_PLATFORM_ERROR_SUCCESS) { + if (result < 0) { nm_assert (!route); _LOGW ("route: get IPv%c route for: %s failed with %s", nm_utils_addr_family_to_char (addr_family), inet_ntop (addr_family, address, buf, sizeof (buf)), - nm_platform_error_to_string (result, buf_err, sizeof (buf_err))); + nm_strerror (result)); } else { nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (route), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); nm_assert (!NMP_OBJECT_IS_STACKINIT (route)); @@ -4807,7 +4855,7 @@ _ip4_dev_route_blacklist_schedule (NMPlatform *self) * route, however it has a wrong metric of zero. We add our own device route (with * proper metric), but need to delete the route that kernel adds. * - * The problem is, that kernel does not immidiately add the route, when adding + * The problem is, that kernel does not immediately add the route, when adding * the address. It only shows up some time later. So, we register here a list * of blacklisted routes, and when they show up within a time out, we assume it's * the kernel generated one, and we delete it. @@ -4898,14 +4946,15 @@ nm_platform_ip4_dev_route_blacklist_set (NMPlatform *self, /*****************************************************************************/ -NMPlatformError +int nm_platform_qdisc_add (NMPlatform *self, NMPNlmFlags flags, const NMPlatformQdisc *qdisc) { - _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); + int ifindex = qdisc->ifindex; + _CHECK_SELF (self, klass, -NME_BUG); - _LOGD ("adding or updating a qdisc: %s", nm_platform_qdisc_to_string (qdisc, NULL, 0)); + _LOG3D ("adding or updating a qdisc: %s", nm_platform_qdisc_to_string (qdisc, NULL, 0)); return klass->qdisc_add (self, flags, qdisc); } @@ -4954,7 +5003,7 @@ nm_platform_qdisc_sync (NMPlatform *self, const NMPObject *q = g_ptr_array_index (known_qdiscs, i); success &= (nm_platform_qdisc_add (self, NMP_NLM_FLAG_ADD, - NMP_OBJECT_CAST_QDISC (q)) == NM_PLATFORM_ERROR_SUCCESS); + NMP_OBJECT_CAST_QDISC (q)) >= 0); } } @@ -4963,14 +5012,15 @@ nm_platform_qdisc_sync (NMPlatform *self, /*****************************************************************************/ -NMPlatformError +int nm_platform_tfilter_add (NMPlatform *self, NMPNlmFlags flags, const NMPlatformTfilter *tfilter) { - _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); + int ifindex = tfilter->ifindex; + _CHECK_SELF (self, klass, -NME_BUG); - _LOGD ("adding or updating a tfilter: %s", nm_platform_tfilter_to_string (tfilter, NULL, 0)); + _LOG3D ("adding or updating a tfilter: %s", nm_platform_tfilter_to_string (tfilter, NULL, 0)); return klass->tfilter_add (self, flags, tfilter); } @@ -5019,7 +5069,7 @@ nm_platform_tfilter_sync (NMPlatform *self, const NMPObject *q = g_ptr_array_index (known_tfilters, i); success &= (nm_platform_tfilter_add (self, NMP_NLM_FLAG_ADD, - NMP_OBJECT_CAST_TFILTER (q)) == NM_PLATFORM_ERROR_SUCCESS); + NMP_OBJECT_CAST_TFILTER (q)) >= 0); } } @@ -5477,6 +5527,7 @@ nm_platform_lnk_vxlan_to_string (const NMPlatformLnkVxlan *lnk, char *buf, gsize char str_dst_port[25]; char str_tos[25]; char str_ttl[25]; + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; if (!nm_utils_to_string_buffer_init_null (lnk, &buf, &len)) return buf; @@ -5487,7 +5538,7 @@ nm_platform_lnk_vxlan_to_string (const NMPlatformLnkVxlan *lnk, char *buf, gsize g_snprintf (str_group, sizeof (str_group), " %s %s", IN_MULTICAST (ntohl (lnk->group)) ? "group" : "remote", - nm_utils_inet4_ntop (lnk->group, NULL)); + nm_utils_inet4_ntop (lnk->group, sbuf)); } if (IN6_IS_ADDR_UNSPECIFIED (&lnk->group6)) str_group6[0] = '\0'; @@ -5496,7 +5547,7 @@ nm_platform_lnk_vxlan_to_string (const NMPlatformLnkVxlan *lnk, char *buf, gsize " %s%s %s", IN6_IS_ADDR_MULTICAST (&lnk->group6) ? "group" : "remote", str_group[0] ? "6" : "", /* usually, a vxlan has either v4 or v6 only. */ - nm_utils_inet6_ntop (&lnk->group6, NULL)); + nm_utils_inet6_ntop (&lnk->group6, sbuf)); } if (lnk->local == 0) @@ -5504,7 +5555,7 @@ nm_platform_lnk_vxlan_to_string (const NMPlatformLnkVxlan *lnk, char *buf, gsize else { g_snprintf (str_local, sizeof (str_local), " local %s", - nm_utils_inet4_ntop (lnk->local, NULL)); + nm_utils_inet4_ntop (lnk->local, sbuf)); } if (IN6_IS_ADDR_UNSPECIFIED (&lnk->local6)) str_local6[0] = '\0'; @@ -5512,7 +5563,7 @@ nm_platform_lnk_vxlan_to_string (const NMPlatformLnkVxlan *lnk, char *buf, gsize g_snprintf (str_local6, sizeof (str_local6), " local%s %s", str_local[0] ? "6" : "", /* usually, a vxlan has either v4 or v6 only. */ - nm_utils_inet6_ntop (&lnk->local6, NULL)); + nm_utils_inet6_ntop (&lnk->local6, sbuf)); } g_snprintf (buf, len, @@ -5554,28 +5605,26 @@ nm_platform_lnk_vxlan_to_string (const NMPlatformLnkVxlan *lnk, char *buf, gsize const char * nm_platform_wireguard_peer_to_string (const NMPWireGuardPeer *peer, char *buf, gsize len) { + char *buf0 = buf; gs_free char *public_key_b64 = NULL; - char s_endpoint[NM_UTILS_INET_ADDRSTRLEN + 100]; + char s_sockaddr[NM_UTILS_INET_ADDRSTRLEN + 100]; + char s_endpoint[20 + sizeof (s_sockaddr)]; char s_addr[NM_UTILS_INET_ADDRSTRLEN]; guint i; nm_utils_to_string_buffer_init (&buf, &len); - if (peer->endpoint_family == AF_INET) { - nm_sprintf_buf (s_endpoint, - " endpoint %s:%u", - nm_utils_inet4_ntop (peer->endpoint_addr.addr4, s_addr), - (guint) peer->endpoint_port); - } else if (peer->endpoint_family == AF_INET6) { + public_key_b64 = g_base64_encode (peer->public_key, sizeof (peer->public_key)); + + if (peer->endpoint.sa.sa_family != AF_UNSPEC) { nm_sprintf_buf (s_endpoint, - " endpoint [%s]:%u", - nm_utils_inet6_ntop (&peer->endpoint_addr.addr6, s_addr), - (guint) peer->endpoint_port); + " endpoint %s", + nm_sock_addr_union_to_string (&peer->endpoint, + s_sockaddr, + sizeof (s_sockaddr))); } else s_endpoint[0] = '\0'; - public_key_b64 = g_base64_encode (peer->public_key, sizeof (peer->public_key)); - nm_utils_strbuf_append (&buf, &len, "public-key %s" "%s" /* preshared-key */ @@ -5584,7 +5633,7 @@ nm_platform_wireguard_peer_to_string (const NMPWireGuardPeer *peer, char *buf, g " tx %"G_GUINT64_FORMAT "%s", /* allowed-ips */ public_key_b64, - nm_utils_mem_all_zero (peer->preshared_key, sizeof (peer->preshared_key)) + nm_utils_memeqzero (peer->preshared_key, sizeof (peer->preshared_key)) ? "" : " preshared-key (hidden)", s_endpoint, @@ -5603,7 +5652,7 @@ nm_platform_wireguard_peer_to_string (const NMPWireGuardPeer *peer, char *buf, g allowed_ip->mask); } - return buf; + return buf0; } const char * @@ -5614,7 +5663,7 @@ nm_platform_lnk_wireguard_to_string (const NMPlatformLnkWireGuard *lnk, char *bu if (!nm_utils_to_string_buffer_init_null (lnk, &buf, &len)) return buf; - if (!nm_utils_mem_all_zero (lnk->public_key, sizeof (lnk->public_key))) + if (!nm_utils_memeqzero (lnk->public_key, sizeof (lnk->public_key))) public_b64 = g_base64_encode (lnk->public_key, sizeof (lnk->public_key)); g_snprintf (buf, len, @@ -5627,7 +5676,7 @@ nm_platform_lnk_wireguard_to_string (const NMPlatformLnkWireGuard *lnk, char *bu ? " public-key " : "", public_b64 ?: "", - nm_utils_mem_all_zero (lnk->private_key, sizeof (lnk->private_key)) + nm_utils_memeqzero (lnk->private_key, sizeof (lnk->private_key)) ? "" : " private-key (hidden)", lnk->listen_port, @@ -5926,13 +5975,21 @@ nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route, char *buf, gsi const char * nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route, char *buf, gsize len) { - char s_network[INET6_ADDRSTRLEN], s_gateway[INET6_ADDRSTRLEN], s_pref_src[INET6_ADDRSTRLEN]; - char s_src_all[INET6_ADDRSTRLEN + 40], s_src[INET6_ADDRSTRLEN]; + char s_network[INET6_ADDRSTRLEN]; + char s_gateway[INET6_ADDRSTRLEN]; + char s_pref_src[INET6_ADDRSTRLEN]; + char s_src_all[INET6_ADDRSTRLEN + 40]; + char s_src[INET6_ADDRSTRLEN]; char str_table[30]; char str_pref[40]; char str_pref2[30]; - char str_dev[TO_STRING_DEV_BUF_SIZE], s_source[50]; - char str_window[32], str_cwnd[32], str_initcwnd[32], str_initrwnd[32], str_mtu[32]; + char str_dev[TO_STRING_DEV_BUF_SIZE]; + char s_source[50]; + char str_window[32]; + char str_cwnd[32]; + char str_initcwnd[32]; + char str_initrwnd[32]; + char str_mtu[32]; char str_rtm_flags[_RTM_FLAGS_TO_STRING_MAXLEN]; if (!nm_utils_to_string_buffer_init_null (route, &buf, &len)) @@ -6982,7 +7039,7 @@ nm_platform_ip_address_cmp_expiry (const NMPlatformIPAddress *a, const NMPlatfor /* if the lifetime is equal, compare the preferred time. */ ta = tb = 0; - if (a->preferred == NM_PLATFORM_LIFETIME_PERMANENT || a->lifetime == 0 /* liftime==0 means permanent! */) + if (a->preferred == NM_PLATFORM_LIFETIME_PERMANENT || a->lifetime == 0 /* lifetime==0 means permanent! */) ta = G_MAXINT64; else if (a->timestamp) ta = ((gint64) a->timestamp) + a->preferred; @@ -7017,44 +7074,43 @@ nm_platform_signal_change_type_to_string (NMPlatformSignalChangeType change_type static void log_link (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformLink *device, NMPlatformSignalChangeType change_type, gpointer user_data) { - - _LOGD ("signal: link %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_link_to_string (device, NULL, 0)); + _LOG3D ("signal: link %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_link_to_string (device, NULL, 0)); } static void log_ip4_address (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformIP4Address *address, NMPlatformSignalChangeType change_type, gpointer user_data) { - _LOGD ("signal: address 4 %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_ip4_address_to_string (address, NULL, 0)); + _LOG3D ("signal: address 4 %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_ip4_address_to_string (address, NULL, 0)); } static void log_ip6_address (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformIP6Address *address, NMPlatformSignalChangeType change_type, gpointer user_data) { - _LOGD ("signal: address 6 %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_ip6_address_to_string (address, NULL, 0)); + _LOG3D ("signal: address 6 %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_ip6_address_to_string (address, NULL, 0)); } static void log_ip4_route (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformIP4Route *route, NMPlatformSignalChangeType change_type, gpointer user_data) { - _LOGD ("signal: route 4 %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_ip4_route_to_string (route, NULL, 0)); + _LOG3D ("signal: route 4 %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_ip4_route_to_string (route, NULL, 0)); } static void log_ip6_route (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformIP6Route *route, NMPlatformSignalChangeType change_type, gpointer user_data) { - _LOGD ("signal: route 6 %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_ip6_route_to_string (route, NULL, 0)); + _LOG3D ("signal: route 6 %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_ip6_route_to_string (route, NULL, 0)); } static void log_qdisc (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformQdisc *qdisc, NMPlatformSignalChangeType change_type, gpointer user_data) { - _LOGD ("signal: qdisc %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_qdisc_to_string (qdisc, NULL, 0)); + _LOG3D ("signal: qdisc %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_qdisc_to_string (qdisc, NULL, 0)); } static void log_tfilter (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformTfilter *tfilter, NMPlatformSignalChangeType change_type, gpointer user_data) { - _LOGD ("signal: tfilter %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_tfilter_to_string (tfilter, NULL, 0)); + _LOG3D ("signal: tfilter %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_tfilter_to_string (tfilter, NULL, 0)); } /*****************************************************************************/ @@ -7069,6 +7125,7 @@ nm_platform_cache_update_emit_signal (NMPlatform *self, gboolean visible_old; const NMPObject *o; const NMPClass *klass; + int ifindex; nm_assert (NM_IN_SET ((NMPlatformSignalChangeType) cache_op, NM_PLATFORM_SIGNAL_NONE, NM_PLATFORM_SIGNAL_ADDED, @@ -7110,6 +7167,7 @@ nm_platform_cache_update_emit_signal (NMPlatform *self, return; } + ifindex = o->object.ifindex; klass = NMP_OBJECT_GET_CLASS (o); if ( klass->obj_type == NMP_OBJECT_TYPE_IP4_ROUTE @@ -7117,10 +7175,10 @@ nm_platform_cache_update_emit_signal (NMPlatform *self, && NM_IN_SET (cache_op, NMP_CACHE_OPS_ADDED, NMP_CACHE_OPS_UPDATED)) _ip4_dev_route_blacklist_notify_route (self, o); - _LOGt ("emit signal %s %s: %s", - klass->signal_type, - nm_platform_signal_change_type_to_string ((NMPlatformSignalChangeType) cache_op), - nmp_object_to_string (o, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + _LOG3t ("emit signal %s %s: %s", + klass->signal_type, + nm_platform_signal_change_type_to_string ((NMPlatformSignalChangeType) cache_op), + nmp_object_to_string (o, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); nmp_object_ref (o); g_signal_emit (self, diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 2bf5966c..412ac597 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -21,12 +21,6 @@ #ifndef __NETWORKMANAGER_PLATFORM_H__ #define __NETWORKMANAGER_PLATFORM_H__ -#include <netinet/in.h> -#include <linux/if.h> -#include <linux/if_addr.h> -#include <linux/if_link.h> -#include <linux/ip6_tunnel.h> - #include "nm-dbus-interface.h" #include "nm-core-types-internal.h" @@ -35,6 +29,7 @@ #include "nm-setting-wired.h" #include "nm-setting-wireless.h" #include "nm-setting-ip-tunnel.h" +#include "nm-utils/nm-errno.h" #define NM_TYPE_PLATFORM (nm_platform_get_type ()) #define NM_PLATFORM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_PLATFORM, NMPlatform)) @@ -53,6 +48,14 @@ /*****************************************************************************/ +/* IFNAMSIZ is both defined in <linux/if.h> and <net/if.h>. In the past, these + * headers conflicted, so we cannot simply include either of them in a header-file.*/ +#define NMP_IFNAMSIZ 16 + +/*****************************************************************************/ + +struct _NMPWireGuardPeer; + struct udev_device; typedef gboolean (*NMPObjectPredicateFunc) (const NMPObject *obj, @@ -149,29 +152,6 @@ typedef enum { } NMPlatformIPRouteCmpType; -typedef enum { /*< skip >*/ - - /* dummy value, to enforce that the enum type is signed and has a size - * to hold an integer. We want to encode errno from <errno.h> as negative - * values. */ - _NM_PLATFORM_ERROR_MININT = G_MININT, - - NM_PLATFORM_ERROR_SUCCESS = 0, - - NM_PLATFORM_ERROR_BUG, - - NM_PLATFORM_ERROR_UNSPECIFIED, - - NM_PLATFORM_ERROR_NOT_FOUND, - NM_PLATFORM_ERROR_EXISTS, - NM_PLATFORM_ERROR_WRONG_TYPE, - NM_PLATFORM_ERROR_NOT_SLAVE, - NM_PLATFORM_ERROR_NO_FIRMWARE, - NM_PLATFORM_ERROR_OPNOTSUPP, - NM_PLATFORM_ERROR_NETLINK, - NM_PLATFORM_ERROR_CANT_SET_MTU, -} NMPlatformError; - typedef enum { /* match-flags are strictly inclusive. That means, @@ -208,7 +188,7 @@ typedef enum { struct _NMPlatformLink { __NMPlatformObject_COMMON; - char name[IFNAMSIZ]; + char name[NMP_IFNAMSIZ]; NMLinkType type; /* rtnl_link_get_type(), IFLA_INFO_KIND. */ @@ -299,10 +279,10 @@ struct _NMPlatformObject { * are permanent. This rule is so that unset addresses (calloc) are permanent by default. * 2 @lifetime==@preferred==NM_PLATFORM_LIFETIME_PERMANENT: @timestamp is irrelevant (but mostly * set to 0). Such addresses are permanent. - * 3 Non permanent addreses should (almost) always have @timestamp > 0. 0 is not a valid timestamp + * 3 Non permanent addresses should (almost) always have @timestamp > 0. 0 is not a valid timestamp * and never returned by nm_utils_get_monotonic_timestamp_s(). In this case @valid/@preferred * is anchored at @timestamp. - * 4 Non permanent addresses with @timestamp == 0 are implicitely anchored at *now*, thus the time + * 4 Non permanent addresses with @timestamp == 0 are implicitly anchored at *now*, thus the time * moves as time goes by. This is usually not useful, except e.g. nm_platform_ip[46]_address_add(). * * Non permanent addresses from DHCP/RA might have the @timestamp set to the moment of when the @@ -355,7 +335,7 @@ struct _NMPlatformIP4Address { * */ in_addr_t peer_address; /* PTP peer address */ - char label[IFNAMSIZ]; + char label[NMP_IFNAMSIZ]; }; /** @@ -417,7 +397,7 @@ typedef union { * On the other hand, for IPv6 you cannot add two IPv6 routes that only differ * by an RTA_METRICS property. * - * When deleting a route, kernel seems to ignore the RTA_METRICS propeties. + * When deleting a route, kernel seems to ignore the RTA_METRICS properties. * That is a problem/bug for IPv4 because you cannot explicitly select which * route to delete. Kernel just picks the first. See rh#1475642. */ \ \ @@ -792,13 +772,14 @@ typedef struct { void (*refresh_all) (NMPlatform *self, NMPObjectType obj_type); - gboolean (*link_add) (NMPlatform *, - const char *name, - NMLinkType type, - const char *veth_peer, - const void *address, - size_t address_len, - const NMPlatformLink **out_link); + int (*link_add) (NMPlatform *, + const char *name, + NMLinkType type, + const char *veth_peer, + const void *address, + size_t address_len, + const NMPlatformLink **out_link); + gboolean (*link_delete) (NMPlatform *, int ifindex); gboolean (*link_refresh) (NMPlatform *, int ifindex); @@ -815,15 +796,15 @@ typedef struct { const char *(*link_get_udi) (NMPlatform *self, int ifindex); struct udev_device *(*link_get_udev_device) (NMPlatform *self, int ifindex); - NMPlatformError (*link_set_user_ipv6ll_enabled) (NMPlatform *, int ifindex, gboolean enabled); + int (*link_set_user_ipv6ll_enabled) (NMPlatform *, int ifindex, gboolean enabled); gboolean (*link_set_token) (NMPlatform *, int ifindex, NMUtilsIPv6IfaceId iid); gboolean (*link_get_permanent_address) (NMPlatform *, int ifindex, guint8 *buf, size_t *length); - NMPlatformError (*link_set_address) (NMPlatform *, int ifindex, gconstpointer address, size_t length); - NMPlatformError (*link_set_mtu) (NMPlatform *, int ifindex, guint32 mtu); + int (*link_set_address) (NMPlatform *, int ifindex, gconstpointer address, size_t length); + int (*link_set_mtu) (NMPlatform *, int ifindex, guint32 mtu); gboolean (*link_set_name) (NMPlatform *, int ifindex, const char *name); gboolean (*link_set_sriov_params) (NMPlatform *, int ifindex, guint num_vfs, int autoprobe); gboolean (*link_set_sriov_vfs) (NMPlatform *self, int ifindex, const NMPlatformVF *const *vfs); @@ -846,6 +827,13 @@ typedef struct { gboolean (*link_can_assume) (NMPlatform *, int ifindex); + int (*link_wireguard_change) (NMPlatform *self, + int ifindex, + const NMPlatformLnkWireGuard *lnk_wireguard, + const struct _NMPWireGuardPeer *peers, + guint peers_len, + gboolean replace_peers); + gboolean (*vlan_add) (NMPlatform *, const char *name, int parent, int vlanid, guint32 vlanflags, const NMPlatformLink **out_link); gboolean (*link_vlan_change) (NMPlatform *self, int ifindex, @@ -927,6 +915,7 @@ typedef struct { gboolean (*wpan_set_pan_id) (NMPlatform *, int ifindex, guint16 pan_id); guint16 (*wpan_get_short_addr) (NMPlatform *, int ifindex); gboolean (*wpan_set_short_addr) (NMPlatform *, int ifindex, guint16 short_addr); + gboolean (*wpan_set_channel) (NMPlatform *, int ifindex, guint8 page, guint8 channel); gboolean (*object_delete) (NMPlatform *, const NMPObject *obj); @@ -950,23 +939,23 @@ typedef struct { gboolean (*ip4_address_delete) (NMPlatform *, int ifindex, in_addr_t address, guint8 plen, in_addr_t peer_address); gboolean (*ip6_address_delete) (NMPlatform *, int ifindex, struct in6_addr address, guint8 plen); - NMPlatformError (*ip_route_add) (NMPlatform *, - NMPNlmFlags flags, - int addr_family, - const NMPlatformIPRoute *route); - NMPlatformError (*ip_route_get) (NMPlatform *self, - int addr_family, - gconstpointer address, - int oif_ifindex, - NMPObject **out_route); + int (*ip_route_add) (NMPlatform *, + NMPNlmFlags flags, + int addr_family, + const NMPlatformIPRoute *route); + int (*ip_route_get) (NMPlatform *self, + int addr_family, + gconstpointer address, + int oif_ifindex, + NMPObject **out_route); - NMPlatformError (*qdisc_add) (NMPlatform *self, - NMPNlmFlags flags, - const NMPlatformQdisc *qdisc); + int (*qdisc_add) (NMPlatform *self, + NMPNlmFlags flags, + const NMPlatformQdisc *qdisc); - NMPlatformError (*tfilter_add) (NMPlatform *self, - NMPNlmFlags flags, - const NMPlatformTfilter *tfilter); + int (*tfilter_add) (NMPlatform *self, + NMPNlmFlags flags, + const NMPlatformTfilter *tfilter); NMPlatformKernelSupportFlags (*check_kernel_support) (NMPlatform * self, NMPlatformKernelSupportFlags request_flags); @@ -1095,22 +1084,23 @@ gboolean nm_platform_netns_push (NMPlatform *platform, NMPNetns **netns); const char *nm_link_type_to_string (NMLinkType link_type); -const char *nm_platform_error_to_string (NMPlatformError error, - char *buf, - gsize buf_len); -#define nm_platform_error_to_string_a(error) \ - (nm_platform_error_to_string ((error), g_alloca (30), 30)) - #define NMP_SYSCTL_PATHID_ABSOLUTE(path) \ ((const char *) NULL), -1, (path) #define NMP_SYSCTL_PATHID_NETDIR_unsafe(dirfd, ifname, path) \ - nm_sprintf_bufa (NM_STRLEN ("net:/sys/class/net//\0") + IFNAMSIZ + strlen (path), \ - "net:/sys/class/net/%s/%s", (ifname), (path)), \ + nm_sprintf_buf_unsafe_a ( NM_STRLEN ("net:/sys/class/net//\0") \ + + NMP_IFNAMSIZ \ + + ({ \ + const gsize _l = strlen (path); \ + \ + nm_assert (_l < 200); \ + _l; \ + }), \ + "net:/sys/class/net/%s/%s", (ifname), (path)), \ (dirfd), (path) #define NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname, path) \ - nm_sprintf_bufa (NM_STRLEN ("net:/sys/class/net//"path"/\0") + IFNAMSIZ, \ + nm_sprintf_bufa (NM_STRLEN ("net:/sys/class/net//"path"/\0") + NMP_IFNAMSIZ, \ "net:/sys/class/net/%s/%s", (ifname), path), \ (dirfd), (""path"") @@ -1120,7 +1110,35 @@ char *nm_platform_sysctl_get (NMPlatform *self, const char *pathid, int dirfd, c gint32 nm_platform_sysctl_get_int32 (NMPlatform *self, const char *pathid, int dirfd, const char *path, gint32 fallback); gint64 nm_platform_sysctl_get_int_checked (NMPlatform *self, const char *pathid, int dirfd, const char *path, guint base, gint64 min, gint64 max, gint64 fallback); -gboolean nm_platform_sysctl_set_ip6_hop_limit_safe (NMPlatform *self, const char *iface, int value); +char *nm_platform_sysctl_ip_conf_get (NMPlatform *platform, + int addr_family, + const char *ifname, + const char *property); + +gint64 nm_platform_sysctl_ip_conf_get_int_checked (NMPlatform *platform, + int addr_family, + const char *ifname, + const char *property, + guint base, + gint64 min, + gint64 max, + gint64 fallback); + +gboolean nm_platform_sysctl_ip_conf_set (NMPlatform *platform, + int addr_family, + const char *ifname, + const char *property, + const char *value); + +gboolean nm_platform_sysctl_ip_conf_set_int64 (NMPlatform *platform, + int addr_family, + const char *ifname, + const char *property, + gint64 value); + +gboolean nm_platform_sysctl_ip_conf_set_ipv6_hop_limit_safe (NMPlatform *self, + const char *iface, + int value); const char *nm_platform_if_indextoname (NMPlatform *self, int ifindex, char *out_ifname/* of size IFNAMSIZ */); int nm_platform_if_nametoindex (NMPlatform *self, const char *ifname); @@ -1135,11 +1153,11 @@ const NMPlatformLink *nm_platform_link_get_by_ifname (NMPlatform *self, const ch const NMPlatformLink *nm_platform_link_get_by_address (NMPlatform *self, NMLinkType link_type, gconstpointer address, size_t length); GPtrArray *nm_platform_link_get_all (NMPlatform *self, gboolean sort_by_name); -NMPlatformError nm_platform_link_dummy_add (NMPlatform *self, const char *name, const NMPlatformLink **out_link); -NMPlatformError nm_platform_link_bridge_add (NMPlatform *self, const char *name, const void *address, size_t address_len, const NMPlatformLink **out_link); -NMPlatformError nm_platform_link_bond_add (NMPlatform *self, const char *name, const NMPlatformLink **out_link); -NMPlatformError nm_platform_link_team_add (NMPlatform *self, const char *name, const NMPlatformLink **out_link); -NMPlatformError nm_platform_link_veth_add (NMPlatform *self, const char *name, const char *peer, const NMPlatformLink **out_link); +int nm_platform_link_dummy_add (NMPlatform *self, const char *name, const NMPlatformLink **out_link); +int nm_platform_link_bridge_add (NMPlatform *self, const char *name, const void *address, size_t address_len, const NMPlatformLink **out_link); +int nm_platform_link_bond_add (NMPlatform *self, const char *name, const NMPlatformLink **out_link); +int nm_platform_link_team_add (NMPlatform *self, const char *name, const NMPlatformLink **out_link); +int nm_platform_link_veth_add (NMPlatform *self, const char *name, const char *peer, const NMPlatformLink **out_link); gboolean nm_platform_link_delete (NMPlatform *self, int ifindex); @@ -1160,7 +1178,7 @@ GPtrArray *nm_platform_lookup_clone (NMPlatform *platform, NMPObjectPredicateFunc predicate, gpointer user_data); -/* convienience methods to lookup the link and access fields of NMPlatformLink. */ +/* convenience methods to lookup the link and access fields of NMPlatformLink. */ int nm_platform_link_get_ifindex (NMPlatform *self, const char *name); const char *nm_platform_link_get_name (NMPlatform *self, int ifindex); NMLinkType nm_platform_link_get_type (NMPlatform *self, int ifindex); @@ -1210,12 +1228,12 @@ const char *nm_platform_link_get_udi (NMPlatform *self, int ifindex); struct udev_device *nm_platform_link_get_udev_device (NMPlatform *self, int ifindex); -NMPlatformError nm_platform_link_set_user_ipv6ll_enabled (NMPlatform *self, int ifindex, gboolean enabled); +int nm_platform_link_set_user_ipv6ll_enabled (NMPlatform *self, int ifindex, gboolean enabled); gboolean nm_platform_link_set_ipv6_token (NMPlatform *self, int ifindex, NMUtilsIPv6IfaceId iid); gboolean nm_platform_link_get_permanent_address (NMPlatform *self, int ifindex, guint8 *buf, size_t *length); -NMPlatformError nm_platform_link_set_address (NMPlatform *self, int ifindex, const void *address, size_t length); -NMPlatformError nm_platform_link_set_mtu (NMPlatform *self, int ifindex, guint32 mtu); +int nm_platform_link_set_address (NMPlatform *self, int ifindex, const void *address, size_t length); +int nm_platform_link_set_mtu (NMPlatform *self, int ifindex, guint32 mtu); gboolean nm_platform_link_set_name (NMPlatform *self, int ifindex, const char *name); gboolean nm_platform_link_set_sriov_params (NMPlatform *self, int ifindex, guint num_vfs, int autoprobe); gboolean nm_platform_link_set_sriov_vfs (NMPlatform *self, int ifindex, const NMPlatformVF *const *vfs); @@ -1259,12 +1277,12 @@ const NMPlatformLnkVlan *nm_platform_link_get_lnk_vlan (NMPlatform *self, int if const NMPlatformLnkVxlan *nm_platform_link_get_lnk_vxlan (NMPlatform *self, int ifindex, const NMPlatformLink **out_link); const NMPlatformLnkWireGuard *nm_platform_link_get_lnk_wireguard (NMPlatform *self, int ifindex, const NMPlatformLink **out_link); -NMPlatformError nm_platform_link_vlan_add (NMPlatform *self, - const char *name, - int parent, - int vlanid, - guint32 vlanflags, - const NMPlatformLink **out_link); +int nm_platform_link_vlan_add (NMPlatform *self, + const char *name, + int parent, + int vlanid, + guint32 vlanflags, + const NMPlatformLink **out_link); gboolean nm_platform_link_vlan_set_ingress_map (NMPlatform *self, int ifindex, int from, int to); gboolean nm_platform_link_vlan_set_egress_map (NMPlatform *self, int ifindex, int from, int to); gboolean nm_platform_link_vlan_change (NMPlatform *self, @@ -1278,18 +1296,18 @@ gboolean nm_platform_link_vlan_change (NMPlatform *self, const NMVlanQosMapping *egress_map, gsize n_egress_map); -NMPlatformError nm_platform_link_vxlan_add (NMPlatform *self, - const char *name, - const NMPlatformLnkVxlan *props, - const NMPlatformLink **out_link); - -NMPlatformError nm_platform_link_infiniband_add (NMPlatform *self, - int parent, - int p_key, - const NMPlatformLink **out_link); -NMPlatformError nm_platform_link_infiniband_delete (NMPlatform *self, - int parent, - int p_key); +int nm_platform_link_vxlan_add (NMPlatform *self, + const char *name, + const NMPlatformLnkVxlan *props, + const NMPlatformLink **out_link); + +int nm_platform_link_infiniband_add (NMPlatform *self, + int parent, + int p_key, + const NMPlatformLink **out_link); +int nm_platform_link_infiniband_delete (NMPlatform *self, + int parent, + int p_key); gboolean nm_platform_link_infiniband_get_properties (NMPlatform *self, int ifindex, int *parent, int *p_key, const char **mode); gboolean nm_platform_link_veth_get_properties (NMPlatform *self, int ifindex, int *out_peer_ifindex); @@ -1318,55 +1336,67 @@ guint16 nm_platform_wpan_get_pan_id (NMPlatform *platform, int ifindex gboolean nm_platform_wpan_set_pan_id (NMPlatform *platform, int ifindex, guint16 pan_id); guint16 nm_platform_wpan_get_short_addr (NMPlatform *platform, int ifindex); gboolean nm_platform_wpan_set_short_addr (NMPlatform *platform, int ifindex, guint16 short_addr); +gboolean nm_platform_wpan_set_channel (NMPlatform *platform, int ifindex, guint8 page, guint8 channel); void nm_platform_ip4_address_set_addr (NMPlatformIP4Address *addr, in_addr_t address, guint8 plen); const struct in6_addr *nm_platform_ip6_address_get_peer (const NMPlatformIP6Address *addr); const NMPlatformIP4Address *nm_platform_ip4_address_get (NMPlatform *self, int ifindex, in_addr_t address, guint8 plen, in_addr_t peer_address); -NMPlatformError nm_platform_link_gre_add (NMPlatform *self, - const char *name, - const NMPlatformLnkGre *props, - const NMPlatformLink **out_link); -NMPlatformError nm_platform_link_ip6tnl_add (NMPlatform *self, - const char *name, - const NMPlatformLnkIp6Tnl *props, - const NMPlatformLink **out_link); -NMPlatformError nm_platform_link_ip6gre_add (NMPlatform *self, - const char *name, - const NMPlatformLnkIp6Tnl *props, - const NMPlatformLink **out_link); -NMPlatformError nm_platform_link_ipip_add (NMPlatform *self, - const char *name, - const NMPlatformLnkIpIp *props, - const NMPlatformLink **out_link); -NMPlatformError nm_platform_link_macsec_add (NMPlatform *self, - const char *name, - int parent, - const NMPlatformLnkMacsec *props, - const NMPlatformLink **out_link); -NMPlatformError nm_platform_link_macvlan_add (NMPlatform *self, - const char *name, - int parent, - const NMPlatformLnkMacvlan *props, - const NMPlatformLink **out_link); -NMPlatformError nm_platform_link_sit_add (NMPlatform *self, - const char *name, - const NMPlatformLnkSit *props, - const NMPlatformLink **out_link); -NMPlatformError nm_platform_link_tun_add (NMPlatform *self, - const char *name, - const NMPlatformLnkTun *props, - const NMPlatformLink **out_link, - int *out_fd); -NMPlatformError nm_platform_link_6lowpan_add (NMPlatform *self, - const char *name, - int parent, - const NMPlatformLink **out_link); +int nm_platform_link_gre_add (NMPlatform *self, + const char *name, + const NMPlatformLnkGre *props, + const NMPlatformLink **out_link); +int nm_platform_link_ip6tnl_add (NMPlatform *self, + const char *name, + const NMPlatformLnkIp6Tnl *props, + const NMPlatformLink **out_link); +int nm_platform_link_ip6gre_add (NMPlatform *self, + const char *name, + const NMPlatformLnkIp6Tnl *props, + const NMPlatformLink **out_link); +int nm_platform_link_ipip_add (NMPlatform *self, + const char *name, + const NMPlatformLnkIpIp *props, + const NMPlatformLink **out_link); +int nm_platform_link_macsec_add (NMPlatform *self, + const char *name, + int parent, + const NMPlatformLnkMacsec *props, + const NMPlatformLink **out_link); +int nm_platform_link_macvlan_add (NMPlatform *self, + const char *name, + int parent, + const NMPlatformLnkMacvlan *props, + const NMPlatformLink **out_link); +int nm_platform_link_sit_add (NMPlatform *self, + const char *name, + const NMPlatformLnkSit *props, + const NMPlatformLink **out_link); +int nm_platform_link_tun_add (NMPlatform *self, + const char *name, + const NMPlatformLnkTun *props, + const NMPlatformLink **out_link, + int *out_fd); +int nm_platform_link_6lowpan_add (NMPlatform *self, + const char *name, + int parent, + const NMPlatformLink **out_link); gboolean nm_platform_link_6lowpan_get_properties (NMPlatform *self, int ifindex, int *out_parent); +int nm_platform_link_wireguard_add (NMPlatform *self, + const char *name, + const NMPlatformLink **out_link); + +int nm_platform_link_wireguard_change (NMPlatform *self, + int ifindex, + const NMPlatformLnkWireGuard *lnk_wireguard, + const struct _NMPWireGuardPeer *peers, + guint peers_len, + gboolean replace_peers); + const NMPlatformIP6Address *nm_platform_ip6_address_get (NMPlatform *self, int ifindex, struct in6_addr address); gboolean nm_platform_object_delete (NMPlatform *self, const NMPObject *route); @@ -1399,11 +1429,11 @@ gboolean nm_platform_ip_address_flush (NMPlatform *self, void nm_platform_ip_route_normalize (int addr_family, NMPlatformIPRoute *route); -NMPlatformError nm_platform_ip_route_add (NMPlatform *self, - NMPNlmFlags flags, - const NMPObject *route); -NMPlatformError nm_platform_ip4_route_add (NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP4Route *route); -NMPlatformError nm_platform_ip6_route_add (NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP6Route *route); +int nm_platform_ip_route_add (NMPlatform *self, + NMPNlmFlags flags, + const NMPObject *route); +int nm_platform_ip4_route_add (NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP4Route *route); +int nm_platform_ip6_route_add (NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP6Route *route); GPtrArray *nm_platform_ip_route_get_prune_list (NMPlatform *self, int addr_family, @@ -1421,22 +1451,22 @@ gboolean nm_platform_ip_route_flush (NMPlatform *self, int addr_family, int ifindex); -NMPlatformError nm_platform_ip_route_get (NMPlatform *self, - int addr_family, - gconstpointer address, - int oif_ifindex, - NMPObject **out_route); +int nm_platform_ip_route_get (NMPlatform *self, + int addr_family, + gconstpointer address, + int oif_ifindex, + NMPObject **out_route); -NMPlatformError nm_platform_qdisc_add (NMPlatform *self, - NMPNlmFlags flags, - const NMPlatformQdisc *qdisc); +int nm_platform_qdisc_add (NMPlatform *self, + NMPNlmFlags flags, + const NMPlatformQdisc *qdisc); gboolean nm_platform_qdisc_sync (NMPlatform *self, int ifindex, GPtrArray *known_qdiscs); -NMPlatformError nm_platform_tfilter_add (NMPlatform *self, - NMPNlmFlags flags, - const NMPlatformTfilter *tfilter); +int nm_platform_tfilter_add (NMPlatform *self, + NMPNlmFlags flags, + const NMPlatformTfilter *tfilter); gboolean nm_platform_tfilter_sync (NMPlatform *self, int ifindex, GPtrArray *known_tfilters); @@ -1467,7 +1497,6 @@ const char *nm_platform_vlan_qos_mapping_to_string (const char *name, char *buf, gsize len); -struct _NMPWireGuardPeer; const char *nm_platform_wireguard_peer_to_string (const struct _NMPWireGuardPeer *peer, char *buf, gsize len); diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c index fdc27440..80e65b49 100644 --- a/src/platform/nmp-object.c +++ b/src/platform/nmp-object.c @@ -24,6 +24,7 @@ #include <unistd.h> #include <linux/rtnetlink.h> +#include <linux/if.h> #include <libudev.h> #include "nm-utils.h" @@ -88,6 +89,192 @@ struct _NMPCache { /*****************************************************************************/ +int +nm_sock_addr_union_cmp (const NMSockAddrUnion *a, const NMSockAddrUnion *b) +{ + nm_assert (!a || NM_IN_SET (a->sa.sa_family, AF_UNSPEC, AF_INET, AF_INET6)); + nm_assert (!b || NM_IN_SET (b->sa.sa_family, AF_UNSPEC, AF_INET, AF_INET6)); + + NM_CMP_SELF (a, b); + + NM_CMP_FIELD (a, b, sa.sa_family); + switch (a->sa.sa_family) { + case AF_INET: + NM_CMP_DIRECT (ntohl (a->in.sin_addr.s_addr), ntohl (b->in.sin_addr.s_addr)); + NM_CMP_DIRECT (htons (a->in.sin_port), htons (b->in.sin_port)); + break; + case AF_INET6: + NM_CMP_DIRECT_IN6ADDR (&a->in6.sin6_addr, &b->in6.sin6_addr); + NM_CMP_DIRECT (htons (a->in6.sin6_port), htons (b->in6.sin6_port)); + NM_CMP_FIELD (a, b, in6.sin6_scope_id); + NM_CMP_FIELD (a, b, in6.sin6_flowinfo); + break; + } + return 0; +} + +void +nm_sock_addr_union_hash_update (const NMSockAddrUnion *a, NMHashState *h) +{ + if (!a) { + nm_hash_update_val (h, 1241364739u); + return; + } + + nm_assert (NM_IN_SET (a->sa.sa_family, AF_UNSPEC, AF_INET, AF_INET6)); + + switch (a->sa.sa_family) { + case AF_INET: + nm_hash_update_vals (h, + a->in.sin_family, + a->in.sin_addr.s_addr, + a->in.sin_port); + return; + case AF_INET6: + nm_hash_update_vals (h, + a->in6.sin6_family, + a->in6.sin6_addr, + a->in6.sin6_port, + a->in6.sin6_scope_id, + a->in6.sin6_flowinfo); + return; + default: + nm_hash_update_val (h, a->sa.sa_family); + return; + } +} + +/** + * nm_sock_addr_union_cpy: + * @dst: the destination #NMSockAddrUnion. It will always be fully initialized, + * to one of the address families AF_INET, AF_INET6, or AF_UNSPEC (in case of + * error). + * @src: (allow-none): the source buffer with an sockaddr to copy. It may be unaligned in + * memory. If not %NULL, the buffer must be at least large enough to contain + * sa.sa_family, and then, depending on sa.sa_family, it must be large enough + * to hold struct sockaddr_in or struct sockaddr_in6. + * + * @dst will always be fully initialized (including setting all un-used bytes to zero). + */ +void +nm_sock_addr_union_cpy (NMSockAddrUnion *dst, + gconstpointer src /* unaligned (const NMSockAddrUnion *) */) +{ + struct sockaddr sa; + gsize src_len; + + nm_assert (dst); + + *dst = (NMSockAddrUnion) NM_SOCK_ADDR_UNION_INIT_UNSPEC; + + if (!src) + return; + + memcpy (&sa.sa_family, &((struct sockaddr *) src)->sa_family, sizeof (sa.sa_family)); + + if (sa.sa_family == AF_INET) + src_len = sizeof (struct sockaddr_in); + else if (sa.sa_family == AF_INET6) + src_len = sizeof (struct sockaddr_in6); + else + return; + + memcpy (dst, src, src_len); + nm_assert (dst->sa.sa_family == sa.sa_family); +} + +/** + * nm_sock_addr_union_cpy_untrusted: + * @dst: the destination #NMSockAddrUnion. It will always be fully initialized, + * to one of the address families AF_INET, AF_INET6, or AF_UNSPEC (in case of + * error). + * @src: the source buffer with an sockaddr to copy. It may be unaligned in + * memory. + * @src_len: the length of @src in bytes. + * + * The function requires @src_len to be either sizeof(struct sockaddr_in) or sizeof (struct sockaddr_in6). + * If that's the case, then @src will be interpreted as such structure (unaligned), and + * accessed. It will check sa.sa_family to match the expected sizes, and if it does, the + * struct will be copied. + * + * On any failure, @dst will be set to sa.sa_family AF_UNSPEC. + * @dst will always be fully initialized (including setting all un-used bytes to zero). + */ +void +nm_sock_addr_union_cpy_untrusted (NMSockAddrUnion *dst, + gconstpointer src /* unaligned (const NMSockAddrUnion *) */, + gsize src_len) +{ + int f_expected; + struct sockaddr sa; + + nm_assert (dst); + + *dst = (NMSockAddrUnion) NM_SOCK_ADDR_UNION_INIT_UNSPEC; + + if (src_len == sizeof (struct sockaddr_in)) + f_expected = AF_INET; + else if (src_len == sizeof (struct sockaddr_in6)) + f_expected = AF_INET6; + else + return; + + memcpy (&sa.sa_family, &((struct sockaddr *) src)->sa_family, sizeof (sa.sa_family)); + + if (sa.sa_family != f_expected) + return; + + memcpy (dst, src, src_len); + nm_assert (dst->sa.sa_family == sa.sa_family); +} + +const char * +nm_sock_addr_union_to_string (const NMSockAddrUnion *sa, + char *buf, + gsize len) +{ + char s_addr[NM_UTILS_INET_ADDRSTRLEN]; + char s_scope_id[40]; + + if (!nm_utils_to_string_buffer_init_null (sa, &buf, &len)) + return buf; + + /* maybe we should use getnameinfo(), but here implement it ourself. + * + * We want to see the actual bytes for debugging (as we understand them), + * and now what getnameinfo() makes of it. Also, it's simpler this way. */ + + switch (sa->sa.sa_family) { + case AF_INET: + g_snprintf (buf, len, + "%s:%u", + nm_utils_inet4_ntop (sa->in.sin_addr.s_addr, s_addr), + (guint) htons (sa->in.sin_port)); + break; + case AF_INET6: + g_snprintf (buf, len, + "[%s%s]:%u", + nm_utils_inet6_ntop (&sa->in6.sin6_addr, s_addr), + ( sa->in6.sin6_scope_id != 0 + ? nm_sprintf_buf (s_scope_id, "%u", sa->in6.sin6_scope_id) + : ""), + (guint) htons (sa->in6.sin6_port)); + break; + case AF_UNSPEC: + g_snprintf (buf, len, "unspec"); + break; + default: + g_snprintf (buf, len, + "{addr-family:%u}", + (unsigned) sa->sa.sa_family); + break; + } + + return buf; +} + +/*****************************************************************************/ + static const NMDedupMultiIdxTypeClass _dedup_multi_idx_type_class; static void @@ -391,14 +578,9 @@ _wireguard_peer_hash_update (const NMPWireGuardPeer *peer, peer->rx_bytes, peer->tx_bytes, peer->last_handshake_time.tv_sec, - peer->last_handshake_time.tv_nsec, - peer->endpoint_port, - peer->endpoint_family); + peer->last_handshake_time.tv_nsec); - if (peer->endpoint_family == AF_INET) - nm_hash_update_val (h, peer->endpoint_addr.addr4); - else if (peer->endpoint_family == AF_INET6) - nm_hash_update_val (h, peer->endpoint_addr.addr6); + nm_sock_addr_union_hash_update (&peer->endpoint, h); for (i = 0; i < peer->allowed_ips_len; i++) _wireguard_allowed_ip_hash_update (&peer->allowed_ips[i], h); @@ -418,15 +600,11 @@ _wireguard_peer_cmp (const NMPWireGuardPeer *a, NM_CMP_FIELD (a, b, tx_bytes); NM_CMP_FIELD (a, b, allowed_ips_len); NM_CMP_FIELD (a, b, persistent_keepalive_interval); - NM_CMP_FIELD (a, b, endpoint_port); - NM_CMP_FIELD (a, b, endpoint_family); + NM_CMP_FIELD (a, b, endpoint.sa.sa_family); NM_CMP_FIELD_MEMCMP (a, b, public_key); NM_CMP_FIELD_MEMCMP (a, b, preshared_key); - if (a->endpoint_family == AF_INET) - NM_CMP_FIELD (a, b, endpoint_addr.addr4); - else if (a->endpoint_family == AF_INET6) - NM_CMP_FIELD_IN6ADDR (a, b, endpoint_addr.addr6); + NM_CMP_RETURN (nm_sock_addr_union_cmp (&a->endpoint, &b->endpoint)); for (i = 0; i < a->allowed_ips_len; i++) { NM_CMP_RETURN (_wireguard_allowed_ip_cmp (&a->allowed_ips[i], @@ -629,9 +807,12 @@ _nmp_object_stackinit_from_class (NMPObject *obj, const NMPClass *klass) nm_assert (obj); nm_assert (klass); - memset (obj, 0, sizeof (NMPObject)); - obj->_class = klass; - obj->parent._ref_count = NM_OBJ_REF_COUNT_STACKINIT; + *obj = (NMPObject) { + .parent = { + .klass = (const NMDedupMultiObjClass *) klass, + ._ref_count = NM_OBJ_REF_COUNT_STACKINIT, + }, + }; } static NMPObject * @@ -643,9 +824,12 @@ _nmp_object_stackinit_from_type (NMPObject *obj, NMPObjectType obj_type) klass = nmp_class_from_type (obj_type); nm_assert (klass); - memset (obj, 0, sizeof (NMPObject)); - obj->_class = klass; - obj->parent._ref_count = NM_OBJ_REF_COUNT_STACKINIT; + *obj = (NMPObject) { + .parent = { + .klass = (const NMDedupMultiObjClass *) klass, + ._ref_count = NM_OBJ_REF_COUNT_STACKINIT, + }, + }; return obj; } @@ -896,11 +1080,9 @@ static const char * \ _vt_cmd_plobj_to_string_id_##type (const NMPlatformObject *_obj, char *buf, gsize buf_len) \ { \ plat_type *const obj = (plat_type *) _obj; \ - char buf1[NM_UTILS_INET_ADDRSTRLEN]; \ - char buf2[NM_UTILS_INET_ADDRSTRLEN]; \ + _nm_unused char buf1[NM_UTILS_INET_ADDRSTRLEN]; \ + _nm_unused char buf2[NM_UTILS_INET_ADDRSTRLEN]; \ \ - (void) buf1; \ - (void) buf2; \ g_snprintf (buf, buf_len, \ __VA_ARGS__); \ return buf; \ @@ -1636,7 +1818,7 @@ nmp_cache_link_connected_needs_toggle (const NMPCache *cache, const NMPObject *m * The flag obj->link.connected depends on the state of other links in the * @cache. See also nmp_cache_link_connected_needs_toggle(). Given an ifindex * of a master, check if the cache contains such a master link that needs - * toogling of the connected flag. + * toggling of the connected flag. * * Returns: NULL if there is no master link with ifindex @master_ifindex that should be toggled. * Otherwise, return the link object from inside the cache with the given ifindex. @@ -1975,6 +2157,8 @@ nmp_cache_lookup_link_full (const NMPCache *cache, } else if (!ifname && !match_fn) return NULL; else { + const NMPObject *obj_best = NULL; + if (ifname) { if (strlen (ifname) >= IFNAMSIZ) return NULL; @@ -1986,16 +2170,21 @@ nmp_cache_lookup_link_full (const NMPCache *cache, nmp_cache_iter_for_each_link (&iter, head_entry, &link) { obj = NMP_OBJECT_UP_CAST (link); - if (visible_only && !nmp_object_is_visible (obj)) - continue; if (link_type != NM_LINK_TYPE_NONE && obj->link.type != link_type) continue; + if (visible_only && !nmp_object_is_visible (obj)) + continue; if (match_fn && !match_fn (obj, user_data)) continue; - return obj; + /* if there are multiple candidates, prefer the visible ones. */ + if ( visible_only + || nmp_object_is_visible (obj)) + return obj; + if (!obj_best) + obj_best = obj; } - return NULL; + return obj_best; } } @@ -2338,7 +2527,7 @@ nmp_cache_remove_netlink (NMPCache *cache, * * Returns: how the cache changed. * - * Even if there was no change in the cace (NMP_CACHE_OPS_UNCHANGED), @out_obj_old + * Even if there was no change in the cache (NMP_CACHE_OPS_UNCHANGED), @out_obj_old * and @out_obj_new will be set accordingly. **/ NMPCacheOpsType @@ -2609,7 +2798,7 @@ update_done: nm_dedup_multi_entry_reorder (entry_cur, NULL, FALSE); break; default: - /* this is an unexecpted case, probably a bug that we need to handle better. */ + /* this is an unexpected case, probably a bug that we need to handle better. */ resync_required = TRUE; break; } @@ -3096,7 +3285,7 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .cmd_obj_dispose = _vt_cmd_obj_dispose_lnk_wireguard, .cmd_obj_to_string = _vt_cmd_obj_to_string_lnk_wireguard, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_wireguard_to_string, - .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_lnk_vlan_hash_update, - .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_vlan_cmp, + .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_lnk_wireguard_hash_update, + .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_wireguard_cmp, }, }; diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h index 97be8321..4649441b 100644 --- a/src/platform/nmp-object.h +++ b/src/platform/nmp-object.h @@ -21,6 +21,8 @@ #ifndef __NMP_OBJECT_H__ #define __NMP_OBJECT_H__ +#include <netinet/in.h> + #include "nm-utils/nm-obj.h" #include "nm-utils/nm-dedup-multi.h" #include "nm-platform.h" @@ -29,6 +31,38 @@ struct udev_device; /*****************************************************************************/ +typedef union { + struct sockaddr sa; + struct sockaddr_in in; + struct sockaddr_in6 in6; +} NMSockAddrUnion; + +#define NM_SOCK_ADDR_UNION_INIT_UNSPEC \ + { \ + .sa = { \ + .sa_family = AF_UNSPEC, \ + }, \ + } + +int nm_sock_addr_union_cmp (const NMSockAddrUnion *a, + const NMSockAddrUnion *b); + +void nm_sock_addr_union_hash_update (const NMSockAddrUnion *a, + NMHashState *h); + +void nm_sock_addr_union_cpy (NMSockAddrUnion *dst, + gconstpointer src /* unaligned (const NMSockAddrUnion *) */); + +void nm_sock_addr_union_cpy_untrusted (NMSockAddrUnion *dst, + gconstpointer src /* unaligned (const NMSockAddrUnion *) */, + gsize src_len); + +const char *nm_sock_addr_union_to_string (const NMSockAddrUnion *sa, + char *buf, + gsize len); + +/*****************************************************************************/ + typedef struct { NMIPAddr addr; guint8 family; @@ -36,10 +70,12 @@ typedef struct { } NMPWireGuardAllowedIP; typedef struct _NMPWireGuardPeer { - NMIPAddr endpoint_addr; + NMSockAddrUnion endpoint; + struct timespec last_handshake_time; guint64 rx_bytes; guint64 tx_bytes; + union { const NMPWireGuardAllowedIP *allowed_ips; guint _construct_idx_start; @@ -48,11 +84,11 @@ typedef struct _NMPWireGuardPeer { guint allowed_ips_len; guint _construct_idx_end; }; + guint16 persistent_keepalive_interval; - guint16 endpoint_port; + guint8 public_key[NMP_WIREGUARD_PUBLIC_KEY_LEN]; guint8 preshared_key[NMP_WIREGUARD_SYMMETRIC_KEY_LEN]; - guint8 endpoint_family; } NMPWireGuardPeer; /*****************************************************************************/ @@ -100,14 +136,14 @@ typedef enum { /*< skip >*/ * * Also, note that links may be considered invisible. This index type * expose all links, even invisible ones. For addresses/routes, this - * distiction doesn't exist, as all addresses/routes that are alive + * distinction doesn't exist, as all addresses/routes that are alive * are visible as well. */ NMP_CACHE_ID_TYPE_OBJECT_TYPE, /* index for the link objects by ifname. */ NMP_CACHE_ID_TYPE_LINK_BY_IFNAME, - /* indeces for the visible default-routes, ignoring ifindex. + /* indices for the visible default-routes, ignoring ifindex. * This index only contains two partitions: all visible default-routes, * separate for IPv4 and IPv6. */ NMP_CACHE_ID_TYPE_DEFAULT_ROUTES, @@ -504,7 +540,7 @@ nmp_object_ref (const NMPObject *obj) } /* ref and unref accept const pointers. NMPObject is supposed to be shared - * and kept immutable. Disallowing to take/retrun a reference to a const + * and kept immutable. Disallowing to take/return a reference to a const * NMPObject is cumbersome, because callers are precisely expected to * keep a ref on the otherwise immutable object. */ g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj), NULL); diff --git a/src/platform/tests/meson.build b/src/platform/tests/meson.build index bedc6916..3704e2ac 100644 --- a/src/platform/tests/meson.build +++ b/src/platform/tests/meson.build @@ -22,7 +22,7 @@ foreach test_unit: test_units 'platform/' + test_unit[0], test_script, timeout: test_unit[3], - args: test_args + [exe.full_path()] + args: test_args + [exe.full_path()], ) endforeach diff --git a/src/platform/tests/test-address.c b/src/platform/tests/test-address.c index ddef8853..d84a806d 100644 --- a/src/platform/tests/test-address.c +++ b/src/platform/tests/test-address.c @@ -189,7 +189,7 @@ test_ip4_address_general_2 (void) inet_pton (AF_INET, IP4_ADDRESS, &addr); g_assert (ifindex > 0); - /* Looks like addresses are not announced by kerenl when the interface + /* Looks like addresses are not announced by kernel when the interface * is down. Link-local IPv6 address is automatically added. */ g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, DEVICE_IFINDEX, NULL)); diff --git a/src/platform/tests/test-cleanup.c b/src/platform/tests/test-cleanup.c index 12d91812..6c73a63e 100644 --- a/src/platform/tests/test-cleanup.c +++ b/src/platform/tests/test-cleanup.c @@ -52,7 +52,7 @@ test_cleanup_internal (void) inet_pton (AF_INET6, "2001:db8:e:f:1:2:3:4", &gateway6); /* Create and set up device */ - g_assert (nm_platform_link_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_SUCCESS); + g_assert (NMTST_NM_ERR_SUCCESS (nm_platform_link_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL))); accept_signal (link_added); free_signal (link_added); g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME), NULL)); @@ -128,8 +128,7 @@ _nmtstp_init_tests (int *argc, char ***argv) void _nmtstp_setup_tests (void) { - nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); - g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME)); + nmtstp_link_delete (NM_PLATFORM_GET, -1, -1, DEVICE_NAME, FALSE); g_test_add_func ("/internal", test_cleanup_internal); /* FIXME: add external cleanup check */ diff --git a/src/platform/tests/test-common.c b/src/platform/tests/test-common.c index 1cb2f516..8e29b5ce 100644 --- a/src/platform/tests/test-common.c +++ b/src/platform/tests/test-common.c @@ -366,9 +366,12 @@ _nmtstp_assert_ip4_route_exists (const char *file, &c); if (c != c_exists && c_exists != -1) { + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + g_error ("[%s:%u] %s(): The ip4 route %s/%d metric %u tos %u shall exist %u times, but platform has it %u times", file, line, func, - nm_utils_inet4_ntop (network, NULL), plen, + nm_utils_inet4_ntop (network, sbuf), + plen, metric, tos, c_exists, @@ -821,7 +824,8 @@ _ip_address_add (NMPlatform *platform, gs_free char *s_valid = NULL; gs_free char *s_preferred = NULL; gs_free char *s_label = NULL; - char b1[NM_UTILS_INET_ADDRSTRLEN], b2[NM_UTILS_INET_ADDRSTRLEN]; + char b1[NM_UTILS_INET_ADDRSTRLEN]; + char b2[NM_UTILS_INET_ADDRSTRLEN]; ifname = nm_platform_link_get_name (platform, ifindex); g_assert (ifname); @@ -834,14 +838,14 @@ _ip_address_add (NMPlatform *platform, s_label = g_strdup_printf ("%s:%s", ifname, label); if (is_v4) { - char s_peer[100]; + char s_peer[NM_UTILS_INET_ADDRSTRLEN + 50]; g_assert (flags == 0); if ( peer_address->addr4 != address->addr4 || nmtst_get_rand_int () % 2) { /* If the peer is the same as the local address, we can omit it. The result should be identical */ - g_snprintf (s_peer, sizeof (s_peer), " peer %s", nm_utils_inet4_ntop (peer_address->addr4, b2)); + nm_sprintf_buf (s_peer, " peer %s", nm_utils_inet4_ntop (peer_address->addr4, b2)); } else s_peer[0] = '\0'; @@ -1006,7 +1010,7 @@ void nmtstp_ip4_route_add (NMPlatform *platform, route.metric = metric; route.mss = mss; - g_assert_cmpint (nm_platform_ip4_route_add (platform, NMP_NLM_FLAG_REPLACE, &route), ==, NM_PLATFORM_ERROR_SUCCESS); + g_assert (NMTST_NM_ERR_SUCCESS (nm_platform_ip4_route_add (platform, NMP_NLM_FLAG_REPLACE, &route))); } void nmtstp_ip6_route_add (NMPlatform *platform, @@ -1030,7 +1034,7 @@ void nmtstp_ip6_route_add (NMPlatform *platform, route.metric = metric; route.mss = mss; - g_assert_cmpint (nm_platform_ip6_route_add (platform, NMP_NLM_FLAG_REPLACE, &route), ==, NM_PLATFORM_ERROR_SUCCESS); + g_assert (NMTST_NM_ERR_SUCCESS (nm_platform_ip6_route_add (platform, NMP_NLM_FLAG_REPLACE, &route))); } /*****************************************************************************/ @@ -1052,7 +1056,8 @@ _ip_address_del (NMPlatform *platform, if (external_command) { const char *ifname; - char b1[NM_UTILS_INET_ADDRSTRLEN], b2[NM_UTILS_INET_ADDRSTRLEN]; + char b1[NM_UTILS_INET_ADDRSTRLEN]; + char b2[NM_UTILS_INET_ADDRSTRLEN]; int success; gboolean had_address; @@ -1198,7 +1203,7 @@ nmtstp_link_veth_add (NMPlatform *platform, nmtstp_assert_wait_for_link (platform, peer, NM_LINK_TYPE_VETH, 10); } } else - success = nm_platform_link_veth_add (platform, name, peer, &pllink) == NM_PLATFORM_ERROR_SUCCESS; + success = NMTST_NM_ERR_SUCCESS (nm_platform_link_veth_add (platform, name, peer, &pllink)); g_assert (success); _assert_pllink (platform, success, pllink, name, NM_LINK_TYPE_VETH); @@ -1225,7 +1230,7 @@ nmtstp_link_dummy_add (NMPlatform *platform, if (success) pllink = nmtstp_assert_wait_for_link (platform, name, NM_LINK_TYPE_DUMMY, 100); } else - success = nm_platform_link_dummy_add (platform, name, &pllink) == NM_PLATFORM_ERROR_SUCCESS; + success = NMTST_NM_ERR_SUCCESS (nm_platform_link_dummy_add (platform, name, &pllink)); g_assert (success); _assert_pllink (platform, success, pllink, name, NM_LINK_TYPE_DUMMY); @@ -1240,7 +1245,8 @@ nmtstp_link_gre_add (NMPlatform *platform, { const NMPlatformLink *pllink = NULL; gboolean success; - char buffer[INET_ADDRSTRLEN]; + char b1[INET_ADDRSTRLEN]; + char b2[INET_ADDRSTRLEN]; NMLinkType link_type; g_assert (nm_utils_is_valid_iface_name (name, NULL)); @@ -1265,15 +1271,15 @@ nmtstp_link_gre_add (NMPlatform *platform, name, type, dev ?: "", - nm_utils_inet4_ntop (lnk->local, NULL), - nm_utils_inet4_ntop (lnk->remote, buffer), + nm_utils_inet4_ntop (lnk->local, b1), + nm_utils_inet4_ntop (lnk->remote, b2), lnk->ttl, lnk->tos, lnk->path_mtu_discovery ? "pmtudisc" : "nopmtudisc"); if (success) pllink = nmtstp_assert_wait_for_link (platform, name, link_type, 100); } else - success = nm_platform_link_gre_add (platform, name, lnk, &pllink) == NM_PLATFORM_ERROR_SUCCESS; + success = NMTST_NM_ERR_SUCCESS (nm_platform_link_gre_add (platform, name, lnk, &pllink)); _assert_pllink (platform, success, pllink, name, link_type); @@ -1288,7 +1294,8 @@ nmtstp_link_ip6tnl_add (NMPlatform *platform, { const NMPlatformLink *pllink = NULL; gboolean success; - char buffer[INET6_ADDRSTRLEN]; + char b1[NM_UTILS_INET_ADDRSTRLEN]; + char b2[NM_UTILS_INET_ADDRSTRLEN]; char encap[20]; char tclass[20]; gboolean encap_ignore; @@ -1326,8 +1333,8 @@ nmtstp_link_ip6tnl_add (NMPlatform *platform, name, mode, dev, - nm_utils_inet6_ntop (&lnk->local, NULL), - nm_utils_inet6_ntop (&lnk->remote, buffer), + nm_utils_inet6_ntop (&lnk->local, b1), + nm_utils_inet6_ntop (&lnk->remote, b2), lnk->ttl, tclass_inherit ? "inherit" : nm_sprintf_buf (tclass, "%02x", lnk->tclass), encap_ignore ? "none" : nm_sprintf_buf (encap, "%u", lnk->encap_limit), @@ -1335,7 +1342,7 @@ nmtstp_link_ip6tnl_add (NMPlatform *platform, if (success) pllink = nmtstp_assert_wait_for_link (platform, name, NM_LINK_TYPE_IP6TNL, 100); } else - success = nm_platform_link_ip6tnl_add (platform, name, lnk, &pllink) == NM_PLATFORM_ERROR_SUCCESS; + success = NMTST_NM_ERR_SUCCESS (nm_platform_link_ip6tnl_add (platform, name, lnk, &pllink)); _assert_pllink (platform, success, pllink, name, NM_LINK_TYPE_IP6TNL); @@ -1350,7 +1357,8 @@ nmtstp_link_ip6gre_add (NMPlatform *platform, { const NMPlatformLink *pllink = NULL; gboolean success; - char buffer[INET6_ADDRSTRLEN]; + char b1[NM_UTILS_INET_ADDRSTRLEN]; + char b2[NM_UTILS_INET_ADDRSTRLEN]; char tclass[20]; gboolean tclass_inherit; @@ -1373,8 +1381,8 @@ nmtstp_link_ip6gre_add (NMPlatform *platform, name, lnk->is_tap ? "ip6gretap" : "ip6gre", dev, - nm_utils_inet6_ntop (&lnk->local, NULL), - nm_utils_inet6_ntop (&lnk->remote, buffer), + nm_utils_inet6_ntop (&lnk->local, b1), + nm_utils_inet6_ntop (&lnk->remote, b2), lnk->ttl, tclass_inherit ? "inherit" : nm_sprintf_buf (tclass, "%02x", lnk->tclass), lnk->flow_label); @@ -1385,7 +1393,7 @@ nmtstp_link_ip6gre_add (NMPlatform *platform, 100); } } else - success = nm_platform_link_ip6gre_add (platform, name, lnk, &pllink) == NM_PLATFORM_ERROR_SUCCESS; + success = NMTST_NM_ERR_SUCCESS (nm_platform_link_ip6gre_add (platform, name, lnk, &pllink)); _assert_pllink (platform, success, pllink, name, lnk->is_tap ? NM_LINK_TYPE_IP6GRETAP : NM_LINK_TYPE_IP6GRE); @@ -1400,7 +1408,8 @@ nmtstp_link_ipip_add (NMPlatform *platform, { const NMPlatformLink *pllink = NULL; gboolean success; - char buffer[INET_ADDRSTRLEN]; + char b1[INET_ADDRSTRLEN]; + char b2[INET_ADDRSTRLEN]; g_assert (nm_utils_is_valid_iface_name (name, NULL)); @@ -1417,15 +1426,15 @@ nmtstp_link_ipip_add (NMPlatform *platform, success = !nmtstp_run_command ("ip tunnel add %s mode ipip %s local %s remote %s ttl %u tos %02x %s", name, dev, - nm_utils_inet4_ntop (lnk->local, NULL), - nm_utils_inet4_ntop (lnk->remote, buffer), + nm_utils_inet4_ntop (lnk->local, b1), + nm_utils_inet4_ntop (lnk->remote, b2), lnk->ttl, lnk->tos, lnk->path_mtu_discovery ? "pmtudisc" : "nopmtudisc"); if (success) pllink = nmtstp_assert_wait_for_link (platform, name, NM_LINK_TYPE_IPIP, 100); } else - success = nm_platform_link_ipip_add (platform, name, lnk, &pllink) == NM_PLATFORM_ERROR_SUCCESS; + success = NMTST_NM_ERR_SUCCESS (nm_platform_link_ipip_add (platform, name, lnk, &pllink)); _assert_pllink (platform, success, pllink, name, NM_LINK_TYPE_IPIP); @@ -1473,7 +1482,7 @@ nmtstp_link_macvlan_add (NMPlatform *platform, if (success) pllink = nmtstp_assert_wait_for_link (platform, name, link_type, 100); } else - success = nm_platform_link_macvlan_add (platform, name, parent, lnk, &pllink) == NM_PLATFORM_ERROR_SUCCESS; + success = NMTST_NM_ERR_SUCCESS (nm_platform_link_macvlan_add (platform, name, parent, lnk, &pllink)); _assert_pllink (platform, success, pllink, name, link_type); @@ -1488,7 +1497,8 @@ nmtstp_link_sit_add (NMPlatform *platform, { const NMPlatformLink *pllink = NULL; gboolean success; - char buffer[INET_ADDRSTRLEN]; + char b1[INET_ADDRSTRLEN]; + char b2[INET_ADDRSTRLEN]; g_assert (nm_utils_is_valid_iface_name (name, NULL)); @@ -1510,15 +1520,15 @@ nmtstp_link_sit_add (NMPlatform *platform, success = !nmtstp_run_command ("ip tunnel add %s mode sit%s local %s remote %s ttl %u tos %02x %s", name, dev, - nm_utils_inet4_ntop (lnk->local, NULL), - nm_utils_inet4_ntop (lnk->remote, buffer), + nm_utils_inet4_ntop (lnk->local, b1), + nm_utils_inet4_ntop (lnk->remote, b2), lnk->ttl, lnk->tos, lnk->path_mtu_discovery ? "pmtudisc" : "nopmtudisc"); if (success) pllink = nmtstp_assert_wait_for_link (platform, name, NM_LINK_TYPE_SIT, 100); } else - success = nm_platform_link_sit_add (platform, name, lnk, &pllink) == NM_PLATFORM_ERROR_SUCCESS; + success = NMTST_NM_ERR_SUCCESS (nm_platform_link_sit_add (platform, name, lnk, &pllink)); _assert_pllink (platform, success, pllink, name, NM_LINK_TYPE_SIT); @@ -1533,8 +1543,8 @@ nmtstp_link_tun_add (NMPlatform *platform, int *out_fd) { const NMPlatformLink *pllink = NULL; - NMPlatformError plerr; int err; + int r; g_assert (nm_utils_is_valid_iface_name (name, NULL)); g_assert (lnk); @@ -1579,8 +1589,8 @@ nmtstp_link_tun_add (NMPlatform *platform, g_error ("failure to add tun/tap device via ip-route"); } else { g_assert (lnk->persist || out_fd); - plerr = nm_platform_link_tun_add (platform, name, lnk, &pllink, out_fd); - g_assert_cmpint (plerr, ==, NM_PLATFORM_ERROR_SUCCESS); + r = nm_platform_link_tun_add (platform, name, lnk, &pllink, out_fd); + g_assert_cmpint (r, ==, 0); } g_assert (pllink); @@ -1596,8 +1606,8 @@ nmtstp_link_vxlan_add (NMPlatform *platform, const NMPlatformLnkVxlan *lnk) { const NMPlatformLink *pllink = NULL; - NMPlatformError plerr; int err; + int r; g_assert (nm_utils_is_valid_iface_name (name, NULL)); @@ -1607,27 +1617,32 @@ nmtstp_link_vxlan_add (NMPlatform *platform, if (external_command) { gs_free char *dev = NULL; - gs_free char *local = NULL, *remote = NULL; + char local[NM_UTILS_INET_ADDRSTRLEN]; + char group[NM_UTILS_INET_ADDRSTRLEN]; if (lnk->parent_ifindex) dev = g_strdup_printf ("dev %s", nm_platform_link_get_name (platform, lnk->parent_ifindex)); if (lnk->local) - local = g_strdup_printf ("%s", nm_utils_inet4_ntop (lnk->local, NULL)); + nm_utils_inet4_ntop (lnk->local, local); else if (memcmp (&lnk->local6, &in6addr_any, sizeof (in6addr_any))) - local = g_strdup_printf ("%s", nm_utils_inet6_ntop (&lnk->local6, NULL)); + nm_utils_inet6_ntop (&lnk->local6, local); + else + local[0] = '\0'; if (lnk->group) - remote = g_strdup_printf ("%s", nm_utils_inet4_ntop (lnk->group, NULL)); + nm_utils_inet4_ntop (lnk->group, group); else if (memcmp (&lnk->group6, &in6addr_any, sizeof (in6addr_any))) - remote = g_strdup_printf ("%s", nm_utils_inet6_ntop (&lnk->group6, NULL)); + nm_utils_inet6_ntop (&lnk->group6, group); + else + group[0] = '\0'; err = nmtstp_run_command ("ip link add %s type vxlan id %u %s local %s group %s ttl %u tos %02x dstport %u srcport %u %u ageing %u", name, lnk->id, dev ?: "", local, - remote, + group, lnk->ttl, lnk->tos, lnk->dst_port, @@ -1641,8 +1656,8 @@ nmtstp_link_vxlan_add (NMPlatform *platform, _LOGI ("Adding vxlan device via iproute2 failed. Assume iproute2 is not up to the task."); } if (!pllink) { - plerr = nm_platform_link_vxlan_add (platform, name, lnk, &pllink); - g_assert_cmpint (plerr, ==, NM_PLATFORM_ERROR_SUCCESS); + r = nm_platform_link_vxlan_add (platform, name, lnk, &pllink); + g_assert (NMTST_NM_ERR_SUCCESS (r)); g_assert (pllink); } @@ -1702,10 +1717,11 @@ nmtstp_link_get (NMPlatform *platform, /*****************************************************************************/ void -nmtstp_link_del (NMPlatform *platform, - gboolean external_command, - int ifindex, - const char *name) +nmtstp_link_delete (NMPlatform *platform, + gboolean external_command, + int ifindex, + const char *name, + gboolean require_exist) { gint64 end_time; const NMPlatformLink *pllink; @@ -1718,7 +1734,10 @@ nmtstp_link_del (NMPlatform *platform, pllink = nmtstp_link_get (platform, ifindex, name); - g_assert (pllink); + if (!pllink) { + g_assert (!require_exist); + return; + } name = name_copy = g_strdup (pllink->name); ifindex = pllink->ifindex; @@ -2072,7 +2091,7 @@ main (int argc, char **argv) result = g_test_run (); - nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); + nmtstp_link_delete (NM_PLATFORM_GET, -1, -1, DEVICE_NAME, FALSE); g_object_unref (NM_PLATFORM_GET); return result; diff --git a/src/platform/tests/test-common.h b/src/platform/tests/test-common.h index 1baadfa1..048fd9dd 100644 --- a/src/platform/tests/test-common.h +++ b/src/platform/tests/test-common.h @@ -21,6 +21,9 @@ #include <syslog.h> #include <string.h> #include <arpa/inet.h> +#include <linux/if.h> +#include <linux/if_link.h> +#include <linux/ip6_tunnel.h> #include "platform/nm-platform.h" #include "platform/nmp-object.h" @@ -324,10 +327,11 @@ const NMPlatformLink *nmtstp_link_vxlan_add (NMPlatform *platform, const char *name, const NMPlatformLnkVxlan *lnk); -void nmtstp_link_del (NMPlatform *platform, - gboolean external_command, - int ifindex, - const char *name); +void nmtstp_link_delete (NMPlatform *platform, + gboolean external_command, + int ifindex, + const char *name, + gboolean require_exist); /*****************************************************************************/ @@ -346,9 +350,9 @@ _nmtstp_env1_wrapper_setup (const NmtstTestData *test_data) _LOGT ("TEST[%s]: setup", test_data->testpath); - nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); - g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME)); - g_assert_cmpint (nm_platform_link_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL), ==, NM_PLATFORM_ERROR_SUCCESS); + nmtstp_link_delete (NM_PLATFORM_GET, -1, -1, DEVICE_NAME, FALSE); + + g_assert (NMTST_NM_ERR_SUCCESS (nm_platform_link_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL))); *p_ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); g_assert_cmpint (*p_ifindex, >, 0); diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c index 057490c4..bfd33058 100644 --- a/src/platform/tests/test-link.c +++ b/src/platform/tests/test-link.c @@ -47,7 +47,7 @@ #define MTU 1357 #define _ADD_DUMMY(platform, name) \ - g_assert_cmpint (nm_platform_link_dummy_add ((platform), (name), NULL), ==, NM_PLATFORM_ERROR_SUCCESS) + g_assert (NMTST_NM_ERR_SUCCESS (nm_platform_link_dummy_add ((platform), (name), NULL))) static void test_bogus(void) @@ -77,7 +77,7 @@ test_bogus(void) g_assert (!addrlen); g_assert (!nm_platform_link_get_address (NM_PLATFORM_GET, BOGUS_IFINDEX, NULL)); - g_assert (nm_platform_link_set_mtu (NM_PLATFORM_GET, BOGUS_IFINDEX, MTU) != NM_PLATFORM_ERROR_SUCCESS); + g_assert (!NMTST_NM_ERR_SUCCESS (nm_platform_link_set_mtu (NM_PLATFORM_GET, BOGUS_IFINDEX, MTU))); g_assert (!nm_platform_link_get_mtu (NM_PLATFORM_GET, BOGUS_IFINDEX)); @@ -107,30 +107,30 @@ software_add (NMLinkType link_type, const char *name) { switch (link_type) { case NM_LINK_TYPE_DUMMY: - return nm_platform_link_dummy_add (NM_PLATFORM_GET, name, NULL) == NM_PLATFORM_ERROR_SUCCESS; + return NMTST_NM_ERR_SUCCESS (nm_platform_link_dummy_add (NM_PLATFORM_GET, name, NULL)); case NM_LINK_TYPE_BRIDGE: - return nm_platform_link_bridge_add (NM_PLATFORM_GET, name, NULL, 0, NULL) == NM_PLATFORM_ERROR_SUCCESS; + return NMTST_NM_ERR_SUCCESS (nm_platform_link_bridge_add (NM_PLATFORM_GET, name, NULL, 0, NULL)); case NM_LINK_TYPE_BOND: { gboolean bond0_exists = !!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "bond0"); - NMPlatformError plerr; + int r; - plerr = nm_platform_link_bond_add (NM_PLATFORM_GET, name, NULL); + r = nm_platform_link_bond_add (NM_PLATFORM_GET, name, NULL); /* Check that bond0 is *not* automatically created. */ if (!bond0_exists) g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "bond0")); - return plerr == NM_PLATFORM_ERROR_SUCCESS; + return r >= 0; } case NM_LINK_TYPE_TEAM: - return nm_platform_link_team_add (NM_PLATFORM_GET, name, NULL) == NM_PLATFORM_ERROR_SUCCESS; + return NMTST_NM_ERR_SUCCESS (nm_platform_link_team_add (NM_PLATFORM_GET, name, NULL)); case NM_LINK_TYPE_VLAN: { SignalData *parent_added; SignalData *parent_changed; /* Don't call link_callback for the bridge interface */ parent_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, PARENT_NAME); - if (nm_platform_link_bridge_add (NM_PLATFORM_GET, PARENT_NAME, NULL, 0, NULL) == NM_PLATFORM_ERROR_SUCCESS) + if (NMTST_NM_ERR_SUCCESS (nm_platform_link_bridge_add (NM_PLATFORM_GET, PARENT_NAME, NULL, 0, NULL))) accept_signal (parent_added); free_signal (parent_added); @@ -147,7 +147,7 @@ software_add (NMLinkType link_type, const char *name) accept_signals (parent_changed, 1, 2); free_signal (parent_changed); - return nm_platform_link_vlan_add (NM_PLATFORM_GET, name, parent_ifindex, VLAN_ID, 0, NULL) == NM_PLATFORM_ERROR_SUCCESS; + return NMTST_NM_ERR_SUCCESS (nm_platform_link_vlan_add (NM_PLATFORM_GET, name, parent_ifindex, VLAN_ID, 0, NULL)); } } default: @@ -343,7 +343,7 @@ test_slave (int master, int type, SignalData *master_changed) ensure_no_signal (link_added); ensure_no_signal (link_changed); ensure_no_signal (link_removed); - nmtstp_link_del (NULL, -1, ifindex, NULL); + nmtstp_link_delete (NULL, -1, ifindex, NULL, TRUE); accept_signals (master_changed, 0, 1); accept_signals (link_changed, 0, 1); accept_signal (link_removed); @@ -439,18 +439,18 @@ test_software (NMLinkType link_type, const char *link_typename) free_signal (link_changed); /* Delete */ - nmtstp_link_del (NULL, -1, ifindex, DEVICE_NAME); + nmtstp_link_delete (NULL, -1, ifindex, DEVICE_NAME, TRUE); accept_signal (link_removed); /* Delete again */ - g_assert (!nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME))); + g_assert (nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME) <= 0); g_assert (!nm_platform_link_delete (NM_PLATFORM_GET, ifindex)); /* VLAN: Delete parent */ if (link_type == NM_LINK_TYPE_VLAN) { SignalData *link_removed_parent = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, link_callback, vlan_parent); - nmtstp_link_del (NULL, -1, vlan_parent, NULL); + nmtstp_link_delete (NULL, -1, vlan_parent, NULL, TRUE); accept_signal (link_removed_parent); free_signal (link_removed_parent); } @@ -466,12 +466,20 @@ test_bridge (void) test_software (NM_LINK_TYPE_BRIDGE, "bridge"); } +static int +_system (const char *cmd) +{ + /* some gcc version really want to warn on -Werror=unused-result. Add a bogus wrapper + * function. */ + return system (cmd); +} + static void test_bond (void) { if (nmtstp_is_root_test () && !g_file_test ("/proc/1/net/bonding", G_FILE_TEST_IS_DIR) && - system("modprobe --show bonding") != 0) { + _system("modprobe --show bonding") != 0) { g_test_skip ("Skipping test for bonding: bonding module not available"); return; } @@ -502,7 +510,7 @@ test_bridge_addr (void) nm_utils_hwaddr_aton ("de:ad:be:ef:00:11", addr, sizeof (addr)); - g_assert_cmpint (nm_platform_link_bridge_add (NM_PLATFORM_GET, DEVICE_NAME, addr, sizeof (addr), &plink), ==, NM_PLATFORM_ERROR_SUCCESS); + g_assert (NMTST_NM_ERR_SUCCESS (nm_platform_link_bridge_add (NM_PLATFORM_GET, DEVICE_NAME, addr, sizeof (addr), &plink))); g_assert (plink); link = *plink; g_assert_cmpstr (link.name, ==, DEVICE_NAME); @@ -518,13 +526,13 @@ test_bridge_addr (void) g_assert (!nm_platform_link_get_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex)); g_assert_cmpint (_nm_platform_uint8_inv (plink->inet6_addr_gen_mode_inv), ==, NM_IN6_ADDR_GEN_MODE_EUI64); - g_assert (nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex, TRUE) == NM_PLATFORM_ERROR_SUCCESS); + g_assert (NMTST_NM_ERR_SUCCESS (nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex, TRUE))); g_assert (nm_platform_link_get_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex)); plink = nm_platform_link_get (NM_PLATFORM_GET, link.ifindex); g_assert (plink); g_assert_cmpint (_nm_platform_uint8_inv (plink->inet6_addr_gen_mode_inv), ==, NM_IN6_ADDR_GEN_MODE_NONE); - g_assert (nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex, FALSE) == NM_PLATFORM_ERROR_SUCCESS); + g_assert (NMTST_NM_ERR_SUCCESS (nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex, FALSE))); g_assert (!nm_platform_link_get_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex)); plink = nm_platform_link_get (NM_PLATFORM_GET, link.ifindex); g_assert (plink); @@ -534,7 +542,7 @@ test_bridge_addr (void) g_assert_cmpint (plink->addr.len, ==, sizeof (addr)); g_assert (!memcmp (plink->addr.data, addr, sizeof (addr))); - nmtstp_link_del (NULL, -1, link.ifindex, link.name); + nmtstp_link_delete (NULL, -1, link.ifindex, link.name, TRUE); } /*****************************************************************************/ @@ -554,11 +562,11 @@ test_internal (void) g_assert (!nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); /* Add device */ - g_assert (nm_platform_link_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_SUCCESS); + g_assert (NMTST_NM_ERR_SUCCESS (nm_platform_link_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL))); accept_signal (link_added); /* Try to add again */ - g_assert (nm_platform_link_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_EXISTS); + g_assert (nm_platform_link_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == -NME_PL_EXISTS); /* Check device index, name and type */ ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); @@ -595,7 +603,7 @@ test_internal (void) g_assert (nm_platform_link_supports_vlans (NM_PLATFORM_GET, ifindex)); /* Set MAC address */ - g_assert (nm_platform_link_set_address (NM_PLATFORM_GET, ifindex, mac, sizeof (mac)) == NM_PLATFORM_ERROR_SUCCESS); + g_assert (NMTST_NM_ERR_SUCCESS (nm_platform_link_set_address (NM_PLATFORM_GET, ifindex, mac, sizeof (mac)))); address = nm_platform_link_get_address (NM_PLATFORM_GET, ifindex, &addrlen); g_assert (addrlen == sizeof(mac)); g_assert (!memcmp (address, mac, addrlen)); @@ -604,12 +612,12 @@ test_internal (void) accept_signal (link_changed); /* Set MTU */ - g_assert (nm_platform_link_set_mtu (NM_PLATFORM_GET, ifindex, MTU) == NM_PLATFORM_ERROR_SUCCESS); + g_assert (NMTST_NM_ERR_SUCCESS (nm_platform_link_set_mtu (NM_PLATFORM_GET, ifindex, MTU))); g_assert_cmpint (nm_platform_link_get_mtu (NM_PLATFORM_GET, ifindex), ==, MTU); accept_signal (link_changed); /* Delete device */ - nmtstp_link_del (NULL, -1, ifindex, DEVICE_NAME); + nmtstp_link_delete (NULL, -1, ifindex, DEVICE_NAME, TRUE); accept_signal (link_removed); /* Try to delete again */ @@ -684,6 +692,233 @@ test_external (void) /*****************************************************************************/ +static guint8 * +_copy_base64 (guint8 *dst, gsize dst_len, const char *base64_src) +{ + g_assert (dst); + g_assert (dst_len > 0); + + if (!base64_src) + memset (dst, 0, dst_len); + else { + gs_free guint8 *b = NULL; + gsize l; + + b = g_base64_decode (base64_src, &l); + g_assert (b); + g_assert (l == dst_len); + + memcpy (dst, b, dst_len); + } + return dst; +} + +typedef struct { + const char *pri; + const char *pub; + const char *pre; +} KeyPair; + +static void +_test_wireguard_change (NMPlatform *platform, + int ifindex, + int test_mode) +{ + const KeyPair self_key = + { "yOWEsaXFxX9/DOkQPzqB9RufZOpfSP4LZZCErP0N0Xo=", "s6pVT2xPwktor9O5bVOSzcPqBu9uzQOUzPQHXLU2jmk=" }; + const KeyPair keys[100] = { + { "+BDHMh11bkheGfvlQpqt8P/H7N1sPXtVi05XraZS0E8=", "QItu7PJadBVXFXGv55CMtVnbRHdrI6E2CGlu2N5oGx4=", "2IvZnKTzbF1UlChznWSsEYtGbPjhYSTT41GXO6zLxvk=" }, + { "qGZyV2BO1nyY/FGYd6elBPirwJC9QyZwqbm2OJAgLkY=", "v8L1FEitO0xo+wW/CVVUnALlw0zGveApSFdlITi/5lI=", "/R2c0JmBNGJzT594NQ0mBJ2XJjxt2QUSo+ZiqeY0EQA=" }, + { "YDgsIb0oe+9NcxIx2r0HEEPQpRMxmRN0ALoLm9Sh40Q=", "nFPs1HaU7uFBvE9xZCMF8oOAjzLpZ49AzDHOluY1O2E=", "zsYED2Ef7zIHJoRBPcen+w4ktrRsLPEfYwZhWIuXfds=" }, + { "kHkosM503LWu43tdYXbNwVOpRrtPgd9XFqcN7k4t6G4=", "b8e092WT+eNmnxCr5WE2QC/MXAjDagfG1g03cs2mBC0=", "VXz0ShGWT7H0CBCg2awfatmOJF15ZtaSMPhMsp+hc3A=" }, + { "4C2w5CEnxH59Y2aa6CJXgLdDtWoNMS2UJounRCM1Jkk=", "gC/R9umlnEQL+Qsz/Y64AlsdMge4ECe5/u/JHZCMWSs=", "2bmL5ISr+V5a7l7xFJ695BLIJyBgx8xnxzybkxiRHOg=" }, + { "KHJSzFGkXcZf/wbH2rr99SYtGKWbL680wA2AcDE94lo=", "BsN23h4aOi458Q3EgMQsodsWQxd9h/RxqskAUpsXfgg=", "nK4Zv34YKEhjuexhq1SgK4oTd4MZJT5gcpYvEuPjc7Q=" }, + { "QGMulXJ9e3AVxtpi8+UUVqPOr/YBWvCNFVsWS9IUnUA=", "kjVclP5Ifi6og2BBCEHKS/aa/WktArB4+ig06lYaVlg=", "0+mmceDPcSRK3vFnYqHd9iAfY+Nyjzf/1KgDeYGlRkQ=" }, + { "AOJiDD4y6GA7P7gugjjQG9Cctvc5Y27fajHz6aU3gU4=", "gEnHn6euHtcMEwZBlX6HANPeN9Of+voBDtltS38xDUw=", "wIH1OxgX6GLxx/bnR+t3fbmjGZDTU3WMxp7t1XGezqM=" }, + { "COsls2BlCltaIrrq1+FU51cWddlmoPPppSeIDunOxGA=", "+n6WuV8Tb1/iZArTrHsyNqkRHABbavBQt9Me72K2KEc=", "t4baiprSO9ZbKD2/RutOY9cr+yCajQWZGCTnQdrFQj0=" }, + { "uHawQq2BRyJlsTPoCa+MfBVnv4MwtRoS+S9FEpFOEVg=", "8lcmr27afeb6iI3BQCaDtvalF2Cl7gxRZgs+nyJ/fEg=", "Eh9o/6W60iujBLIHuRfNrPhOWAn7PambT2JRln9iwA0=" }, + { "yL7hmoE/JfRGAotRzx9xOpjfrDA3BFlPEemFiQw40Wk=", "BHK0PHi5kp7rOfQ46oc9xlVpB+pZeZYXTtH7EXr5TwU=", "BS2h2ZZyW0BlYMmLR29xyHEcZ4jtO7rgj1jkz/EEaxU=" }, + { "ON8YrTHQgoC5e0mAR9FakZ8hZ/9I7ysuE21sG546J1Y=", "Bm3l5I6iH1tDrv6EgdZU9PzHqp0H26Z6ggmmIypwxy8=", "qKVfbCnK1EI3eC28SsL+jyBnEnIF/nRJQJzDevBFdNQ=" }, + { "KGLO0RI0VlQFCG+ETUqk25HdaKUnUPXKOKkTh/w8BXU=", "sBDBwQFC7k8UMdMzuavKBkBjYYKigzvYmGF5rm6muQc=", "BNfZF9d7540pUGGOQCh3iDxAZcPBqX4qqM00GSLPBek=" }, + { "KGdWyEodIn7KKI2e4EFK9tT6Bt3bNMFBRFVTKjjJm2E=", "lrbjU/Xn9wDCZQiU8E5fY6igTSozghvo47QeVIIWaUk=", "LczqW48MW8qDQIZYRELsz/VCzVDc5niROvk7OqrTxR0=" }, + { "wO3xinUGABgEmX+RJZbBbOtAmBuPPFG6oVdimvoo90w=", "dCIvTzR6EerOOsRKnWly1a9WGzbJ4qc+6t3SSzLgWFk=", "wFj0zpr5PadBoBy0couLuZ1qudZbXLbV/j3UT+AyKeo=" }, + { "+JNOBlO4tp9vvQk6UO4r3sILyEgjl+BBoWketZufyn0=", "Q6LSv9y7YQkJEzQ/1mpjJEgOrO8GYPUTgcizjh7Cm34=", "kg7AG9MuN04xPJ5Z0IcNZ4a8d1b/n4GsGIeyA4FaaSE=" }, + { "+EJcThLRwjZ+h1CNNFu15HWzznf4u/lPVw8hifTm2Ec=", "Kkn2jFqwBzyIFQfD0OpmePFSmBYmhKagv4pGgkqsWgE=", "jYpxojj8WKYe/XIXMP+uv1Fv0+TKrs83tqfzP0AGdcI=" }, + { "+DKFqSMNFmxriEFj3qatuzYeTJ9+xWYspZ4ydL3eC0Q=", "3o37bsg6HhRg/M9+fTlLcFYc2w/Bz9rLQySvvYCKbRE=", "Jb9qoDIBat1EexlgfpRbXa7OflptME8/zt93bldkiVE=" }, + { "EH3MjFOMqRoDFQz+hSlJpntWBeH3lTk6WPjTIQjr42o=", "PbPewED/nxSBLdM7AXMj7uS3bCgAAg8M6F4iPLd0b1U=", "pj4+UgOGkpJwlRvX5BRXZzmzAUnDtUtJsS7LzbcJWzw=" }, + { "kL6M2KvO+vPBLEc/a0DpEHTibQ1bwaMRT9b9SkzeP0Y=", "pS3G2bHHlkOE6UHP0qVitDuxXgjEaZTviTjNc55RbVs=", "ZVZpWOtYqhX3CpF1kATg/38J6pvUJo8AS1sVYjs3rUE=" }, + { "sFlcRLDn36fnew2Ld92IHJwnKifdS3aF1MWRPs6K3Wg=", "OpjUOTiWExaDYULTINB4yQFqc3mnU3RjQzGRV+KtdFY=", "of0V/uoFRNljv/XTt/tXgoquLRH93Ty0KNiaPUpEi2w=" }, + { "UJ8hjDsg3jfsnnfPH8Gw9FnCb6taTuviurAfZu+kEFg=", "3byjHksUOv8CNjGGKvHvvrJDURBhCIL5UtfZbgyVWCE=", "9f7dbWif51gGrE7R9LeuewQSrvGFGTOB3ceJC67jSkI=" }, + { "iFFPKGIfqeUKY/w72KAZSjd/PGTqCakHYBV10xMDfnI=", "ehneHATNSXtsJTOiPjVSc0QARkihgcfcvoXKFWKfQnI=", "yKdqDBcRwA7RCg4GiY/b5IsWcExPleOBde/hjxc36a4=" }, + { "GDGocdPJTPUAllxQo7SpXZKqMPn7lpxQELQUX9ETHmE=", "n0ScNEou4ekfrXRRXvcADLu2Afj8g5D3TuDP/I4KrnY=", "8QhswqAhi/ehhcmCwQF5aSh80TvIGC/gRL5jBn5wOH8=" }, + { "UCcrlN8fX2ZdNdhaEBNwktwL+H0ZO7fhaj7rgdUutmw=", "LF8J728ilXs4TphnrgR6r0p7W3912DYnsXkGMEPQnRE=", "cYfMjxl2REYir7frGeB0u+NHAaFYF02ysgpBhOL5ygc=" }, + { "SH0657XuIiHidVmViXZF30RUWtkuWXcWWHmKZHTiOG8=", "7k6j49W1u5qgLE5MQUc1osPVW1oPPhzjrGvJ7o9YamY=", "dV2+7rNk/3LR2IcwYg/c+Wvzep1yjY7/u1I+nnlTQ00=" }, + { "qFQWs6jzrscV42pGISQyA7JDvFFAvEQCWJi584VHD2g=", "AT63nHKLC17yUvkR4lOVPxCr4DD3QhXmcmecOTn+Amc=", "2Qe2fwJbFcu1CmKpktElOFkSQMqlyvlV3ZUIAd/Dcts=" }, + { "6J+yLxgPwWtUbk9I3zbeD8RuK6XkQjJ0wTJ1zSVhflQ=", "NJzMBYyPZjk3eLmgdKaOeWyNER5YZF1mR8Umeiu9f28=", "pp+4+XHw5ZmHGJ7WbZ1xLYRsnTI17QIbb0bzHzYZrBs=" }, + { "KJGoWYNVDUWcEMg+E4tljv1LiWAbdRw2QVapYqdFa1Y=", "M2SGk9WVnzYNGnT777G/JE8uUsY2f7mszTwlue73UDE=", "Jg8N7GbhbYB400foFP+OH0v+hCaL1jW61bajSA6EZqc=" }, + { "uAgrgppPyIvk8S1CHUmaCaORsgFFfBreB8pxXmbSdXw=", "dJ22bER4fD3qF2/yIGWQ7SgmZ990sy/SbANjmUMkzws=", "mKkd0OoAR9sClmD+k3fL9weBsoCy5GQGz9BP0kAQIjc=" }, + { "EI3Q8gePNPrtFyoMcv7hOihQgroF/dfjzPn8yvpGPWQ=", "Y9QhJFeiyuuIZNPU56B6U//ZK+XTTe4EP7h/p3Q+dE8=", "qBVUYw9rTWaxn55nUd55NpCWtxOUSWLt4WJGusiDS8Q=" }, + { "oEQvdRm/yHkx+JvlhHGT5RUFrFEUleKb9DCT55EqZ30=", "8hsh/UHwWADTHntOJq4dy0o7ahcNAAlo2rDpjzzrVXk=", "/GF2inW2mPtA26IgFdgOEBbBEerT740wWuP/8NyANdc=" }, + { "6OWrGKZKNsgfRezSUw29EnHymcgKyEKvX5/pZQlLmWs=", "oaJeO6YSS2dodNEf97DvgWrYnFFelG5daEdN84jVVGw=", "T5wvTdyVxK0LY96kouLjs06oUfhGChfty8OUL1Mddro=" }, + { "mPmzQbh2R+r1DC5hSquzKM1SDrxUdfnBPRPJrpqrgkE=", "BtUHAnYWjDjBI42qBf9dJqezTUikYsF96o6PKEWPrVo=", "MxU8EMmq+vVHpuK/AkFZrZDF2b+VbSqukZLPbNsCcgo=" }, + { "8GVxuoo1Veyr+nqxr5Q4vmsMf5qfiXwSlQ4q3+BU60g=", "uSOLe/E9/OjIgUOk0NMBHB45E0+q4Rd+IUO2UOxmKlM=", "+30F+56OE0Sr3wY2clKw4kgE+2XiucMg7xjK6EemXuk=" }, + { "qORPKb+qFuU/9TpFbRUupHsqm9iyk9pa6cpik+EVDkg=", "bMZxxd+Z9I0XA1h9U8JEY+/mRxWGnvbXDZ5Dxz7YzS0=", "SmkkqOz4OhHuSL6cxuRm9+Mlt50Sfd0sMDFTC78gqOE=" }, + { "2Ko3IYhXKcdOMIJGNpASk9saNSZsI64lyJPOoxpQ2kI=", "xVOc9PxY1VFaZfemmKi+Ei2liHhmeTu+JMa+rS00gnA=", "+398DlW8kWeI2aRaC4QfcrEjwqPKCohyDeWdaI1wvv0=" }, + { "CPioGCVpxnym62nH/QoCt1RiiaaxUcuFjvh5kRhjqHA=", "W0XxlBLrZgFKhggMvvv6oFf/RJbfs92qv8JK9e+5i2o=", "bsK2U6CRAUv1uVgYQ7NpqjWWswFIDiDPDEtU1XQygSA=" }, + { "AF17siKaeiO85hikYN0IWCMGWqPm1UOoCkXMltJGUVk=", "B+PFos9aN2S5bLxzGZHljRZj41j3rIx8RWu0vDUzq1w=", "Qb8d6iDYv3m1h7PE8j0Exl2cSwpHkim/fJ1S4P7MYvY=" }, + { "wMFDBTJzx5tDCBhMkrptYJ8w9EeURjc4xeDQpevxAWo=", "4ec439EXE5WQzvtV9reSX+aMmdq5k7o9Ayt8oQp+RhQ=", "jwQlvdNH5WtSSU10H+fh/JisOlaBaohDPEp/BYnTt1Q=" }, + { "4GaJpIFigNDwd31O84pLIMM/o2qhp0ydlI/ydD/2a2Q=", "r/LdkCoK5/BPGdq2+XJO8sCRhI+8ULFmg0887V43PAI=", "Da+3ZZvEJdx4TYFMIDUlbkmytILnSTNxTKX+sQdjMd4=" }, + { "gMnojGqCLmMfGp2m31xlKZ/rIV2b8ockw9DPahRyu0c=", "H6tKCTosnM4BXKqflXrkTdJyNlCIZhQ3ZRxfrvSdrDk=", "4Z6K3LKIMV89plcjMb9CzSzJl03SWRe/++geBMZcOtY=" }, + { "wKCg22aNNoHnDJ0oAKE46FcSSsREW4AaGn5WxCSeXUs=", "9NDTFC0iPt4HbbWepLHhN5poNTN2fdxJKNadsNT7qzY=", "GSVTOCnfLpJ1VCOLHaKSjCMv7/OlcnQiP5+5woqkud8=" }, + { "oCoykq7pcJcg2X2V5TBRzGwn8hzzHC05WUreuotdznY=", "DxfwnbMqr5Wn5SAyFolfEmNQT6l84Oq69ngpg6H6Iio=", "p1RHBuqhuDa1MAQ2lbqmUQFu0CTwYlf73fWZSj9tQhA=" }, + { "UO7YVyRUVkcKr7c63VWWV2zj36XD3HyDfLZCqvrZmFc=", "360lzYtIyHq5lv/QXSCe4bL4G2J1jBXFJ8yS+Ycr7Bc=", "RRPQ1XWF1HN8Us4dtfn2eemdjgtWm7U8r7mM3y00NOk=" }, + { "eCGFYV/NuGP4H552E8Of1xU+3IvZxGyX+p5UFGW8iHI=", "LqhZ4AS9dQ/MhsQnE5Oy7Q8INXY+P+mrfGY5dtg9SlE=", "SICfqs8T5wP6IzATDCT4ovamBKPdkZ7JP4Cfsb3izec=" }, + { "oI3HBZknoIMMZw1BuYMkTBylt25reX7AbCqtWQv8cno=", "B7dUgLgvQhi0RGmvaMrmf26WdjEVrhaiBclVkCKd4AA=", "5O9K9pLXwxFAt5lfMWh4qGbwX1BM7sz0QGYxAnR77dk=" }, + { "sBfYn14EFVIS2M/E1aahP7mOmRNbNtyDChDMS1s6aGs=", "FLYv0ZvzxMkc9A7OzhC4P1ZRu1aKIQd7u6gqfdekC3c=", "kaYLcNCXnCLgiB8fleMQuboUJsj5u3YAmXL9x3ywV0M=" }, + { "qFwZESU/XYZXUtxwrGsFU9qPAFTzjm7EhTS1Q6ajGlM=", "hraQQaqJCkS2yQXv+ccMOVh9V9a/qgSZJgdMAhrt8ms=", "72oDfnWOn39gdk/ncw8Lv267I0I+m73SwxrpYojpWYk=" }, + { "YHhp6Zf/miuc2QXeI2lTezy0lL0pTv2b+nWNkmjYQWI=", "UhNO5arLzF0WlZSgNOx7+IjWN+GSxDdQxZRp8uIwsyI=", "1Q39Nzv2NGI9zWKWMpYLURAMZUg+FP+OboHHzFU8Anc=" }, + { "mLBBXKaCJ+7qeBZpS3wxGi/SQ4kLzun+K+QwwdwfJVQ=", "gIq/nh7NwCJ36MvRnyrWHaRWu8lTmwfN2NvsjVl6SXQ=", "AahcNR91GDyJBIP+vC8ZuIV8ukqjSGtd8s+cmjVC2Ao=" }, + { "OJG4LZlNNngFtAEQdbVVWVm6QAjOOauGcMZGbQrb40M=", "pFHAC6HaWAOtvTRRVfSHvzG05mp4SJZXKsN/tkSF0kM=", "IoXT3wIqWNxQhYuHWl12ODq/P7RM9LwaqglhmjKg+0g=" }, + { "OOwBFOQNhepiqDf04DehQLh1gpBNOluDF1ia752Yfng=", "u715uJ/XhdjXjThCTJ9w6zzXnIhp3VCxhtso1wk+oBQ=", "5x1Ip3Ym0KzDjGhiYjmpeWWr+dgrZlYwfr02GngPOTM=" }, + { "eNPFnwkQy1qw+IjFAlrDA6+sIsxbWDlzbNSsBW8R1UA=", "OaOXaAfPb1MRpWadawFje8YZ0oxJgdCIDIP8c5X+r1U=", "NtfaRRD0GqujnaQQoNoBbtovgO4dfVwEmEQx/YgnDpw=" }, + { "OLdaZItbtxH3mGqItkibIJp7KV27FrQavjhd5zq6s3Q=", "SLSmAYxkMCGj0DO35cMLkC3NVAqK2VmVFndbOZEdA24=", "SnBO68XQTDjxYbmYaAeEHgLwD2u4D+BPT86raRuUQZM=" }, + { "UBQOEz08izwr4eEK/SnQUpkt+TxCjo6Sya/XOGMLOE0=", "wQwrwezI9LzKevGsJJCBHDG8noR0yIEtOK5Rig97SSo=", "DpyS+0d7lrlFWkztsniG2v/j44vcuvWz3sPeghRyb5A=" }, + { "mN98iuqUKh67ggUdq9ZIQNZCZM90fgycTVqYKEo+DkY=", "GYdXVW1jpS0dN1q9zMehubP7LfYqs34kszN0bXQqxxA=", "AJPIHffB4uvvJJki4xCG0VORVBbF6bc2mZQqUx+idPc=" }, + { "OEd/1it8C3o+NOWxDI3DfLMXVBHJQg15N3E8F8d99l8=", "QL1NcuUkoXxDy7M9VjGslCejcUlnUDHRghFVnr+8fmA=", "nven9Dicl8U6QXuDO8rRNtjd4NYaa90SU+Gmv435XKY=" }, + { "AFMCGDu2oAP68miucsi4fmYX2KeRZnsEGv8tQm8JEng=", "1sNxvk8uZhFsBUgxOXmuCMjDAgBbjVeWe9oaFk5Osy0=", "t5iI5XXd56S5q0Y9HC91gzgF9uGjL9FIy6NUaKqkydo=" }, + { "CIAwfJghQHHr4YlztN9at6/iWkrEVCGFAxNVuQCuT3I=", "zpUOF1h17g7RpBzrVlN7oTRz6e+dxcDL8OsAtHwgLC8=", "kOSwC1p6Uoti9E9Eg7ViPZwCytuvp5Fr5Buw677aogU=" }, + { "sC8vrAVBU0zvhWDRfzfySjvopXm2/cTMkTLmioyO3Es=", "p6H7GWm8NfgyO5OCX/COjvVT4MAnTs9ZUj4uZMK8XHA=", "9Tzqo57V/h7+6nSNAHSBKdmU7ultlvZbAnNKSRlrLi4=" }, + { "eI63gjxCZGnzqZxPEi/ifYphXhxIRI2ZxK8jzqo3mmU=", "XyNzEuU9x37fxFCnrZH89Krs5/UqGVx5wNkGfQCAYz0=", "vZ2fTlRPnJQ+q33YdS5p1aweqPGj/kTMc4Uq80FtFjI=" }, + { "aNJlGtm79/RS4SQ/PC4YM6LFo9zAqDr2/RjLqk/z/1A=", "lgZ9akPrABmfHQMlfNFnnpAJzGtcsaU9mUjEYKfzZHc=", "d0Xt1Bcgphd1HMI0RneA4VdBbMZL1qNGJAvFhb080eA=" }, + { "oONSnHirNh3cuH93Ty0C9AXKebGY+cdF3R0DtPzIQlo=", "TuREKfA8EVQiYWsPx8veUzjN2cz/b72limSLWlrCWxw=", "vEqqKbpZf0EM6EApMUaUH65r3Zr81Y/DSODhE4H7U3Y=" }, + { "+D6RyLEaHJ9YF9WDyOlwh87KaNJcc6lqX8Arp6yqHF0=", "EpecjfIo1/EEbmsgUtzEDqLu2ut+SMmzqaBL9Z/MlCA=", "oYfO6/7XQgEYT9zmr4sqFrk0muK/fEv3FfD8MzZzjkE=" }, + { "OCmW5KQql2PRMJnsMYQjXlr6TSYUbxJBknqZtXJPSHM=", "ZZR2ghHlCwAJu/XlsEZuNS6XiGPwuXzyMPPywYFapVM=", "fqSCXq+pKJJ6yNvlOi+tyQ9E4Y6kc4kblGrVqN2WuXA=" }, + { "APWXDAe8d2ia1CUbf/IzSPXOUjR8TVuJgmISiWw0/EY=", "jrT5P5YCkG+U7cfNTvCKy0GSEgsjwmJtHg+8HBP6ZCA=", "t75aUjZXMPir8Ao0yhVClh9/BdWxSL+11CjK3iELNWk=" }, + { "8Nw4sRis7M/6Om+3w5YHXthyMzLGuP48teqdzbHNPlA=", "lj3q3ZYij3ZJ/QunK8n9I00cv/Z+O1TU1kFFl8x3DTE=", "adqB79P7gbXEYYnSd1/UPCwFffTAPXa9kHWynRBYcGo=" }, + { "yOupps5XbjV0fIZKnGhrpcxB7yDQzbBILC0UMJyVS1c=", "+MDV/t9UCIdgm3IkH3BZlxaRPJ3lejRmrm4UPApq4mk=", "AOJPmQxsU6hjOd+9mHnF0VL7Afih3P1Fr625xFT4FtY=" }, + { "cD2DEl4MBwONuTV0db5XreoVjQAUZFNXqIeFEU3KFkE=", "2CeHrjN7tBX48k4Sgv5fIHG06e57q/ucCL+8DIRmfXw=", "3EUo6MRzs6rSoY/7AFs8wiBiTXPcHzerLh6Xp3aMGKo=" }, + { "EA+S3a9ZeOLiRbhTxaT2wkpyDheAmai+UJa6SFGzSm8=", "GGByUKZx/FPa2OkJoqVTHXx+6jrIpIw5rf0rp43MHko=", "BXoDA3yn0JcMV7hHVzEqhlwAORvhToFO1qG00nas92A=" }, + { "eBeJi/imBiV52WEqrwAprUQggqdQmvTTmWtLq8pDDkM=", "zCX26ZTOZHLpq5x5aIUL1XhIVoXJLp/zcXwnmFA3jBo=", "Dm/DCxXWYXEsmQgxAD3KREK2PF0bUSnV5WRAaya8s1I=" }, + { "8C7p+EQO+CnWUSjHVu3PpeWpUIbLy48zpftZu021plM=", "DxpnF/IbKAh6kmWC5Jpj8iw387EDkrvjsjOb9fbTSng=", "bGrk0OshJB+0oQOK0QGKU8+lotnIDz3oeUnMZGienyM=" }, + { "COIez7YcBJiOJCLxxWV5UGLW5/o009YI0aszlD/PiUc=", "eD9USWV37LFIOxlDSHyOmfFqNJFpORRlzEI+HoF/czI=", "n+/ra86gUSF2pNZS51nt2JgrzXnQJl+dWswOq/Ahs94=" }, + { "iBBSTG9VLC+T9+ahNaQ4umZoig8o7w1DaeOw+cD2BGc=", "XnAxqDlvGnQ6aakv8ABGHVj07qVQfk4NChZbstTMBxg=", "7KKSwu/4yWr1UzFmNMGtiaSwdYMhP/HKbrQLlABL4UE=" }, + { "eNmwattflehr9+KsVqTuwt1YaAc5ONkaIaTQt9Gkhn8=", "1NNVvm++YTTGMKyAXfGOCZ4aDDdFFH5Um3vAg4XimDo=", "bXNrnDTP0pBay9ytZe7xpiKoSi12F7WUXqoIeI++Xvo=" }, + { "QJotpmZINx9eptKpkh9j3JlEDcHdWnjEbicdBS7gPXM=", "3gLYKeoruVZ/AYjym0gciDvRHj45UIWyHhNjWj2Wj28=", "NwuUkkE5yOWT7wed7bltgAk71miz3cSooiIDAdv6kKw=" }, + { "OAYGuxH70OPQvhVIX4BhSCWUyzAI5H2IkYxKgC/AO0M=", "Rj9iNF/FagkXfdLPqc9LHfaoGR8GlvY3gun2FilE508=", "hkNXLVMlBRsMEaQKkSzevcEK2sMu0AShGKQJMNqdzWM=" }, + { "uBrgZ7wLHrOV/0dNiEqo7FjY9VnJqL5eUDHJWAc9QXg=", "3Hnln8ZHfSaK4OzESJe5U6NcLaW56wzfZICzvzefnSo=", "DhXsehe5FAmbUidXT5ZpZIAuu1eF9rkU6cF3FBoKwOE=" }, + { "sJknn3CHvx/812EWU3ddLdLLZFBKsc+wx35GXyiRsHY=", "qqa2dNSt0jWozGyqpokP392H5/DOAUUZmUpyZDaUEEE=", "1Dyz8CvmF17oKT/wG2fu3vRzPzgQv8/OY9GJYew4FG8=" }, + { "yOumS9HN68ZwIm+5hZol3jFQ0DB4SKuW/ld3y8wioGk=", "6PowsbKj/fKSzXZMAfaSkP3fE+4AThL9xm6ysQzMDxg=", "vF8cKu9X9FxgCjyVZ5RG7nuue8RelF5Qsb8Efme4M4A=" }, + { "mISm5vQfPdK72SsaHh6O1/ARvaWCtm+KZNcpTsyt500=", "YCORDQDpb1U8vADdstBgXkg0N7QaAc5VoXJ4QFuA/UY=", "JlrBmfaCgbfEVD9YQq3c03WwwsHWc1nBwp1JkFORC3A=" }, + { "cPyu3Qry6qbsiOJKFGRziZ9LJWJ47k3ZSXiGkQXuQm0=", "/cmBZTbqEp8sababPAxGb3OvDAEE7MlwPOwEFHE+7yM=", "lp0Hhc/rVtpT5FtLLccChqDl3El48XtP6Wm6JwjI7jo=" }, + { "YKsMYU0SINbPwWw4RDCJV6GnzDlSp1ZNwUw2euGWi0A=", "/KmBReATQbFnLg8YKV0jwhqKeishRoWvlVtMX3550Vk=", "7fXpPSMo1Fw2sOXtjTtvFU+DbZvS/FWB9wAsywWx6R4=" }, + { "WD0YI3y71eIp/GXw9i+7scEiQKSBkGihZWE+s6fGmWo=", "RdthAL/qPnAZFb3xBgRMiAtGHNjgokzoKX9iO2K5qhc=", "4dk4HGkT9dBmomGNorDE/hLr/HEFhljtl4zz3M4sG58=" }, + { "oD8KWhJYZVhutJRb0kZlZnB22QUXzi2FfPRD0ll65UM=", "MYTBGHh4Ukj97pKj6qcfWmxGNQzmU3/aBOX2f1tfhG0=", "VT1gC+a9nRJzYMi/TPvRVnn3IQlaop/jKmmxZePEME0=" }, + { "0Ns/1SOiqR2CpHRG03QNzJJd5gxTm1XJmSkFlugjQ3k=", "KvQAI+ekNOa2xfEvfyc9JGcS+CTUrnnhsKrlyJGJixg=", "J8LmSX6zElX3S9q4PNvh2NKUtAiQ3oHiYjSJ7yErPlY=" }, + { "mD6TeF4ezSPXN/csN1OhoAREFSXllI+zl4DUOInVq2Q=", "WmLJ9ep2EqFcSftnYFJsmWyUxqL0zzuSzVEv94PcISM=", "U2+ILy2NDmmfgSW78C8dl8GyHESUc1lXPHPpg5F+gr8=" }, + { "SMJoXOYgHz8HSzY+ByeWLcSP5qFwv7YjRe0bcKesRnM=", "DEsNSOY3TEs9J2YgqroQ4xKq8T8xNJQjvvE4UrTItQw=", "Bws1Hk2+lO+JQ7ME16EbwAdsBkWsGvti0Gb6LY2Lrms=" }, + { "iGhXF0Hg0tqZmpwAMiolxvbvTPClQ7LlBAspSSyFEHE=", "7Xxzpwl7yRWehHNWTYVtFkdChJdXhtY4Mtw1fA9QcCg=", "Swjfv0PjuaE8Oq3a17BVno5I+q49dZlPwKK1bPUoKNI=" }, + { "MIazjx3qTi6Qz3WzhtCPw3i4Q2uZBHcuMoh++ZGFYUk=", "oni8pbFqk9Ya+Fx+911Nl1SN0FD/hR1jwb2RH3t/pRk=", "ZYAFcj67LkbNURYbSnCCWGxAG8QLDGWwbl968mA6ZA4=" }, + { "MAadYdiFM2cPuJF19q20Yoo5KJabuR9TUQ9jG5nvA1w=", "5OcE8XV+UPoBVbgqBQdVF62GZCW9DOQEdxrQsktPPBA=", "FCZsEFXouy+xtxv8X7VroXtvPG1Z1HFHL724tz1jcUI=" }, + { "+GwxMmD2dee5+QmvXNI0NdP+rNWoSXTN42otbp0aZ24=", "Y0N44baz9ihclCUnv6rRbDqCYu4BxQlBfNnTz3NNe2A=", "/LqSgkVQNkQ/oBiZSgpM9Rw7BJv0RvRpEQpvlizvHy0=" }, + { "8FREpCtncOcT7+W2nW4aYSjmSbADtVSH9rIliQZZUH4=", "fTNSd0JeREhXmPfjrmrAu6Lu/yHkB9GyxR3SyO4kZ28=", "262KN/iG/iJEaZeerFm1yVtvhFVGgQFwSvtxTcjZzeg=" }, + { "EDhaRQGtscjoSE9wJOnSXoQVtVruIqyzknty+x/vDWo=", "eMmMgws6ZxDIxZ6QSwGjZO1Mx/r5T+fJjSTKGMBk/BU=", "0CyaJV6AG9bZ0C4yeZ/RDsOs9BdNqZpUxAsD30WmJO4=" }, + { "CJV0UB2YdvVDG1cs4oiJgHAS+f1FocGr/vGCfiovsWQ=", "9/O9GWZEOXVm7On8lftL27PffRORju8OKl6gZd/74CA=", "A+kXRVNOwIrA5DUa+3v7dpRC+Gbxm23LTiYmOUAXUyY=" }, + { "gCjDsJUwZGA7BjYVoCQsvdIgN9Q4lBHlSyKwUrl751A=", "HRwS8T9y2qPYk7JVU/8Y+6cS+Bk8XCLCXxwN/ttbQiI=", "iFotjA6rhUfkDv4S/wspJgEWunEmrlGSGsXcJ0+8laQ=" }, + { "6N5pL4gsuK+shHpDxirTnAGdyKXIlYHyfIhtB0njJGA=", "CVZvW7NaN2XMEEKHodghBA9hLCwee/jrmttiWh/CmEg=", "OpPEd3Sp8r6KdjNDTN4bVHETlGJ92BCK74FCdEaDe9g=" }, + { "UIPPTUdvhlg8qEDv6JRxM4/8F5ORjJz4ud82QZrgeEY=", "7Nd13z5EpB3ChytvQC1CxvDY7n0H8r2Y7lzLEY8hdEk=", "b22PvgU0M2QfNC7ZGN+RXNe5fjOzMsY32IcHTwLNIqw=" }, + { "oBn53Q5fmxKX02PgI6F47Rb+XoLeFQO07ok2tYhk0lE=", "e0gtPDKXCZSoNW1uHqBPQXLfiYgyeqPMU2zZJgPXACI=", "wmjW2wDT2EzFkyaGui7YWNLTRu8Q4eD/GVKM2utZkEs=" }, + }; + gs_unref_ptrarray GPtrArray *allowed_ips_keep_alive = NULL; + gs_unref_array GArray *peers = NULL; + NMPlatformLnkWireGuard lnk_wireguard; + int r; + guint i; + + allowed_ips_keep_alive = g_ptr_array_new_with_free_func (g_free); + + peers = g_array_new (FALSE, TRUE, sizeof (NMPWireGuardPeer)); + + lnk_wireguard = (NMPlatformLnkWireGuard) { + .listen_port = 50754, + .fwmark = 0x1102, + }; + _copy_base64 (lnk_wireguard.private_key, sizeof (lnk_wireguard.private_key), self_key.pri); + _copy_base64 (lnk_wireguard.public_key, sizeof (lnk_wireguard.public_key), self_key.pub); + + if (test_mode == 0) { + /* no peers. */ + } else if (NM_IN_SET (test_mode, 1, 2)) { + guint num_peers = (test_mode == 1) ? 1 : G_N_ELEMENTS (keys); + + for (i = 0; i < num_peers; i++) { + NMPWireGuardPeer peer; + char s_addr[NM_UTILS_INET_ADDRSTRLEN]; + NMSockAddrUnion endpoint; + guint i_allowed_ips, n_allowed_ips; + NMPWireGuardAllowedIP *allowed_ips; + + if ((i % 2) == 1) { + endpoint = (NMSockAddrUnion) { + .in = { + .sin_family = AF_INET, + .sin_addr = nmtst_inet4_from_string (nm_sprintf_buf (s_addr, "192.168.7.%d", i)), + .sin_port = htons (14000 + i), + }, + }; + } else { + endpoint = (NMSockAddrUnion) { + .in6 = { + .sin6_family = AF_INET6, + .sin6_addr = *nmtst_inet6_from_string (nm_sprintf_buf (s_addr, "a:b:c:e::1:%d", i)), + .sin6_port = htons (16000 + i), + }, + }; + } + + if (test_mode == 1) + n_allowed_ips = 1; + else + n_allowed_ips = i % 10; + allowed_ips = g_new0 (NMPWireGuardAllowedIP, n_allowed_ips); + g_ptr_array_add (allowed_ips_keep_alive, allowed_ips); + for (i_allowed_ips = 0; i_allowed_ips < n_allowed_ips; i_allowed_ips++) { + NMPWireGuardAllowedIP *aip = &allowed_ips[i_allowed_ips]; + + aip->family = (i_allowed_ips % 2) ? AF_INET : AF_INET6; + if (aip->family == AF_INET) { + aip->addr.addr4 = nmtst_inet4_from_string (nm_sprintf_buf (s_addr, "10.%u.%u.0", i, i_allowed_ips)); + aip->mask = 32 - (i_allowed_ips % 8); + } else { + aip->addr.addr6 = *nmtst_inet6_from_string (nm_sprintf_buf (s_addr, "a:d:f:%02x:%02x::", i, i_allowed_ips)); + aip->mask = 128 - (i_allowed_ips % 10); + } + } + + peer = (NMPWireGuardPeer) { + .persistent_keepalive_interval = 60+i, + .endpoint = endpoint, + .allowed_ips = n_allowed_ips > 0 ? allowed_ips : NULL, + .allowed_ips_len = n_allowed_ips, + }; + _copy_base64 (peer.public_key, sizeof (peer.public_key), keys[i].pub); + _copy_base64 (peer.preshared_key, sizeof (peer.preshared_key), (i % 3) ? NULL : keys[i].pre); + + g_array_append_val (peers, peer); + } + } else + g_assert_not_reached (); + + r = nm_platform_link_wireguard_change (platform, + ifindex, + &lnk_wireguard, + (const NMPWireGuardPeer *) peers->data, + peers->len, + TRUE); + g_assert (NMTST_NM_ERR_SUCCESS (r)); +} + +/*****************************************************************************/ + typedef struct { NMLinkType link_type; int test_mode; @@ -697,6 +932,7 @@ test_software_detect (gconstpointer user_data) int ifindex, ifindex_parent; const NMPlatformLink *plink; const NMPObject *lnk; + int r; guint i_step; const gboolean ext = test_data->external_command; NMPlatformLnkTun lnk_tun; @@ -897,7 +1133,7 @@ test_software_detect (gconstpointer user_data) dummy = nmtstp_link_dummy_add (NM_PLATFORM_GET, FALSE, "dummy-tmp"); g_assert_cmpint (dummy->ifindex, ==, i); - nmtstp_link_del (NM_PLATFORM_GET, FALSE, dummy->ifindex, NULL); + nmtstp_link_delete (NM_PLATFORM_GET, FALSE, dummy->ifindex, NULL, TRUE); } if (!nmtstp_link_macvlan_add (NULL, ext, DEVICE_NAME, ifindex_parent, &lnk_macvlan)) @@ -1005,6 +1241,19 @@ test_software_detect (gconstpointer user_data) : NULL)); break; } + case NM_LINK_TYPE_WIREGUARD: { + const NMPlatformLink *link; + + r = nm_platform_link_wireguard_add (NM_PLATFORM_GET, DEVICE_NAME, &link); + if (r == -EOPNOTSUPP) { + g_test_skip ("wireguard not supported (modprobe wireguard?)"); + goto out_delete_parent; + } + + g_assert (NMTST_NM_ERR_SUCCESS (r)); + g_assert (NMP_OBJECT_GET_TYPE (NMP_OBJECT_UP_CAST (link)) == NMP_OBJECT_TYPE_LINK); + break; + } default: g_assert_not_reached (); } @@ -1217,7 +1466,7 @@ test_software_detect (gconstpointer user_data) g_assert_cmpint (plnk->dst_port, ==, 4789); if ( plnk->src_port_min != 0 || plnk->src_port_max != 0) { - /* on some kernels, omiting the port range results in setting + /* on some kernels, omitting the port range results in setting * following default port range. */ g_assert_cmpint (plnk->src_port_min, ==, 32768); g_assert_cmpint (plnk->src_port_max, ==, 61000); @@ -1238,14 +1487,26 @@ test_software_detect (gconstpointer user_data) } break; } + case NM_LINK_TYPE_WIREGUARD: { + const NMPlatformLnkWireGuard *plnk = &lnk->lnk_wireguard; + + g_assert (plnk == nm_platform_link_get_lnk_wireguard (NM_PLATFORM_GET, ifindex, NULL)); + + if (plink->n_ifi_flags & IFF_UP) { + _test_wireguard_change (NM_PLATFORM_GET, plink->ifindex, test_data->test_mode); + if (_LOGD_ENABLED ()) + _system ("WG_HIDE_KEYS=never wg show all"); + } + break; + } default: g_assert_not_reached (); } } - nmtstp_link_del (NULL, -1, ifindex, DEVICE_NAME); + nmtstp_link_delete (NULL, -1, ifindex, DEVICE_NAME, TRUE); out_delete_parent: - nmtstp_link_del (NULL, -1, ifindex_parent, PARENT_NAME); + nmtstp_link_delete (NULL, -1, ifindex_parent, PARENT_NAME, TRUE); } static void @@ -1822,8 +2083,8 @@ test_vlan_set_xgress (void) _assert_vlan_flags (ifindex, NM_VLAN_FLAG_REORDER_HEADERS | NM_VLAN_FLAG_GVRP); } - nmtstp_link_del (NULL, -1, ifindex, DEVICE_NAME); - nmtstp_link_del (NULL, -1, ifindex_parent, PARENT_NAME); + nmtstp_link_delete (NULL, -1, ifindex, DEVICE_NAME, TRUE); + nmtstp_link_delete (NULL, -1, ifindex_parent, PARENT_NAME, TRUE); } /*****************************************************************************/ @@ -1879,7 +2140,7 @@ test_create_many_links_do (guint n_devices) if (EX == 2) nmtstp_run_command_check ("ip link delete %s", name); else - nmtstp_link_del (NULL, EX, g_array_index (ifindexes, int, i), name); + nmtstp_link_delete (NULL, EX, g_array_index (ifindexes, int, i), name, TRUE); } _LOGI (">>> process events after deleting devices..."); @@ -1965,7 +2226,7 @@ test_nl_bugs_veth (void) }); out: - nmtstp_link_del (NULL, -1, ifindex_veth0, IFACE_VETH0); + nmtstp_link_delete (NULL, -1, ifindex_veth0, IFACE_VETH0, TRUE); g_assert (!nmtstp_link_get (NM_PLATFORM_GET, ifindex_veth0, IFACE_VETH0)); g_assert (!nmtstp_link_get (NM_PLATFORM_GET, ifindex_veth1, IFACE_VETH1)); nmtstp_namespace_handle_release (ns_handle); @@ -2018,7 +2279,7 @@ again: } g_assert (!nmtstp_link_get (NM_PLATFORM_GET, ifindex_bond0, IFACE_BOND0)); - nmtstp_link_del (NULL, -1, ifindex_dummy0, IFACE_DUMMY0); + nmtstp_link_delete (NULL, -1, ifindex_dummy0, IFACE_DUMMY0, TRUE); } /*****************************************************************************/ @@ -2072,8 +2333,8 @@ again: goto again; } - nmtstp_link_del (NULL, -1, ifindex_bridge0, IFACE_BRIDGE0); - nmtstp_link_del (NULL, -1, ifindex_dummy0, IFACE_DUMMY0); + nmtstp_link_delete (NULL, -1, ifindex_bridge0, IFACE_BRIDGE0, TRUE); + nmtstp_link_delete (NULL, -1, ifindex_dummy0, IFACE_DUMMY0, TRUE); } /*****************************************************************************/ @@ -2637,8 +2898,8 @@ test_sysctl_rename (void) } nm_platform_process_events (PL); - nmtstp_link_del (PL, -1, ifindex[0], NULL); - nmtstp_link_del (PL, -1, ifindex[1], NULL); + nmtstp_link_delete (PL, -1, ifindex[0], NULL, TRUE); + nmtstp_link_delete (PL, -1, ifindex[1], NULL, TRUE); } /*****************************************************************************/ @@ -2717,7 +2978,7 @@ test_sysctl_netns_switch (void) else g_assert_cmpint (ifindex_tmp, ==, -1); - nmtstp_link_del (PL, FALSE, ifindex, NULL); + nmtstp_link_delete (PL, FALSE, ifindex, NULL, TRUE); } /*****************************************************************************/ @@ -2800,12 +3061,8 @@ test_ethtool_features_get (void) ethtool_features_dump (features); - if (_LOGT_ENABLED ()) { - int ignore; - - ignore = system ("ethtool -k lo"); - (void) ignore; - } + if (_LOGT_ENABLED ()) + _system ("ethtool -k lo"); if (!do_set) { requested = gfree_keeper->pdata[i_run * 2 - 2]; @@ -2829,9 +3086,9 @@ _nmtstp_init_tests (int *argc, char ***argv) void _nmtstp_setup_tests (void) { - nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); - nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, SLAVE_NAME)); - nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, PARENT_NAME)); + nmtstp_link_delete (NM_PLATFORM_GET, -1, -1, DEVICE_NAME, FALSE); + nmtstp_link_delete (NM_PLATFORM_GET, -1, -1, SLAVE_NAME, FALSE); + nmtstp_link_delete (NM_PLATFORM_GET, -1, -1, PARENT_NAME, FALSE); g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME)); g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, SLAVE_NAME)); g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, PARENT_NAME)); @@ -2862,6 +3119,9 @@ _nmtstp_setup_tests (void) test_software_detect_add ("/link/software/detect/vlan", NM_LINK_TYPE_VLAN, 0); test_software_detect_add ("/link/software/detect/vxlan/0", NM_LINK_TYPE_VXLAN, 0); test_software_detect_add ("/link/software/detect/vxlan/1", NM_LINK_TYPE_VXLAN, 1); + test_software_detect_add ("/link/software/detect/wireguard/0", NM_LINK_TYPE_WIREGUARD, 0); + test_software_detect_add ("/link/software/detect/wireguard/1", NM_LINK_TYPE_WIREGUARD, 1); + test_software_detect_add ("/link/software/detect/wireguard/2", NM_LINK_TYPE_WIREGUARD, 2); g_test_add_func ("/link/software/vlan/set-xgress", test_vlan_set_xgress); diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c index 85b14b57..4c0f686f 100644 --- a/src/platform/tests/test-route.c +++ b/src/platform/tests/test-route.c @@ -427,7 +427,7 @@ test_ip4_route_get (void) { int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); in_addr_t a; - NMPlatformError result; + int result; nm_auto_nmpobj NMPObject *route = NULL; const NMPlatformIP4Route *r; @@ -446,7 +446,7 @@ test_ip4_route_get (void) nmtst_get_rand_int () % 2 ? 0 : ifindex, &route); - g_assert (result == NM_PLATFORM_ERROR_SUCCESS); + g_assert (NMTST_NM_ERR_SUCCESS (result)); g_assert (NMP_OBJECT_GET_TYPE (route) == NMP_OBJECT_TYPE_IP4_ROUTE); g_assert (!NMP_OBJECT_IS_STACKINIT (route)); g_assert (route->parent._ref_count == 1); @@ -565,7 +565,7 @@ test_ip4_route_options (gconstpointer test_data) } for (i = 0; i < rts_n; i++) - g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, NMP_NLM_FLAG_REPLACE, &rts_add[i]) == NM_PLATFORM_ERROR_SUCCESS); + g_assert (NMTST_NM_ERR_SUCCESS (nm_platform_ip4_route_add (NM_PLATFORM_GET, NMP_NLM_FLAG_REPLACE, &rts_add[i]))); for (i = 0; i < rts_n; i++) { rts_cmp[i] = rts_add[i]; @@ -589,7 +589,7 @@ test_ip6_route_get (void) { int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); const struct in6_addr *a; - NMPlatformError result; + int result; nm_auto_nmpobj NMPObject *route = NULL; const NMPlatformIP6Route *r; @@ -608,7 +608,7 @@ test_ip6_route_get (void) nmtst_get_rand_int () % 2 ? 0 : ifindex, &route); - g_assert (result == NM_PLATFORM_ERROR_SUCCESS); + g_assert (NMTST_NM_ERR_SUCCESS (result)); g_assert (NMP_OBJECT_GET_TYPE (route) == NMP_OBJECT_TYPE_IP6_ROUTE); g_assert (!NMP_OBJECT_IS_STACKINIT (route)); g_assert (route->parent._ref_count == 1); @@ -724,7 +724,7 @@ test_ip6_route_options (gconstpointer test_data) _wait_for_ipv6_addr_non_tentative (NM_PLATFORM_GET, 400, IFINDEX, addr_n, addr_in6); for (i = 0; i < rts_n; i++) - g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, NMP_NLM_FLAG_REPLACE, &rts_add[i]) == NM_PLATFORM_ERROR_SUCCESS); + g_assert (NMTST_NM_ERR_SUCCESS (nm_platform_ip6_route_add (NM_PLATFORM_GET, NMP_NLM_FLAG_REPLACE, &rts_add[i]))); for (i = 0; i < rts_n; i++) { rts_cmp[i] = rts_add[i]; @@ -823,7 +823,7 @@ again_find_idx: order_idx[order_len++] = idx; r->ifindex = iface_data[idx].ifindex; - g_assert (nm_platform_ip4_route_add (platform, NMP_NLM_FLAG_APPEND, r) == NM_PLATFORM_ERROR_SUCCESS); + g_assert (NMTST_NM_ERR_SUCCESS (nm_platform_ip4_route_add (platform, NMP_NLM_FLAG_APPEND, r))); } else { i = nmtst_get_rand_int () % order_len; idx = order_idx[i]; diff --git a/src/platform/wifi/nm-wifi-utils-nl80211.c b/src/platform/wifi/nm-wifi-utils-nl80211.c index b3bb2bb6..0df3d2d8 100644 --- a/src/platform/wifi/nm-wifi-utils-nl80211.c +++ b/src/platform/wifi/nm-wifi-utils-nl80211.c @@ -30,7 +30,9 @@ #include <net/ethernet.h> #include <unistd.h> #include <linux/nl80211.h> +#include <linux/if.h> +#include "nm-utils/nm-errno.h" #include "platform/nm-netlink.h" #include "nm-wifi-utils-private.h" #include "platform/nm-platform.h" @@ -38,11 +40,16 @@ #include "nm-utils.h" #define _NMLOG_PREFIX_NAME "wifi-nl80211" -#define _NMLOG(level, domain, ...) \ +#define _NMLOG_DOMAIN LOGD_PLATFORM | LOGD_WIFI +#define _NMLOG(level, ...) \ G_STMT_START { \ - nm_log ((level), (domain), NULL, NULL, \ - "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - _NMLOG_PREFIX_NAME \ + char _ifname_buf[IFNAMSIZ]; \ + const char *_ifname = self ? nmp_utils_if_indextoname (self->parent.ifindex, _ifname_buf) : NULL; \ + \ + nm_log ((level), _NMLOG_DOMAIN, _ifname ?: NULL, NULL, \ + "%s%s%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + _NMLOG_PREFIX_NAME, \ + NM_PRINT_FMT_QUOTED (_ifname, " (", _ifname, ")", "") \ _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ } G_STMT_END @@ -103,16 +110,16 @@ nla_put_failure: } static struct nl_msg * -nl80211_alloc_msg (NMWifiUtilsNl80211 *nl80211, guint32 cmd, guint32 flags) +nl80211_alloc_msg (NMWifiUtilsNl80211 *self, guint32 cmd, guint32 flags) { - return _nl80211_alloc_msg (nl80211->id, nl80211->parent.ifindex, nl80211->phy, cmd, flags); + return _nl80211_alloc_msg (self->id, self->parent.ifindex, self->phy, cmd, flags); } static int -_nl80211_send_and_recv (struct nl_sock *nl_sock, - struct nl_msg *msg, - int (*valid_handler) (struct nl_msg *, void *), - void *valid_data) +nl80211_send_and_recv (NMWifiUtilsNl80211 *self, + struct nl_msg *msg, + int (*valid_handler) (struct nl_msg *, void *), + void *valid_data) { int err; int done = 0; @@ -129,7 +136,7 @@ _nl80211_send_and_recv (struct nl_sock *nl_sock, g_return_val_if_fail (msg != NULL, -ENOMEM); - err = nl_send_auto (nl_sock, msg); + err = nl_send_auto (self->nl_sock, msg); if (err < 0) return err; @@ -137,19 +144,18 @@ _nl80211_send_and_recv (struct nl_sock *nl_sock, * done will be 1, on error it will be < 0. */ while (!done) { - err = nl_recvmsgs (nl_sock, &cb); + err = nl_recvmsgs (self->nl_sock, &cb); if (err < 0 && err != -EAGAIN) { /* Kernel scan list can change while we are dumping it, as new scan * results from H/W can arrive. BSS info is assured to be consistent * and we don't need consistent view of whole scan list. Hence do * not warn on DUMP_INTR error for get scan command. */ - if (err == -NLE_DUMP_INTR && + if (err == -NME_NL_DUMP_INTR && genlmsg_hdr (nlmsg_hdr (msg))->cmd == NL80211_CMD_GET_SCAN) break; - _LOGW (LOGD_WIFI, "nl_recvmsgs() error: (%d) %s", - err, nl_geterror (err)); + _LOGW ("nl_recvmsgs() error: (%d) %s", err, nm_strerror (err)); break; } } @@ -159,22 +165,12 @@ _nl80211_send_and_recv (struct nl_sock *nl_sock, return err; } -static int -nl80211_send_and_recv (NMWifiUtilsNl80211 *nl80211, - struct nl_msg *msg, - int (*valid_handler) (struct nl_msg *, void *), - void *valid_data) -{ - return _nl80211_send_and_recv (nl80211->nl_sock, msg, - valid_handler, valid_data); -} - static void dispose (GObject *object) { - NMWifiUtilsNl80211 *nl80211 = NM_WIFI_UTILS_NL80211 (object); + NMWifiUtilsNl80211 *self = NM_WIFI_UTILS_NL80211 (object); - g_clear_pointer (&nl80211->freqs, g_free); + g_clear_pointer (&self->freqs, g_free); } struct nl80211_iface_info { @@ -213,15 +209,15 @@ nl80211_iface_info_handler (struct nl_msg *msg, void *arg) static NM80211Mode wifi_nl80211_get_mode (NMWifiUtils *data) { - NMWifiUtilsNl80211 *nl80211 = (NMWifiUtilsNl80211 *) data; + NMWifiUtilsNl80211 *self = (NMWifiUtilsNl80211 *) data; struct nl80211_iface_info iface_info = { .mode = NM_802_11_MODE_UNKNOWN, }; nm_auto_nlmsg struct nl_msg *msg = NULL; - msg = nl80211_alloc_msg (nl80211, NL80211_CMD_GET_INTERFACE, 0); + msg = nl80211_alloc_msg (self, NL80211_CMD_GET_INTERFACE, 0); - if (nl80211_send_and_recv (nl80211, msg, nl80211_iface_info_handler, + if (nl80211_send_and_recv (self, msg, nl80211_iface_info_handler, &iface_info) < 0) return NM_802_11_MODE_UNKNOWN; @@ -231,11 +227,11 @@ wifi_nl80211_get_mode (NMWifiUtils *data) static gboolean wifi_nl80211_set_mode (NMWifiUtils *data, const NM80211Mode mode) { - NMWifiUtilsNl80211 *nl80211 = (NMWifiUtilsNl80211 *) data; + NMWifiUtilsNl80211 *self = (NMWifiUtilsNl80211 *) data; nm_auto_nlmsg struct nl_msg *msg = NULL; int err; - msg = nl80211_alloc_msg (nl80211, NL80211_CMD_SET_INTERFACE, 0); + msg = nl80211_alloc_msg (self, NL80211_CMD_SET_INTERFACE, 0); switch (mode) { case NM_802_11_MODE_INFRA: @@ -251,7 +247,7 @@ wifi_nl80211_set_mode (NMWifiUtils *data, const NM80211Mode mode) g_assert_not_reached (); } - err = nl80211_send_and_recv (nl80211, msg, NULL, NULL); + err = nl80211_send_and_recv (self, msg, NULL, NULL); return err >= 0; nla_put_failure: @@ -261,14 +257,14 @@ nla_put_failure: static gboolean wifi_nl80211_set_powersave (NMWifiUtils *data, guint32 powersave) { - NMWifiUtilsNl80211 *nl80211 = (NMWifiUtilsNl80211 *) data; + NMWifiUtilsNl80211 *self = (NMWifiUtilsNl80211 *) data; nm_auto_nlmsg struct nl_msg *msg = NULL; int err; - msg = nl80211_alloc_msg (nl80211, NL80211_CMD_SET_POWER_SAVE, 0); + msg = nl80211_alloc_msg (self, NL80211_CMD_SET_POWER_SAVE, 0); NLA_PUT_U32 (msg, NL80211_ATTR_PS_STATE, powersave == 1 ? NL80211_PS_ENABLED : NL80211_PS_DISABLED); - err = nl80211_send_and_recv (nl80211, msg, NULL, NULL); + err = nl80211_send_and_recv (self, msg, NULL, NULL); return err >= 0; nla_put_failure: @@ -318,13 +314,13 @@ nl80211_get_wake_on_wlan_handler (struct nl_msg *msg, void *arg) static NMSettingWirelessWakeOnWLan wifi_nl80211_get_wake_on_wlan (NMWifiUtils *data) { - NMWifiUtilsNl80211 *nl80211 = (NMWifiUtilsNl80211 *) data; + NMWifiUtilsNl80211 *self = (NMWifiUtilsNl80211 *) data; NMSettingWirelessWakeOnWLan wowl = NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE; nm_auto_nlmsg struct nl_msg *msg = NULL; - msg = nl80211_alloc_msg (nl80211, NL80211_CMD_GET_WOWLAN, 0); + msg = nl80211_alloc_msg (self, NL80211_CMD_GET_WOWLAN, 0); - nl80211_send_and_recv (nl80211, msg, nl80211_get_wake_on_wlan_handler, &wowl); + nl80211_send_and_recv (self, msg, nl80211_get_wake_on_wlan_handler, &wowl); return wowl; } @@ -332,7 +328,7 @@ wifi_nl80211_get_wake_on_wlan (NMWifiUtils *data) static gboolean wifi_nl80211_set_wake_on_wlan (NMWifiUtils *data, NMSettingWirelessWakeOnWLan wowl) { - NMWifiUtilsNl80211 *nl80211 = (NMWifiUtilsNl80211 *) data; + NMWifiUtilsNl80211 *self = (NMWifiUtilsNl80211 *) data; nm_auto_nlmsg struct nl_msg *msg = NULL; struct nlattr *triggers; int err; @@ -340,7 +336,7 @@ wifi_nl80211_set_wake_on_wlan (NMWifiUtils *data, NMSettingWirelessWakeOnWLan wo if (wowl == NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE) return TRUE; - msg = nl80211_alloc_msg (nl80211, NL80211_CMD_SET_WOWLAN, 0); + msg = nl80211_alloc_msg (self, NL80211_CMD_SET_WOWLAN, 0); if (!msg) return FALSE; @@ -363,7 +359,7 @@ wifi_nl80211_set_wake_on_wlan (NMWifiUtils *data, NMSettingWirelessWakeOnWLan wo nla_nest_end(msg, triggers); - err = nl80211_send_and_recv (nl80211, msg, NULL, NULL); + err = nl80211_send_and_recv (self, msg, NULL, NULL); return err >= 0; @@ -491,25 +487,25 @@ nl80211_bss_dump_handler (struct nl_msg *msg, void *arg) } static void -nl80211_get_bss_info (NMWifiUtilsNl80211 *nl80211, +nl80211_get_bss_info (NMWifiUtilsNl80211 *self, struct nl80211_bss_info *bss_info) { nm_auto_nlmsg struct nl_msg *msg = NULL; memset (bss_info, 0, sizeof (*bss_info)); - msg = nl80211_alloc_msg (nl80211, NL80211_CMD_GET_SCAN, NLM_F_DUMP); + msg = nl80211_alloc_msg (self, NL80211_CMD_GET_SCAN, NLM_F_DUMP); - nl80211_send_and_recv (nl80211, msg, nl80211_bss_dump_handler, bss_info); + nl80211_send_and_recv (self, msg, nl80211_bss_dump_handler, bss_info); } static guint32 wifi_nl80211_get_freq (NMWifiUtils *data) { - NMWifiUtilsNl80211 *nl80211 = (NMWifiUtilsNl80211 *) data; + NMWifiUtilsNl80211 *self = (NMWifiUtilsNl80211 *) data; struct nl80211_bss_info bss_info; - nl80211_get_bss_info (nl80211, &bss_info); + nl80211_get_bss_info (self, &bss_info); return bss_info.freq; } @@ -517,12 +513,12 @@ wifi_nl80211_get_freq (NMWifiUtils *data) static guint32 wifi_nl80211_find_freq (NMWifiUtils *data, const guint32 *freqs) { - NMWifiUtilsNl80211 *nl80211 = (NMWifiUtilsNl80211 *) data; + NMWifiUtilsNl80211 *self = (NMWifiUtilsNl80211 *) data; int i; - for (i = 0; i < nl80211->num_freqs; i++) { + for (i = 0; i < self->num_freqs; i++) { while (*freqs) { - if (nl80211->freqs[i] == *freqs) + if (self->freqs[i] == *freqs) return *freqs; freqs++; } @@ -533,10 +529,10 @@ wifi_nl80211_find_freq (NMWifiUtils *data, const guint32 *freqs) static gboolean wifi_nl80211_get_bssid (NMWifiUtils *data, guint8 *out_bssid) { - NMWifiUtilsNl80211 *nl80211 = (NMWifiUtilsNl80211 *) data; + NMWifiUtilsNl80211 *self = (NMWifiUtilsNl80211 *) data; struct nl80211_bss_info bss_info; - nl80211_get_bss_info (nl80211, &bss_info); + nl80211_get_bss_info (self, &bss_info); if (bss_info.valid) memcpy (out_bssid, bss_info.bssid, ETH_ALEN); @@ -615,7 +611,7 @@ nl80211_station_handler (struct nl_msg *msg, void *arg) } static void -nl80211_get_ap_info (NMWifiUtilsNl80211 *nl80211, +nl80211_get_ap_info (NMWifiUtilsNl80211 *self, struct nl80211_station_info *sta_info) { nm_auto_nlmsg struct nl_msg *msg = NULL; @@ -623,15 +619,15 @@ nl80211_get_ap_info (NMWifiUtilsNl80211 *nl80211, memset (sta_info, 0, sizeof (*sta_info)); - nl80211_get_bss_info (nl80211, &bss_info); + nl80211_get_bss_info (self, &bss_info); if (!bss_info.valid) return; - msg = nl80211_alloc_msg (nl80211, NL80211_CMD_GET_STATION, 0); + msg = nl80211_alloc_msg (self, NL80211_CMD_GET_STATION, 0); if (msg) { NLA_PUT (msg, NL80211_ATTR_MAC, ETH_ALEN, bss_info.bssid); - nl80211_send_and_recv (nl80211, msg, nl80211_station_handler, sta_info); + nl80211_send_and_recv (self, msg, nl80211_station_handler, sta_info); if (!sta_info->signal_valid) { /* Fall back to bss_info signal quality (both are in percent) */ sta_info->signal = bss_info.beacon_signal; @@ -647,10 +643,10 @@ nla_put_failure: static guint32 wifi_nl80211_get_rate (NMWifiUtils *data) { - NMWifiUtilsNl80211 *nl80211 = (NMWifiUtilsNl80211 *) data; + NMWifiUtilsNl80211 *self = (NMWifiUtilsNl80211 *) data; struct nl80211_station_info sta_info; - nl80211_get_ap_info (nl80211, &sta_info); + nl80211_get_ap_info (self, &sta_info); return sta_info.txrate; } @@ -658,21 +654,21 @@ wifi_nl80211_get_rate (NMWifiUtils *data) static int wifi_nl80211_get_qual (NMWifiUtils *data) { - NMWifiUtilsNl80211 *nl80211 = (NMWifiUtilsNl80211 *) data; + NMWifiUtilsNl80211 *self = (NMWifiUtilsNl80211 *) data; struct nl80211_station_info sta_info; - nl80211_get_ap_info (nl80211, &sta_info); + nl80211_get_ap_info (self, &sta_info); return sta_info.signal; } static gboolean wifi_nl80211_indicate_addressing_running (NMWifiUtils *data, gboolean running) { - NMWifiUtilsNl80211 *nl80211 = (NMWifiUtilsNl80211 *) data; + NMWifiUtilsNl80211 *self = (NMWifiUtilsNl80211 *) data; nm_auto_nlmsg struct nl_msg *msg = NULL; int err; - msg = nl80211_alloc_msg (nl80211, + msg = nl80211_alloc_msg (self, running ? 98 /* NL80211_CMD_CRIT_PROTOCOL_START */ : 99 /* NL80211_CMD_CRIT_PROTOCOL_STOP */, @@ -690,7 +686,7 @@ wifi_nl80211_indicate_addressing_running (NMWifiUtils *data, gboolean running) 5000); } - err = nl80211_send_and_recv (nl80211, msg, NULL, NULL); + err = nl80211_send_and_recv (self, msg, NULL, NULL); return err >= 0; nla_put_failure: @@ -698,6 +694,7 @@ nla_put_failure: } struct nl80211_device_info { + NMWifiUtilsNl80211 *self; int phy; guint32 *freqs; int num_freqs; @@ -723,6 +720,7 @@ static int nl80211_wiphy_info_handler (struct nl_msg *msg, void *arg) struct nlattr *tb[NL80211_ATTR_MAX + 1]; struct genlmsghdr *gnlh = nlmsg_data (nlmsg_hdr (msg)); struct nl80211_device_info *info = arg; + NMWifiUtilsNl80211 *self = info->self; struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1]; struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1]; struct nlattr *nl_band; @@ -872,8 +870,7 @@ static int nl80211_wiphy_info_handler (struct nl_msg *msg, void *arg) case WLAN_CIPHER_SUITE_SMS4: break; default: - _LOGD (LOGD_PLATFORM | LOGD_WIFI, - "don't know the meaning of NL80211_ATTR_CIPHER_SUITE %#8.8x.", + _LOGD ("don't know the meaning of NL80211_ATTR_CIPHER_SUITE %#8.8x.", ciphers[i]); break; } @@ -929,86 +926,66 @@ nm_wifi_utils_nl80211_class_init (NMWifiUtilsNl80211Class *klass) NMWifiUtils * nm_wifi_utils_nl80211_new (int ifindex, struct nl_sock *genl) { - gs_unref_object NMWifiUtilsNl80211 *nl80211 = NULL; + gs_unref_object NMWifiUtilsNl80211 *self = NULL; nm_auto_nlmsg struct nl_msg *msg = NULL; - struct nl80211_device_info device_info = {}; - char ifname[IFNAMSIZ]; + struct nl80211_device_info device_info = { }; if (!genl) return NULL; - if (!nmp_utils_if_indextoname (ifindex, ifname)) { - _LOGW (LOGD_PLATFORM | LOGD_WIFI, - "can't determine interface name for ifindex %d", ifindex); - nm_sprintf_buf (ifname, "if %d", ifindex); - } - - nl80211 = g_object_new (NM_TYPE_WIFI_UTILS_NL80211, NULL); + self = g_object_new (NM_TYPE_WIFI_UTILS_NL80211, NULL); - nl80211->parent.ifindex = ifindex; - nl80211->nl_sock = genl; + self->parent.ifindex = ifindex; + self->nl_sock = genl; - nl80211->id = genl_ctrl_resolve (nl80211->nl_sock, "nl80211"); - if (nl80211->id < 0) { - _LOGD (LOGD_WIFI, "genl_ctrl_resolve: failed to resolve \"nl80211\""); + self->id = genl_ctrl_resolve (self->nl_sock, "nl80211"); + if (self->id < 0) { + _LOGD ("genl_ctrl_resolve: failed to resolve \"nl80211\""); return NULL; } - nl80211->phy = -1; + self->phy = -1; - msg = nl80211_alloc_msg (nl80211, NL80211_CMD_GET_WIPHY, 0); + msg = nl80211_alloc_msg (self, NL80211_CMD_GET_WIPHY, 0); - if (nl80211_send_and_recv (nl80211, msg, nl80211_wiphy_info_handler, + device_info.self = self; + if (nl80211_send_and_recv (self, msg, nl80211_wiphy_info_handler, &device_info) < 0) { - _LOGD (LOGD_PLATFORM | LOGD_WIFI, - "(%s): NL80211_CMD_GET_WIPHY request failed", - ifname); + _LOGD ("NL80211_CMD_GET_WIPHY request failed"); return NULL; } if (!device_info.success) { - _LOGD (LOGD_PLATFORM | LOGD_WIFI, - "(%s): NL80211_CMD_GET_WIPHY request indicated failure", - ifname); + _LOGD ("NL80211_CMD_GET_WIPHY request indicated failure"); return NULL; } if (!device_info.supported) { - _LOGD (LOGD_PLATFORM | LOGD_WIFI, - "(%s): driver does not fully support nl80211, falling back to WEXT", - ifname); + _LOGD ("driver does not fully support nl80211, falling back to WEXT"); return NULL; } if (!device_info.can_scan_ssid) { - _LOGE (LOGD_PLATFORM | LOGD_WIFI, - "(%s): driver does not support SSID scans", - ifname); + _LOGE ("driver does not support SSID scans"); return NULL; } if (device_info.num_freqs == 0 || device_info.freqs == NULL) { - nm_log_err (LOGD_PLATFORM | LOGD_WIFI, - "(%s): driver reports no supported frequencies", - ifname); + _LOGE ("driver reports no supported frequencies"); return NULL; } if (device_info.caps == 0) { - _LOGE (LOGD_PLATFORM | LOGD_WIFI, - "(%s): driver doesn't report support of any encryption", - ifname); + _LOGE ("driver doesn't report support of any encryption"); return NULL; } - nl80211->phy = device_info.phy; - nl80211->freqs = device_info.freqs; - nl80211->num_freqs = device_info.num_freqs; - nl80211->parent.caps = device_info.caps; - nl80211->can_wowlan = device_info.can_wowlan; + self->phy = device_info.phy; + self->freqs = device_info.freqs; + self->num_freqs = device_info.num_freqs; + self->parent.caps = device_info.caps; + self->can_wowlan = device_info.can_wowlan; - _LOGI (LOGD_PLATFORM | LOGD_WIFI, - "(%s): using nl80211 for WiFi device control", - ifname); - return (NMWifiUtils *) g_steal_pointer (&nl80211); + _LOGD ("using nl80211 for Wi-Fi device control"); + return (NMWifiUtils *) g_steal_pointer (&self); } diff --git a/src/platform/wifi/nm-wifi-utils-wext.c b/src/platform/wifi/nm-wifi-utils-wext.c index e52ae5a3..597f3152 100644 --- a/src/platform/wifi/nm-wifi-utils-wext.c +++ b/src/platform/wifi/nm-wifi-utils-wext.c @@ -511,7 +511,7 @@ wifi_wext_set_mesh_ssid (NMWifiUtils *data, const guint8 *ssid, gsize len) errsv = errno; _LOGE (LOGD_PLATFORM | LOGD_WIFI | LOGD_OLPC, - "(%s): error setting SSID to '%s': %s", + "(%s): error setting SSID to %s: %s", ifname, (ssid_str = _nm_utils_ssid_to_string_arr (ssid, len)), strerror (errsv)); @@ -751,7 +751,7 @@ nm_wifi_utils_wext_new (int ifindex, gboolean check_scan) wext->parent.caps |= NM_WIFI_DEVICE_CAP_FREQ_5GHZ; _LOGI (LOGD_PLATFORM | LOGD_WIFI, - "(%s): using WEXT for WiFi device control", + "(%s): using WEXT for Wi-Fi device control", ifname); return (NMWifiUtils *) wext; @@ -771,7 +771,7 @@ nm_wifi_utils_wext_is_wifi (const char *iface) /* performing an ioctl on a non-existing name may cause the automatic * loading of kernel modules, which should be avoided. * - * Usually, we should thus make sure that an inteface with this name + * Usually, we should thus make sure that an interface with this name * exists. * * Note that wifi_wext_is_wifi() has only one caller which just verified diff --git a/src/platform/wifi/nm-wifi-utils.h b/src/platform/wifi/nm-wifi-utils.h index 6cd178bd..36148b5a 100644 --- a/src/platform/wifi/nm-wifi-utils.h +++ b/src/platform/wifi/nm-wifi-utils.h @@ -62,7 +62,7 @@ gboolean nm_wifi_utils_get_bssid (NMWifiUtils *data, guint8 *out_bssid); /* Returns current bitrate in Kbps */ guint32 nm_wifi_utils_get_rate (NMWifiUtils *data); -/* Returns quality 0 - 100% on succes, or -1 on error */ +/* Returns quality 0 - 100% on success, or -1 on error */ int nm_wifi_utils_get_qual (NMWifiUtils *data); /* Tells the driver DHCP or SLAAC is running */ diff --git a/src/platform/wpan/nm-wpan-utils.c b/src/platform/wpan/nm-wpan-utils.c index 0544539a..f8708bc6 100644 --- a/src/platform/wpan/nm-wpan-utils.c +++ b/src/platform/wpan/nm-wpan-utils.c @@ -21,15 +21,23 @@ #include "nm-wpan-utils.h" +#include <linux/if.h> + +#include "nm-utils/nm-errno.h" #include "platform/linux/nl802154.h" #include "platform/nm-netlink.h" +#include "platform/nm-platform-utils.h" #define _NMLOG_PREFIX_NAME "wpan-nl802154" #define _NMLOG(level, domain, ...) \ G_STMT_START { \ - nm_log ((level), (domain), NULL, NULL, \ - "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - _NMLOG_PREFIX_NAME \ + char _ifname_buf[IFNAMSIZ]; \ + const char *_ifname = self ? nmp_utils_if_indextoname (self->ifindex, _ifname_buf) : NULL; \ + \ + nm_log ((level), (domain), _ifname ?: NULL, NULL, \ + "%s%s%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + _NMLOG_PREFIX_NAME, \ + NM_PRINT_FMT_QUOTED (_ifname, " (", _ifname, ")", "") \ _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ } G_STMT_END @@ -95,10 +103,10 @@ nl802154_alloc_msg (NMWpanUtils *self, guint32 cmd, guint32 flags) } static int -_nl802154_send_and_recv (struct nl_sock *nl_sock, - struct nl_msg *msg, - int (*valid_handler) (struct nl_msg *, void *), - void *valid_data) +nl802154_send_and_recv (NMWpanUtils *self, + struct nl_msg *msg, + int (*valid_handler) (struct nl_msg *, void *), + void *valid_data) { int err; int done = 0; @@ -115,7 +123,7 @@ _nl802154_send_and_recv (struct nl_sock *nl_sock, g_return_val_if_fail (msg != NULL, -ENOMEM); - err = nl_send_auto (nl_sock, msg); + err = nl_send_auto (self->nl_sock, msg); if (err < 0) return err; @@ -123,10 +131,10 @@ _nl802154_send_and_recv (struct nl_sock *nl_sock, * done will be 1, on error it will be < 0. */ while (!done) { - err = nl_recvmsgs (nl_sock, &cb); + err = nl_recvmsgs (self->nl_sock, &cb); if (err < 0 && err != -EAGAIN) { _LOGW (LOGD_PLATFORM, "nl_recvmsgs() error: (%d) %s", - err, nl_geterror (err)); + err, nm_strerror (err)); break; } } @@ -136,16 +144,6 @@ _nl802154_send_and_recv (struct nl_sock *nl_sock, return err; } -static int -nl802154_send_and_recv (NMWpanUtils *self, - struct nl_msg *msg, - int (*valid_handler) (struct nl_msg *, void *), - void *valid_data) -{ - return _nl802154_send_and_recv (self->nl_sock, msg, - valid_handler, valid_data); -} - struct nl802154_interface { guint16 pan_id; guint16 short_addr; @@ -248,6 +246,24 @@ nla_put_failure: return FALSE; } +gboolean +nm_wpan_utils_set_channel (NMWpanUtils *self, guint8 page, guint8 channel) +{ + nm_auto_nlmsg struct nl_msg *msg = NULL; + int err; + + g_return_val_if_fail (self != NULL, FALSE); + + msg = nl802154_alloc_msg (self, NL802154_CMD_SET_CHANNEL, 0); + NLA_PUT_U8 (msg, NL802154_ATTR_PAGE, page); + NLA_PUT_U8 (msg, NL802154_ATTR_CHANNEL, channel); + err = nl802154_send_and_recv (self, msg, NULL, NULL); + return err >= 0; + +nla_put_failure: + return FALSE; +} + /*****************************************************************************/ static void @@ -264,23 +280,22 @@ NMWpanUtils * nm_wpan_utils_new (int ifindex, struct nl_sock *genl, gboolean check_scan) { NMWpanUtils *self; - int id; g_return_val_if_fail (ifindex > 0, NULL); if (!genl) return NULL; - id = genl_ctrl_resolve (genl, "nl802154"); - if (id < 0) { - _LOGD (LOGD_PLATFORM, "genl_ctrl_resolve: failed to resolve \"nl802154\""); - return NULL; - } - self = g_object_new (NM_TYPE_WPAN_UTILS, NULL); self->ifindex = ifindex; self->nl_sock = genl; - self->id = id; + self->id = genl_ctrl_resolve (genl, "nl802154"); + + if (self->id < 0) { + _LOGD (LOGD_PLATFORM, "genl_ctrl_resolve: failed to resolve \"nl802154\""); + g_object_unref (self); + return NULL; + } return self; } diff --git a/src/platform/wpan/nm-wpan-utils.h b/src/platform/wpan/nm-wpan-utils.h index f7d0c03e..1b54ec49 100644 --- a/src/platform/wpan/nm-wpan-utils.h +++ b/src/platform/wpan/nm-wpan-utils.h @@ -44,4 +44,6 @@ gboolean nm_wpan_utils_set_pan_id (NMWpanUtils *self, guint16 pan_id); guint16 nm_wpan_utils_get_short_addr (NMWpanUtils *self); gboolean nm_wpan_utils_set_short_addr (NMWpanUtils *self, guint16 short_addr); +gboolean nm_wpan_utils_set_channel (NMWpanUtils *self, guint8 page, guint8 channel); + #endif /* __WPAN_UTILS_H__ */ diff --git a/src/ppp/meson.build b/src/ppp/meson.build index 20ed64ed..a0f2df50 100644 --- a/src/ppp/meson.build +++ b/src/ppp/meson.build @@ -2,7 +2,7 @@ name = 'nm-pppd-plugin' deps = [ dl_dep, - nm_core_dep + nm_core_dep, ] nm_pppd_plugin = shared_module( @@ -16,13 +16,13 @@ nm_pppd_plugin = shared_module( '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_GLIB', ], install: true, - install_dir: pppd_plugin_dir + install_dir: pppd_plugin_dir, ) name = 'nm-ppp-plugin' deps = [ - nm_dep + nm_dep, ] linker_script = join_paths(meson.current_source_dir(), 'nm-ppp-plugin.ver') @@ -36,5 +36,5 @@ core_plugins += shared_module( ], link_depends: linker_script, install: true, - install_dir: nm_plugindir + install_dir: nm_plugindir, ) diff --git a/src/ppp/nm-ppp-manager-call.c b/src/ppp/nm-ppp-manager-call.c index 3d6fee49..8f658416 100644 --- a/src/ppp/nm-ppp-manager-call.c +++ b/src/ppp/nm-ppp-manager-call.c @@ -126,12 +126,13 @@ nm_ppp_manager_start (NMPPPManager *self, NMPPPManagerStopHandle * nm_ppp_manager_stop (NMPPPManager *self, + GCancellable *cancellable, NMPPPManagerStopCallback callback, gpointer user_data) { g_return_val_if_fail (ppp_ops, NULL); - return ppp_ops->stop (self, callback, user_data); + return ppp_ops->stop (self, cancellable, callback, user_data); } void diff --git a/src/ppp/nm-ppp-manager-call.h b/src/ppp/nm-ppp-manager-call.h index daf8a82e..a93ed11f 100644 --- a/src/ppp/nm-ppp-manager-call.h +++ b/src/ppp/nm-ppp-manager-call.h @@ -40,6 +40,7 @@ gboolean nm_ppp_manager_start (NMPPPManager *self, GError **error); NMPPPManagerStopHandle *nm_ppp_manager_stop (NMPPPManager *self, + GCancellable *cancellable, NMPPPManagerStopCallback callback, gpointer user_data); diff --git a/src/ppp/nm-ppp-manager.c b/src/ppp/nm-ppp-manager.c index b231ff20..d8fdbab2 100644 --- a/src/ppp/nm-ppp-manager.c +++ b/src/ppp/nm-ppp-manager.c @@ -137,9 +137,12 @@ G_DEFINE_TYPE (NMPPPManager, nm_ppp_manager, NM_TYPE_DBUS_OBJECT) static void _ppp_cleanup (NMPPPManager *self); static NMPPPManagerStopHandle *_ppp_manager_stop (NMPPPManager *self, + GCancellable *cancellable, NMPPPManagerStopCallback callback, gpointer user_data); +static void _ppp_manager_stop_cancel (NMPPPManagerStopHandle *handle); + /*****************************************************************************/ static void @@ -672,61 +675,6 @@ out: /*****************************************************************************/ -typedef struct { - GPtrArray *array; - GStringChunk *chunk; -} NMCmdLine; - -static NMCmdLine * -nm_cmd_line_new (void) -{ - NMCmdLine *cmd; - - cmd = g_slice_new (NMCmdLine); - cmd->array = g_ptr_array_new (); - cmd->chunk = g_string_chunk_new (1024); - - return cmd; -} - -static void -nm_cmd_line_destroy (NMCmdLine *cmd) -{ - g_ptr_array_free (cmd->array, TRUE); - g_string_chunk_free (cmd->chunk); - g_slice_free (NMCmdLine, cmd); -} - -static char * -nm_cmd_line_to_str (NMCmdLine *cmd) -{ - char *str; - - g_ptr_array_add (cmd->array, NULL); - str = g_strjoinv (" ", (char **) cmd->array->pdata); - g_ptr_array_remove_index (cmd->array, cmd->array->len - 1); - - return str; -} - -static void -nm_cmd_line_add_string (NMCmdLine *cmd, const char *str) -{ - g_ptr_array_add (cmd->array, g_string_chunk_insert (cmd->chunk, str)); -} - -static void -nm_cmd_line_add_int (NMCmdLine *cmd, int i) -{ - char *str; - - str = g_strdup_printf ("%d", i); - nm_cmd_line_add_string (cmd, str); - g_free (str); -} - -/*****************************************************************************/ - NM_UTILS_LOOKUP_STR_DEFINE_STATIC (pppd_exit_code_to_str, int, NM_UTILS_LOOKUP_DEFAULT ("Unknown error"), NM_UTILS_LOOKUP_STR_ITEM ( 1, "Fatal pppd error"); @@ -791,14 +739,14 @@ pppd_timed_out (gpointer data) NMPPPManager *self = NM_PPP_MANAGER (data); _LOGW ("pppd timed out or didn't initialize our dbus module"); - _ppp_manager_stop (self, NULL, NULL); + _ppp_manager_stop (self, NULL, NULL, NULL); g_signal_emit (self, signals[STATE_CHANGED], 0, (guint) NM_PPP_STATUS_DEAD); return FALSE; } -static NMCmdLine * +static GPtrArray * create_pppd_cmd_line (NMPPPManager *self, NMSettingPpp *setting, NMSettingPppoe *pppoe, @@ -811,9 +759,8 @@ create_pppd_cmd_line (NMPPPManager *self, { NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (self); const char *pppd_binary = NULL; - NMCmdLine *cmd; + gs_unref_ptrarray GPtrArray *cmd = NULL; gboolean ppp_debug; - static int unit; g_return_val_if_fail (setting != NULL, NULL); @@ -833,53 +780,50 @@ create_pppd_cmd_line (NMPPPManager *self, return NULL; } - /* Create pppd command line */ - cmd = nm_cmd_line_new (); - nm_cmd_line_add_string (cmd, pppd_binary); + cmd = g_ptr_array_new_with_free_func (g_free); + + nm_strv_ptrarray_add_string_dup (cmd, pppd_binary); - nm_cmd_line_add_string (cmd, "nodetach"); - nm_cmd_line_add_string (cmd, "lock"); + nm_strv_ptrarray_add_string_dup (cmd, "nodetach"); + nm_strv_ptrarray_add_string_dup (cmd, "lock"); /* NM handles setting the default route */ - nm_cmd_line_add_string (cmd, "nodefaultroute"); + nm_strv_ptrarray_add_string_dup (cmd, "nodefaultroute"); if (!ip4_enabled) - nm_cmd_line_add_string (cmd, "noip"); + nm_strv_ptrarray_add_string_dup (cmd, "noip"); if (ip6_enabled) { /* Allow IPv6 to be configured by IPV6CP */ - nm_cmd_line_add_string (cmd, "ipv6"); - nm_cmd_line_add_string (cmd, ","); + nm_strv_ptrarray_add_string_dup (cmd, "ipv6"); + nm_strv_ptrarray_add_string_dup (cmd, ","); } else - nm_cmd_line_add_string (cmd, "noipv6"); + nm_strv_ptrarray_add_string_dup (cmd, "noipv6"); ppp_debug = !!getenv ("NM_PPP_DEBUG"); if (nm_logging_enabled (LOGL_DEBUG, LOGD_PPP)) ppp_debug = TRUE; if (ppp_debug) - nm_cmd_line_add_string (cmd, "debug"); + nm_strv_ptrarray_add_string_dup (cmd, "debug"); if (ppp_name) { - nm_cmd_line_add_string (cmd, "user"); - nm_cmd_line_add_string (cmd, ppp_name); + nm_strv_ptrarray_add_string_dup (cmd, "user"); + nm_strv_ptrarray_add_string_dup (cmd, ppp_name); } if (pppoe) { - char *dev_str; const char *pppoe_service; - nm_cmd_line_add_string (cmd, "plugin"); - nm_cmd_line_add_string (cmd, "rp-pppoe.so"); + nm_strv_ptrarray_add_string_dup (cmd, "plugin"); + nm_strv_ptrarray_add_string_dup (cmd, "rp-pppoe.so"); - dev_str = g_strdup_printf ("nic-%s", priv->parent_iface); - nm_cmd_line_add_string (cmd, dev_str); - g_free (dev_str); + nm_strv_ptrarray_add_string_concat (cmd, "nic-", priv->parent_iface); pppoe_service = nm_setting_pppoe_get_service (pppoe); if (pppoe_service) { - nm_cmd_line_add_string (cmd, "rp_pppoe_service"); - nm_cmd_line_add_string (cmd, pppoe_service); + nm_strv_ptrarray_add_string_dup (cmd, "rp_pppoe_service"); + nm_strv_ptrarray_add_string_dup (cmd, pppoe_service); } } else if (adsl) { const char *protocol = nm_setting_adsl_get_protocol (adsl); @@ -888,110 +832,110 @@ create_pppd_cmd_line (NMPPPManager *self, guint32 vpi = nm_setting_adsl_get_vpi (adsl); guint32 vci = nm_setting_adsl_get_vci (adsl); const char *encaps = nm_setting_adsl_get_encapsulation (adsl); - char *vpivci; - nm_cmd_line_add_string (cmd, "plugin"); - nm_cmd_line_add_string (cmd, "pppoatm.so"); + nm_strv_ptrarray_add_string_dup (cmd, "plugin"); + nm_strv_ptrarray_add_string_dup (cmd, "pppoatm.so"); - vpivci = g_strdup_printf("%d.%d", vpi, vci); - nm_cmd_line_add_string (cmd, vpivci); - g_free (vpivci); + nm_strv_ptrarray_add_string_printf (cmd, "%d.%d", vpi, vci); if (g_strcmp0 (encaps, NM_SETTING_ADSL_ENCAPSULATION_LLC) == 0) - nm_cmd_line_add_string (cmd, "llc-encaps"); + nm_strv_ptrarray_add_string_dup (cmd, "llc-encaps"); else /*if (g_strcmp0 (encaps, NM_SETTING_ADSL_ENCAPSULATION_VCMUX) == 0)*/ - nm_cmd_line_add_string (cmd, "vc-encaps"); + nm_strv_ptrarray_add_string_dup (cmd, "vc-encaps"); } else if (!strcmp (protocol, NM_SETTING_ADSL_PROTOCOL_PPPOE)) { - nm_cmd_line_add_string (cmd, "plugin"); - nm_cmd_line_add_string (cmd, "rp-pppoe.so"); - nm_cmd_line_add_string (cmd, priv->parent_iface); + nm_strv_ptrarray_add_string_dup (cmd, "plugin"); + nm_strv_ptrarray_add_string_dup (cmd, "rp-pppoe.so"); + nm_strv_ptrarray_add_string_dup (cmd, priv->parent_iface); } - nm_cmd_line_add_string (cmd, "noipdefault"); + nm_strv_ptrarray_add_string_dup (cmd, "noipdefault"); } else { - nm_cmd_line_add_string (cmd, priv->parent_iface); + nm_strv_ptrarray_add_string_dup (cmd, priv->parent_iface); /* Don't send some random address as the local address */ - nm_cmd_line_add_string (cmd, "noipdefault"); + nm_strv_ptrarray_add_string_dup (cmd, "noipdefault"); } if (nm_setting_ppp_get_baud (setting)) - nm_cmd_line_add_int (cmd, nm_setting_ppp_get_baud (setting)); + nm_strv_ptrarray_add_int (cmd, nm_setting_ppp_get_baud (setting)); else if (baud_override) - nm_cmd_line_add_int (cmd, (int) baud_override); + nm_strv_ptrarray_add_int (cmd, baud_override); /* noauth by default, because we certainly don't have any information * with which to verify anything the peer gives us if we ask it to * authenticate itself, which is what 'auth' really means. */ - nm_cmd_line_add_string (cmd, "noauth"); + nm_strv_ptrarray_add_string_dup (cmd, "noauth"); if (nm_setting_ppp_get_refuse_eap (setting)) - nm_cmd_line_add_string (cmd, "refuse-eap"); + nm_strv_ptrarray_add_string_dup (cmd, "refuse-eap"); if (nm_setting_ppp_get_refuse_pap (setting)) - nm_cmd_line_add_string (cmd, "refuse-pap"); + nm_strv_ptrarray_add_string_dup (cmd, "refuse-pap"); if (nm_setting_ppp_get_refuse_chap (setting)) - nm_cmd_line_add_string (cmd, "refuse-chap"); + nm_strv_ptrarray_add_string_dup (cmd, "refuse-chap"); if (nm_setting_ppp_get_refuse_mschap (setting)) - nm_cmd_line_add_string (cmd, "refuse-mschap"); + nm_strv_ptrarray_add_string_dup (cmd, "refuse-mschap"); if (nm_setting_ppp_get_refuse_mschapv2 (setting)) - nm_cmd_line_add_string (cmd, "refuse-mschap-v2"); + nm_strv_ptrarray_add_string_dup (cmd, "refuse-mschap-v2"); if (nm_setting_ppp_get_nobsdcomp (setting)) - nm_cmd_line_add_string (cmd, "nobsdcomp"); + nm_strv_ptrarray_add_string_dup (cmd, "nobsdcomp"); if (nm_setting_ppp_get_no_vj_comp (setting)) - nm_cmd_line_add_string (cmd, "novj"); + nm_strv_ptrarray_add_string_dup (cmd, "novj"); if (nm_setting_ppp_get_nodeflate (setting)) - nm_cmd_line_add_string (cmd, "nodeflate"); + nm_strv_ptrarray_add_string_dup (cmd, "nodeflate"); if (nm_setting_ppp_get_require_mppe (setting)) - nm_cmd_line_add_string (cmd, "require-mppe"); + nm_strv_ptrarray_add_string_dup (cmd, "require-mppe"); if (nm_setting_ppp_get_require_mppe_128 (setting)) - nm_cmd_line_add_string (cmd, "require-mppe-128"); + nm_strv_ptrarray_add_string_dup (cmd, "require-mppe-128"); if (nm_setting_ppp_get_mppe_stateful (setting)) - nm_cmd_line_add_string (cmd, "mppe-stateful"); + nm_strv_ptrarray_add_string_dup (cmd, "mppe-stateful"); if (nm_setting_ppp_get_crtscts (setting)) - nm_cmd_line_add_string (cmd, "crtscts"); + nm_strv_ptrarray_add_string_dup (cmd, "crtscts"); /* Always ask for DNS, we don't have to use them if the connection * overrides the returned servers. */ - nm_cmd_line_add_string (cmd, "usepeerdns"); + nm_strv_ptrarray_add_string_dup (cmd, "usepeerdns"); if (nm_setting_ppp_get_mru (setting)) { - nm_cmd_line_add_string (cmd, "mru"); - nm_cmd_line_add_int (cmd, nm_setting_ppp_get_mru (setting)); + nm_strv_ptrarray_add_string_dup (cmd, "mru"); + nm_strv_ptrarray_add_int (cmd, nm_setting_ppp_get_mru (setting)); } if (nm_setting_ppp_get_mtu (setting)) { - nm_cmd_line_add_string (cmd, "mtu"); - nm_cmd_line_add_int (cmd, nm_setting_ppp_get_mtu (setting)); + nm_strv_ptrarray_add_string_dup (cmd, "mtu"); + nm_strv_ptrarray_add_int (cmd, nm_setting_ppp_get_mtu (setting)); } - nm_cmd_line_add_string (cmd, "lcp-echo-failure"); - nm_cmd_line_add_int (cmd, nm_setting_ppp_get_lcp_echo_failure (setting)); + nm_strv_ptrarray_add_string_dup (cmd, "lcp-echo-failure"); + nm_strv_ptrarray_add_int (cmd, nm_setting_ppp_get_lcp_echo_failure (setting)); - nm_cmd_line_add_string (cmd, "lcp-echo-interval"); - nm_cmd_line_add_int (cmd, nm_setting_ppp_get_lcp_echo_interval (setting)); + nm_strv_ptrarray_add_string_dup (cmd, "lcp-echo-interval"); + nm_strv_ptrarray_add_int (cmd, nm_setting_ppp_get_lcp_echo_interval (setting)); /* Avoid pppd to exit if no traffic going through */ - nm_cmd_line_add_string (cmd, "idle"); - nm_cmd_line_add_int (cmd, 0); + nm_strv_ptrarray_add_string_dup (cmd, "idle"); + nm_strv_ptrarray_add_string_dup (cmd, "0"); - nm_cmd_line_add_string (cmd, "ipparam"); - nm_cmd_line_add_string (cmd, nm_dbus_object_get_path (NM_DBUS_OBJECT (self))); + nm_strv_ptrarray_add_string_dup (cmd, "ipparam"); + nm_strv_ptrarray_add_string_dup (cmd, nm_dbus_object_get_path (NM_DBUS_OBJECT (self))); - nm_cmd_line_add_string (cmd, "plugin"); - nm_cmd_line_add_string (cmd, NM_PPPD_PLUGIN); + nm_strv_ptrarray_add_string_dup (cmd, "plugin"); + nm_strv_ptrarray_add_string_dup (cmd, NM_PPPD_PLUGIN); if (pppoe && nm_setting_pppoe_get_parent (pppoe)) { + static int unit; + /* The PPP interface is going to be renamed, so pass a * different unit each time so that activations don't * race with each others. */ - nm_cmd_line_add_string (cmd, "unit"); - nm_cmd_line_add_int (cmd, unit); + nm_strv_ptrarray_add_string_dup (cmd, "unit"); + nm_strv_ptrarray_add_int (cmd, unit); unit = unit < G_MAXINT ? unit + 1 : 0; } - return cmd; + g_ptr_array_add (cmd, NULL); + return g_steal_pointer (&cmd); } static void @@ -1035,8 +979,8 @@ _ppp_manager_start (NMPPPManager *self, gs_unref_object NMSettingPpp *s_ppp_free = NULL; NMSettingPppoe *pppoe_setting; NMSettingAdsl *adsl_setting; - NMCmdLine *ppp_cmd; - char *cmd_str; + gs_unref_ptrarray GPtrArray *ppp_cmd = NULL; + gs_free char *cmd_str = NULL; struct stat st; const char *ip6_method, *ip4_method; gboolean ip6_enabled = FALSE; @@ -1086,10 +1030,10 @@ _ppp_manager_start (NMPPPManager *self, adsl_setting = (NMSettingAdsl *) nm_connection_get_setting (connection, NM_TYPE_SETTING_ADSL); /* Figure out what address methods should be enabled */ - ip4_method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); - ip4_enabled = g_strcmp0 (ip4_method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0; - ip6_method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); - ip6_enabled = g_strcmp0 (ip6_method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0; + ip4_method = nm_utils_get_ip_config_method (connection, AF_INET); + ip4_enabled = nm_streq (ip4_method, NM_SETTING_IP4_CONFIG_METHOD_AUTO); + ip6_method = nm_utils_get_ip_config_method (connection, AF_INET6); + ip6_enabled = nm_streq (ip6_method, NM_SETTING_IP6_CONFIG_METHOD_AUTO); ppp_cmd = create_pppd_cmd_line (self, s_ppp, @@ -1101,23 +1045,25 @@ _ppp_manager_start (NMPPPManager *self, ip6_enabled, err); if (!ppp_cmd) - goto out; - - g_ptr_array_add (ppp_cmd->array, NULL); + goto fail; _LOGI ("starting PPP connection"); - cmd_str = nm_cmd_line_to_str (ppp_cmd); _LOGD ("command line: %s", cmd_str); - g_free (cmd_str); + (cmd_str = g_strjoinv (" ", (char **) ppp_cmd->pdata)); priv->pid = 0; - if (!g_spawn_async (NULL, (char **) ppp_cmd->array->pdata, NULL, + if (!g_spawn_async (NULL, + (char **) ppp_cmd->pdata, + NULL, G_SPAWN_DO_NOT_REAP_CHILD, - nm_utils_setpgid, NULL, - &priv->pid, err)) { - goto out; - } + nm_utils_setpgid, + NULL, + &priv->pid, + err)) + goto fail; + + nm_assert (priv->pid > 0); _LOGI ("pppd started with pid %lld", (long long) priv->pid); @@ -1125,14 +1071,10 @@ _ppp_manager_start (NMPPPManager *self, priv->ppp_timeout_handler = g_timeout_add_seconds (timeout_secs, pppd_timed_out, self); priv->act_req = g_object_ref (req); -out: - if (ppp_cmd) - nm_cmd_line_destroy (ppp_cmd); - - if (priv->pid <= 0) - nm_dbus_object_unexport (NM_DBUS_OBJECT (self)); - - return priv->pid > 0; + return TRUE; +fail: + nm_dbus_object_unexport (NM_DBUS_OBJECT (self)); + return FALSE; } static void @@ -1170,6 +1112,10 @@ struct _NMPPPManagerStopHandle { * pppd process terminated. */ GObject *shutdown_waitobj; + GCancellable *cancellable; + + gulong cancellable_id; + guint idle_id; }; @@ -1179,6 +1125,13 @@ _stop_handle_complete (NMPPPManagerStopHandle *handle, gboolean was_cancelled) gs_unref_object NMPPPManager *self = NULL; NMPPPManagerStopCallback callback; + if (handle->cancellable_id) { + g_cancellable_disconnect (handle->cancellable, + nm_steal_int (&handle->cancellable_id)); + } + + g_clear_object (&handle->cancellable); + self = g_steal_pointer (&handle->self); if (!self) return; @@ -1219,8 +1172,20 @@ _stop_idle_cb (gpointer user_data) return G_SOURCE_REMOVE; } +static void +_stop_cancelled_cb (GCancellable *cancellable, + gpointer user_data) +{ + NMPPPManagerStopHandle *handle = user_data; + + nm_clear_g_signal_handler (handle->cancellable, + &handle->cancellable_id); + _ppp_manager_stop_cancel (handle); +} + static NMPPPManagerStopHandle * _ppp_manager_stop (NMPPPManager *self, + GCancellable *cancellable, NMPPPManagerStopCallback callback, gpointer user_data) { @@ -1246,6 +1211,13 @@ _ppp_manager_stop (NMPPPManager *self, handle->self = g_object_ref (self); handle->callback = callback; handle->user_data = user_data; + if (cancellable) { + handle->cancellable = g_object_ref (cancellable); + handle->cancellable_id = g_cancellable_connect (cancellable, + G_CALLBACK (_stop_cancelled_cb), + handle, + NULL); + } if (!priv->pid) { /* No PID. There is nothing to kill, however, invoke the callback in @@ -1272,6 +1244,8 @@ _ppp_manager_stop (NMPPPManager *self, return handle; } +/*****************************************************************************/ + static void _ppp_manager_stop_cancel (NMPPPManagerStopHandle *handle) { @@ -1360,7 +1334,7 @@ dispose (GObject *object) * still stop. */ g_warn_if_fail (!priv->pid); g_warn_if_fail (!nm_dbus_object_is_exported (NM_DBUS_OBJECT (self))); - _ppp_manager_stop (self, NULL, NULL); + _ppp_manager_stop (self, NULL, NULL, NULL); g_clear_object (&priv->act_req); diff --git a/src/ppp/nm-ppp-plugin-api.h b/src/ppp/nm-ppp-plugin-api.h index 558de2c2..95ddd211 100644 --- a/src/ppp/nm-ppp-plugin-api.h +++ b/src/ppp/nm-ppp-plugin-api.h @@ -40,6 +40,7 @@ typedef const struct { GError **err); NMPPPManagerStopHandle *(*stop) (NMPPPManager *manager, + GCancellable *cancellable, NMPPPManagerStopCallback callback, gpointer user_data); diff --git a/src/ppp/nm-pppd-plugin.c b/src/ppp/nm-pppd-plugin.c index 09196340..5e99be41 100644 --- a/src/ppp/nm-pppd-plugin.c +++ b/src/ppp/nm-pppd-plugin.c @@ -132,7 +132,7 @@ nm_phasechange (void *data, int arg) NULL, NULL); } - if (ppp_status == PHASE_RUNNING) { + if (ppp_status == NM_PPP_STATUS_RUNNING) { index = if_nametoindex (ifname); /* Make a sync call to ensure that when the call * terminates the interface already has its final diff --git a/src/settings/nm-agent-manager.c b/src/settings/nm-agent-manager.c index 953d3ce4..edadee14 100644 --- a/src/settings/nm-agent-manager.c +++ b/src/settings/nm-agent-manager.c @@ -1059,49 +1059,6 @@ _con_get_request_start_validated (NMAuthChain *chain, } static void -has_system_secrets_check (NMSetting *setting, - const char *key, - const GValue *value, - GParamFlags flags, - gpointer user_data) -{ - NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE; - gboolean *has_system = user_data; - - if (!(flags & NM_SETTING_PARAM_SECRET)) - return; - - /* Clear out system-owned or always-ask secrets */ - if (NM_IS_SETTING_VPN (setting) && !strcmp (key, NM_SETTING_VPN_SECRETS)) { - GHashTableIter iter; - const char *secret_name = NULL; - - /* VPNs are special; need to handle each secret separately */ - g_hash_table_iter_init (&iter, (GHashTable *) g_value_get_boxed (value)); - while (g_hash_table_iter_next (&iter, (gpointer *) &secret_name, NULL)) { - secret_flags = NM_SETTING_SECRET_FLAG_NONE; - nm_setting_get_secret_flags (setting, secret_name, &secret_flags, NULL); - if (secret_flags == NM_SETTING_SECRET_FLAG_NONE) - *has_system = TRUE; - } - } else { - if (!nm_setting_get_secret_flags (setting, key, &secret_flags, NULL)) - g_return_if_reached (); - if (secret_flags == NM_SETTING_SECRET_FLAG_NONE) - *has_system = TRUE; - } -} - -static gboolean -has_system_secrets (NMConnection *connection) -{ - gboolean has_system = FALSE; - - nm_connection_for_each_setting_value (connection, has_system_secrets_check, &has_system); - return has_system; -} - -static void _con_get_request_start (Request *req) { NMAgentManager *self; @@ -1121,7 +1078,8 @@ _con_get_request_start (Request *req) * unprivileged users. */ if ( (req->con.get.flags != NM_SECRET_AGENT_GET_SECRETS_FLAG_NONE) - && (req->con.get.existing_secrets || has_system_secrets (req->con.connection))) { + && ( req->con.get.existing_secrets + || _nm_connection_aggregate (req->con.connection, NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS, NULL))) { _LOGD (NULL, "("LOG_REQ_FMT") request has system secrets; checking agent %s for MODIFY", LOG_REQ_ARG (req), agent_dbus_owner); diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c index 4c25d0e9..0beb5ea7 100644 --- a/src/settings/nm-settings-connection.c +++ b/src/settings/nm-settings-connection.c @@ -640,7 +640,9 @@ nm_settings_connection_update (NMSettingsConnection *self, gboolean replaced = FALSE; gs_free char *logmsg_change = NULL; GError *local = NULL; + gs_unref_object NMConnection *simple = NULL; gs_unref_variant GVariant *con_agent_secrets = NULL; + gs_unref_variant GVariant *new_agent_secrets = NULL; g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE); @@ -681,6 +683,16 @@ nm_settings_connection_update (NMSettingsConnection *self, replace_connection = reread_connection ?: new_connection; + /* Save agent-owned secrets from the new connection for later use */ + if (new_connection) { + simple = nm_simple_connection_new_clone (new_connection); + nm_connection_clear_secrets_with_flags (simple, + secrets_filter_cb, + GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED)); + new_agent_secrets = nm_connection_to_dbus (simple, NM_CONNECTION_SERIALIZE_ONLY_SECRETS); + g_clear_object (&simple); + } + /* Disconnect the changed signal to ensure we don't set Unsaved when * it's not required. */ @@ -691,7 +703,6 @@ nm_settings_connection_update (NMSettingsConnection *self, && !nm_connection_compare (nm_settings_connection_get_connection (self), replace_connection, NM_SETTING_COMPARE_FLAG_EXACT)) { - gs_unref_object NMConnection *simple = NULL; if (log_diff_name) { nm_utils_log_connection_diff (replace_connection, nm_settings_connection_get_connection (self), LOGL_DEBUG, LOGD_CORE, log_diff_name, "++ ", @@ -738,6 +749,15 @@ nm_settings_connection_update (NMSettingsConnection *self, (void) nm_connection_update_secrets (nm_settings_connection_get_connection (self), NULL, con_agent_secrets, NULL); } + /* Apply agent-owned secrets from the new connection so that + * they can be sent to agents */ + if (new_agent_secrets) { + (void) nm_connection_update_secrets (nm_settings_connection_get_connection (self), + NULL, + new_agent_secrets, + NULL); + } + nm_settings_connection_recheck_visibility (self); if ( replaced @@ -770,7 +790,7 @@ out: else if (new_connection) _LOGI ("write: successfully updated (%s)", logmsg_change); else - _LOGI ("write: successfully commited (%s)", logmsg_change); + _LOGI ("write: successfully committed (%s)", logmsg_change); } return TRUE; } @@ -1318,7 +1338,7 @@ nm_settings_connection_get_secrets (NMSettingsConnection *self, /* we remember the current version-id of the secret-agents. The version-id is strictly increasing, * as new agents register the number. We know hence, that this request was made against a certain * set of secret-agents. - * If after making this request a new secret-agent registeres, the version-id increases. + * If after making this request a new secret-agent registers, the version-id increases. * Then we know that the this request probably did not yet include the latest secret-agent. */ priv->last_secret_agent_version_id = nm_agent_manager_get_agent_version_id (priv->agent_mgr); @@ -1640,38 +1660,6 @@ typedef struct { } UpdateInfo; static void -has_some_secrets_cb (NMSetting *setting, - const char *key, - const GValue *value, - GParamFlags flags, - gpointer user_data) -{ - GParamSpec *pspec; - - if (NM_IS_SETTING_VPN (setting)) { - if (nm_setting_vpn_get_num_secrets (NM_SETTING_VPN(setting))) - *((gboolean *) user_data) = TRUE; - return; - } - - pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (setting)), key); - if (pspec) { - if ( (flags & NM_SETTING_PARAM_SECRET) - && !g_param_value_defaults (pspec, (GValue *)value)) - *((gboolean *) user_data) = TRUE; - } -} - -static gboolean -any_secrets_present (NMConnection *self) -{ - gboolean has_secrets = FALSE; - - nm_connection_for_each_setting_value (self, has_some_secrets_cb, &has_secrets); - return has_secrets; -} - -static void cached_secrets_to_connection (NMSettingsConnection *self, NMConnection *connection) { NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); @@ -1738,7 +1726,7 @@ update_auth_cb (NMSettingsConnection *self, } if (info->new_settings) { - if (!any_secrets_present (info->new_settings)) { + if (!_nm_connection_aggregate (info->new_settings, NM_CONNECTION_AGGREGATE_ANY_SECRETS, NULL)) { /* If the new connection has no secrets, we do not want to remove all * secrets, rather we keep all the existing ones. Do that by merging * them in to the new connection. diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c index 2253d56a..74de6cc2 100644 --- a/src/settings/nm-settings.c +++ b/src/settings/nm-settings.c @@ -129,6 +129,8 @@ typedef struct { NMHostnameManager *hostname_manager; + NMSettingsConnection *startup_complete_blocked_by; + guint connections_len; bool started:1; @@ -182,19 +184,23 @@ static void check_startup_complete (NMSettings *self) { NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - NMSettingsConnection *conn; + NMSettingsConnection *sett_conn; if (priv->startup_complete) return; - c_list_for_each_entry (conn, &priv->connections_lst_head, _connections_lst) { - if (!nm_settings_connection_get_ready (conn)) + c_list_for_each_entry (sett_conn, &priv->connections_lst_head, _connections_lst) { + if (!nm_settings_connection_get_ready (sett_conn)) { + nm_g_object_ref_set (&priv->startup_complete_blocked_by, sett_conn); return; + } } + g_clear_object (&priv->startup_complete_blocked_by); + /* the connection_ready_changed signal handler is no longer needed. */ - c_list_for_each_entry (conn, &priv->connections_lst_head, _connections_lst) - g_signal_handlers_disconnect_by_func (conn, G_CALLBACK (connection_ready_changed), self); + c_list_for_each_entry (sett_conn, &priv->connections_lst_head, _connections_lst) + g_signal_handlers_disconnect_by_func (sett_conn, G_CALLBACK (connection_ready_changed), self); priv->startup_complete = TRUE; _notify (self, PROP_STARTUP_COMPLETE); @@ -372,7 +378,7 @@ _clear_connections_cached_list (NMSettingsPrivate *priv) * @out_len: (out): (allow-none): returns the number of returned * connections. * - * Returns: (transfer-none): a list of NMSettingsConnections. The list is + * Returns: (transfer none): a list of NMSettingsConnections. The list is * unsorted and NULL terminated. The result is never %NULL, in case of no * connections, it returns an empty list. * The returned list is cached internally, only valid until the next @@ -840,10 +846,10 @@ connection_removed (NMSettingsConnection *connection, gpointer user_data) if (priv->connections_loaded) g_signal_emit (self, signals[CONNECTION_REMOVED], 0, connection); - g_object_unref (connection); - check_startup_complete (self); + g_object_unref (connection); + g_object_unref (self); /* Balanced by a ref in claim_connection() */ } @@ -1754,12 +1760,17 @@ nm_settings_device_removed (NMSettings *self, NMDevice *device, gboolean quittin /*****************************************************************************/ -gboolean -nm_settings_get_startup_complete (NMSettings *self) +const char * +nm_settings_get_startup_complete_blocked_reason (NMSettings *self) { NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + const char *uuid = NULL; - return priv->startup_complete; + if (priv->startup_complete) + return NULL; + if (priv->startup_complete_blocked_by) + uuid = nm_settings_connection_get_uuid (priv->startup_complete_blocked_by); + return uuid ?: "unknown"; } /*****************************************************************************/ @@ -1845,7 +1856,7 @@ get_property (GObject *object, guint prop_id, g_value_set_boxed (value, NULL); break; case PROP_STARTUP_COMPLETE: - g_value_set_boolean (value, nm_settings_get_startup_complete (self)); + g_value_set_boolean (value, !nm_settings_get_startup_complete_blocked_reason (self)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -1878,6 +1889,8 @@ dispose (GObject *object) NMSettings *self = NM_SETTINGS (object); NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + g_clear_object (&priv->startup_complete_blocked_by); + g_slist_free_full (priv->auths, (GDestroyNotify) nm_auth_chain_destroy); priv->auths = NULL; diff --git a/src/settings/nm-settings.h b/src/settings/nm-settings.h index 38d8ad4e..eb74c09c 100644 --- a/src/settings/nm-settings.h +++ b/src/settings/nm-settings.h @@ -112,6 +112,6 @@ void nm_settings_device_added (NMSettings *self, NMDevice *device); void nm_settings_device_removed (NMSettings *self, NMDevice *device, gboolean quitting); -gboolean nm_settings_get_startup_complete (NMSettings *self); +const char *nm_settings_get_startup_complete_blocked_reason (NMSettings *self); #endif /* __NM_SETTINGS_H__ */ diff --git a/src/settings/plugins/ibft/meson.build b/src/settings/plugins/ibft/meson.build index c7dbe459..c33b24cc 100644 --- a/src/settings/plugins/ibft/meson.build +++ b/src/settings/plugins/ibft/meson.build @@ -8,7 +8,7 @@ libnms_ibft_core = static_library( sources = files( 'nms-ibft-connection.c', - 'nms-ibft-plugin.c' + 'nms-ibft-plugin.c', ) libnm_settings_plugin_ibft = shared_module( @@ -19,7 +19,7 @@ libnm_settings_plugin_ibft = shared_module( link_args: ldflags_linker_script_settings, link_depends: linker_script_settings, install: true, - install_dir: nm_plugindir + install_dir: nm_plugindir, ) core_plugins += libnm_settings_plugin_ibft @@ -29,7 +29,7 @@ core_plugins += libnm_settings_plugin_ibft run_target( 'check-local-symbols-settings-ibft', command: [check_so_symbols, libnm_settings_plugin_ibft.full_path()], - depends: libnm_settings_plugin_ibft + depends: libnm_settings_plugin_ibft, ) check-local-symbols-settings-ibft: src/settings/plugins/ibft/libnm-settings-plugin-ibft.la diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns1 b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns1 new file mode 100755 index 00000000..54f02da6 --- /dev/null +++ b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns1 @@ -0,0 +1,21 @@ +#!/bin/bash + +cat << EOF +# BEGIN RECORD +iface.initiatorname = iqn.pjones6 +iface.hwaddress = 00:33:21:98:b9:f0 +iface.bootproto = STATIC +iface.ipaddress = 192.168.32.72 +iface.subnet_mask = 255.255.252.0 +iface.gateway = 192.168.35.254 +iface.primary_dns = 10000.500.250.1 +iface.secondary_dns = 10.16.255.3 +iface.vlan_id = 0 +iface.net_ifacename = eth0 +node.name = iqn.0.2008-11.com.blahblah:iscsi0 +node.conn[0].address = 10.16.52.16 +node.conn[0].port = 3260 +node.boot_lun = 00000000 +# END RECORD +EOF + diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns2 b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns2 new file mode 100755 index 00000000..ebd7a9ca --- /dev/null +++ b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns2 @@ -0,0 +1,21 @@ +#!/bin/bash + +cat << EOF +# BEGIN RECORD +iface.initiatorname = iqn.pjones6 +iface.hwaddress = 00:33:21:98:b9:f0 +iface.bootproto = STATIC +iface.ipaddress = 192.168.32.72 +iface.subnet_mask = 255.255.252.0 +iface.gateway = 192.168.35.254 +iface.primary_dns = 10.16.255.2 +iface.secondary_dns = blah.foo.bar.baz +iface.vlan_id = 0 +iface.net_ifacename = eth0 +node.name = iqn.0.2008-11.com.blahblah:iscsi0 +node.conn[0].address = 10.16.52.16 +node.conn[0].port = 3260 +node.boot_lun = 00000000 +# END RECORD +EOF + diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-entry b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-entry new file mode 100755 index 00000000..4e326048 --- /dev/null +++ b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-entry @@ -0,0 +1,20 @@ +#!/bin/bash + +cat << EOF +# BEGIN RECORD +iface.initiatorname = iqn.pjones6 +iface.hwaddress = 00:33:21:98:b9:f0 +iface.bootproto = STATIC +iface.ipaddress 192.168.32.72 +iface.subnet_mask = 255.255.252.0 +iface.gateway = 192.168.35.254 +iface.primary_dns = 10.16.255.2 +iface.secondary_dns = 10.16.255.3 +iface.vlan_id = 0 +iface.net_ifacename = eth0 +node.name = iqn.0.2008-11.com.blahblah:iscsi0 +node.conn[0].address = 10.16.52.16 +node.conn[0].port = 3260 +node.boot_lun = 00000000 +# END RECORD + diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-gateway b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-gateway new file mode 100755 index 00000000..5390a6c3 --- /dev/null +++ b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-gateway @@ -0,0 +1,21 @@ +#!/bin/bash + +cat << EOF +# BEGIN RECORD +iface.initiatorname = iqn.pjones6 +iface.hwaddress = 00:33:21:98:b9:f0 +iface.bootproto = STATIC +iface.ipaddress = 192.168.32.72 +iface.subnet_mask = 255.255.252.0 +iface.gateway = bb.cc.dd.ee +iface.primary_dns = 10.16.255.2 +iface.secondary_dns = 10.16.255.3 +iface.vlan_id = 0 +iface.net_ifacename = eth0 +node.name = iqn.0.2008-11.com.blahblah:iscsi0 +node.conn[0].address = 10.16.52.16 +node.conn[0].port = 3260 +node.boot_lun = 00000000 +# END RECORD +EOF + diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-ipaddr b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-ipaddr new file mode 100755 index 00000000..b41cd1f1 --- /dev/null +++ b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-ipaddr @@ -0,0 +1,21 @@ +#!/bin/bash + +cat << EOF +# BEGIN RECORD +iface.initiatorname = iqn.pjones6 +iface.hwaddress = 00:33:21:98:b9:f0 +iface.bootproto = STATIC +iface.ipaddress = aa.bb.cc.dd +iface.subnet_mask = 255.255.252.0 +iface.gateway = 192.168.35.254 +iface.primary_dns = 10.16.255.2 +iface.secondary_dns = 10.16.255.3 +iface.vlan_id = 0 +iface.net_ifacename = eth0 +node.name = iqn.0.2008-11.com.blahblah:iscsi0 +node.conn[0].address = 10.16.52.16 +node.conn[0].port = 3260 +node.boot_lun = 00000000 +# END RECORD +EOF + diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-record b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-record new file mode 100755 index 00000000..22b34e6f --- /dev/null +++ b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-record @@ -0,0 +1,18 @@ +#!/bin/bash + +cat << EOF +# BEGIN RECORD +iface.initiatorname = iqn.pjones6 +iface.hwaddress = 00:33:21:98:b9:f0 +iface.bootproto = DHCP +iface.gateway = 10.16.52.254 +iface.primary_dns = 10.16.255.2 +iface.secondary_dns = 10.16.255.3 +iface.vlan_id = 0 +iface.net_ifacename = eth0 +node.name = iqn.0.2008-11.com.blahblah:iscsi0 +node.conn[0].address = 10.16.52.16 +node.conn[0].port = 3260 +node.boot_lun = 00000000 +EOF + diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-dhcp b/src/settings/plugins/ibft/tests/iscsiadm-test-dhcp new file mode 100755 index 00000000..556b0586 --- /dev/null +++ b/src/settings/plugins/ibft/tests/iscsiadm-test-dhcp @@ -0,0 +1,33 @@ +#!/bin/bash + +cat << EOF +# BEGIN RECORD +iface.initiatorname = iqn.pjones6 +iface.hwaddress = 00:33:21:98:b9:f0 +iface.bootproto = DHCP +iface.gateway = 10.16.52.254 +iface.primary_dns = 10.16.255.2 +iface.secondary_dns = 10.16.255.3 +iface.vlan_id = 0 +iface.net_ifacename = eth0 +node.name = iqn.0.2008-11.com.blahblah:iscsi0 +node.conn[0].address = 10.16.52.16 +node.conn[0].port = 3260 +node.boot_lun = 00000000 +# END RECORD +# BEGIN RECORD +iface.initiatorname = iqn.pjones6 +iface.hwaddress = 00:33:21:98:b9:f1 +iface.bootproto = DHCP +iface.gateway = 10.16.52.254 +iface.primary_dns = 10.16.255.2 +iface.secondary_dns = 10.16.255.3 +iface.vlan_id = 0 +iface.net_ifacename = eth1 +node.name = iqn.1.2008-11.com.blahblah:iscsi1 +node.conn[0].address = 10.16.52.16 +node.conn[0].port = 3260 +node.boot_lun = 00000000 +# END RECORD +EOF + diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-static b/src/settings/plugins/ibft/tests/iscsiadm-test-static new file mode 100755 index 00000000..51711480 --- /dev/null +++ b/src/settings/plugins/ibft/tests/iscsiadm-test-static @@ -0,0 +1,35 @@ +#!/bin/bash + +cat << EOF +# BEGIN RECORD +iface.initiatorname = iqn.pjones6 +iface.hwaddress = 00:33:21:98:b9:f0 +iface.bootproto = STATIC +iface.ipaddress = 192.168.32.72 +iface.subnet_mask = 255.255.252.0 +iface.gateway = 192.168.35.254 +iface.primary_dns = 10.16.255.2 +iface.secondary_dns = 10.16.255.3 +iface.vlan_id = 0 +iface.net_ifacename = eth0 +node.name = iqn.0.2008-11.com.blahblah:iscsi0 +node.conn[0].address = 10.16.52.16 +node.conn[0].port = 3260 +node.boot_lun = 00000000 +# END RECORD +# BEGIN RECORD +iface.initiatorname = iqn.pjones6 +iface.hwaddress = 00:33:21:98:b9:f1 +iface.bootproto = DHCP +iface.gateway = 10.16.52.254 +iface.primary_dns = 10.16.255.2 +iface.secondary_dns = 10.16.255.3 +iface.vlan_id = 0 +iface.net_ifacename = eth1 +node.name = iqn.1.2008-11.com.blahblah:iscsi1 +node.conn[0].address = 10.16.52.16 +node.conn[0].port = 3260 +node.boot_lun = 00000000 +# END RECORD +EOF + diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-vlan b/src/settings/plugins/ibft/tests/iscsiadm-test-vlan new file mode 100755 index 00000000..59b80bd0 --- /dev/null +++ b/src/settings/plugins/ibft/tests/iscsiadm-test-vlan @@ -0,0 +1,19 @@ +#!/bin/bash + +cat << EOF +# BEGIN RECORD 6.2.0.873-21 +iface.initiatorname = iqn.2010-04.org.ipxe:d05faa97-c4be-44f6-a723-efde9aa399a0 +iface.transport_name = tcp +iface.hwaddress = 00:33:21:98:b9:f0 +iface.bootproto = STATIC +iface.ipaddress = 192.168.6.200 +iface.subnet_mask = 255.255.255.0 +iface.vlan_id = 123 +iface.net_ifacename = eth0 +node.name = iqn.2003-01.org.x:disk1 +node.conn[0].address = 192.168.6.32 +node.conn[0].port = 3260 +node.boot_lun = 01000000 +# END RECORD +EOF + diff --git a/src/settings/plugins/ibft/tests/meson.build b/src/settings/plugins/ibft/tests/meson.build index 8b5e143a..e2f9ca7e 100644 --- a/src/settings/plugins/ibft/tests/meson.build +++ b/src/settings/plugins/ibft/tests/meson.build @@ -6,11 +6,11 @@ exe = executable( test_unit, test_unit + '.c', dependencies: test_nm_dep, - link_with: libnms_ibft_core + link_with: libnms_ibft_core, ) test( 'ibft/' + test_unit, test_script, - args: test_args + [exe.full_path()] + args: test_args + [exe.full_path()], ) diff --git a/src/settings/plugins/ifcfg-rh/meson.build b/src/settings/plugins/ifcfg-rh/meson.build index e84ae80c..9024782a 100644 --- a/src/settings/plugins/ifcfg-rh/meson.build +++ b/src/settings/plugins/ifcfg-rh/meson.build @@ -1,6 +1,6 @@ install_data( 'nm-ifcfg-rh.conf', - install_dir: dbus_conf_dir + install_dir: dbus_conf_dir, ) name = 'nmdbus-ifcfg-rh' @@ -9,7 +9,7 @@ dbus_sources = gnome.gdbus_codegen( name, 'nm-ifcfg-rh.xml', interface_prefix: 'com.redhat', - namespace: 'NMDBus' + namespace: 'NMDBus', ) libnmdbus_ifcfg_rh = static_library( @@ -23,11 +23,11 @@ core_sources = files( 'nms-ifcfg-rh-reader.c', 'nms-ifcfg-rh-utils.c', 'nms-ifcfg-rh-writer.c', - 'shvar.c' + 'shvar.c', ) deps = [ - nm_dep + nm_dep, ] libnms_ifcfg_rh_core = static_library( @@ -46,7 +46,7 @@ libnm_settings_plugin_ifcfg_rh = shared_module( link_args: ldflags_linker_script_settings, link_depends: linker_script_settings, install: true, - install_dir: nm_plugindir + install_dir: nm_plugindir, ) core_plugins += libnm_settings_plugin_ifcfg_rh @@ -56,7 +56,7 @@ core_plugins += libnm_settings_plugin_ifcfg_rh run_target( 'check-local-symbols-settings-ifcfg-rh', command: [check_so_symbols, libnm_settings_plugin_ifcfg_rh.full_path()], - depends: libnm_settings_plugin_ifcfg_rh + depends: libnm_settings_plugin_ifcfg_rh, ) check-local-symbols-settings-ifcfg-rh: src/settings/plugins/ifcfg-rh/libnm-settings-plugin-ifcfg-rh.la diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c index 6cac8cb6..05d4d738 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c @@ -539,7 +539,7 @@ read_connections (SettingsPluginIfcfg *plugin) * iterating over the files. * * To have sensible, reproducible behavior, sort the paths by last modification - * time prefering older files. + * time preferring older files. */ paths = _paths_from_connections (priv->connections); g_ptr_array_sort_with_data (filenames, (GCompareDataFunc) _sort_paths, paths); @@ -602,12 +602,9 @@ load_connection (NMSettingsPlugin *config, { SettingsPluginIfcfg *plugin = SETTINGS_PLUGIN_IFCFG (config); NMIfcfgConnection *connection; - int dir_len = strlen (IFCFG_DIR); char *ifcfg_path; - if ( strncmp (filename, IFCFG_DIR, dir_len) != 0 - || filename[dir_len] != '/' - || strchr (filename + dir_len + 1, '/') != NULL) + if (!nm_utils_file_is_in_path (filename, IFCFG_DIR)) return FALSE; /* get the real ifcfg-path. This allows us to properly @@ -987,7 +984,7 @@ config_changed_cb (NMConfig *config, * won't be offered. * * On SIGHUP and SIGUSR1 try to re-connect to D-Bus. So in the unlikely - * event that the D-Bus conneciton is broken, that allows for recovery + * event that the D-Bus connection is broken, that allows for recovery * without need for restarting NetworkManager. */ if (!NM_FLAGS_ANY (changes, NM_CONFIG_CHANGE_CAUSE_SIGHUP | NM_CONFIG_CHANGE_CAUSE_SIGUSR1)) diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c index 09a37991..6eb99d3b 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c @@ -189,7 +189,7 @@ _secret_password_raw_to_bytes (const char *ifcfg_key, password_raw += 2; secret = nm_secret_buf_new (strlen (password_raw) / 2 + 3); - if (!_nm_utils_str2bin_full (password_raw, FALSE, ":", secret->bin, secret->len, &len)) { + if (!_nm_utils_hexstr2bin_full (password_raw, FALSE, FALSE, ":", 0, secret->bin, secret->len, &len)) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "Invalid hex password in %s", ifcfg_key); @@ -670,7 +670,7 @@ read_full_ip4_address (shvarFile *ifcfg, &has_key, &a, error)) return FALSE; if (has_key) - *out_gateway = g_strdup (nm_utils_inet4_ntop (a, inet_buf)); + *out_gateway = nm_utils_inet4_ntop_dup (a); } /* Prefix */ @@ -810,7 +810,7 @@ enum { * setting, and one for initscript's handle_ip_file(), which takes the lines * and passes them to `ip route add`. The modes are similar, but certain properties * are not allowed for OPTIONS. - * The mode is differenciated by having an @options_route argument. + * The mode is differentiated by having an @options_route argument. * * Returns: returns a negative errno on failure. On success, it returns 0 * and @out_route. @@ -873,7 +873,7 @@ parse_route_line (const char *line, }; nm_assert (line); - nm_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); + nm_assert_addr_family (addr_family); nm_assert (!options_route || nm_ip_route_get_family (options_route) == addr_family); /* initscripts read the legacy route file line-by-line and @@ -1018,6 +1018,7 @@ parse_line_type_addr_with_prefix: if (info->type == PARSE_LINE_TYPE_ADDR) { if (!nm_utils_parse_inaddr_bin (addr_family, s, + NULL, &info->v.addr.addr)) { if ( info == &infos[PARSE_LINE_ATTR_ROUTE_VIA] && nm_streq (s, "(null)")) { @@ -1045,6 +1046,7 @@ parse_line_type_addr_with_prefix: prefix = 0; } else if (!nm_utils_parse_inaddr_prefix_bin (addr_family, s, + NULL, &info->v.addr.addr, &prefix)) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, @@ -1150,7 +1152,7 @@ next: : "")); break; case PARSE_LINE_TYPE_FLAG: - /* NOTE: the flag (for "onlink") only allows to explictly set "TRUE". + /* NOTE: the flag (for "onlink") only allows to explicitly set "TRUE". * There is no way to express an explicit "FALSE" setting * of this attribute, hence, the file format cannot encode * that configuration. */ @@ -1527,7 +1529,6 @@ make_ip4_setting (shvarFile *ifcfg, gboolean never_default; gint64 timeout; int priority; - char inet_buf[NM_UTILS_INET_ADDRSTRLEN]; const char *const *item; guint32 route_table; @@ -1679,7 +1680,7 @@ make_ip4_setting (shvarFile *ifcfg, PARSE_WARNING ("ignoring GATEWAY (/etc/sysconfig/network) for %s " "because the connection has no static addresses", f); } else - gateway = g_strdup (nm_utils_inet4_ntop (a, inet_buf)); + gateway = nm_utils_inet4_ntop_dup (a); } } } @@ -2226,20 +2227,19 @@ make_sriov_setting (shvarFile *ifcfg) { gs_unref_hashtable GHashTable *keys = NULL; gs_unref_ptrarray GPtrArray *vfs = NULL; - NMTernary autoprobe_drivers; + int autoprobe_drivers; NMSettingSriov *s_sriov; - int total_vfs; + gint64 total_vfs; - total_vfs = svGetValueInt64 (ifcfg, "SRIOV_TOTAL_VFS", 10, 0, G_MAXINT32, 0); - if (!total_vfs) - return NULL; + + total_vfs = svGetValueInt64 (ifcfg, "SRIOV_TOTAL_VFS", 10, 0, G_MAXUINT32, -1); autoprobe_drivers = svGetValueInt64 (ifcfg, "SRIOV_AUTOPROBE_DRIVERS", 10, - NM_TERNARY_FALSE, + NM_TERNARY_DEFAULT, NM_TERNARY_TRUE, - NM_TERNARY_DEFAULT); + -2); keys = svGetKeys (ifcfg, SV_KEY_TYPE_SRIOV_VF); if (keys) { @@ -2261,7 +2261,7 @@ make_sriov_setting (shvarFile *ifcfg) key += NM_STRLEN ("SRIOV_VF"); - vf = _nm_utils_sriov_vf_from_strparts (key, value, &error); + vf = _nm_utils_sriov_vf_from_strparts (key, value, TRUE, &error); if (!vf) { PARSE_WARNING ("ignoring invalid SR-IOV VF '%s %s': %s", key, value, error->message); @@ -2273,11 +2273,21 @@ make_sriov_setting (shvarFile *ifcfg) } } + /* Create the setting when at least one key is set */ + if ( total_vfs < 0 + && !vfs + && autoprobe_drivers < NM_TERNARY_DEFAULT) + return NULL; + s_sriov = (NMSettingSriov *) nm_setting_sriov_new (); + + autoprobe_drivers = NM_MAX (autoprobe_drivers, NM_TERNARY_DEFAULT); + total_vfs = NM_MAX (total_vfs, 0); + g_object_set (s_sriov, - NM_SETTING_SRIOV_TOTAL_VFS, total_vfs, + NM_SETTING_SRIOV_TOTAL_VFS, (guint) total_vfs, NM_SETTING_SRIOV_VFS, vfs, - NM_SETTING_SRIOV_AUTOPROBE_DRIVERS, (int) autoprobe_drivers, + NM_SETTING_SRIOV_AUTOPROBE_DRIVERS, autoprobe_drivers, NULL); return (NMSetting *) s_sriov; @@ -3151,7 +3161,7 @@ eap_tls_reader (const char *eap_method, /* FIXME: writer does not actually write IEEE_8021X_CLIENT_CERT_PASSWORD and other * certificate related passwords. It should, because otherwise persisting such profiles * to ifcfg looses information. As this currently only matters for PKCS11 URIs, it seems - * a seldomly used feature so that it is not fixed yet. */ + * a seldom used feature so that it is not fixed yet. */ _secret_set_from_ifcfg (s_8021x, ifcfg, keys_ifcfg, @@ -3526,7 +3536,7 @@ fill_8021x (shvarFile *ifcfg, goto next; /* Some EAP methods don't provide keying material, thus they - * cannot be used with WiFi unless they are an inner method + * cannot be used with Wi-Fi unless they are an inner method * used with TTLS or PEAP or whatever. */ if (wifi && eap->wifi_phase2_only) { diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c index b70690cc..f5be7520 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c @@ -2223,16 +2223,15 @@ write_sriov_setting (NMConnection *connection, shvarFile *ifcfg) svUnsetAll (ifcfg, SV_KEY_TYPE_SRIOV_VF); - s_sriov = NM_SETTING_SRIOV (nm_connection_get_setting (connection, NM_TYPE_SETTING_SRIOV)); - if (s_sriov) - num = nm_setting_sriov_get_total_vfs (s_sriov); - if (num == 0) { + s_sriov = NM_SETTING_SRIOV (nm_connection_get_setting (connection, + NM_TYPE_SETTING_SRIOV)); + if (!s_sriov) { svUnsetValue (ifcfg, "SRIOV_TOTAL_VFS"); svUnsetValue (ifcfg, "SRIOV_AUTOPROBE_DRIVERS"); return; } - svSetValueInt64 (ifcfg, "SRIOV_TOTAL_VFS", num); + svSetValueInt64 (ifcfg, "SRIOV_TOTAL_VFS", nm_setting_sriov_get_total_vfs (s_sriov)); b = nm_setting_sriov_get_autoprobe_drivers (s_sriov); if (b != NM_TERNARY_DEFAULT) diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c index fe82fbdd..3259d936 100644 --- a/src/settings/plugins/ifcfg-rh/shvar.c +++ b/src/settings/plugins/ifcfg-rh/shvar.c @@ -330,7 +330,7 @@ _gstr_init (GString **str, const char *value, gsize i) * Unescaping usually does not extend the length of a string, * so we might be tempted to allocate a fixed buffer of length * (strlen(value)+CONST). - * However, due to $'\Ux' escapes, the maxium length is some + * However, due to $'\Ux' escapes, the maximum length is some * (FACTOR*strlen(value) + CONST), which is non trivial to get * right in all cases. Also, we would have to provision for the * very unlikely extreme case. @@ -453,7 +453,7 @@ svUnescape (const char *value, char **to_free) if (NM_IN_SET (value[i], '$', '`', '"', '\\')) { /* Drop the backslash. */ } else if (NM_IN_SET (value[i], '\'', '~')) { - /* '\'' and '~' in double qoutes are not handled special by shell. + /* '\'' and '~' in double quotes are not handled special by shell. * However, old versions of svEscape() would wrongly use double-quoting * with backslash escaping for these characters (expecting svUnescape() * to remove the backslash). @@ -649,7 +649,7 @@ void _nmtst_svFileSetName (shvarFile *s, const char *fileName) { /* changing the file name is not supported for regular - * operation. Only allowed to use in tests, othewise, + * operation. Only allowed to use in tests, otherwise, * the filename is immutable. */ g_free (s->fileName); s->fileName = g_strdup (fileName); diff --git a/src/settings/plugins/ifcfg-rh/tests/meson.build b/src/settings/plugins/ifcfg-rh/tests/meson.build index 29bc9699..f65494bb 100644 --- a/src/settings/plugins/ifcfg-rh/tests/meson.build +++ b/src/settings/plugins/ifcfg-rh/tests/meson.build @@ -6,12 +6,12 @@ exe = executable( test_unit, test_unit + '.c', dependencies: test_nm_dep, - link_with: libnms_ifcfg_rh_core + link_with: libnms_ifcfg_rh_core, ) test( 'ifcfg-rh/' + test_unit, test_script, timeout: 90, - args: test_args + [exe.full_path()] + args: test_args + [exe.full_path()], ) diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Band_A.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Band_A.cexpected index a95a58db..ddbd986f 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Band_A.cexpected +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Band_A.cexpected @@ -13,6 +13,6 @@ IPV6_AUTOCONF=yes IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no IPV6_ADDR_GEN_MODE=stable-privacy -NAME="Test Write WiFi Band A" +NAME="Test Write Wi-Fi Band A" UUID=${UUID} ONBOOT=yes diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Hidden.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Hidden.cexpected index cf325f35..495a24d3 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Hidden.cexpected +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Hidden.cexpected @@ -12,6 +12,6 @@ IPV6_AUTOCONF=yes IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no IPV6_ADDR_GEN_MODE=stable-privacy -NAME="Test Write WiFi Hidden" +NAME="Test Write Wi-Fi Hidden" UUID=${UUID} ONBOOT=yes diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_always.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_always.cexpected index f3704f10..aec6918a 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_always.cexpected +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_always.cexpected @@ -13,6 +13,6 @@ IPV6_AUTOCONF=yes IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no IPV6_ADDR_GEN_MODE=stable-privacy -NAME="Test Write WiFi MAC always" +NAME="Test Write Wi-Fi MAC always" UUID=${UUID} ONBOOT=yes diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_default.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_default.cexpected index 005c6179..9d47163f 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_default.cexpected +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_default.cexpected @@ -12,6 +12,6 @@ IPV6_AUTOCONF=yes IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no IPV6_ADDR_GEN_MODE=stable-privacy -NAME="Test Write WiFi MAC default" +NAME="Test Write Wi-Fi MAC default" UUID=${UUID} ONBOOT=yes diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_missing.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_missing.cexpected index dff17ef2..43c07ddb 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_missing.cexpected +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_missing.cexpected @@ -13,6 +13,6 @@ IPV6_AUTOCONF=yes IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no IPV6_ADDR_GEN_MODE=stable-privacy -NAME="Test Write WiFi MAC missing" +NAME="Test Write Wi-Fi MAC missing" UUID=${UUID} ONBOOT=yes diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_never.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_never.cexpected index 94274cf9..21f2e2de 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_never.cexpected +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_never.cexpected @@ -13,6 +13,6 @@ IPV6_AUTOCONF=yes IPV6_DEFROUTE=yes IPV6_FAILURE_FATAL=no IPV6_ADDR_GEN_MODE=stable-privacy -NAME="Test Write WiFi MAC never" +NAME="Test Write Wi-Fi MAC never" UUID=${UUID} ONBOOT=yes diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-4 b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-4 index a7156e33..2c1b7fb4 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-4 +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-4 @@ -6,7 +6,7 @@ # expected. # # Also note that setting NAME will replace the last -# occurance, and delete all previous once. +# occurrence, and delete all previous once. #L1 NAME=l2 diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-4.expected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-4.expected index 674df840..cf3f45bd 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-4.expected +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-write-unknown-4.expected @@ -6,7 +6,7 @@ # expected. # # Also note that setting NAME will replace the last -# occurance, and delete all previous once. +# occurrence, and delete all previous once. #L1 diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/network-test-wired-never-default b/src/settings/plugins/ifcfg-rh/tests/network-scripts/network-test-wired-never-default index 9a292679..4347405e 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/network-test-wired-never-default +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/network-test-wired-never-default @@ -1,4 +1,4 @@ GATEWAYDEV=eth0 -# when devices in IPV6_DEFAULTDEV and IPV6_DEFAULTGW don't match the one in IPV6_DEFAULTGW is prefered +# when devices in IPV6_DEFAULTDEV and IPV6_DEFAULTGW don't match the one in IPV6_DEFAULTGW is preferred IPV6_DEFAULTDEV=eth4 IPV6_DEFAULTGW=2001::1234%eth0 diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index 472bb8a6..d135ea43 100644 --- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -2374,7 +2374,7 @@ test_read_wifi_open (void) g_assert_cmpstr (nm_setting_wireless_get_mode (s_wireless), ==, "infrastructure"); g_assert_cmpint (nm_setting_wireless_get_channel (s_wireless), ==, 1); - /* ===== WiFi SECURITY SETTING ===== */ + /* ===== Wi-Fi SECURITY SETTING ===== */ s_wsec = nm_connection_get_setting_wireless_security (connection); g_assert (s_wsec == NULL); @@ -3275,7 +3275,7 @@ test_read_wifi_dynamic_wep_leap (void) s_wifi = nm_connection_get_setting_wireless (connection); g_assert (s_wifi); - /* ===== WiFi SECURITY SETTING ===== */ + /* ===== Wi-Fi SECURITY SETTING ===== */ s_wsec = nm_connection_get_setting_wireless_security (connection); g_assert (s_wsec); @@ -3501,7 +3501,7 @@ test_write_wifi_hidden (void) nm_connection_add_setting (connection, NM_SETTING (s_con)); g_object_set (s_con, - NM_SETTING_CONNECTION_ID, "Test Write WiFi Hidden", + NM_SETTING_CONNECTION_ID, "Test Write Wi-Fi Hidden", NM_SETTING_CONNECTION_UUID, nm_utils_uuid_generate_a (), NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME, NULL); @@ -3584,7 +3584,7 @@ test_write_wifi_mac_random (gconstpointer user_data) s_con = (NMSettingConnection *) nm_setting_connection_new (); nm_connection_add_setting (connection, NM_SETTING (s_con)); - val = g_strdup_printf ("Test Write WiFi MAC %s", name); + val = g_strdup_printf ("Test Write Wi-Fi MAC %s", name); g_object_set (s_con, NM_SETTING_CONNECTION_ID, val, NM_SETTING_CONNECTION_UUID, nm_utils_uuid_generate_a (), @@ -3803,7 +3803,7 @@ test_write_wifi_band_a (void) nm_connection_add_setting (connection, NM_SETTING (s_con)); g_object_set (s_con, - NM_SETTING_CONNECTION_ID, "Test Write WiFi Band A", + NM_SETTING_CONNECTION_ID, "Test Write Wi-Fi Band A", NM_SETTING_CONNECTION_UUID, nm_utils_uuid_generate_a (), NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME, NULL); @@ -7402,8 +7402,6 @@ test_write_mobile_broadband (gconstpointer data) /* GSM setting */ s_gsm = (NMSettingGsm *) nm_setting_gsm_new (); nm_connection_add_setting (connection, NM_SETTING (s_gsm)); - - g_object_set (s_gsm, NM_SETTING_GSM_NUMBER, "*99#", NULL); } else { /* CDMA setting */ s_cdma = (NMSettingCdma *) nm_setting_cdma_new (); diff --git a/src/settings/plugins/ifupdown/meson.build b/src/settings/plugins/ifupdown/meson.build index 826c7458..42edd438 100644 --- a/src/settings/plugins/ifupdown/meson.build +++ b/src/settings/plugins/ifupdown/meson.build @@ -1,11 +1,11 @@ sources = files( 'nms-ifupdown-interface-parser.c', - 'nms-ifupdown-parser.c' + 'nms-ifupdown-parser.c', ) deps = [ libudev_dep, - nm_dep + nm_dep, ] libnms_ifupdown_core = static_library( @@ -16,7 +16,7 @@ libnms_ifupdown_core = static_library( sources = files( 'nms-ifupdown-connection.c', - 'nms-ifupdown-plugin.c' + 'nms-ifupdown-plugin.c', ) libnm_settings_plugin_ifupdown = shared_module( @@ -27,7 +27,7 @@ libnm_settings_plugin_ifupdown = shared_module( link_args: ldflags_linker_script_settings, link_depends: linker_script_settings, install: true, - install_dir: nm_plugindir + install_dir: nm_plugindir, ) core_plugins += libnm_settings_plugin_ifupdown @@ -37,7 +37,7 @@ core_plugins += libnm_settings_plugin_ifupdown run_target( 'check-local-symbols-settings-ifupdown', command: [check_so_symbols, libnm_settings_plugin_ifupdown.full_path()], - depends: libnm_settings_plugin_ifupdown + depends: libnm_settings_plugin_ifupdown, ) check-local-symbols-settings-ifupdown: src/settings/plugins/ifupdown/libnm-settings-plugin-ifupdown.la diff --git a/src/settings/plugins/ifupdown/tests/meson.build b/src/settings/plugins/ifupdown/tests/meson.build index 5a2383d9..9b844c75 100644 --- a/src/settings/plugins/ifupdown/tests/meson.build +++ b/src/settings/plugins/ifupdown/tests/meson.build @@ -4,11 +4,11 @@ exe = executable( test_unit, test_unit + '.c', dependencies: test_nm_dep, - link_with: libnms_ifupdown_core + link_with: libnms_ifupdown_core, ) test( 'ifupdown/' + test_unit, test_script, - args: test_args + [exe.full_path()] + args: test_args + [exe.full_path()], ) diff --git a/src/settings/plugins/keyfile/nms-keyfile-plugin.c b/src/settings/plugins/keyfile/nms-keyfile-plugin.c index 346b78c0..ae9bea13 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-plugin.c +++ b/src/settings/plugins/keyfile/nms-keyfile-plugin.c @@ -36,6 +36,7 @@ #include "nm-utils.h" #include "nm-config.h" #include "nm-core-internal.h" +#include "nm-keyfile-internal.h" #include "settings/nm-settings-plugin.h" @@ -171,7 +172,6 @@ update_connection (NMSKeyfilePlugin *self, NMSKeyfileConnection *connection_by_uuid; GError *local = NULL; const char *uuid; - int dir_len; g_return_val_if_fail (!source || NM_IS_CONNECTION (source), NULL); g_return_val_if_fail (full_path || source, NULL); @@ -179,17 +179,8 @@ update_connection (NMSKeyfilePlugin *self, if (full_path) _LOGD ("loading from file \"%s\"...", full_path); - if (g_str_has_prefix (full_path, nms_keyfile_utils_get_path ())) { - dir_len = strlen (nms_keyfile_utils_get_path ()); - } else if (g_str_has_prefix (full_path, NM_CONFIG_KEYFILE_PATH_IN_MEMORY)) { - dir_len = NM_STRLEN (NM_CONFIG_KEYFILE_PATH_IN_MEMORY); - } else { - /* Just make sure the file name is not going go pass the following check. */ - dir_len = strlen (full_path); - } - - if ( full_path[dir_len] != '/' - || strchr (full_path + dir_len + 1, '/') != NULL) { + if ( !nm_utils_file_is_in_path (full_path, nms_keyfile_utils_get_path ()) + && !nm_utils_file_is_in_path (full_path, NM_KEYFILE_PATH_NAME_RUN)) { g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, "File not in recognized system-connections directory"); return FALSE; @@ -322,7 +313,7 @@ dir_changed (GFileMonitor *monitor, gboolean exists; full_path = g_file_get_path (file); - if (nms_keyfile_utils_should_ignore_file (full_path, FALSE)) { + if (nm_keyfile_utils_ignore_filename (full_path, FALSE)) { g_free (full_path); return; } @@ -444,7 +435,7 @@ _read_dir (GPtrArray *filenames, } while ((item = g_dir_read_name (dir))) { - if (nms_keyfile_utils_should_ignore_file (item, require_extension)) + if (nm_keyfile_utils_ignore_filename (item, require_extension)) continue; g_ptr_array_add (filenames, g_build_filename (path, item, NULL)); } @@ -467,7 +458,7 @@ read_connections (NMSettingsPlugin *config) filenames = g_ptr_array_new_with_free_func (g_free); - _read_dir (filenames, NM_CONFIG_KEYFILE_PATH_IN_MEMORY, TRUE); + _read_dir (filenames, NM_KEYFILE_PATH_NAME_RUN, TRUE); _read_dir (filenames, nms_keyfile_utils_get_path (), FALSE); alive_connections = g_hash_table_new (nm_direct_hash, NULL); @@ -476,7 +467,7 @@ read_connections (NMSettingsPlugin *config) * iterating over the files. * * To have sensible, reproducible behavior, sort the paths by last modification - * time prefering older files. + * time preferring older files. */ paths = _paths_from_connections (priv->connections); g_ptr_array_sort_with_data (filenames, (GCompareDataFunc) _sort_paths, paths); @@ -523,35 +514,6 @@ get_connections (NMSettingsPlugin *config) } static gboolean -_file_is_in_path (const char *abs_filename, - const char *abs_path) -{ - gsize l; - - /* FIXME: ensure that both paths are at least normalized (coalescing ".", - * duplicate '/', and trailing '/'). */ - - nm_assert (abs_filename && abs_filename[0] == '/'); - nm_assert (abs_path && abs_path[0] == '/'); - - l = strlen (abs_path); - if (strncmp (abs_filename, abs_path, l) != 0) - return FALSE; - - abs_filename += l; - while (abs_filename[0] == '/') - abs_filename++; - - if (!abs_filename[0]) - return FALSE; - - if (strchr (abs_filename, '/')) - return FALSE; - - return TRUE; -} - -static gboolean load_connection (NMSettingsPlugin *config, const char *filename) { @@ -559,25 +521,14 @@ load_connection (NMSettingsPlugin *config, NMSKeyfileConnection *connection; gboolean require_extension; - /* the test whether to require a file extension tries to figure out whether - * the provided filename is inside /etc or /run. - * - * However, on Posix a filename just resolves to an Inode, and there can - * be any kind of paths that point to the same Inode. It's not generally possible - * to check for that (unless, we would stat all files in the target directory - * and see whether their inode matches). - * - * So, when loading the file do something simpler: require that the path - * starts with the well-known prefix. This rejects symlinks or hard links - * which would actually also point to the same file. */ - if (_file_is_in_path (filename, nms_keyfile_utils_get_path ())) + if (nm_utils_file_is_in_path (filename, nms_keyfile_utils_get_path ())) require_extension = FALSE; - else if (_file_is_in_path (filename, NM_CONFIG_KEYFILE_PATH_IN_MEMORY)) + else if (nm_utils_file_is_in_path (filename, NM_KEYFILE_PATH_NAME_RUN)) require_extension = TRUE; else return FALSE; - if (nms_keyfile_utils_should_ignore_file (filename, require_extension)) + if (nm_keyfile_utils_ignore_filename (filename, require_extension)) return FALSE; connection = update_connection (self, NULL, filename, find_by_path (self, filename), TRUE, NULL, NULL); diff --git a/src/settings/plugins/keyfile/nms-keyfile-reader.c b/src/settings/plugins/keyfile/nms-keyfile-reader.c index 580a857a..314b1033 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-reader.c +++ b/src/settings/plugins/keyfile/nms-keyfile-reader.c @@ -142,11 +142,11 @@ nms_keyfile_reader_from_keyfile (GKeyFile *key_file, if (!connection) return NULL; - if (g_str_has_suffix (filename, NMS_KEYFILE_PATH_SUFFIX_NMCONNECTION)) { + if (g_str_has_suffix (filename, NM_KEYFILE_PATH_SUFFIX_NMCONNECTION)) { gsize l = strlen (filename); - if (l > NM_STRLEN (NMS_KEYFILE_PATH_SUFFIX_NMCONNECTION)) - filename_id = g_strndup (filename, l - NM_STRLEN (NMS_KEYFILE_PATH_SUFFIX_NMCONNECTION)); + if (l > NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMCONNECTION)) + filename_id = g_strndup (filename, l - NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMCONNECTION)); } nm_keyfile_read_ensure_id (connection, filename_id ?: filename); @@ -172,7 +172,8 @@ nms_keyfile_reader_from_file (const char *full_filename, nm_assert (full_filename && full_filename[0] == '/'); nm_assert (!profile_dir || profile_dir[0] == '/'); - if (!nms_keyfile_utils_check_file_permissions (full_filename, + if (!nms_keyfile_utils_check_file_permissions (NMS_KEYFILE_FILETYPE_KEYFILE, + full_filename, NULL, error)) return NULL; diff --git a/src/settings/plugins/keyfile/nms-keyfile-utils.c b/src/settings/plugins/keyfile/nms-keyfile-utils.c index c3bfcdee..8d4ec943 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-utils.c +++ b/src/settings/plugins/keyfile/nms-keyfile-utils.c @@ -26,108 +26,221 @@ #include <string.h> #include <sys/stat.h> +#include "nm-keyfile-internal.h" +#include "nm-utils.h" #include "nm-setting-wired.h" #include "nm-setting-wireless.h" #include "nm-setting-wireless-security.h" #include "nm-config.h" -#define NM_CONFIG_KEYFILE_PATH_DEFAULT NMCONFDIR "/system-connections" - /*****************************************************************************/ -static const char temp_letters[] = -"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; - -/* - * Check '.[a-zA-Z0-9]{6}' file suffix used for temporary files by g_file_set_contents() (mkstemp()). - */ -static gboolean -check_mkstemp_suffix (const char *path) +char * +nms_keyfile_loaded_uuid_filename (const char *dirname, + const char *uuid, + gboolean temporary) { - const char *ptr; - - g_return_val_if_fail (path != NULL, FALSE); + char filename[250]; + + nm_assert (dirname && dirname[0] == '/'); + nm_assert (uuid && nm_utils_is_uuid (uuid) && !strchr (uuid, '/')); + + if (g_snprintf (filename, + sizeof (filename), + "%s%s%s%s", + NM_KEYFILE_PATH_PREFIX_NMLOADED, + uuid, + NM_KEYFILE_PATH_SUFFIX_NMCONNECTION, + temporary ? "~" : "") >= sizeof (filename)) { + /* valid uuids are limited in length. The buffer should always be large + * enough. */ + nm_assert_not_reached (); + return NULL; + } - /* Matches *.[a-zA-Z0-9]{6} suffix of mkstemp()'s temporary files */ - ptr = strrchr (path, '.'); - if (ptr && (strspn (ptr + 1, temp_letters) == 6) && (! ptr[7])) - return TRUE; - return FALSE; + return g_build_filename (dirname, filename, NULL); } -static gboolean -check_prefix_dot (const char *base) +gboolean +nms_keyfile_loaded_uuid_read (const char *dirname, + const char *filename, + char **out_full_filename, + char **out_uuid, + char **out_loaded_path) { - nm_assert (base && base[0]); + const char *uuid; + const char *tmp; + gsize len; + gs_free char *full_filename = NULL; + gs_free char *ln = NULL; + + nm_assert (dirname && dirname[0] == '/'); + nm_assert (filename && filename[0] && !strchr (filename, '/')); + + if (filename[0] != '.') { + /* the hidden-uuid filename must start with '.'. That is, + * so that it does not conflict with regular keyfiles according + * to nm_keyfile_utils_ignore_filename(). */ + return FALSE; + } + + len = strlen (filename); + if ( len <= NM_STRLEN (NM_KEYFILE_PATH_PREFIX_NMLOADED) + || memcmp (filename, NM_KEYFILE_PATH_PREFIX_NMLOADED, NM_STRLEN (NM_KEYFILE_PATH_PREFIX_NMLOADED)) != 0) { + /* the filename does not have the right prefix. */ + return FALSE; + } + + tmp = &filename[NM_STRLEN (NM_KEYFILE_PATH_PREFIX_NMLOADED)]; + len -= NM_STRLEN (NM_KEYFILE_PATH_PREFIX_NMLOADED); + + if ( len <= NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMCONNECTION) + || memcmp (&tmp[len - NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMCONNECTION)], + NM_KEYFILE_PATH_SUFFIX_NMCONNECTION, + NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMCONNECTION)) != 0) { + /* the file does not have the right suffix. */ + return FALSE; + } + len -= NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMCONNECTION); + + if (!NM_IN_SET (len, 36, 40)) { + /* the remaining part of the filename has not the right length to + * contain a UUID (according to nm_utils_is_uuid()). */ + return FALSE; + } + + uuid = nm_strndup_a (100, tmp, len, NULL); + if (!nm_utils_is_uuid (uuid)) + return FALSE; - return base[0] == '.'; + full_filename = g_build_filename (dirname, filename, NULL); + + if (!nms_keyfile_utils_check_file_permissions (NMS_KEYFILE_FILETYPE_NMLOADED, + full_filename, + NULL, + NULL)) + return FALSE; + + ln = nm_utils_read_link_absolute (full_filename, NULL); + if (!ln) + return FALSE; + + NM_SET_OUT (out_uuid, g_strdup (uuid)); + NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename)); + NM_SET_OUT (out_loaded_path, g_steal_pointer (&ln)); + return TRUE; } -static gboolean -check_suffix (const char *base, const char *tag) +gboolean +nms_keyfile_loaded_uuid_read_from_file (const char *full_filename, + char **out_dirname, + char **out_filename, + char **out_uuid, + char **out_loaded_path) { - int len, tag_len; + gs_free char *dirname = NULL; + gs_free char *filename = NULL; - g_return_val_if_fail (base != NULL, TRUE); - g_return_val_if_fail (tag != NULL, TRUE); + nm_assert (full_filename && full_filename[0] == '/'); - len = strlen (base); - tag_len = strlen (tag); - if ((len > tag_len) && !g_ascii_strcasecmp (base + len - tag_len, tag)) - return TRUE; - return FALSE; -} + filename = g_path_get_basename (full_filename); + dirname = g_path_get_dirname (full_filename); -#define SWP_TAG ".swp" -#define SWPX_TAG ".swpx" -#define PEM_TAG ".pem" -#define DER_TAG ".der" + if (!nms_keyfile_loaded_uuid_read (dirname, + filename, + NULL, + out_uuid, + out_loaded_path)) + return FALSE; + + NM_SET_OUT (out_dirname, g_steal_pointer (&dirname)); + NM_SET_OUT (out_filename, g_steal_pointer (&filename)); + return TRUE; +} gboolean -nms_keyfile_utils_should_ignore_file (const char *filename, gboolean require_extension) +nms_keyfile_loaded_uuid_write (const char *dirname, + const char *uuid, + const char *loaded_path, + gboolean allow_relative, + char **out_full_filename) { - gs_free char *base = NULL; - - g_return_val_if_fail (filename != NULL, TRUE); - - base = g_path_get_basename (filename); - g_return_val_if_fail (base != NULL, TRUE); - - /* Ignore hidden and backup files */ - /* should_ignore_file() must mirror escape_filename() */ - if (check_prefix_dot (base) || check_suffix (base, "~")) - return TRUE; - /* Ignore temporary files */ - if (check_mkstemp_suffix (base)) - return TRUE; - /* Ignore 802.1x certificates and keys */ - if (check_suffix (base, PEM_TAG) || check_suffix (base, DER_TAG)) - return TRUE; - - if (require_extension) { - gsize l = strlen (base); - - if ( l <= NM_STRLEN (NMS_KEYFILE_PATH_SUFFIX_NMCONNECTION) - || !g_str_has_suffix (base, NMS_KEYFILE_PATH_SUFFIX_NMCONNECTION)) - return TRUE; + gs_free char *full_filename_tmp = NULL; + gs_free char *full_filename = NULL; + + nm_assert (dirname && dirname[0] == '/'); + nm_assert (uuid && nm_utils_is_uuid (uuid) && !strchr (uuid, '/')); + nm_assert (!loaded_path || loaded_path[0] == '/'); + + full_filename_tmp = nms_keyfile_loaded_uuid_filename (dirname, uuid, TRUE); + + nm_assert (g_str_has_suffix (full_filename_tmp, "~")); + nm_assert (nm_utils_file_is_in_path (full_filename_tmp, dirname)); + + (void) unlink (full_filename_tmp); + + if (!loaded_path) { + gboolean success = TRUE; + + full_filename_tmp[strlen (full_filename_tmp) - 1] = '\0'; + if (unlink (full_filename_tmp) != 0) + success = NM_IN_SET (errno, ENOENT); + NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename_tmp)); + return success; + } + + if (allow_relative) { + const char *f; + + f = nm_utils_file_is_in_path (loaded_path, dirname); + if (f) { + /* @loaded_path points to a file directly in @dirname. + * Don't use absolute paths. */ + loaded_path = f; + } + } + + if (symlink (loaded_path, full_filename_tmp) != 0) { + full_filename_tmp[strlen (full_filename_tmp) - 1] = '\0'; + NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename_tmp)); + return FALSE; } - return FALSE; + full_filename = g_strdup (full_filename_tmp); + full_filename[strlen (full_filename) - 1] = '\0'; + if (rename (full_filename_tmp, full_filename) != 0) { + (void) unlink (full_filename_tmp); + NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename)); + return FALSE; + } + + NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename)); + return TRUE; } /*****************************************************************************/ gboolean -nms_keyfile_utils_check_file_permissions_stat (const struct stat *st, +nms_keyfile_utils_check_file_permissions_stat (NMSKeyfileFiletype filetype, + const struct stat *st, GError **error) { g_return_val_if_fail (st, FALSE); - if (!S_ISREG (st->st_mode)) { - g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "file is not a regular file"); - return FALSE; - } + if (filetype == NMS_KEYFILE_FILETYPE_KEYFILE) { + if (!S_ISREG (st->st_mode)) { + g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "file is not a regular file"); + return FALSE; + } + } else if (filetype == NMS_KEYFILE_FILETYPE_NMLOADED) { + if (!S_ISLNK (st->st_mode)) { + g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "file is not a slink"); + return FALSE; + } + } else + g_return_val_if_reached (FALSE); if (!NM_FLAGS_HAS (nm_utils_get_testing (), NM_UTILS_TEST_NO_KEYFILE_OWNER_CHECK)) { if (st->st_uid != 0) { @@ -137,7 +250,8 @@ nms_keyfile_utils_check_file_permissions_stat (const struct stat *st, return FALSE; } - if (st->st_mode & 0077) { + if ( filetype == NMS_KEYFILE_FILETYPE_KEYFILE + && (st->st_mode & 0077)) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "File permissions (%03o) are insecure", st->st_mode); @@ -149,7 +263,8 @@ nms_keyfile_utils_check_file_permissions_stat (const struct stat *st, } gboolean -nms_keyfile_utils_check_file_permissions (const char *filename, +nms_keyfile_utils_check_file_permissions (NMSKeyfileFiletype filetype, + const char *filename, struct stat *out_st, GError **error) { @@ -158,14 +273,24 @@ nms_keyfile_utils_check_file_permissions (const char *filename, g_return_val_if_fail (filename && filename[0] == '/', FALSE); - if (stat (filename, &st) != 0) { - errsv = errno; - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "cannot access file: %s", g_strerror (errsv)); - return FALSE; - } + if (filetype == NMS_KEYFILE_FILETYPE_KEYFILE) { + if (stat (filename, &st) != 0) { + errsv = errno; + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "cannot access file: %s", g_strerror (errsv)); + return FALSE; + } + } else if (filetype == NMS_KEYFILE_FILETYPE_NMLOADED) { + if (lstat (filename, &st) != 0) { + errsv = errno; + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "cannot access file: %s", g_strerror (errsv)); + return FALSE; + } + } else + g_return_val_if_reached (FALSE); - if (!nms_keyfile_utils_check_file_permissions_stat (&st, error)) + if (!nms_keyfile_utils_check_file_permissions_stat (filetype, &st, error)) return FALSE; NM_SET_OUT (out_st, st); @@ -174,50 +299,6 @@ nms_keyfile_utils_check_file_permissions (const char *filename, /*****************************************************************************/ -char * -nms_keyfile_utils_escape_filename (const char *filename, - gboolean with_extension) -{ - GString *str; - const char *f = filename; - /* keyfile used to escape with '*', do not change that behavior. - * - * But for newly added escapings, use '_' instead. - * Also, @with_extension is new-style. */ - const char ESCAPE_CHAR = with_extension ? '_' : '*'; - const char ESCAPE_CHAR2 = '_'; - - g_return_val_if_fail (filename && filename[0], NULL); - - str = g_string_sized_new (60); - - /* Convert '/' to ESCAPE_CHAR */ - for (f = filename; f[0]; f++) { - if (f[0] == '/') - g_string_append_c (str, ESCAPE_CHAR); - else - g_string_append_c (str, f[0]); - } - - /* escape_filename() must avoid anything that should_ignore_file() would reject. - * We can escape here more aggressivly then what we would read back. */ - if (check_prefix_dot (str->str)) - str->str[0] = ESCAPE_CHAR2; - if (check_suffix (str->str, "~")) - str->str[str->len - 1] = ESCAPE_CHAR2; - if ( check_mkstemp_suffix (str->str) - || check_suffix (str->str, PEM_TAG) - || check_suffix (str->str, DER_TAG)) - g_string_append_c (str, ESCAPE_CHAR2); - - if (with_extension) - g_string_append (str, NMS_KEYFILE_PATH_SUFFIX_NMCONNECTION); - - return g_string_free (str, FALSE);; -} - -/*****************************************************************************/ - const char * nms_keyfile_utils_get_path (void) { @@ -229,7 +310,7 @@ nms_keyfile_utils_get_path (void) NM_CONFIG_KEYFILE_KEY_KEYFILE_PATH, NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY); if (!path) - path = g_strdup (""NM_CONFIG_KEYFILE_PATH_DEFAULT""); + path = g_strdup (""NM_KEYFILE_PATH_NAME_ETC_DEFAULT""); } return path; } diff --git a/src/settings/plugins/keyfile/nms-keyfile-utils.h b/src/settings/plugins/keyfile/nms-keyfile-utils.h index 297dd4ea..bc601dad 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-utils.h +++ b/src/settings/plugins/keyfile/nms-keyfile-utils.h @@ -23,27 +23,52 @@ #include "NetworkManagerUtils.h" -#define NM_CONFIG_KEYFILE_PATH_IN_MEMORY NMRUNDIR "/system-connections" - -#define NMS_KEYFILE_PATH_SUFFIX_NMCONNECTION ".nmconnection" - #define NMS_KEYFILE_CONNECTION_LOG_PATH(path) ((path) ?: "in-memory") #define NMS_KEYFILE_CONNECTION_LOG_FMT "%s (%s,\"%s\")" #define NMS_KEYFILE_CONNECTION_LOG_ARG(con) NMS_KEYFILE_CONNECTION_LOG_PATH (nm_settings_connection_get_filename ((NMSettingsConnection *) (con))), nm_settings_connection_get_uuid ((NMSettingsConnection *) (con)), nm_settings_connection_get_id ((NMSettingsConnection *) (con)) #define NMS_KEYFILE_CONNECTION_LOG_FMTD "%s (%s,\"%s\",%p)" #define NMS_KEYFILE_CONNECTION_LOG_ARGD(con) NMS_KEYFILE_CONNECTION_LOG_PATH (nm_settings_connection_get_filename ((NMSettingsConnection *) (con))), nm_settings_connection_get_uuid ((NMSettingsConnection *) (con)), nm_settings_connection_get_id ((NMSettingsConnection *) (con)), (con) -gboolean nms_keyfile_utils_should_ignore_file (const char *filename, gboolean require_extension); - -char *nms_keyfile_utils_escape_filename (const char *filename, gboolean with_extension); +typedef enum { + NMS_KEYFILE_FILETYPE_KEYFILE, + NMS_KEYFILE_FILETYPE_NMLOADED, +} NMSKeyfileFiletype; const char *nms_keyfile_utils_get_path (void); +/*****************************************************************************/ + +char *nms_keyfile_loaded_uuid_filename (const char *dirname, + const char *uuid, + gboolean temporary); + +gboolean nms_keyfile_loaded_uuid_read (const char *dirname, + const char *filename, + char **out_full_filename, + char **out_uuid, + char **out_loaded_path); + +gboolean nms_keyfile_loaded_uuid_read_from_file (const char *full_filename, + char **out_dirname, + char **out_filename, + char **out_uuid, + char **out_loaded_path); + +gboolean nms_keyfile_loaded_uuid_write (const char *dirname, + const char *uuid, + const char *loaded_path, + gboolean allow_relative, + char **out_full_filename); + +/*****************************************************************************/ + struct stat; -gboolean nms_keyfile_utils_check_file_permissions_stat (const struct stat *st, +gboolean nms_keyfile_utils_check_file_permissions_stat (NMSKeyfileFiletype filetype, + const struct stat *st, GError **error); -gboolean nms_keyfile_utils_check_file_permissions (const char *filename, +gboolean nms_keyfile_utils_check_file_permissions (NMSKeyfileFiletype filetype, + const char *filename, struct stat *out_st, GError **error); diff --git a/src/settings/plugins/keyfile/nms-keyfile-writer.c b/src/settings/plugins/keyfile/nms-keyfile-writer.c index df26ea60..23a6a77c 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-writer.c +++ b/src/settings/plugins/keyfile/nms-keyfile-writer.c @@ -230,7 +230,7 @@ _internal_write_connection (NMConnection *connection, if (existing_path != NULL && !rename) { path = g_strdup (existing_path); } else { - char *filename_escaped = nms_keyfile_utils_escape_filename (id, with_extension); + char *filename_escaped = nm_keyfile_utils_create_filename (id, with_extension); path = g_build_filename (keyfile_dir, filename_escaped, NULL); g_free (filename_escaped); @@ -256,7 +256,7 @@ _internal_write_connection (NMConnection *connection, else filename = g_strdup_printf ("%s-%s-%u", id, nm_connection_get_uuid (connection), i); - filename_escaped = nms_keyfile_utils_escape_filename (filename, with_extension); + filename_escaped = nm_keyfile_utils_create_filename (filename, with_extension); g_free (path); path = g_strdup_printf ("%s/%s", keyfile_dir, filename_escaped); @@ -356,7 +356,7 @@ nms_keyfile_writer_connection (NMConnection *connection, if (save_to_disk) keyfile_dir = nms_keyfile_utils_get_path (); else - keyfile_dir = NM_CONFIG_KEYFILE_PATH_IN_MEMORY; + keyfile_dir = NM_KEYFILE_PATH_NAME_RUN; return _internal_write_connection (connection, keyfile_dir, diff --git a/src/settings/plugins/keyfile/tests/meson.build b/src/settings/plugins/keyfile/tests/meson.build index 8b94b256..4253fe3c 100644 --- a/src/settings/plugins/keyfile/tests/meson.build +++ b/src/settings/plugins/keyfile/tests/meson.build @@ -11,5 +11,5 @@ exe = executable( test( 'keyfile/' + test_unit, test_script, - args: test_args + [exe.full_path()] + args: test_args + [exe.full_path()], ) diff --git a/src/settings/plugins/keyfile/tests/test-keyfile.c b/src/settings/plugins/keyfile/tests/test-keyfile.c index 4a0e01b3..cdc9bfb0 100644 --- a/src/settings/plugins/keyfile/tests/test-keyfile.c +++ b/src/settings/plugins/keyfile/tests/test-keyfile.c @@ -1177,7 +1177,6 @@ test_write_bt_dun_connection (void) NM_SETTING_GSM_APN, "internet2.voicestream.com", NM_SETTING_GSM_USERNAME, "george.clinton", NM_SETTING_GSM_PASSWORD, "parliament", - NM_SETTING_GSM_NUMBER, "*99#", NULL); write_test_connection_and_reread (connection, TRUE); @@ -1259,7 +1258,6 @@ test_write_gsm_connection (void) NM_SETTING_GSM_APN, "internet2.voicestream.com", NM_SETTING_GSM_USERNAME, "george.clinton.again", NM_SETTING_GSM_PASSWORD, "parliament2", - NM_SETTING_GSM_NUMBER, "*99#", NM_SETTING_GSM_PIN, "123456", NM_SETTING_GSM_NETWORK_ID, "254098", NM_SETTING_GSM_HOME_ONLY, TRUE, @@ -2073,7 +2071,7 @@ test_write_new_wireless_group_names (void) NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME, NULL); - /* WiFi setting */ + /* Wi-Fi setting */ s_wifi = (NMSettingWireless *) nm_setting_wireless_new (); nm_connection_add_setting (connection, NM_SETTING (s_wifi)); @@ -2084,7 +2082,7 @@ test_write_new_wireless_group_names (void) NULL); g_bytes_unref (ssid); - /* WiFi security setting */ + /* Wi-Fi security setting */ s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new (); nm_connection_add_setting (connection, NM_SETTING (s_wsec)); g_object_set (s_wsec, @@ -2333,7 +2331,6 @@ test_write_flags_property (void) s_gsm = nm_setting_gsm_new (); nm_connection_add_setting (connection, s_gsm); g_object_set (s_gsm, - NM_SETTING_GSM_NUMBER, "#99*", NM_SETTING_GSM_APN, "myapn", NM_SETTING_GSM_USERNAME, "adfasdfasdf", NM_SETTING_GSM_PASSWORD_FLAGS, NM_SETTING_SECRET_FLAG_NOT_SAVED | NM_SETTING_SECRET_FLAG_NOT_REQUIRED, @@ -2466,18 +2463,18 @@ _escape_filename (gboolean with_extension, const char *filename, gboolean would_ g_assert (filename && filename[0]); - if (!!would_be_ignored != !!nms_keyfile_utils_should_ignore_file (filename, with_extension)) { + if (!!would_be_ignored != !!nm_keyfile_utils_ignore_filename (filename, with_extension)) { if (would_be_ignored) g_error ("We expect filename \"%s\" to be ignored, but it isn't", filename); else g_error ("We expect filename \"%s\" not to be ignored, but it is", filename); } - esc = nms_keyfile_utils_escape_filename (filename, with_extension); + esc = nm_keyfile_utils_create_filename (filename, with_extension); g_assert (esc && esc[0]); g_assert (!strchr (esc, '/')); - if (nms_keyfile_utils_should_ignore_file (esc, with_extension)) + if (nm_keyfile_utils_ignore_filename (esc, with_extension)) g_error ("Escaping filename \"%s\" yielded \"%s\", but this is ignored", filename, esc); } @@ -2503,12 +2500,117 @@ test_nm_keyfile_plugin_utils_escape_filename (void) _escape_filename (FALSE, ".#emacs-locking", TRUE); _escape_filename (FALSE, "file-with-tilde~", TRUE); _escape_filename (FALSE, ".file-with-dot", TRUE); + _escape_filename (FALSE, "/some/path/with/trailing/slash/", TRUE); + _escape_filename (FALSE, "/some/path/without/trailing/slash", FALSE); _escape_filename (TRUE, "lala", TRUE); } /*****************************************************************************/ +static void +_assert_keyfile_loaded_uuid (const char *dirname, + const char *uuid, + const char *loaded_path, + gboolean allow_relative, + const char *exp_full_filename, + const char *exp_uuid, + const char *exp_symlink_target, + const char *exp_loaded_path) +{ + gs_free char *full_filename = NULL; + gs_free char *symlink_target = NULL; + gs_free char *uuid2 = NULL; + gs_free char *loaded_path2 = NULL; + gs_free char *dirname3 = NULL; + gs_free char *filename3 = NULL; + gs_free char *uuid3 = NULL; + gs_free char *loaded_path3 = NULL; + gboolean success; + gs_free char *filename = NULL; + + g_assert (dirname && dirname[0] == '/'); + g_assert (exp_full_filename && exp_full_filename[0]); + g_assert (!exp_loaded_path || exp_loaded_path[0] == '/'); + + filename = g_path_get_basename (exp_full_filename); + + full_filename = nms_keyfile_loaded_uuid_filename (dirname, uuid, FALSE); + g_assert_cmpstr (full_filename, ==, full_filename); + nm_clear_g_free (&full_filename); + + + g_assert (nms_keyfile_loaded_uuid_write (dirname, uuid, loaded_path, allow_relative, &full_filename)); + g_assert_cmpstr (full_filename, ==, exp_full_filename); + nm_clear_g_free (&full_filename); + + if (exp_symlink_target) + g_assert (g_file_test (exp_full_filename, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_SYMLINK)); + else + g_assert (!g_file_test (exp_full_filename, G_FILE_TEST_EXISTS)); + symlink_target = g_file_read_link (exp_full_filename, NULL); + g_assert_cmpstr (symlink_target, ==, exp_symlink_target); + + + success = nms_keyfile_loaded_uuid_read (dirname, filename, &full_filename, &uuid2, &loaded_path2); + g_assert_cmpint (!!exp_uuid, ==, success); + if (success) + g_assert_cmpstr (full_filename, ==, exp_full_filename); + else + g_assert_cmpstr (full_filename, ==, NULL); + nm_clear_g_free (&full_filename); + g_assert_cmpstr (uuid2, ==, exp_uuid); + g_assert_cmpstr (loaded_path2, ==, exp_loaded_path); + + + success = nms_keyfile_loaded_uuid_read_from_file (exp_full_filename, &dirname3, &filename3, &uuid3, &loaded_path3); + g_assert_cmpint (!!exp_uuid, ==, success); + if (success) { + g_assert_cmpstr (dirname3, ==, dirname); + g_assert_cmpstr (filename3, ==, filename); + } else { + g_assert_cmpstr (dirname3, ==, NULL); + g_assert_cmpstr (filename3, ==, NULL); + } + g_assert_cmpstr (uuid3, ==, exp_uuid); + g_assert_cmpstr (loaded_path3, ==, exp_loaded_path); +} + +static void +test_loaded_uuid (void) +{ + const char *uuid = "3c03fd17-ddc3-4100-a954-88b6fafff959"; + gs_free char *filename = g_strdup_printf ("%s%s%s", + NM_KEYFILE_PATH_PREFIX_NMLOADED, + uuid, + NM_KEYFILE_PATH_SUFFIX_NMCONNECTION); + gs_free char *full_filename = g_strdup_printf ("%s/%s", + TEST_SCRATCH_DIR, + filename); + const char *loaded_path0 = NM_KEYFILE_PATH_NMLOADED_NULL; + const char *loaded_path1 = "/some/where/but/not/scratch/dir"; + const char *filename2 = "foo1"; + gs_free char *loaded_path2 = g_strdup_printf ("%s/%s", + TEST_SCRATCH_DIR, + filename2); + + _assert_keyfile_loaded_uuid (TEST_SCRATCH_DIR, uuid, NULL, FALSE, full_filename, NULL, NULL, NULL); + _assert_keyfile_loaded_uuid (TEST_SCRATCH_DIR, uuid, NULL, TRUE, full_filename, NULL, NULL, NULL); + + _assert_keyfile_loaded_uuid (TEST_SCRATCH_DIR, uuid, loaded_path0, FALSE, full_filename, uuid, loaded_path0, loaded_path0); + _assert_keyfile_loaded_uuid (TEST_SCRATCH_DIR, uuid, loaded_path0, TRUE, full_filename, uuid, loaded_path0, loaded_path0); + + _assert_keyfile_loaded_uuid (TEST_SCRATCH_DIR, uuid, loaded_path1, FALSE, full_filename, uuid, loaded_path1, loaded_path1); + _assert_keyfile_loaded_uuid (TEST_SCRATCH_DIR, uuid, loaded_path1, TRUE, full_filename, uuid, loaded_path1, loaded_path1); + + _assert_keyfile_loaded_uuid (TEST_SCRATCH_DIR, uuid, loaded_path2, FALSE, full_filename, uuid, loaded_path2, loaded_path2); + _assert_keyfile_loaded_uuid (TEST_SCRATCH_DIR, uuid, loaded_path2, TRUE, full_filename, uuid, filename2, loaded_path2); + + (void) unlink (full_filename); +} + +/*****************************************************************************/ + NMTST_DEFINE (); int main (int argc, char **argv) @@ -2591,6 +2693,7 @@ int main (int argc, char **argv) g_test_add_func ("/keyfile/test_nm_keyfile_plugin_utils_escape_filename", test_nm_keyfile_plugin_utils_escape_filename); + g_test_add_func ("/keyfile/test_loaded_uuid", test_loaded_uuid); + return g_test_run (); } - diff --git a/src/supplicant/nm-supplicant-config.c b/src/supplicant/nm-supplicant-config.c index 043b5550..4acb634e 100644 --- a/src/supplicant/nm-supplicant-config.c +++ b/src/supplicant/nm-supplicant-config.c @@ -26,6 +26,8 @@ #include <string.h> #include <stdlib.h> +#include "nm-core-internal.h" + #include "nm-supplicant-settings-verify.h" #include "nm-setting.h" #include "nm-auth-subject.h" @@ -371,7 +373,6 @@ nm_supplicant_config_add_setting_macsec (NMSupplicantConfig * self, NMSettingMacsec * setting, GError **error) { - gs_unref_bytes GBytes *bytes = NULL; const char *value; char buf[32]; int port; @@ -395,43 +396,50 @@ nm_supplicant_config_add_setting_macsec (NMSupplicantConfig * self, } if (nm_setting_macsec_get_mode (setting) == NM_SETTING_MACSEC_MODE_PSK) { + guint8 buffer_cak[NM_SETTING_MACSEC_MKA_CAK_LENGTH/2]; + guint8 buffer_ckn[NM_SETTING_MACSEC_MKA_CKN_LENGTH/2]; + if (!nm_supplicant_config_add_option (self, "key_mgmt", "NONE", -1, NULL, error)) return FALSE; - /* CAK */ value = nm_setting_macsec_get_mka_cak (setting); - if (!value) { + if ( !value + || !_nm_utils_hexstr2bin_buf (value, + FALSE, + FALSE, + NULL, + buffer_cak)) { g_set_error_literal (error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG, - "missing MKA CAK"); + value ? "invalid MKA CAK" : "missing MKA CAK"); return FALSE; } - - bytes = nm_utils_hexstr2bin (value); if (!nm_supplicant_config_add_option (self, "mka_cak", - g_bytes_get_data (bytes, NULL), - g_bytes_get_size (bytes), + (char *) buffer_cak, + sizeof (buffer_cak), "<hidden>", error)) return FALSE; - /* CKN */ value = nm_setting_macsec_get_mka_ckn (setting); - if (!value) { + if ( !value + || !_nm_utils_hexstr2bin_buf (value, + FALSE, + FALSE, + NULL, + buffer_ckn)) { g_set_error_literal (error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG, - "missing MKA CKN"); + value ? "invalid MKA CKN" : "missing MKA CKN"); return FALSE; } - - bytes = nm_utils_hexstr2bin (value); if (!nm_supplicant_config_add_option (self, "mka_ckn", - g_bytes_get_data (bytes, NULL), - g_bytes_get_size (bytes), + (char *) buffer_ckn, + sizeof (buffer_ckn), NULL, error)) return FALSE; @@ -563,7 +571,7 @@ nm_supplicant_config_add_bgscan (NMSupplicantConfig *self, NM_SETTING_WIRELESS_MODE_ADHOC)) return TRUE; - /* Don't scan when the connection is locked to a specifc AP, since + /* Don't scan when the connection is locked to a specific AP, since * intra-ESS roaming (which requires periodic scanning) isn't being * used due to the specific AP lock. (bgo #513820) */ @@ -648,31 +656,28 @@ add_string_val (NMSupplicantConfig *self, static void wep128_passphrase_hash (const char *input, - size_t input_len, - guint8 *out_digest, - size_t *out_digest_len) + gsize input_len, + guint8 *digest /* 13 bytes */) { - GChecksum *sum; + nm_auto_free_checksum GChecksum *sum = NULL; + guint8 md5[NM_UTILS_CHECKSUM_LENGTH_MD5]; guint8 data[64]; int i; - g_return_if_fail (out_digest != NULL); - g_return_if_fail (out_digest_len != NULL); - g_return_if_fail (*out_digest_len >= 16); + nm_assert (input); + nm_assert (input_len); + nm_assert (digest); /* Get at least 64 bytes by repeating the passphrase into the buffer */ for (i = 0; i < sizeof (data); i++) data[i] = input[i % input_len]; sum = g_checksum_new (G_CHECKSUM_MD5); - g_assert (sum); g_checksum_update (sum, data, sizeof (data)); - g_checksum_get_digest (sum, out_digest, out_digest_len); - g_checksum_free (sum); + nm_utils_checksum_get_digest (sum, md5); - g_assert (*out_digest_len == 16); /* WEP104 keys are 13 bytes in length (26 hex characters) */ - *out_digest_len = 13; + memcpy (digest, md5, 13); } static gboolean @@ -682,9 +687,10 @@ add_wep_key (NMSupplicantConfig *self, NMWepKeyType wep_type, GError **error) { - size_t key_len = key ? strlen (key) : 0; + gsize key_len; - if (!key || !key_len) + if ( !key + || (key_len = strlen (key)) == 0) return TRUE; if (wep_type == NM_WEP_KEY_TYPE_UNKNOWN) { @@ -697,10 +703,16 @@ 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)) { - gs_unref_bytes GBytes *bytes = NULL; - - bytes = nm_utils_hexstr2bin (key); - if (!bytes) { + guint8 buffer[26/2]; + + if (!_nm_utils_hexstr2bin_full (key, + FALSE, + FALSE, + NULL, + key_len / 2, + buffer, + sizeof (buffer), + NULL)) { g_set_error (error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG, "cannot add wep-key %s to suplicant config because key is not hex", name); @@ -708,8 +720,8 @@ add_wep_key (NMSupplicantConfig *self, } if (!nm_supplicant_config_add_option (self, name, - g_bytes_get_data (bytes, NULL), - g_bytes_get_size (bytes), + (char *) buffer, + key_len / 2, "<hidden>", error)) return FALSE; @@ -723,11 +735,10 @@ add_wep_key (NMSupplicantConfig *self, return FALSE; } } else if (wep_type == NM_WEP_KEY_TYPE_PASSPHRASE) { - guint8 digest[16]; - size_t digest_len = sizeof (digest); + guint8 digest[13]; - wep128_passphrase_hash (key, key_len, digest, &digest_len); - if (!nm_supplicant_config_add_option (self, name, (const char *) digest, digest_len, "<hidden>", error)) + wep128_passphrase_hash (key, key_len, digest); + if (!nm_supplicant_config_add_option (self, name, (const char *) digest, sizeof (digest), "<hidden>", error)) return FALSE; } @@ -747,6 +758,7 @@ nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self, NMSupplicantConfigPrivate *priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE (self); const char *key_mgmt, *key_mgmt_conf, *auth_alg; const char *psk; + gboolean set_pmf; g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), FALSE); g_return_val_if_fail (setting != NULL, FALSE); @@ -796,20 +808,22 @@ nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self, size_t psk_len = strlen (psk); if (psk_len == 64) { - gs_unref_bytes GBytes *bytes = NULL; + guint8 buffer[32]; /* Hex PSK */ - bytes = nm_utils_hexstr2bin (psk); - if (!bytes) { + if (!_nm_utils_hexstr2bin_buf (psk, + FALSE, + FALSE, + NULL, + buffer)) { g_set_error (error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG, "Cannot add psk to supplicant config due to invalid hex"); return FALSE; } - if (!nm_supplicant_config_add_option (self, "psk", - g_bytes_get_data (bytes, NULL), - g_bytes_get_size (bytes), + (char *) buffer, + sizeof (buffer), "<hidden>", error)) return FALSE; @@ -834,13 +848,14 @@ nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self, pmf = NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE; /* Check if we actually support PMF */ + set_pmf = TRUE; if (!priv->support_pmf) { if (pmf == NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED) { g_set_error_literal (error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG, "Supplicant does not support PMF"); return FALSE; - } else if (pmf == NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL) - pmf = NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE; + } + set_pmf = FALSE; } /* Only WPA-specific things when using WPA */ @@ -854,13 +869,14 @@ nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self, if (!ADD_STRING_LIST_VAL (self, setting, wireless_security, group, groups, "group", ' ', TRUE, NULL, error)) return FALSE; - if ( !nm_streq (key_mgmt, "wpa-none") + if ( set_pmf + && !nm_streq (key_mgmt, "wpa-none") && NM_IN_SET (pmf, - NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL, + NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE, NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED)) { if (!nm_supplicant_config_add_option (self, "ieee80211w", - pmf == NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL ? "1" : "2", + pmf == NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE ? "0" : "2", -1, NULL, error)) diff --git a/src/supplicant/nm-supplicant-interface.c b/src/supplicant/nm-supplicant-interface.c index 5237acb2..c30adc58 100644 --- a/src/supplicant/nm-supplicant-interface.c +++ b/src/supplicant/nm-supplicant-interface.c @@ -22,6 +22,7 @@ #include "nm-default.h" #include "nm-supplicant-interface.h" +#include "nm-supplicant-manager.h" #include <stdio.h> #include <string.h> @@ -31,12 +32,15 @@ #include "nm-core-internal.h" #include "nm-dbus-compat.h" -#define WPAS_DBUS_IFACE_INTERFACE WPAS_DBUS_INTERFACE ".Interface" -#define WPAS_DBUS_IFACE_INTERFACE_WPS WPAS_DBUS_INTERFACE ".Interface.WPS" -#define WPAS_DBUS_IFACE_BSS WPAS_DBUS_INTERFACE ".BSS" -#define WPAS_DBUS_IFACE_NETWORK WPAS_DBUS_INTERFACE ".Network" -#define WPAS_ERROR_INVALID_IFACE WPAS_DBUS_INTERFACE ".InvalidInterface" -#define WPAS_ERROR_EXISTS_ERROR WPAS_DBUS_INTERFACE ".InterfaceExists" +#define WPAS_DBUS_IFACE_INTERFACE WPAS_DBUS_INTERFACE ".Interface" +#define WPAS_DBUS_IFACE_INTERFACE_WPS WPAS_DBUS_INTERFACE ".Interface.WPS" +#define WPAS_DBUS_IFACE_INTERFACE_P2P_DEVICE WPAS_DBUS_INTERFACE ".Interface.P2PDevice" +#define WPAS_DBUS_IFACE_BSS WPAS_DBUS_INTERFACE ".BSS" +#define WPAS_DBUS_IFACE_PEER WPAS_DBUS_INTERFACE ".Peer" +#define WPAS_DBUS_IFACE_GROUP WPAS_DBUS_INTERFACE ".Group" +#define WPAS_DBUS_IFACE_NETWORK WPAS_DBUS_INTERFACE ".Network" +#define WPAS_ERROR_INVALID_IFACE WPAS_DBUS_INTERFACE ".InvalidInterface" +#define WPAS_ERROR_EXISTS_ERROR WPAS_DBUS_INTERFACE ".InterfaceExists" /*****************************************************************************/ @@ -45,6 +49,11 @@ typedef struct { gulong change_id; } BssData; +typedef struct { + GDBusProxy *proxy; + gulong change_id; +} PeerData; + struct _AddNetworkData; typedef struct { @@ -74,26 +83,38 @@ typedef struct _AddNetworkData { } AddNetworkData; enum { - STATE, /* change in the interface's state */ - REMOVED, /* interface was removed by the supplicant */ - BSS_UPDATED, /* a new BSS appeared or an existing had properties changed */ - BSS_REMOVED, /* supplicant removed BSS from its scan list */ - SCAN_DONE, /* wifi scan is complete */ - CREDENTIALS_REQUEST, /* 802.1x identity or password requested */ - WPS_CREDENTIALS, /* WPS credentials received */ + STATE, /* change in the interface's state */ + REMOVED, /* interface was removed by the supplicant */ + BSS_UPDATED, /* a new BSS appeared or an existing had properties changed */ + BSS_REMOVED, /* supplicant removed BSS from its scan list */ + PEER_UPDATED, /* a new Peer appeared or an existing had properties changed */ + PEER_REMOVED, /* supplicant removed Peer from its scan list */ + SCAN_DONE, /* wifi scan is complete */ + CREDENTIALS_REQUEST, /* 802.1x identity or password requested */ + WPS_CREDENTIALS, /* WPS credentials received */ + GROUP_STARTED, /* a new Group (interface) was created */ + GROUP_FINISHED, /* a Group (interface) has been finished */ + GROUP_FORMATION_FAILURE, /* P2P Group formation failed */ LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; NM_GOBJECT_PROPERTIES_DEFINE (NMSupplicantInterface, PROP_IFACE, + PROP_OBJECT_PATH, + PROP_P2P_GROUP_JOINED, + PROP_P2P_GROUP_PATH, + PROP_P2P_GROUP_OWNER, PROP_SCANNING, PROP_CURRENT_BSS, PROP_DRIVER, + PROP_P2P_AVAILABLE, PROP_FAST_SUPPORT, PROP_AP_SUPPORT, PROP_PMF_SUPPORT, PROP_FILS_SUPPORT, + PROP_P2P_SUPPORT, + PROP_WFD_SUPPORT, ); typedef struct { @@ -104,6 +125,8 @@ typedef struct { NMSupplicantFeature ap_support; /* Lightweight AP mode support */ NMSupplicantFeature pmf_support; NMSupplicantFeature fils_support; + NMSupplicantFeature p2p_support; + NMSupplicantFeature wfd_support; guint32 max_scan_ssids; guint32 ready_count; @@ -120,6 +143,14 @@ typedef struct { GCancellable * init_cancellable; GDBusProxy * iface_proxy; GCancellable * other_cancellable; + GDBusProxy * p2p_proxy; + GDBusProxy * group_proxy; + + gboolean p2p_proxy_acquired; + gboolean group_proxy_acquired; + gboolean p2p_capable; + + gboolean p2p_group_owner; WpsData *wps_data; @@ -129,6 +160,8 @@ typedef struct { GHashTable * bss_proxies; char * current_bss; + GHashTable * peer_proxies; + gint64 last_scan; /* timestamp as returned by nm_utils_get_monotonic_timestamp_ms() */ } NMSupplicantInterfacePrivate; @@ -311,6 +344,122 @@ bss_add_new (NMSupplicantInterface *self, const char *object_path) self); } +static void +peer_data_destroy (gpointer user_data) +{ + PeerData *peer_data = user_data; + + nm_clear_g_signal_handler (peer_data->proxy, &peer_data->change_id); + g_object_unref (peer_data->proxy); + g_slice_free (PeerData, peer_data); +} + +static void +peer_proxy_properties_changed_cb (GDBusProxy *proxy, + GVariant *changed_properties, + char **invalidated_properties, + gpointer user_data) +{ + NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); + + g_signal_emit (self, signals[PEER_UPDATED], 0, + g_dbus_proxy_get_object_path (proxy), + changed_properties); +} + +static GVariant * +peer_proxy_get_properties (NMSupplicantInterface *self, GDBusProxy *proxy) +{ + gs_strfreev char **properties = NULL; + GVariantBuilder builder; + char **iter; + + iter = properties = g_dbus_proxy_get_cached_property_names (proxy); + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); + if (iter) { + while (*iter) { + GVariant *copy = g_dbus_proxy_get_cached_property (proxy, *iter); + + g_variant_builder_add (&builder, "{sv}", *iter++, copy); + g_variant_unref (copy); + } + } + return g_variant_builder_end (&builder); +} + +static void +peer_proxy_acquired_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) +{ + NMSupplicantInterface *self; + NMSupplicantInterfacePrivate *priv; + gs_free_error GError *error = NULL; + GVariant *props = NULL; + const char *object_path; + PeerData *peer_data; + gboolean success; + + success = g_async_initable_init_finish (G_ASYNC_INITABLE (proxy), result, &error); + if ( !success + && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = NM_SUPPLICANT_INTERFACE (user_data); + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + if (!success) { + _LOGD ("failed to acquire Peer proxy: (%s)", error->message); + g_hash_table_remove (priv->peer_proxies, + g_dbus_proxy_get_object_path (proxy)); + return; + } + + object_path = g_dbus_proxy_get_object_path (proxy); + peer_data = g_hash_table_lookup (priv->peer_proxies, object_path); + if (!peer_data) + return; + + peer_data->change_id = g_signal_connect (proxy, "g-properties-changed", G_CALLBACK (peer_proxy_properties_changed_cb), self); + + props = peer_proxy_get_properties (self, proxy); + + g_signal_emit (self, signals[PEER_UPDATED], 0, + g_dbus_proxy_get_object_path (proxy), + g_variant_ref_sink (props)); + g_variant_unref (props); +} + +static void +peer_add_new (NMSupplicantInterface *self, const char *object_path) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + GDBusProxy *peer_proxy; + PeerData *peer_data; + + g_return_if_fail (object_path != NULL); + + if (g_hash_table_lookup (priv->peer_proxies, object_path)) + return; + + peer_proxy = g_object_new (G_TYPE_DBUS_PROXY, + "g-bus-type", G_BUS_TYPE_SYSTEM, + "g-flags", G_DBUS_PROXY_FLAGS_NONE, + "g-name", WPAS_DBUS_SERVICE, + "g-object-path", object_path, + "g-interface-name", WPAS_DBUS_IFACE_PEER, + NULL); + peer_data = g_slice_new0 (PeerData); + peer_data->proxy = peer_proxy; + g_hash_table_insert (priv->peer_proxies, + (char *) g_dbus_proxy_get_object_path (peer_proxy), + peer_data); + g_async_initable_init_async (G_ASYNC_INITABLE (peer_proxy), + G_PRIORITY_DEFAULT, + priv->other_cancellable, + (GAsyncReadyCallback) peer_proxy_acquired_cb, + self); +} + /*****************************************************************************/ static void @@ -450,12 +599,24 @@ static void parse_capabilities (NMSupplicantInterface *self, GVariant *capabilities) { NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - gboolean have_active = FALSE, have_ssid = FALSE; + gboolean have_active = FALSE, have_p2p = FALSE, have_ssid = FALSE; gint32 max_scan_ssids = -1; const char **array; g_return_if_fail (capabilities && g_variant_is_of_type (capabilities, G_VARIANT_TYPE_VARDICT)); + if ( g_variant_lookup (capabilities, "Modes", "^a&s", &array) + && array) { + if (g_strv_contains (array, "p2p")) + have_p2p = TRUE; + g_free (array); + } + + if (priv->p2p_capable != have_p2p) { + priv->p2p_capable = have_p2p; + _notify (self, PROP_P2P_AVAILABLE); + } + if ( g_variant_lookup (capabilities, "Scan", "^a&s", &array) && array) { if (g_strv_contains (array, "active")) @@ -555,6 +716,49 @@ iface_check_netreply_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_ iface_check_ready (self); } +static void +iface_set_pmf_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) +{ + NMSupplicantInterface *self; + gs_unref_variant GVariant *variant = NULL; + gs_free_error GError *error = NULL; + + variant = g_dbus_proxy_call_finish (proxy, result, &error); + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = NM_SUPPLICANT_INTERFACE (user_data); + + /* This can fail if the supplicant doesn't support PMF */ + if (error) + _LOGD ("failed to set Pmf=1: %s", error->message); + + iface_check_ready (self); +} + +gboolean +nm_supplicant_interface_get_p2p_group_joined (NMSupplicantInterface *self) +{ + return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->group_proxy_acquired; +} + +const char* +nm_supplicant_interface_get_p2p_group_path (NMSupplicantInterface *self) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + if (priv->group_proxy_acquired) + return g_dbus_proxy_get_object_path (priv->group_proxy); + else + return NULL; +} + +gboolean +nm_supplicant_interface_get_p2p_group_owner (NMSupplicantInterface *self) +{ + return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->p2p_group_owner; +} + NMSupplicantFeature nm_supplicant_interface_get_ap_support (NMSupplicantInterface *self) { @@ -573,6 +777,18 @@ nm_supplicant_interface_get_fils_support (NMSupplicantInterface *self) return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->fils_support; } +NMSupplicantFeature +nm_supplicant_interface_get_p2p_support (NMSupplicantInterface *self) +{ + return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->p2p_support; +} + +NMSupplicantFeature +nm_supplicant_interface_get_wfd_support (NMSupplicantInterface *self) +{ + return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->wfd_support; +} + void nm_supplicant_interface_set_ap_support (NMSupplicantInterface *self, NMSupplicantFeature ap_support) @@ -613,6 +829,24 @@ nm_supplicant_interface_set_fils_support (NMSupplicantInterface *self, priv->fils_support = fils_support; } +void +nm_supplicant_interface_set_p2p_support (NMSupplicantInterface *self, + NMSupplicantFeature p2p_support) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + priv->p2p_support = p2p_support; +} + +void +nm_supplicant_interface_set_wfd_support (NMSupplicantInterface *self, + NMSupplicantFeature wfd_support) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + priv->wfd_support = wfd_support; +} + /*****************************************************************************/ static void @@ -1100,10 +1334,260 @@ props_changed_cb (GDBusProxy *proxy, _LOGW ("connection disconnected (reason %d)", priv->disconnect_reason); } + /* We may not have priv->dev set yet if this interface was created from a + * known wpa_supplicant interface without knowing the device name. + */ + if (priv->dev == NULL && g_variant_lookup (changed_properties, "Ifname", "&s", &s)) { + priv->dev = g_strdup (s); + _notify (self, PROP_IFACE); + } + + g_object_thaw_notify (G_OBJECT (self)); +} + +static void +group_props_changed_cb (GDBusProxy *proxy, + GVariant *changed_properties, + char **invalidated_properties, + gpointer user_data) +{ + NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + char *s; + + g_object_freeze_notify (G_OBJECT (self)); + +#if 0 + v = g_variant_lookup_value (properties, "BSSID", G_VARIANT_TYPE_BYTESTRING); + if (v) { + bytes = g_variant_get_fixed_array (v, &len, 1); + if ( len == ETH_ALEN + && memcmp (bytes, nm_ip_addr_zero.addr_eth, ETH_ALEN) != 0 + && memcmp (bytes, (char[ETH_ALEN]) { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, ETH_ALEN) != 0) + nm_wifi_p2p_group_set_bssid_bin (group, bytes); + g_variant_unref (v); + } + + v = g_variant_lookup_value (properties, "SSID", G_VARIANT_TYPE_BYTESTRING); + if (v) { + bytes = g_variant_get_fixed_array (v, &len, 1); + len = MIN (32, len); + + /* Stupid ieee80211 layer uses <hidden> */ + if ( bytes && len + && !(((len == 8) || (len == 9)) && !memcmp (bytes, "<hidden>", 8)) + && !nm_utils_is_empty_ssid (bytes, len)) + nm_wifi_p2p_group_set_ssid (group, bytes, len); + + g_variant_unref (v); + } +#endif + + if (g_variant_lookup (changed_properties, "Role", "s", &s)) { + priv->p2p_group_owner = g_strcmp0 (s, "GO") == 0; + _notify (self, PROP_P2P_GROUP_OWNER); + g_free (s); + } + + /* NOTE: We do not seem to get any property change notifications for the Members + * property. However, we can keep track of these indirectly either by querying + * the groups that each peer is in or listening to the Join/Disconnect + * notifications. + */ + + g_object_thaw_notify (G_OBJECT (self)); +} + +static void +group_proxy_acquired_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) +{ + NMSupplicantInterface *self; + NMSupplicantInterfacePrivate *priv; + gs_free_error GError *error = NULL; + gboolean success; + + success = g_async_initable_init_finish (G_ASYNC_INITABLE (proxy), result, &error); + if ( !success + && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = NM_SUPPLICANT_INTERFACE (user_data); + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + if (!success) { + _LOGD ("failed to acquire Group proxy: (%s)", error->message); + g_clear_object (&priv->group_proxy); + return; + } + + priv->group_proxy_acquired = TRUE; + _notify (self, PROP_P2P_GROUP_JOINED); + _notify (self, PROP_P2P_GROUP_PATH); + + iface_check_ready (self); +} + +static void +p2p_props_changed_cb (GDBusProxy *proxy, + GVariant *changed_properties, + GStrv invalidated_properties, + gpointer user_data) +{ + NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + const char **array, **iter; + const char *path = NULL; + + g_object_freeze_notify (G_OBJECT (self)); + + if (g_variant_lookup (changed_properties, "Peers", "^a&o", &array)) { + iter = array; + while (*iter) + peer_add_new (self, *iter++); + g_free (array); + } + + if (g_variant_lookup (changed_properties, "Group", "&o", &path)) { + if (priv->group_proxy && g_strcmp0 (path, g_dbus_proxy_get_object_path (priv->group_proxy)) == 0) { + /* We already have the proxy, nothing to do. */ + } else if (path && g_strcmp0 (path, "/") != 0) { + if (priv->group_proxy != NULL) { + _LOGW ("P2P: Unexpected udpate of the group object path"); + priv->group_proxy_acquired = FALSE; + _notify (self, PROP_P2P_GROUP_JOINED); + _notify (self, PROP_P2P_GROUP_PATH); + g_clear_object (&priv->group_proxy); + } + + /* Delay ready state if we have not reached it yet. */ + if (priv->ready_count) + priv->ready_count++; + + priv->group_proxy = g_object_new (G_TYPE_DBUS_PROXY, + "g-bus-type", G_BUS_TYPE_SYSTEM, + "g-flags", G_DBUS_PROXY_FLAGS_NONE, + "g-name", WPAS_DBUS_SERVICE, + "g-object-path", path, + "g-interface-name", WPAS_DBUS_IFACE_GROUP, + NULL); + g_signal_connect (priv->group_proxy, "g-properties-changed", G_CALLBACK (group_props_changed_cb), self); + g_async_initable_init_async (G_ASYNC_INITABLE (priv->group_proxy), + G_PRIORITY_DEFAULT, + priv->other_cancellable, + (GAsyncReadyCallback) group_proxy_acquired_cb, + self); + } else { + priv->group_proxy_acquired = FALSE; + _notify (self, PROP_P2P_GROUP_JOINED); + _notify (self, PROP_P2P_GROUP_PATH); + g_clear_object (&priv->group_proxy); + } + } + g_object_thaw_notify (G_OBJECT (self)); } static void +p2p_device_found (GDBusProxy *proxy, + const char *path, + gpointer user_data) +{ + NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); + + peer_add_new (self, path); +} + +static void +p2p_device_lost (GDBusProxy *proxy, + const char *path, + gpointer user_data) +{ + NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + PeerData *peer_data; + + peer_data = g_hash_table_lookup (priv->peer_proxies, path); + if (!peer_data) + return; + g_hash_table_steal (priv->peer_proxies, path); + g_signal_emit (self, signals[PEER_REMOVED], 0, path); + peer_data_destroy (peer_data); +} + +static void +p2p_group_started (GDBusProxy *proxy, + GVariant *params, + gpointer user_data) +{ + NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + NMSupplicantInterface *iface = NULL; + char *group_path = NULL; + char *iface_path = NULL; + + /* There is one more parameter: the role, but we don't really care about that here. */ + if (!g_variant_lookup (params, "group_object", "&o", &group_path)) { + _LOGW ("P2P: GroupStarted signal is missing the \"group_object\" parameter"); + return; + } + + if (!g_variant_lookup (params, "interface_object", "&o", &iface_path)) { + _LOGW ("P2P: GroupStarted signal is missing the \"interface\" parameter"); + return; + } + + if (g_strcmp0 (iface_path, priv->object_path) == 0) { + _LOGW ("P2P: GroupStarted on existing interface"); + iface = g_object_ref (self); + } else { + iface = nm_supplicant_manager_create_interface_from_path (nm_supplicant_manager_get (), + iface_path); + if (iface == NULL) { + _LOGW ("P2P: Group interface already exists in GroupStarted handler, aborting further processing."); + return; + } + } + + /* Signal existance of the (new) interface. */ + g_signal_emit (self, signals[GROUP_STARTED], 0, iface); + g_object_unref (iface); +} + +static void +p2p_group_formation_failure (GDBusProxy *proxy, + const char *group, + gpointer user_data) +{ + NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); + + g_signal_emit (self, signals[GROUP_FORMATION_FAILURE], 0, group); +} + +static void +p2p_group_finished (GDBusProxy *proxy, + GVariant *params, + gpointer user_data) +{ + NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + const char *iface_path = NULL; + /* TODO: Group finished is called on the management interface! + * This means the signal consumer will currently need to assume which + * interface is finishing or it needs to match the object paths. + */ + + if (!g_variant_lookup (params, "interface_object", "&o", &iface_path)) { + _LOGW ("P2P: GroupFinished signal is missing the \"interface\" parameter"); + return; + } + + _LOGD ("P2P: GroupFinished signal on interface %s for interface %s", priv->object_path, iface_path); + + /* Signal group finish interface (on management interface). */ + g_signal_emit (self, signals[GROUP_FINISHED], 0, iface_path); +} + +static void on_iface_proxy_acquired (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) { NMSupplicantInterface *self; @@ -1155,8 +1639,21 @@ on_iface_proxy_acquired (GDBusProxy *proxy, GAsyncResult *result, gpointer user_ NULL, NULL); + /* Initialize global PMF setting to 'optional' */ + priv->ready_count++; + g_dbus_proxy_call (priv->iface_proxy, + DBUS_INTERFACE_PROPERTIES ".Set", + g_variant_new ("(ssv)", + WPAS_DBUS_IFACE_INTERFACE, + "Pmf", + g_variant_new_string ("1")), + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->init_cancellable, + (GAsyncReadyCallback) iface_set_pmf_cb, + self); + /* Check whether NetworkReply and AP mode are supported */ - priv->ready_count = 1; g_dbus_proxy_call (priv->iface_proxy, "NetworkReply", g_variant_new ("(oss)", @@ -1188,13 +1685,63 @@ on_iface_proxy_acquired (GDBusProxy *proxy, GAsyncResult *result, gpointer user_ } static void +on_p2p_proxy_acquired (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) +{ + NMSupplicantInterface *self; + NMSupplicantInterfacePrivate *priv; + gs_free_error GError *error = NULL; + + if (!g_async_initable_init_finish (G_ASYNC_INITABLE (proxy), result, &error)) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + self = NM_SUPPLICANT_INTERFACE (user_data); + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + _LOGW ("failed to acquire wpa_supplicant p2p proxy: (%s)", error->message); + + g_clear_object (&priv->p2p_proxy); + + iface_check_ready (self); + } + return; + } + + self = NM_SUPPLICANT_INTERFACE (user_data); + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + _nm_dbus_signal_connect (priv->p2p_proxy, "DeviceFound", G_VARIANT_TYPE ("(o)"), + G_CALLBACK (p2p_device_found), self); + _nm_dbus_signal_connect (priv->p2p_proxy, "DeviceLost", G_VARIANT_TYPE ("(o)"), + G_CALLBACK (p2p_device_lost), self); + _nm_dbus_signal_connect (priv->p2p_proxy, "GroupStarted", G_VARIANT_TYPE ("(a{sv})"), + G_CALLBACK (p2p_group_started), self); + _nm_dbus_signal_connect (priv->p2p_proxy, "GroupFormationFailure", G_VARIANT_TYPE ("(s)"), + G_CALLBACK (p2p_group_formation_failure), self); + _nm_dbus_signal_connect (priv->p2p_proxy, "GroupFinished", G_VARIANT_TYPE ("(a{sv})"), + G_CALLBACK (p2p_group_finished), self); + /* TODO: + * * WpsFailed + * * FindStopped + * * GONegotationFailure + * * InvitationReceived + */ + + priv->p2p_proxy_acquired = TRUE; + _notify (self, PROP_P2P_AVAILABLE); + + iface_check_ready (self); +} + +static void interface_add_done (NMSupplicantInterface *self, const char *path) { NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); _LOGD ("interface added to supplicant"); + /* Iface ready check happens in iface_check_netreply_cb */ + priv->ready_count = 1; + priv->object_path = g_strdup (path); + _notify (self, PROP_OBJECT_PATH); priv->iface_proxy = g_object_new (G_TYPE_DBUS_PROXY, "g-bus-type", G_BUS_TYPE_SYSTEM, "g-flags", G_DBUS_PROXY_FLAGS_NONE, @@ -1208,6 +1755,23 @@ interface_add_done (NMSupplicantInterface *self, const char *path) priv->init_cancellable, (GAsyncReadyCallback) on_iface_proxy_acquired, self); + + if (priv->p2p_support == NM_SUPPLICANT_FEATURE_YES) { + priv->ready_count++; + priv->p2p_proxy = g_object_new (G_TYPE_DBUS_PROXY, + "g-bus-type", G_BUS_TYPE_SYSTEM, + "g-flags", G_DBUS_PROXY_FLAGS_NONE, + "g-name", WPAS_DBUS_SERVICE, + "g-object-path", priv->object_path, + "g-interface-name", WPAS_DBUS_IFACE_INTERFACE_P2P_DEVICE, + NULL); + g_signal_connect (priv->p2p_proxy, "g-properties-changed", G_CALLBACK (p2p_props_changed_cb), self); + g_async_initable_init_async (G_ASYNC_INITABLE (priv->p2p_proxy), + G_PRIORITY_DEFAULT, + priv->init_cancellable, + (GAsyncReadyCallback) on_p2p_proxy_acquired, + self); + } } static void @@ -1289,6 +1853,39 @@ interface_add_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) } } +static void +interface_removed_cb (GDBusProxy *proxy, + const char *path, + gpointer user_data) +{ + NMSupplicantInterface *self; + NMSupplicantInterfacePrivate *priv; + + self = NM_SUPPLICANT_INTERFACE (user_data); + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + if (g_strcmp0 (priv->object_path, path) != 0) + return; + + _LOGD ("Received interface removed signal"); + + /* The interface may lose its last reference during signal handling otherwise. */ + g_object_ref (self); + + /* Invalidate the object path to prevent the manager from trying to remove + * a non-existing interface. */ + g_clear_pointer (&priv->object_path, g_free); + _notify (self, PROP_OBJECT_PATH); + + /* No need to clean up everything now, that will happen at dispose time. */ + + /* Interface is down and has been removed. */ + set_state (self, NM_SUPPLICANT_INTERFACE_STATE_DOWN); + g_signal_emit (self, signals[REMOVED], 0); + + g_object_unref (self); +} + #if HAVE_WEXT #define DEFAULT_WIFI_DRIVER "nl80211,wext" #else @@ -1303,7 +1900,6 @@ on_wpas_proxy_acquired (GDBusProxy *proxy, GAsyncResult *result, gpointer user_d gs_free_error GError *error = NULL; GDBusProxy *wpas_proxy; GVariantBuilder props; - const char *driver_name = NULL; wpas_proxy = g_dbus_proxy_new_for_bus_finish (result, &error); if (!wpas_proxy) { @@ -1320,41 +1916,53 @@ on_wpas_proxy_acquired (GDBusProxy *proxy, GAsyncResult *result, gpointer user_d priv->wpas_proxy = wpas_proxy; + /* Watch for interface removal. */ + _nm_dbus_signal_connect (priv->wpas_proxy, "InterfaceRemoved", G_VARIANT_TYPE ("(o)"), + G_CALLBACK (interface_removed_cb), self); + /* Try to add the interface to the supplicant. If the supplicant isn't * running, this will start it via D-Bus activation and return the response * when the supplicant has started. */ - switch (priv->driver) { - case NM_SUPPLICANT_DRIVER_WIRELESS: - driver_name = DEFAULT_WIFI_DRIVER; - break; - case NM_SUPPLICANT_DRIVER_WIRED: - driver_name = "wired"; - break; - case NM_SUPPLICANT_DRIVER_MACSEC: - driver_name = "macsec_linux"; - break; - } + if (priv->dev != NULL) { + const char *driver_name = NULL; + + switch (priv->driver) { + case NM_SUPPLICANT_DRIVER_WIRELESS: + driver_name = DEFAULT_WIFI_DRIVER; + break; + case NM_SUPPLICANT_DRIVER_WIRED: + driver_name = "wired"; + break; + case NM_SUPPLICANT_DRIVER_MACSEC: + driver_name = "macsec_linux"; + break; + } - g_return_if_fail (driver_name); + g_return_if_fail (driver_name); - g_variant_builder_init (&props, G_VARIANT_TYPE_VARDICT); - g_variant_builder_add (&props, "{sv}", - "Driver", - g_variant_new_string (driver_name)); - g_variant_builder_add (&props, "{sv}", - "Ifname", - g_variant_new_string (priv->dev)); + g_variant_builder_init (&props, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add (&props, "{sv}", + "Driver", + g_variant_new_string (driver_name)); + g_variant_builder_add (&props, "{sv}", + "Ifname", + g_variant_new_string (priv->dev)); - g_dbus_proxy_call (priv->wpas_proxy, - "CreateInterface", - g_variant_new ("(a{sv})", &props), - G_DBUS_CALL_FLAGS_NONE, - -1, - priv->init_cancellable, - (GAsyncReadyCallback) interface_add_cb, - self); + g_dbus_proxy_call (priv->wpas_proxy, + "CreateInterface", + g_variant_new ("(a{sv})", &props), + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->init_cancellable, + (GAsyncReadyCallback) interface_add_cb, + self); + } else if (priv->object_path) { + interface_add_done (self, priv->object_path); + } else { + g_assert_not_reached (); + } } static void @@ -1374,8 +1982,7 @@ interface_add (NMSupplicantInterface *self) priv->init_cancellable = g_cancellable_new (); g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | - G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, NULL, WPAS_DBUS_SERVICE, WPAS_DBUS_PATH, @@ -1863,6 +2470,142 @@ nm_supplicant_interface_get_max_scan_ssids (NMSupplicantInterface *self) /*****************************************************************************/ +void +nm_supplicant_interface_p2p_start_find (NMSupplicantInterface *self, + guint timeout) +{ + NMSupplicantInterfacePrivate *priv; + GVariantBuilder builder; + + g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (self)); + g_return_if_fail (timeout > 0 && timeout <= 600); + + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + /* Find parameters */ + g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add (&builder, "{sv}", "Timeout", g_variant_new_int32 (timeout)); + + g_dbus_proxy_call (priv->p2p_proxy, + "Find", + g_variant_new ("(a{sv})", &builder), + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->other_cancellable, + (GAsyncReadyCallback) log_result_cb, + self); +} + +void +nm_supplicant_interface_p2p_stop_find (NMSupplicantInterface *self) +{ + NMSupplicantInterfacePrivate *priv; + + g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (self)); + + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + g_dbus_proxy_call (priv->p2p_proxy, + "StopFind", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->other_cancellable, + (GAsyncReadyCallback) scan_request_cb, + self); +} + +/*****************************************************************************/ + +void +nm_supplicant_interface_p2p_connect (NMSupplicantInterface * self, + const char * peer, + const char * wps_method, + const char * wps_pin) +{ + NMSupplicantInterfacePrivate *priv; + GVariantBuilder builder; + + g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (self)); + + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + /* Don't do anything if there is no connection to the supplicant yet. */ + if (!priv->p2p_proxy || !priv->object_path) + return; + + /* Connect parameters */ + g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); + + g_variant_builder_add (&builder, "{sv}", "wps_method", g_variant_new_string (wps_method)); + + if (wps_pin) + g_variant_builder_add (&builder, "{sv}", "pin", g_variant_new_string (wps_pin)); + + g_variant_builder_add (&builder, "{sv}", "peer", g_variant_new_object_path (peer)); + + g_variant_builder_add (&builder, "{sv}", "join", g_variant_new_boolean (FALSE)); + g_variant_builder_add (&builder, "{sv}", "persistent", g_variant_new_boolean (FALSE)); + g_variant_builder_add (&builder, "{sv}", "go_intent", g_variant_new_int32 (7)); + + g_dbus_proxy_call (priv->p2p_proxy, + "Connect", + g_variant_new ("(a{sv})", &builder), + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->other_cancellable, + (GAsyncReadyCallback) log_result_cb, + "p2p connect"); +} + +void +nm_supplicant_interface_p2p_cancel_connect (NMSupplicantInterface * self) +{ + NMSupplicantInterfacePrivate *priv; + + g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (self)); + + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + /* Don't do anything if there is no connection to the supplicant yet. */ + if (!priv->p2p_proxy || !priv->object_path) + return; + + g_dbus_proxy_call (priv->p2p_proxy, + "Cancel", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->other_cancellable, + (GAsyncReadyCallback) log_result_cb, + "cancel p2p connect"); +} + +void +nm_supplicant_interface_p2p_disconnect (NMSupplicantInterface * self) +{ + NMSupplicantInterfacePrivate *priv; + + g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (self)); + + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + /* Don't do anything if there is no connection to the supplicant. */ + if (!priv->p2p_proxy || !priv->object_path) + return; + + g_dbus_proxy_call (priv->p2p_proxy, + "Disconnect", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->other_cancellable, + (GAsyncReadyCallback) log_result_cb, + "p2p disconnect"); +} + +/*****************************************************************************/ + static void get_property (GObject *object, guint prop_id, @@ -1878,6 +2621,18 @@ get_property (GObject *object, case PROP_CURRENT_BSS: g_value_set_string (value, priv->current_bss); break; + case PROP_P2P_GROUP_JOINED: + g_value_set_boolean (value, priv->p2p_capable && priv->group_proxy_acquired); + break; + case PROP_P2P_GROUP_PATH: + g_value_set_string (value, nm_supplicant_interface_get_p2p_group_path (NM_SUPPLICANT_INTERFACE (object))); + break; + case PROP_P2P_GROUP_OWNER: + g_value_set_boolean (value, priv->p2p_group_owner); + break; + case PROP_P2P_AVAILABLE: + g_value_set_boolean (value, priv->p2p_capable && priv->p2p_proxy_acquired); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1896,7 +2651,10 @@ set_property (GObject *object, case PROP_IFACE: /* construct-only */ priv->dev = g_value_dup_string (value); - g_return_if_fail (priv->dev); + break; + case PROP_OBJECT_PATH: + /* construct-only */ + priv->object_path = g_value_dup_string (value); break; case PROP_DRIVER: /* construct-only */ @@ -1918,6 +2676,14 @@ set_property (GObject *object, /* construct-only */ priv->fils_support = g_value_get_int (value); break; + case PROP_P2P_SUPPORT: + /* construct-only */ + priv->p2p_support = g_value_get_int (value); + break; + case PROP_WFD_SUPPORT: + /* construct-only */ + priv->wfd_support = g_value_get_int (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1931,25 +2697,34 @@ nm_supplicant_interface_init (NMSupplicantInterface * self) priv->state = NM_SUPPLICANT_INTERFACE_STATE_INIT; priv->bss_proxies = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, bss_data_destroy); + priv->peer_proxies = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, peer_data_destroy); } NMSupplicantInterface * nm_supplicant_interface_new (const char *ifname, + const char *object_path, NMSupplicantDriver driver, NMSupplicantFeature fast_support, NMSupplicantFeature ap_support, NMSupplicantFeature pmf_support, - NMSupplicantFeature fils_support) + NMSupplicantFeature fils_support, + NMSupplicantFeature p2p_support, + NMSupplicantFeature wfd_support) { - g_return_val_if_fail (ifname != NULL, NULL); + /* One of ifname or path need to be set */ + g_return_val_if_fail (ifname != NULL || object_path != NULL, NULL); + g_return_val_if_fail (ifname == NULL || object_path == NULL, NULL); return g_object_new (NM_TYPE_SUPPLICANT_INTERFACE, NM_SUPPLICANT_INTERFACE_IFACE, ifname, + NM_SUPPLICANT_INTERFACE_OBJECT_PATH, object_path, NM_SUPPLICANT_INTERFACE_DRIVER, (guint) driver, NM_SUPPLICANT_INTERFACE_FAST_SUPPORT, (int) fast_support, NM_SUPPLICANT_INTERFACE_AP_SUPPORT, (int) ap_support, NM_SUPPLICANT_INTERFACE_PMF_SUPPORT, (int) pmf_support, NM_SUPPLICANT_INTERFACE_FILS_SUPPORT, (int) fils_support, + NM_SUPPLICANT_INTERFACE_P2P_SUPPORT, (int) p2p_support, + NM_SUPPLICANT_INTERFACE_WFD_SUPPORT, (int) wfd_support, NULL); } @@ -1979,12 +2754,21 @@ dispose (GObject *object) if (priv->iface_proxy) g_signal_handlers_disconnect_by_data (priv->iface_proxy, object); g_clear_object (&priv->iface_proxy); + if (priv->p2p_proxy) + g_signal_handlers_disconnect_by_data (priv->p2p_proxy, object); + g_clear_object (&priv->p2p_proxy); + if (priv->group_proxy) + g_signal_handlers_disconnect_by_data (priv->group_proxy, object); + g_clear_object (&priv->group_proxy); nm_clear_g_cancellable (&priv->init_cancellable); nm_clear_g_cancellable (&priv->other_cancellable); + if (priv->wpas_proxy) + g_signal_handlers_disconnect_by_data (priv->wpas_proxy, object); g_clear_object (&priv->wpas_proxy); g_clear_pointer (&priv->bss_proxies, g_hash_table_destroy); + g_clear_pointer (&priv->peer_proxies, g_hash_table_destroy); g_clear_pointer (&priv->net_path, g_free); g_clear_pointer (&priv->dev, g_free); @@ -2019,12 +2803,38 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass) G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_OBJECT_PATH] = + g_param_spec_string (NM_SUPPLICANT_INTERFACE_OBJECT_PATH, "", "", + NULL, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + obj_properties[PROP_P2P_GROUP_JOINED] = + g_param_spec_boolean (NM_SUPPLICANT_INTERFACE_P2P_GROUP_JOINED, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + obj_properties[PROP_P2P_GROUP_PATH] = + g_param_spec_string (NM_SUPPLICANT_INTERFACE_P2P_GROUP_PATH, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + obj_properties[PROP_P2P_GROUP_OWNER] = + g_param_spec_boolean (NM_SUPPLICANT_INTERFACE_P2P_GROUP_OWNER, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); obj_properties[PROP_DRIVER] = g_param_spec_uint (NM_SUPPLICANT_INTERFACE_DRIVER, "", "", 0, G_MAXUINT, NM_SUPPLICANT_DRIVER_WIRELESS, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_P2P_AVAILABLE] = + g_param_spec_boolean (NM_SUPPLICANT_INTERFACE_P2P_AVAILABLE, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); obj_properties[PROP_FAST_SUPPORT] = g_param_spec_int (NM_SUPPLICANT_INTERFACE_FAST_SUPPORT, "", "", NM_SUPPLICANT_FEATURE_UNKNOWN, @@ -2057,6 +2867,22 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass) G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_P2P_SUPPORT] = + g_param_spec_int (NM_SUPPLICANT_INTERFACE_P2P_SUPPORT, "", "", + NM_SUPPLICANT_FEATURE_UNKNOWN, + NM_SUPPLICANT_FEATURE_YES, + NM_SUPPLICANT_FEATURE_UNKNOWN, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + obj_properties[PROP_WFD_SUPPORT] = + g_param_spec_int (NM_SUPPLICANT_INTERFACE_WFD_SUPPORT, "", "", + NM_SUPPLICANT_FEATURE_UNKNOWN, + NM_SUPPLICANT_FEATURE_YES, + NM_SUPPLICANT_FEATURE_UNKNOWN, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); @@ -2092,6 +2918,22 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass) NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_STRING); + signals[PEER_UPDATED] = + g_signal_new (NM_SUPPLICANT_INTERFACE_PEER_UPDATED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_VARIANT); + + signals[PEER_REMOVED] = + g_signal_new (NM_SUPPLICANT_INTERFACE_PEER_REMOVED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_STRING); + signals[SCAN_DONE] = g_signal_new (NM_SUPPLICANT_INTERFACE_SCAN_DONE, G_OBJECT_CLASS_TYPE (object_class), @@ -2115,4 +2957,28 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass) 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_VARIANT); + + signals[GROUP_STARTED] = + g_signal_new (NM_SUPPLICANT_INTERFACE_GROUP_STARTED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 1, NM_TYPE_SUPPLICANT_INTERFACE); + + signals[GROUP_FINISHED] = + g_signal_new (NM_SUPPLICANT_INTERFACE_GROUP_FINISHED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_STRING); + + signals[GROUP_FORMATION_FAILURE] = + g_signal_new (NM_SUPPLICANT_INTERFACE_GROUP_FORMATION_FAILURE, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_VARIANT); } diff --git a/src/supplicant/nm-supplicant-interface.h b/src/supplicant/nm-supplicant-interface.h index 0365fdcd..0aa7732a 100644 --- a/src/supplicant/nm-supplicant-interface.h +++ b/src/supplicant/nm-supplicant-interface.h @@ -55,33 +55,48 @@ typedef enum { /* Properties */ #define NM_SUPPLICANT_INTERFACE_IFACE "iface" +#define NM_SUPPLICANT_INTERFACE_OBJECT_PATH "object-path" #define NM_SUPPLICANT_INTERFACE_SCANNING "scanning" #define NM_SUPPLICANT_INTERFACE_CURRENT_BSS "current-bss" +#define NM_SUPPLICANT_INTERFACE_P2P_GROUP_JOINED "p2p-group-joined" +#define NM_SUPPLICANT_INTERFACE_P2P_GROUP_PATH "p2p-group-path" +#define NM_SUPPLICANT_INTERFACE_P2P_GROUP_OWNER "p2p-group-owner" #define NM_SUPPLICANT_INTERFACE_DRIVER "driver" +#define NM_SUPPLICANT_INTERFACE_P2P_AVAILABLE "p2p-available" #define NM_SUPPLICANT_INTERFACE_FAST_SUPPORT "fast-support" #define NM_SUPPLICANT_INTERFACE_AP_SUPPORT "ap-support" #define NM_SUPPLICANT_INTERFACE_PMF_SUPPORT "pmf-support" #define NM_SUPPLICANT_INTERFACE_FILS_SUPPORT "fils-support" +#define NM_SUPPLICANT_INTERFACE_P2P_SUPPORT "p2p-support" +#define NM_SUPPLICANT_INTERFACE_WFD_SUPPORT "wfd-support" /* Signals */ #define NM_SUPPLICANT_INTERFACE_STATE "state" #define NM_SUPPLICANT_INTERFACE_REMOVED "removed" #define NM_SUPPLICANT_INTERFACE_BSS_UPDATED "bss-updated" #define NM_SUPPLICANT_INTERFACE_BSS_REMOVED "bss-removed" +#define NM_SUPPLICANT_INTERFACE_PEER_UPDATED "peer-updated" +#define NM_SUPPLICANT_INTERFACE_PEER_REMOVED "peer-removed" #define NM_SUPPLICANT_INTERFACE_SCAN_DONE "scan-done" #define NM_SUPPLICANT_INTERFACE_CREDENTIALS_REQUEST "credentials-request" #define NM_SUPPLICANT_INTERFACE_WPS_CREDENTIALS "wps-credentials" +#define NM_SUPPLICANT_INTERFACE_GROUP_STARTED "group-started" +#define NM_SUPPLICANT_INTERFACE_GROUP_FINISHED "group-finished" +#define NM_SUPPLICANT_INTERFACE_GROUP_FORMATION_FAILURE "group-formation-failure" typedef struct _NMSupplicantInterfaceClass NMSupplicantInterfaceClass; GType nm_supplicant_interface_get_type (void); NMSupplicantInterface * nm_supplicant_interface_new (const char *ifname, + const char *object_path, NMSupplicantDriver driver, NMSupplicantFeature fast_support, NMSupplicantFeature ap_support, NMSupplicantFeature pmf_support, - NMSupplicantFeature fils_support); + NMSupplicantFeature fils_support, + NMSupplicantFeature p2p_support, + NMSupplicantFeature wfd_support); void nm_supplicant_interface_set_supplicant_available (NMSupplicantInterface *self, gboolean available); @@ -120,14 +135,33 @@ guint nm_supplicant_interface_get_max_scan_ssids (NMSupplicantInterface *self); gboolean nm_supplicant_interface_get_has_credentials_request (NMSupplicantInterface *self); +gboolean nm_supplicant_interface_get_p2p_group_joined (NMSupplicantInterface *self); + +const char* nm_supplicant_interface_get_p2p_group_path (NMSupplicantInterface *self); + +gboolean nm_supplicant_interface_get_p2p_group_owner (NMSupplicantInterface *self); + gboolean nm_supplicant_interface_credentials_reply (NMSupplicantInterface *self, const char *field, const char *value, GError **error); +void nm_supplicant_interface_p2p_start_find (NMSupplicantInterface *self, + guint timeout); +void nm_supplicant_interface_p2p_stop_find (NMSupplicantInterface *self); + +void nm_supplicant_interface_p2p_connect (NMSupplicantInterface * self, + const char * peer, + const char * wps_method, + const char * wps_pin); +void nm_supplicant_interface_p2p_cancel_connect (NMSupplicantInterface * self); +void nm_supplicant_interface_p2p_disconnect (NMSupplicantInterface * self); + NMSupplicantFeature nm_supplicant_interface_get_ap_support (NMSupplicantInterface *self); NMSupplicantFeature nm_supplicant_interface_get_pmf_support (NMSupplicantInterface *self); NMSupplicantFeature nm_supplicant_interface_get_fils_support (NMSupplicantInterface *self); +NMSupplicantFeature nm_supplicant_interface_get_p2p_support (NMSupplicantInterface *self); +NMSupplicantFeature nm_supplicant_interface_get_wfd_support (NMSupplicantInterface *self); void nm_supplicant_interface_set_ap_support (NMSupplicantInterface *self, NMSupplicantFeature apmode); @@ -141,6 +175,12 @@ void nm_supplicant_interface_set_pmf_support (NMSupplicantInterface *self, void nm_supplicant_interface_set_fils_support (NMSupplicantInterface *self, NMSupplicantFeature fils_support); +void nm_supplicant_interface_set_p2p_support (NMSupplicantInterface *self, + NMSupplicantFeature p2p_support); + +void nm_supplicant_interface_set_wfd_support (NMSupplicantInterface *self, + NMSupplicantFeature wfd_support); + void nm_supplicant_interface_enroll_wps (NMSupplicantInterface *self, const char *const type, const char *bssid, diff --git a/src/supplicant/nm-supplicant-manager.c b/src/supplicant/nm-supplicant-manager.c index 5ab96f88..64a057f3 100644 --- a/src/supplicant/nm-supplicant-manager.c +++ b/src/supplicant/nm-supplicant-manager.c @@ -41,6 +41,8 @@ typedef struct { NMSupplicantFeature ap_support; NMSupplicantFeature pmf_support; NMSupplicantFeature fils_support; + NMSupplicantFeature p2p_support; + NMSupplicantFeature wfd_support; guint die_count_reset_id; guint die_count; } NMSupplicantManagerPrivate; @@ -159,11 +161,71 @@ nm_supplicant_manager_create_interface (NMSupplicantManager *self, } iface = nm_supplicant_interface_new (ifname, + NULL, driver, priv->fast_support, priv->ap_support, priv->pmf_support, - priv->fils_support); + priv->fils_support, + priv->p2p_support, + priv->wfd_support); + + priv->ifaces = g_slist_prepend (priv->ifaces, iface); + g_object_add_toggle_ref ((GObject *) iface, _sup_iface_last_ref, self); + + /* If we're making the supplicant take a time out for a bit, don't + * let the supplicant interface start immediately, just let it hang + * around in INIT state until we're ready to talk to the supplicant + * again. + */ + if (is_available (self)) + nm_supplicant_interface_set_supplicant_available (iface, TRUE); + + return iface; +} + +/** + * nm_supplicant_manager_create_interface_from_path: + * @self: the #NMSupplicantManager + * @object_path: the DBus object path for which to obtain the supplicant interface + * + * Note: the manager owns a reference to the instance and the only way to + * get the manager to release it, is by dropping all other references + * to the supplicant-interface (or destroying the manager). + * + * Returns: (transfer full): returns a #NMSupplicantInterface or %NULL. + * Must be unrefed at the end. + * */ +NMSupplicantInterface * +nm_supplicant_manager_create_interface_from_path (NMSupplicantManager *self, + const char *object_path) +{ + NMSupplicantManagerPrivate *priv; + NMSupplicantInterface *iface; + GSList *ifaces; + + g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (self), NULL); + g_return_val_if_fail (object_path != NULL, NULL); + + priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); + + _LOGD ("creating new supplicant interface for dbus path %s", object_path); + + /* assert against not requesting duplicate interfaces. */ + for (ifaces = priv->ifaces; ifaces; ifaces = ifaces->next) { + if (g_strcmp0 (nm_supplicant_interface_get_object_path (ifaces->data), object_path) == 0) + g_return_val_if_reached (NULL); + } + + iface = nm_supplicant_interface_new (NULL, + object_path, + NM_SUPPLICANT_DRIVER_WIRELESS, + priv->fast_support, + priv->ap_support, + priv->pmf_support, + priv->fils_support, + priv->p2p_support, + priv->wfd_support); priv->ifaces = g_slist_prepend (priv->ifaces, iface); g_object_add_toggle_ref ((GObject *) iface, _sup_iface_last_ref, self); @@ -199,6 +261,8 @@ update_capabilities (NMSupplicantManager *self) priv->ap_support = NM_SUPPLICANT_FEATURE_UNKNOWN; priv->pmf_support = NM_SUPPLICANT_FEATURE_UNKNOWN; priv->fils_support = NM_SUPPLICANT_FEATURE_UNKNOWN; + /* P2P support is newer than the capabilities property */ + priv->p2p_support = NM_SUPPLICANT_FEATURE_NO; value = g_dbus_proxy_get_cached_property (priv->proxy, "Capabilities"); if (value) { @@ -207,6 +271,7 @@ update_capabilities (NMSupplicantManager *self) priv->ap_support = NM_SUPPLICANT_FEATURE_NO; priv->pmf_support = NM_SUPPLICANT_FEATURE_NO; priv->fils_support = NM_SUPPLICANT_FEATURE_NO; + priv->p2p_support = NM_SUPPLICANT_FEATURE_NO; if (array) { if (g_strv_contains (array, "ap")) priv->ap_support = NM_SUPPLICANT_FEATURE_YES; @@ -214,17 +279,20 @@ update_capabilities (NMSupplicantManager *self) priv->pmf_support = NM_SUPPLICANT_FEATURE_YES; if (g_strv_contains (array, "fils")) priv->fils_support = NM_SUPPLICANT_FEATURE_YES; + if (g_strv_contains (array, "p2p")) + priv->p2p_support = NM_SUPPLICANT_FEATURE_YES; g_free (array); } } g_variant_unref (value); } - /* Tell all interfaces about results of the AP/PMF/FILS check */ + /* Tell all interfaces about results of the AP/PMF/FILS/P2P check */ for (ifaces = priv->ifaces; ifaces; ifaces = ifaces->next) { nm_supplicant_interface_set_ap_support (ifaces->data, priv->ap_support); nm_supplicant_interface_set_pmf_support (ifaces->data, priv->pmf_support); nm_supplicant_interface_set_fils_support (ifaces->data, priv->fils_support); + nm_supplicant_interface_set_p2p_support (ifaces->data, priv->p2p_support); } _LOGD ("AP mode is %ssupported", @@ -236,6 +304,9 @@ update_capabilities (NMSupplicantManager *self) _LOGD ("FILS is %ssupported", (priv->fils_support == NM_SUPPLICANT_FEATURE_YES) ? "" : (priv->fils_support == NM_SUPPLICANT_FEATURE_NO) ? "not " : "possibly "); + _LOGD ("P2P is %ssupported", + (priv->p2p_support == NM_SUPPLICANT_FEATURE_YES) ? "" : + (priv->p2p_support == NM_SUPPLICANT_FEATURE_NO) ? "not " : "possibly "); /* EAP-FAST */ priv->fast_support = NM_SUPPLICANT_FEATURE_NO; @@ -264,6 +335,20 @@ update_capabilities (NMSupplicantManager *self) _LOGD ("EAP-FAST is %ssupported", (priv->fast_support == NM_SUPPLICANT_FEATURE_YES) ? "" : (priv->fast_support == NM_SUPPLICANT_FEATURE_NO) ? "not " : "possibly "); + + priv->wfd_support = NM_SUPPLICANT_FEATURE_NO; + value = g_dbus_proxy_get_cached_property (priv->proxy, "WFDIEs"); + if (value) { + priv->wfd_support = NM_SUPPLICANT_FEATURE_YES; + g_variant_unref (value); + } + + for (ifaces = priv->ifaces; ifaces; ifaces = ifaces->next) + nm_supplicant_interface_set_wfd_support (ifaces->data, priv->fast_support); + + _LOGD ("WFD is %ssupported", + (priv->wfd_support == NM_SUPPLICANT_FEATURE_YES) ? "" : + (priv->wfd_support == NM_SUPPLICANT_FEATURE_NO) ? "not " : "possibly "); } static void diff --git a/src/supplicant/nm-supplicant-manager.h b/src/supplicant/nm-supplicant-manager.h index 8928cf20..7225a36b 100644 --- a/src/supplicant/nm-supplicant-manager.h +++ b/src/supplicant/nm-supplicant-manager.h @@ -41,5 +41,7 @@ NMSupplicantManager *nm_supplicant_manager_get (void); NMSupplicantInterface *nm_supplicant_manager_create_interface (NMSupplicantManager *mgr, const char *ifname, NMSupplicantDriver driver); +NMSupplicantInterface *nm_supplicant_manager_create_interface_from_path (NMSupplicantManager *self, + const char *object_path); #endif /* __NETWORKMANAGER_SUPPLICANT_MANAGER_H__ */ diff --git a/src/supplicant/tests/meson.build b/src/supplicant/tests/meson.build index 5e4cbdbe..7cc9d6af 100644 --- a/src/supplicant/tests/meson.build +++ b/src/supplicant/tests/meson.build @@ -9,5 +9,5 @@ exe = executable( test( 'supplicant/' + test_unit, test_script, - args: test_args + [exe.full_path()] + args: test_args + [exe.full_path()], ) diff --git a/src/supplicant/tests/test-supplicant-config.c b/src/supplicant/tests/test-supplicant-config.c index 36831e67..d7ec1fe2 100644 --- a/src/supplicant/tests/test-supplicant-config.c +++ b/src/supplicant/tests/test-supplicant-config.c @@ -359,8 +359,8 @@ test_wifi_wpa_psk (const char *detail, NMTST_EXPECT_NM_INFO ("Config: added 'pairwise' value 'TKIP CCMP'"); NMTST_EXPECT_NM_INFO ("Config: added 'group' value 'TKIP CCMP'"); switch (pmf) { - case NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL: - NMTST_EXPECT_NM_INFO ("Config: added 'ieee80211w' value '1'"); + case NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE: + NMTST_EXPECT_NM_INFO ("Config: added 'ieee80211w' value '0'"); break; case NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED: NMTST_EXPECT_NM_INFO ("Config: added 'ieee80211w' value '2'"); diff --git a/src/systemd/meson.build b/src/systemd/meson.build index 870721b0..9dea4fb5 100644 --- a/src/systemd/meson.build +++ b/src/systemd/meson.build @@ -1,72 +1,46 @@ -sources = files( - 'sd-adapt/nm-sd-adapt.c', - 'src/basic/alloc-util.c', - 'src/basic/escape.c', - 'src/basic/env-util.c', - 'src/basic/ether-addr-util.c', - 'src/basic/extract-word.c', - 'src/basic/fd-util.c', - 'src/basic/fileio.c', - 'src/basic/fs-util.c', - 'src/basic/hash-funcs.c', - 'src/basic/hashmap.c', - 'src/basic/hexdecoct.c', - 'src/basic/hostname-util.c', - 'src/basic/in-addr-util.c', - 'src/basic/io-util.c', - 'src/basic/mempool.c', - 'src/basic/parse-util.c', - 'src/basic/path-util.c', - 'src/basic/prioq.c', - 'src/basic/process-util.c', - 'src/basic/random-util.c', - 'src/basic/socket-util.c', - 'src/basic/stat-util.c', - 'src/basic/string-table.c', - 'src/basic/string-util.c', - 'src/basic/strv.c', - 'src/basic/time-util.c', - 'src/basic/utf8.c', - 'src/basic/util.c', - 'src/libsystemd-network/arp-util.c', - 'src/libsystemd-network/dhcp-identifier.c', - 'src/libsystemd-network/dhcp-network.c', - 'src/libsystemd-network/dhcp-option.c', - 'src/libsystemd-network/dhcp-packet.c', - 'src/libsystemd-network/dhcp6-network.c', - 'src/libsystemd-network/dhcp6-option.c', - 'src/libsystemd-network/lldp-neighbor.c', - 'src/libsystemd-network/lldp-network.c', - 'src/libsystemd-network/network-internal.c', - 'src/libsystemd-network/sd-dhcp-client.c', - 'src/libsystemd-network/sd-dhcp-lease.c', - 'src/libsystemd-network/sd-dhcp6-client.c', - 'src/libsystemd-network/sd-dhcp6-lease.c', - 'src/libsystemd-network/sd-ipv4acd.c', - 'src/libsystemd-network/sd-ipv4ll.c', - 'src/libsystemd-network/sd-lldp.c', - 'src/libsystemd/sd-event/sd-event.c', - 'src/libsystemd/sd-id128/id128-util.c', - 'src/libsystemd/sd-id128/sd-id128.c', - 'src/shared/dns-domain.c', - 'nm-sd.c' -) - -incs = [ - src_inc, - include_directories( - 'sd-adapt', - 'src/basic', - 'src/libsystemd-network', - 'src/shared', - 'src/systemd' - ) -] - -libsystemd_nm = static_library( - 'systemd-nm', - sources: sources, - include_directories: incs, - dependencies: nm_core_dep, - c_args: '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_SYSTEMD' +libnm_systemd_core = static_library( + 'nm-systemd-core', + sources: files( + 'sd-adapt-core/nm-sd-adapt-core.c', + 'src/libsystemd-network/arp-util.c', + 'src/libsystemd-network/dhcp-identifier.c', + 'src/libsystemd-network/dhcp-network.c', + 'src/libsystemd-network/dhcp-option.c', + 'src/libsystemd-network/dhcp-packet.c', + 'src/libsystemd-network/dhcp6-network.c', + 'src/libsystemd-network/dhcp6-option.c', + 'src/libsystemd-network/lldp-neighbor.c', + 'src/libsystemd-network/lldp-network.c', + 'src/libsystemd-network/network-internal.c', + 'src/libsystemd-network/sd-dhcp-client.c', + 'src/libsystemd-network/sd-dhcp-lease.c', + 'src/libsystemd-network/sd-dhcp6-client.c', + 'src/libsystemd-network/sd-dhcp6-lease.c', + 'src/libsystemd-network/sd-ipv4acd.c', + 'src/libsystemd-network/sd-ipv4ll.c', + 'src/libsystemd-network/sd-lldp.c', + 'src/libsystemd/sd-event/event-util.c', + 'src/libsystemd/sd-event/sd-event.c', + 'src/libsystemd/sd-id128/id128-util.c', + 'src/libsystemd/sd-id128/sd-id128.c', + 'src/shared/dns-domain.c', + 'nm-sd.c', + 'nm-sd-utils-core.c', + ), + include_directories: [ + src_inc, + include_directories( + 'sd-adapt-core', + 'src/libsystemd-network', + 'src/libsystemd/sd-event', + 'src/shared', + 'src/systemd', + ) + ], + dependencies: [ + nm_core_dep, + ], + c_args: [ + '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_SYSTEMD', + ], ) diff --git a/src/systemd/nm-sd-utils-core.c b/src/systemd/nm-sd-utils-core.c new file mode 100644 index 00000000..42560789 --- /dev/null +++ b/src/systemd/nm-sd-utils-core.c @@ -0,0 +1,40 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright (C) 2018 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-sd-utils-core.h" + +#include "nm-core-internal.h" + +#include "nm-sd-adapt-core.h" + +#include "sd-id128.h" + +/*****************************************************************************/ + +NMUuid * +nm_sd_utils_id128_get_machine (NMUuid *out_uuid) +{ + g_assert (out_uuid); + + G_STATIC_ASSERT_EXPR (sizeof (*out_uuid) == sizeof (sd_id128_t)); + if (sd_id128_get_machine ((sd_id128_t *) out_uuid) < 0) + return NULL; + return out_uuid; +} diff --git a/src/systemd/nm-sd-utils-core.h b/src/systemd/nm-sd-utils-core.h new file mode 100644 index 00000000..a7b092b3 --- /dev/null +++ b/src/systemd/nm-sd-utils-core.h @@ -0,0 +1,30 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright (C) 2018 Red Hat, Inc. + */ + +#ifndef __NM_SD_UTILS_CORE_H__ +#define __NM_SD_UTILS_CORE_H__ + +/*****************************************************************************/ + +struct _NMUuid; + +struct _NMUuid *nm_sd_utils_id128_get_machine (struct _NMUuid *out_uuid); + +/*****************************************************************************/ + +#endif /* __NM_SD_UTILS_CORE_H__ */ diff --git a/src/systemd/nm-sd.c b/src/systemd/nm-sd.c index cbef91d1..4009c617 100644 --- a/src/systemd/nm-sd.c +++ b/src/systemd/nm-sd.c @@ -133,9 +133,13 @@ nm_sd_event_attach_default (void) /*****************************************************************************/ +const bool mempool_use_allowed = true; + +/*****************************************************************************/ + /* ensure that defines in nm-sd.h correspond to the internal defines. */ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" #include "dhcp-lease-internal.h" /*****************************************************************************/ diff --git a/src/systemd/sd-adapt/condition.h b/src/systemd/sd-adapt-core/condition.h index d3a6812a..d3a6812a 100644 --- a/src/systemd/sd-adapt/condition.h +++ b/src/systemd/sd-adapt-core/condition.h diff --git a/src/systemd/sd-adapt/conf-parser.h b/src/systemd/sd-adapt-core/conf-parser.h index 637892c2..637892c2 100644 --- a/src/systemd/sd-adapt/conf-parser.h +++ b/src/systemd/sd-adapt-core/conf-parser.h diff --git a/src/systemd/sd-adapt/khash.h b/src/systemd/sd-adapt-core/khash.h index 637892c2..637892c2 100644 --- a/src/systemd/sd-adapt/khash.h +++ b/src/systemd/sd-adapt-core/khash.h diff --git a/src/systemd/sd-adapt/nm-sd-adapt.c b/src/systemd/sd-adapt-core/nm-sd-adapt-core.c index 4e308276..d7ed687e 100644 --- a/src/systemd/sd-adapt/nm-sd-adapt.c +++ b/src/systemd/sd-adapt-core/nm-sd-adapt-core.c @@ -18,7 +18,7 @@ #include "nm-default.h" -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" #include "fd-util.h" diff --git a/src/systemd/sd-adapt-core/nm-sd-adapt-core.h b/src/systemd/sd-adapt-core/nm-sd-adapt-core.h new file mode 100644 index 00000000..8c07c53a --- /dev/null +++ b/src/systemd/sd-adapt-core/nm-sd-adapt-core.h @@ -0,0 +1,101 @@ +/* This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright (C) 2014 - 2018 Red Hat, Inc. + */ + +#ifndef __NM_SD_ADAPT_CORE_H__ +#define __NM_SD_ADAPT_CORE_H__ + +#include "nm-default.h" + +#include <stdbool.h> +#include <sys/resource.h> +#include <time.h> + +#include "systemd/sd-adapt-shared/nm-sd-adapt-shared.h" + +#ifndef HAVE_SYS_AUXV_H +#define HAVE_SYS_AUXV_H 0 +#endif + +/***************************************************************************** + * The remainder of the header is only enabled when building the systemd code + * itself. + *****************************************************************************/ + +#if (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_SYSTEMD + +#include <netinet/in.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <elf.h> +#ifdef HAVE_SYS_AUXV_H +#include <sys/auxv.h> +#endif +#include <unistd.h> +#include <sys/syscall.h> +#include <sys/ioctl.h> + +#include <net/if_arp.h> + +/* Missing in Linux 3.2.0, in Ubuntu 12.04 */ +#ifndef BPF_XOR +#define BPF_XOR 0xa0 +#endif + +#ifndef ETHERTYPE_LLDP +#define ETHERTYPE_LLDP 0x88cc +#endif + +#ifndef HAVE_SECURE_GETENV +# ifdef HAVE___SECURE_GETENV +# define secure_getenv __secure_getenv +# else +# error neither secure_getenv nor __secure_getenv is available +# endif +#endif + +/*****************************************************************************/ + +static inline int +sd_notify (int unset_environment, const char *state) +{ + return 0; +} + +/* Can't include both net/if.h and linux/if.h; so have to define this here */ +#ifndef IF_NAMESIZE +#define IF_NAMESIZE 16 +#endif + +#ifndef IFNAMSIZ +#define IFNAMSIZ IF_NAMESIZE +#endif + +#ifndef MAX_HANDLE_SZ +#define MAX_HANDLE_SZ 128 +#endif + +#include "sd-id128.h" +#include "sparse-endian.h" +#include "async.h" +#include "util.h" + +#endif /* (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_SYSTEMD */ + +#endif /* __NM_SD_ADAPT_CORE_H__ */ + diff --git a/src/systemd/sd-adapt/sd-daemon.h b/src/systemd/sd-adapt-core/sd-daemon.h index 637892c2..637892c2 100644 --- a/src/systemd/sd-adapt/sd-daemon.h +++ b/src/systemd/sd-adapt-core/sd-daemon.h diff --git a/src/systemd/sd-adapt/sd-device.h b/src/systemd/sd-adapt-core/sd-device.h index 637892c2..637892c2 100644 --- a/src/systemd/sd-adapt/sd-device.h +++ b/src/systemd/sd-adapt-core/sd-device.h diff --git a/src/systemd/sd-adapt/stat-util.h b/src/systemd/sd-adapt-core/stat-util.h index 637892c2..637892c2 100644 --- a/src/systemd/sd-adapt/stat-util.h +++ b/src/systemd/sd-adapt-core/stat-util.h diff --git a/src/systemd/sd-adapt/architecture.h b/src/systemd/sd-adapt/architecture.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/architecture.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/btrfs-util.h b/src/systemd/sd-adapt/btrfs-util.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/btrfs-util.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/build.h b/src/systemd/sd-adapt/build.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/build.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/cgroup-util.h b/src/systemd/sd-adapt/cgroup-util.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/cgroup-util.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/copy.h b/src/systemd/sd-adapt/copy.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/copy.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/def.h b/src/systemd/sd-adapt/def.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/def.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/device-nodes.h b/src/systemd/sd-adapt/device-nodes.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/device-nodes.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/dirent-util.h b/src/systemd/sd-adapt/dirent-util.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/dirent-util.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/errno-list.h b/src/systemd/sd-adapt/errno-list.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/errno-list.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/format-util.h b/src/systemd/sd-adapt/format-util.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/format-util.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/glob-util.h b/src/systemd/sd-adapt/glob-util.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/glob-util.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/gunicode.h b/src/systemd/sd-adapt/gunicode.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/gunicode.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/ioprio.h b/src/systemd/sd-adapt/ioprio.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/ioprio.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/locale-util.h b/src/systemd/sd-adapt/locale-util.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/locale-util.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/memfd-util.h b/src/systemd/sd-adapt/memfd-util.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/memfd-util.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/missing.h b/src/systemd/sd-adapt/missing.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/missing.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/mkdir.h b/src/systemd/sd-adapt/mkdir.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/mkdir.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/nm-sd-adapt.h b/src/systemd/sd-adapt/nm-sd-adapt.h deleted file mode 100644 index e163eeda..00000000 --- a/src/systemd/sd-adapt/nm-sd-adapt.h +++ /dev/null @@ -1,191 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 2014 - 2015 Red Hat, Inc. - */ - -#ifndef NM_SD_ADAPT_H -#define NM_SD_ADAPT_H - -#include "nm-default.h" - -#include <stdbool.h> -#include <syslog.h> -#include <sys/resource.h> -#include <time.h> - -#if defined(HAVE_DECL_REALLOCARRAY) && HAVE_DECL_REALLOCARRAY == 1 -#define HAVE_REALLOCARRAY 1 -#else -#define HAVE_REALLOCARRAY 0 -#endif - -#if defined(HAVE_DECL_EXPLICIT_BZERO) && HAVE_DECL_EXPLICIT_BZERO == 1 -#define HAVE_EXPLICIT_BZERO 1 -#else -#define HAVE_EXPLICIT_BZERO 0 -#endif - -#define ENABLE_DEBUG_HASHMAP 0 - -#ifndef HAVE_SYS_AUXV_H -#define HAVE_SYS_AUXV_H 0 -#endif - -/*****************************************************************************/ - -static inline NMLogLevel -_slog_level_to_nm (int slevel) -{ - switch (LOG_PRI (slevel)) { - case LOG_DEBUG: return LOGL_DEBUG; - case LOG_WARNING: return LOGL_WARN; - case LOG_CRIT: - case LOG_ERR: return LOGL_ERR; - case LOG_INFO: - case LOG_NOTICE: - default: return LOGL_INFO; - } -} - -static inline int -_nm_log_get_max_level_realm (void) -{ - /* inline function, to avoid coverity warning about constant expression. */ - return LOG_DEBUG; -} -#define log_get_max_level_realm(realm) _nm_log_get_max_level_realm () - -#define log_internal_realm(level, error, file, line, func, format, ...) \ -({ \ - const int _nm_e = (error); \ - const NMLogLevel _nm_l = _slog_level_to_nm ((level)); \ - \ - if (nm_logging_enabled (_nm_l, LOGD_SYSTEMD)) { \ - const char *_nm_location = strrchr ((""file), '/'); \ - \ - _nm_log_impl (_nm_location ? _nm_location + 1 : (""file), (line), (func), _nm_l, LOGD_DHCP, _nm_e, NULL, NULL, ("%s"format), "libsystemd: ", ## __VA_ARGS__); \ - } \ - (_nm_e > 0 ? -_nm_e : _nm_e); \ -}) - -#define log_assert_failed(text, file, line, func) \ -G_STMT_START { \ - log_internal (LOG_CRIT, 0, file, line, func, "Assertion '%s' failed at %s:%u, function %s(). Aborting.", text, file, line, func); \ - g_assert_not_reached (); \ -} G_STMT_END - -#define log_assert_failed_unreachable(text, file, line, func) \ -G_STMT_START { \ - log_internal (LOG_CRIT, 0, file, line, func, "Code should not be reached '%s' at %s:%u, function %s(). Aborting.", text, file, line, func); \ - g_assert_not_reached (); \ -} G_STMT_END - -#define log_assert_failed_return(text, file, line, func) \ -({ \ - log_internal (LOG_DEBUG, 0, file, line, func, "Assertion '%s' failed at %s:%u, function %s(). Ignoring.", text, file, line, func); \ - g_return_if_fail_warning (G_LOG_DOMAIN, G_STRFUNC, text); \ - (void) 0; \ -}) - -/***************************************************************************** - * The remainder of the header is only enabled when building the systemd code - * itself. - *****************************************************************************/ - -#if (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_SYSTEMD - -#include <netinet/in.h> -#include <string.h> -#include <stdio.h> -#include <errno.h> -#include <elf.h> -#ifdef HAVE_SYS_AUXV_H -#include <sys/auxv.h> -#endif -#include <unistd.h> -#include <sys/syscall.h> -#include <sys/ioctl.h> - -#include <net/if_arp.h> - -/* Missing in Linux 3.2.0, in Ubuntu 12.04 */ -#ifndef BPF_XOR -#define BPF_XOR 0xa0 -#endif - -#ifndef ETHERTYPE_LLDP -#define ETHERTYPE_LLDP 0x88cc -#endif - -#ifndef HAVE_SECURE_GETENV -# ifdef HAVE___SECURE_GETENV -# define secure_getenv __secure_getenv -# else -# error neither secure_getenv nor __secure_getenv is available -# endif -#endif - -#define VALGRIND 0 - -static inline pid_t -raw_getpid (void) { -#if defined(__alpha__) - return (pid_t) syscall (__NR_getxpid); -#else - return (pid_t) syscall (__NR_getpid); -#endif -} - -/*****************************************************************************/ - -/* work around missing uchar.h */ -typedef guint16 char16_t; -typedef guint32 char32_t; - -/*****************************************************************************/ - -static inline int -sd_notify (int unset_environment, const char *state) -{ - return 0; -} - -/* Can't include both net/if.h and linux/if.h; so have to define this here */ -#ifndef IF_NAMESIZE -#define IF_NAMESIZE 16 -#endif - -#ifndef IFNAMSIZ -#define IFNAMSIZ IF_NAMESIZE -#endif - -#ifndef MAX_HANDLE_SZ -#define MAX_HANDLE_SZ 128 -#endif - -#include "sd-id128.h" -#include "sparse-endian.h" -#include "async.h" -#include "util.h" - -static inline pid_t gettid(void) { - return (pid_t) syscall(SYS_gettid); -} - -#endif /* (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_SYSTEMD */ - -#endif /* NM_SD_ADAPT_H */ - diff --git a/src/systemd/sd-adapt/procfs-util.h b/src/systemd/sd-adapt/procfs-util.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/procfs-util.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/raw-clone.h b/src/systemd/sd-adapt/raw-clone.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/raw-clone.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/terminal-util.h b/src/systemd/sd-adapt/terminal-util.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/terminal-util.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/unaligned.h b/src/systemd/sd-adapt/unaligned.h deleted file mode 100644 index 17dc0444..00000000 --- a/src/systemd/sd-adapt/unaligned.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -#include "nm-utils/unaligned.h" diff --git a/src/systemd/sd-adapt/user-util.h b/src/systemd/sd-adapt/user-util.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/user-util.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/sd-adapt/virt.h b/src/systemd/sd-adapt/virt.h deleted file mode 100644 index 637892c2..00000000 --- a/src/systemd/sd-adapt/virt.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -/* dummy header */ diff --git a/src/systemd/src/basic/alloc-util.c b/src/systemd/src/basic/alloc-util.c deleted file mode 100644 index ef405098..00000000 --- a/src/systemd/src/basic/alloc-util.c +++ /dev/null @@ -1,83 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <stdint.h> -#include <string.h> - -#include "alloc-util.h" -#include "macro.h" -#include "util.h" - -void* memdup(const void *p, size_t l) { - void *ret; - - assert(l == 0 || p); - - ret = malloc(l); - if (!ret) - return NULL; - - memcpy(ret, p, l); - return ret; -} - -void* memdup_suffix0(const void *p, size_t l) { - void *ret; - - assert(l == 0 || p); - - /* The same as memdup() but place a safety NUL byte after the allocated memory */ - - ret = malloc(l + 1); - if (!ret) - return NULL; - - *((uint8_t*) mempcpy(ret, p, l)) = 0; - return ret; -} - -void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size) { - size_t a, newalloc; - void *q; - - assert(p); - assert(allocated); - - if (*allocated >= need) - return *p; - - newalloc = MAX(need * 2, 64u / size); - a = newalloc * size; - - /* check for overflows */ - if (a < size * need) - return NULL; - - q = realloc(*p, a); - if (!q) - return NULL; - - *p = q; - *allocated = newalloc; - return q; -} - -void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size) { - size_t prev; - uint8_t *q; - - assert(p); - assert(allocated); - - prev = *allocated; - - q = greedy_realloc(p, allocated, need, size); - if (!q) - return NULL; - - if (*allocated > prev) - memzero(q + prev * size, (*allocated - prev) * size); - - return q; -} diff --git a/src/systemd/src/basic/alloc-util.h b/src/systemd/src/basic/alloc-util.h deleted file mode 100644 index ebe42889..00000000 --- a/src/systemd/src/basic/alloc-util.h +++ /dev/null @@ -1,130 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <alloca.h> -#include <stddef.h> -#include <stdlib.h> -#include <string.h> - -#include "macro.h" - -#define new(t, n) ((t*) malloc_multiply(sizeof(t), (n))) - -#define new0(t, n) ((t*) calloc((n), sizeof(t))) - -#define newa(t, n) \ - ({ \ - assert(!size_multiply_overflow(sizeof(t), n)); \ - (t*) alloca(sizeof(t)*(n)); \ - }) - -#define newa0(t, n) \ - ({ \ - assert(!size_multiply_overflow(sizeof(t), n)); \ - (t*) alloca0(sizeof(t)*(n)); \ - }) - -#define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n))) - -#define newdup_suffix0(t, p, n) ((t*) memdup_suffix0_multiply(p, sizeof(t), (n))) - -#define malloc0(n) (calloc(1, (n))) - -static inline void *mfree(void *memory) { - free(memory); - return NULL; -} - -#define free_and_replace(a, b) \ - ({ \ - free(a); \ - (a) = (b); \ - (b) = NULL; \ - 0; \ - }) - -void* memdup(const void *p, size_t l) _alloc_(2); -void* memdup_suffix0(const void *p, size_t l) _alloc_(2); - -static inline void freep(void *p) { - free(*(void**) p); -} - -#define _cleanup_free_ _cleanup_(freep) - -static inline bool size_multiply_overflow(size_t size, size_t need) { - return _unlikely_(need != 0 && size > (SIZE_MAX / need)); -} - -_malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t size, size_t need) { - if (size_multiply_overflow(size, need)) - return NULL; - - return malloc(size * need); -} - -#if !HAVE_REALLOCARRAY -_alloc_(2, 3) static inline void *reallocarray(void *p, size_t need, size_t size) { - if (size_multiply_overflow(size, need)) - return NULL; - - return realloc(p, size * need); -} -#endif - -_alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t size, size_t need) { - if (size_multiply_overflow(size, need)) - return NULL; - - return memdup(p, size * need); -} - -_alloc_(2, 3) static inline void *memdup_suffix0_multiply(const void *p, size_t size, size_t need) { - if (size_multiply_overflow(size, need)) - return NULL; - - return memdup_suffix0(p, size * need); -} - -void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size); -void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size); - -#define GREEDY_REALLOC(array, allocated, need) \ - greedy_realloc((void**) &(array), &(allocated), (need), sizeof((array)[0])) - -#define GREEDY_REALLOC0(array, allocated, need) \ - greedy_realloc0((void**) &(array), &(allocated), (need), sizeof((array)[0])) - -#define alloca0(n) \ - ({ \ - char *_new_; \ - size_t _len_ = n; \ - _new_ = alloca(_len_); \ - (void *) memset(_new_, 0, _len_); \ - }) - -/* It's not clear what alignment glibc/gcc alloca() guarantee, hence provide a guaranteed safe version */ -#define alloca_align(size, align) \ - ({ \ - void *_ptr_; \ - size_t _mask_ = (align) - 1; \ - _ptr_ = alloca((size) + _mask_); \ - (void*)(((uintptr_t)_ptr_ + _mask_) & ~_mask_); \ - }) - -#define alloca0_align(size, align) \ - ({ \ - void *_new_; \ - size_t _size_ = (size); \ - _new_ = alloca_align(_size_, (align)); \ - (void*)memset(_new_, 0, _size_); \ - }) - -/* Takes inspiration from Rusts's Option::take() method: reads and returns a pointer, but at the same time resets it to - * NULL. See: https://doc.rust-lang.org/std/option/enum.Option.html#method.take */ -#define TAKE_PTR(ptr) \ - ({ \ - typeof(ptr) _ptr_ = (ptr); \ - (ptr) = NULL; \ - _ptr_; \ - }) diff --git a/src/systemd/src/basic/async.h b/src/systemd/src/basic/async.h deleted file mode 100644 index 31606131..00000000 --- a/src/systemd/src/basic/async.h +++ /dev/null @@ -1,7 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -int asynchronous_job(void* (*func)(void *p), void *arg); - -int asynchronous_sync(pid_t *ret_pid); -int asynchronous_close(int fd); diff --git a/src/systemd/src/basic/env-util.c b/src/systemd/src/basic/env-util.c deleted file mode 100644 index 52d12a8a..00000000 --- a/src/systemd/src/basic/env-util.c +++ /dev/null @@ -1,791 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <errno.h> -#include <limits.h> -#include <stdarg.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "env-util.h" -#include "escape.h" -#include "extract-word.h" -#include "macro.h" -#include "parse-util.h" -#include "string-util.h" -#include "strv.h" -#include "utf8.h" - -#if 0 /* NM_IGNORED */ -#define VALID_CHARS_ENV_NAME \ - DIGITS LETTERS \ - "_" - -#ifndef ARG_MAX -#define ARG_MAX ((size_t) sysconf(_SC_ARG_MAX)) -#endif - -static bool env_name_is_valid_n(const char *e, size_t n) { - const char *p; - - if (!e) - return false; - - if (n <= 0) - return false; - - if (e[0] >= '0' && e[0] <= '9') - return false; - - /* POSIX says the overall size of the environment block cannot - * be > ARG_MAX, an individual assignment hence cannot be - * either. Discounting the equal sign and trailing NUL this - * hence leaves ARG_MAX-2 as longest possible variable - * name. */ - if (n > ARG_MAX - 2) - return false; - - for (p = e; p < e + n; p++) - if (!strchr(VALID_CHARS_ENV_NAME, *p)) - return false; - - return true; -} - -bool env_name_is_valid(const char *e) { - if (!e) - return false; - - return env_name_is_valid_n(e, strlen(e)); -} - -bool env_value_is_valid(const char *e) { - if (!e) - return false; - - if (!utf8_is_valid(e)) - return false; - - /* bash allows tabs and newlines in environment variables, and so - * should we */ - if (string_has_cc(e, "\t\n")) - return false; - - /* POSIX says the overall size of the environment block cannot - * be > ARG_MAX, an individual assignment hence cannot be - * either. Discounting the shortest possible variable name of - * length 1, the equal sign and trailing NUL this hence leaves - * ARG_MAX-3 as longest possible variable value. */ - if (strlen(e) > ARG_MAX - 3) - return false; - - return true; -} - -bool env_assignment_is_valid(const char *e) { - const char *eq; - - eq = strchr(e, '='); - if (!eq) - return false; - - if (!env_name_is_valid_n(e, eq - e)) - return false; - - if (!env_value_is_valid(eq + 1)) - return false; - - /* POSIX says the overall size of the environment block cannot - * be > ARG_MAX, hence the individual variable assignments - * cannot be either, but let's leave room for one trailing NUL - * byte. */ - if (strlen(e) > ARG_MAX - 1) - return false; - - return true; -} - -bool strv_env_is_valid(char **e) { - char **p, **q; - - STRV_FOREACH(p, e) { - size_t k; - - if (!env_assignment_is_valid(*p)) - return false; - - /* Check if there are duplicate assginments */ - k = strcspn(*p, "="); - STRV_FOREACH(q, p + 1) - if (strneq(*p, *q, k) && (*q)[k] == '=') - return false; - } - - return true; -} - -bool strv_env_name_is_valid(char **l) { - char **p, **q; - - STRV_FOREACH(p, l) { - if (!env_name_is_valid(*p)) - return false; - - STRV_FOREACH(q, p + 1) - if (streq(*p, *q)) - return false; - } - - return true; -} - -bool strv_env_name_or_assignment_is_valid(char **l) { - char **p, **q; - - STRV_FOREACH(p, l) { - if (!env_assignment_is_valid(*p) && !env_name_is_valid(*p)) - return false; - - STRV_FOREACH(q, p + 1) - if (streq(*p, *q)) - return false; - } - - return true; -} - -static int env_append(char **r, char ***k, char **a) { - assert(r); - assert(k); - - if (!a) - return 0; - - /* Add the entries of a to *k unless they already exist in *r - * in which case they are overridden instead. This assumes - * there is enough space in the r array. */ - - for (; *a; a++) { - char **j; - size_t n; - - n = strcspn(*a, "="); - - if ((*a)[n] == '=') - n++; - - for (j = r; j < *k; j++) - if (strneq(*j, *a, n)) - break; - - if (j >= *k) - (*k)++; - else - free(*j); - - *j = strdup(*a); - if (!*j) - return -ENOMEM; - } - - return 0; -} - -char **strv_env_merge(size_t n_lists, ...) { - size_t n = 0; - char **l, **k, **r; - va_list ap; - size_t i; - - /* Merges an arbitrary number of environment sets */ - - va_start(ap, n_lists); - for (i = 0; i < n_lists; i++) { - l = va_arg(ap, char**); - n += strv_length(l); - } - va_end(ap); - - r = new(char*, n+1); - if (!r) - return NULL; - - k = r; - - va_start(ap, n_lists); - for (i = 0; i < n_lists; i++) { - l = va_arg(ap, char**); - if (env_append(r, &k, l) < 0) - goto fail; - } - va_end(ap); - - *k = NULL; - - return r; - -fail: - va_end(ap); - strv_free(r); - - return NULL; -} - -static bool env_match(const char *t, const char *pattern) { - assert(t); - assert(pattern); - - /* pattern a matches string a - * a matches a= - * a matches a=b - * a= matches a= - * a=b matches a=b - * a= does not match a - * a=b does not match a= - * a=b does not match a - * a=b does not match a=c */ - - if (streq(t, pattern)) - return true; - - if (!strchr(pattern, '=')) { - size_t l = strlen(pattern); - - return strneq(t, pattern, l) && t[l] == '='; - } - - return false; -} - -static bool env_entry_has_name(const char *entry, const char *name) { - const char *t; - - assert(entry); - assert(name); - - t = startswith(entry, name); - if (!t) - return false; - - return *t == '='; -} - -char **strv_env_delete(char **x, size_t n_lists, ...) { - size_t n, i = 0; - char **k, **r; - va_list ap; - - /* Deletes every entry from x that is mentioned in the other - * string lists */ - - n = strv_length(x); - - r = new(char*, n+1); - if (!r) - return NULL; - - STRV_FOREACH(k, x) { - size_t v; - - va_start(ap, n_lists); - for (v = 0; v < n_lists; v++) { - char **l, **j; - - l = va_arg(ap, char**); - STRV_FOREACH(j, l) - if (env_match(*k, *j)) - goto skip; - } - va_end(ap); - - r[i] = strdup(*k); - if (!r[i]) { - strv_free(r); - return NULL; - } - - i++; - continue; - - skip: - va_end(ap); - } - - r[i] = NULL; - - assert(i <= n); - - return r; -} - -char **strv_env_unset(char **l, const char *p) { - - char **f, **t; - - if (!l) - return NULL; - - assert(p); - - /* Drops every occurrence of the env var setting p in the - * string list. Edits in-place. */ - - for (f = t = l; *f; f++) { - - if (env_match(*f, p)) { - free(*f); - continue; - } - - *(t++) = *f; - } - - *t = NULL; - return l; -} - -char **strv_env_unset_many(char **l, ...) { - - char **f, **t; - - if (!l) - return NULL; - - /* Like strv_env_unset() but applies many at once. Edits in-place. */ - - for (f = t = l; *f; f++) { - bool found = false; - const char *p; - va_list ap; - - va_start(ap, l); - - while ((p = va_arg(ap, const char*))) { - if (env_match(*f, p)) { - found = true; - break; - } - } - - va_end(ap); - - if (found) { - free(*f); - continue; - } - - *(t++) = *f; - } - - *t = NULL; - return l; -} - -int strv_env_replace(char ***l, char *p) { - char **f; - const char *t, *name; - - assert(p); - - /* Replace first occurrence of the env var or add a new one in the - * string list. Drop other occurences. Edits in-place. Does not copy p. - * p must be a valid key=value assignment. - */ - - t = strchr(p, '='); - assert(t); - - name = strndupa(p, t - p); - - for (f = *l; f && *f; f++) - if (env_entry_has_name(*f, name)) { - free_and_replace(*f, p); - strv_env_unset(f + 1, *f); - return 0; - } - - /* We didn't find a match, we need to append p or create a new strv */ - if (strv_push(l, p) < 0) - return -ENOMEM; - return 1; -} - -char **strv_env_set(char **x, const char *p) { - - char **k; - _cleanup_strv_free_ char **r = NULL; - char* m[2] = { (char*) p, NULL }; - - /* Overrides the env var setting of p, returns a new copy */ - - r = new(char*, strv_length(x)+2); - if (!r) - return NULL; - - k = r; - if (env_append(r, &k, x) < 0) - return NULL; - - if (env_append(r, &k, m) < 0) - return NULL; - - *k = NULL; - - return TAKE_PTR(r); -} - -char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) { - char **i; - - assert(name); - - if (k <= 0) - return NULL; - - STRV_FOREACH_BACKWARDS(i, l) - if (strneq(*i, name, k) && - (*i)[k] == '=') - return *i + k + 1; - - if (flags & REPLACE_ENV_USE_ENVIRONMENT) { - const char *t; - - t = strndupa(name, k); - return getenv(t); - }; - - return NULL; -} - -char *strv_env_get(char **l, const char *name) { - assert(name); - - return strv_env_get_n(l, name, strlen(name), 0); -} - -char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) { - char **p, **q; - int k = 0; - - STRV_FOREACH(p, e) { - size_t n; - bool duplicate = false; - - if (!env_assignment_is_valid(*p)) { - if (invalid_callback) - invalid_callback(*p, userdata); - free(*p); - continue; - } - - n = strcspn(*p, "="); - STRV_FOREACH(q, p + 1) - if (strneq(*p, *q, n) && (*q)[n] == '=') { - duplicate = true; - break; - } - - if (duplicate) { - free(*p); - continue; - } - - e[k++] = *p; - } - - if (e) - e[k] = NULL; - - return e; -} - -char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) { - enum { - WORD, - CURLY, - VARIABLE, - VARIABLE_RAW, - TEST, - DEFAULT_VALUE, - ALTERNATE_VALUE, - } state = WORD; - - const char *e, *word = format, *test_value; - char *k; - _cleanup_free_ char *r = NULL; - size_t i, len; - int nest = 0; - - assert(format); - - for (e = format, i = 0; *e && i < n; e ++, i ++) - switch (state) { - - case WORD: - if (*e == '$') - state = CURLY; - break; - - case CURLY: - if (*e == '{') { - k = strnappend(r, word, e-word-1); - if (!k) - return NULL; - - free_and_replace(r, k); - - word = e-1; - state = VARIABLE; - nest++; - } else if (*e == '$') { - k = strnappend(r, word, e-word); - if (!k) - return NULL; - - free_and_replace(r, k); - - word = e+1; - state = WORD; - - } else if (flags & REPLACE_ENV_ALLOW_BRACELESS && strchr(VALID_CHARS_ENV_NAME, *e)) { - k = strnappend(r, word, e-word-1); - if (!k) - return NULL; - - free_and_replace(r, k); - - word = e-1; - state = VARIABLE_RAW; - - } else - state = WORD; - break; - - case VARIABLE: - if (*e == '}') { - const char *t; - - t = strv_env_get_n(env, word+2, e-word-2, flags); - - k = strappend(r, t); - if (!k) - return NULL; - - free_and_replace(r, k); - - word = e+1; - state = WORD; - } else if (*e == ':') { - if (!(flags & REPLACE_ENV_ALLOW_EXTENDED)) - /* Treat this as unsupported syntax, i.e. do no replacement */ - state = WORD; - else { - len = e-word-2; - state = TEST; - } - } - break; - - case TEST: - if (*e == '-') - state = DEFAULT_VALUE; - else if (*e == '+') - state = ALTERNATE_VALUE; - else { - state = WORD; - break; - } - - test_value = e+1; - break; - - case DEFAULT_VALUE: /* fall through */ - case ALTERNATE_VALUE: - assert(flags & REPLACE_ENV_ALLOW_EXTENDED); - - if (*e == '{') { - nest++; - break; - } - - if (*e != '}') - break; - - nest--; - if (nest == 0) { - const char *t; - _cleanup_free_ char *v = NULL; - - t = strv_env_get_n(env, word+2, len, flags); - - if (t && state == ALTERNATE_VALUE) - t = v = replace_env_n(test_value, e-test_value, env, flags); - else if (!t && state == DEFAULT_VALUE) - t = v = replace_env_n(test_value, e-test_value, env, flags); - - k = strappend(r, t); - if (!k) - return NULL; - - free_and_replace(r, k); - - word = e+1; - state = WORD; - } - break; - - case VARIABLE_RAW: - assert(flags & REPLACE_ENV_ALLOW_BRACELESS); - - if (!strchr(VALID_CHARS_ENV_NAME, *e)) { - const char *t; - - t = strv_env_get_n(env, word+1, e-word-1, flags); - - k = strappend(r, t); - if (!k) - return NULL; - - free_and_replace(r, k); - - word = e--; - i--; - state = WORD; - } - break; - } - - if (state == VARIABLE_RAW) { - const char *t; - - assert(flags & REPLACE_ENV_ALLOW_BRACELESS); - - t = strv_env_get_n(env, word+1, e-word-1, flags); - return strappend(r, t); - } else - return strnappend(r, word, e-word); -} - -char **replace_env_argv(char **argv, char **env) { - char **ret, **i; - size_t k = 0, l = 0; - - l = strv_length(argv); - - ret = new(char*, l+1); - if (!ret) - return NULL; - - STRV_FOREACH(i, argv) { - - /* If $FOO appears as single word, replace it by the split up variable */ - if ((*i)[0] == '$' && !IN_SET((*i)[1], '{', '$')) { - char *e; - char **w, **m = NULL; - size_t q; - - e = strv_env_get(env, *i+1); - if (e) { - int r; - - r = strv_split_extract(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_QUOTES); - if (r < 0) { - ret[k] = NULL; - strv_free(ret); - return NULL; - } - } else - m = NULL; - - q = strv_length(m); - l = l + q - 1; - - w = reallocarray(ret, l + 1, sizeof(char *)); - if (!w) { - ret[k] = NULL; - strv_free(ret); - strv_free(m); - return NULL; - } - - ret = w; - if (m) { - memcpy(ret + k, m, q * sizeof(char*)); - free(m); - } - - k += q; - continue; - } - - /* If ${FOO} appears as part of a word, replace it by the variable as-is */ - ret[k] = replace_env(*i, env, 0); - if (!ret[k]) { - strv_free(ret); - return NULL; - } - k++; - } - - ret[k] = NULL; - return ret; -} -#endif /* NM_IGNORED */ - -int getenv_bool(const char *p) { - const char *e; - - e = getenv(p); - if (!e) - return -ENXIO; - - return parse_boolean(e); -} - -#if 0 /* NM_IGNORED */ -int getenv_bool_secure(const char *p) { - const char *e; - - e = secure_getenv(p); - if (!e) - return -ENXIO; - - return parse_boolean(e); -} - -int serialize_environment(FILE *f, char **environment) { - char **e; - - STRV_FOREACH(e, environment) { - _cleanup_free_ char *ce; - - ce = cescape(*e); - if (!ce) - return -ENOMEM; - - fprintf(f, "env=%s\n", ce); - } - - /* caller should call ferror() */ - - return 0; -} - -int deserialize_environment(char ***environment, const char *line) { - char *uce; - int r; - - assert(line); - assert(environment); - - assert(startswith(line, "env=")); - r = cunescape(line + 4, 0, &uce); - if (r < 0) - return r; - - return strv_env_replace(environment, uce); -} -#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/env-util.h b/src/systemd/src/basic/env-util.h deleted file mode 100644 index 174433ea..00000000 --- a/src/systemd/src/basic/env-util.h +++ /dev/null @@ -1,50 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <stdbool.h> -#include <stddef.h> -#include <stdio.h> - -#include "macro.h" -#include "string.h" - -bool env_name_is_valid(const char *e); -bool env_value_is_valid(const char *e); -bool env_assignment_is_valid(const char *e); - -enum { - REPLACE_ENV_USE_ENVIRONMENT = 1u, - REPLACE_ENV_ALLOW_BRACELESS = 2u, - REPLACE_ENV_ALLOW_EXTENDED = 4u, -}; - -char *replace_env_n(const char *format, size_t n, char **env, unsigned flags); -char **replace_env_argv(char **argv, char **env); - -static inline char *replace_env(const char *format, char **env, unsigned flags) { - return replace_env_n(format, strlen(format), env, flags); -} - -bool strv_env_is_valid(char **e); -#define strv_env_clean(l) strv_env_clean_with_callback(l, NULL, NULL) -char **strv_env_clean_with_callback(char **l, void (*invalid_callback)(const char *p, void *userdata), void *userdata); - -bool strv_env_name_is_valid(char **l); -bool strv_env_name_or_assignment_is_valid(char **l); - -char **strv_env_merge(size_t n_lists, ...); -char **strv_env_delete(char **x, size_t n_lists, ...); /* New copy */ - -char **strv_env_set(char **x, const char *p); /* New copy ... */ -char **strv_env_unset(char **l, const char *p); /* In place ... */ -char **strv_env_unset_many(char **l, ...) _sentinel_; -int strv_env_replace(char ***l, char *p); /* In place ... */ - -char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) _pure_; -char *strv_env_get(char **x, const char *n) _pure_; - -int getenv_bool(const char *p); -int getenv_bool_secure(const char *p); - -int serialize_environment(FILE *f, char **environment); -int deserialize_environment(char ***environment, const char *line); diff --git a/src/systemd/src/basic/escape.c b/src/systemd/src/basic/escape.c deleted file mode 100644 index 5c82a545..00000000 --- a/src/systemd/src/basic/escape.c +++ /dev/null @@ -1,509 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <errno.h> -#include <stdlib.h> -#include <string.h> - -#include "alloc-util.h" -#include "escape.h" -#include "hexdecoct.h" -#include "macro.h" -#include "utf8.h" - -int cescape_char(char c, char *buf) { - char *buf_old = buf; - - /* Needs space for 4 characters in the buffer */ - - switch (c) { - - case '\a': - *(buf++) = '\\'; - *(buf++) = 'a'; - break; - case '\b': - *(buf++) = '\\'; - *(buf++) = 'b'; - break; - case '\f': - *(buf++) = '\\'; - *(buf++) = 'f'; - break; - case '\n': - *(buf++) = '\\'; - *(buf++) = 'n'; - break; - case '\r': - *(buf++) = '\\'; - *(buf++) = 'r'; - break; - case '\t': - *(buf++) = '\\'; - *(buf++) = 't'; - break; - case '\v': - *(buf++) = '\\'; - *(buf++) = 'v'; - break; - case '\\': - *(buf++) = '\\'; - *(buf++) = '\\'; - break; - case '"': - *(buf++) = '\\'; - *(buf++) = '"'; - break; - case '\'': - *(buf++) = '\\'; - *(buf++) = '\''; - break; - - default: - /* For special chars we prefer octal over - * hexadecimal encoding, simply because glib's - * g_strescape() does the same */ - if ((c < ' ') || (c >= 127)) { - *(buf++) = '\\'; - *(buf++) = octchar((unsigned char) c >> 6); - *(buf++) = octchar((unsigned char) c >> 3); - *(buf++) = octchar((unsigned char) c); - } else - *(buf++) = c; - break; - } - - return buf - buf_old; -} - -char *cescape_length(const char *s, size_t n) { - const char *f; - char *r, *t; - - assert(s || n == 0); - - /* Does C style string escaping. May be reversed with - * cunescape(). */ - - r = new(char, n*4 + 1); - if (!r) - return NULL; - - for (f = s, t = r; f < s + n; f++) - t += cescape_char(*f, t); - - *t = 0; - - return r; -} - -char *cescape(const char *s) { - assert(s); - - return cescape_length(s, strlen(s)); -} - -int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit) { - int r = 1; - - assert(p); - assert(*p); - assert(ret); - - /* Unescapes C style. Returns the unescaped character in ret. - * Sets *eight_bit to true if the escaped sequence either fits in - * one byte in UTF-8 or is a non-unicode literal byte and should - * instead be copied directly. - */ - - if (length != (size_t) -1 && length < 1) - return -EINVAL; - - switch (p[0]) { - - case 'a': - *ret = '\a'; - break; - case 'b': - *ret = '\b'; - break; - case 'f': - *ret = '\f'; - break; - case 'n': - *ret = '\n'; - break; - case 'r': - *ret = '\r'; - break; - case 't': - *ret = '\t'; - break; - case 'v': - *ret = '\v'; - break; - case '\\': - *ret = '\\'; - break; - case '"': - *ret = '"'; - break; - case '\'': - *ret = '\''; - break; - - case 's': - /* This is an extension of the XDG syntax files */ - *ret = ' '; - break; - - case 'x': { - /* hexadecimal encoding */ - int a, b; - - if (length != (size_t) -1 && length < 3) - return -EINVAL; - - a = unhexchar(p[1]); - if (a < 0) - return -EINVAL; - - b = unhexchar(p[2]); - if (b < 0) - return -EINVAL; - - /* Don't allow NUL bytes */ - if (a == 0 && b == 0) - return -EINVAL; - - *ret = (a << 4U) | b; - *eight_bit = true; - r = 3; - break; - } - - case 'u': { - /* C++11 style 16bit unicode */ - - int a[4]; - size_t i; - uint32_t c; - - if (length != (size_t) -1 && length < 5) - return -EINVAL; - - for (i = 0; i < 4; i++) { - a[i] = unhexchar(p[1 + i]); - if (a[i] < 0) - return a[i]; - } - - c = ((uint32_t) a[0] << 12U) | ((uint32_t) a[1] << 8U) | ((uint32_t) a[2] << 4U) | (uint32_t) a[3]; - - /* Don't allow 0 chars */ - if (c == 0) - return -EINVAL; - - *ret = c; - r = 5; - break; - } - - case 'U': { - /* C++11 style 32bit unicode */ - - int a[8]; - size_t i; - char32_t c; - - if (length != (size_t) -1 && length < 9) - return -EINVAL; - - for (i = 0; i < 8; i++) { - a[i] = unhexchar(p[1 + i]); - if (a[i] < 0) - return a[i]; - } - - c = ((uint32_t) a[0] << 28U) | ((uint32_t) a[1] << 24U) | ((uint32_t) a[2] << 20U) | ((uint32_t) a[3] << 16U) | - ((uint32_t) a[4] << 12U) | ((uint32_t) a[5] << 8U) | ((uint32_t) a[6] << 4U) | (uint32_t) a[7]; - - /* Don't allow 0 chars */ - if (c == 0) - return -EINVAL; - - /* Don't allow invalid code points */ - if (!unichar_is_valid(c)) - return -EINVAL; - - *ret = c; - r = 9; - break; - } - - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': { - /* octal encoding */ - int a, b, c; - char32_t m; - - if (length != (size_t) -1 && length < 3) - return -EINVAL; - - a = unoctchar(p[0]); - if (a < 0) - return -EINVAL; - - b = unoctchar(p[1]); - if (b < 0) - return -EINVAL; - - c = unoctchar(p[2]); - if (c < 0) - return -EINVAL; - - /* don't allow NUL bytes */ - if (a == 0 && b == 0 && c == 0) - return -EINVAL; - - /* Don't allow bytes above 255 */ - m = ((uint32_t) a << 6U) | ((uint32_t) b << 3U) | (uint32_t) c; - if (m > 255) - return -EINVAL; - - *ret = m; - *eight_bit = true; - r = 3; - break; - } - - default: - return -EINVAL; - } - - return r; -} - -int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret) { - char *r, *t; - const char *f; - size_t pl; - - assert(s); - assert(ret); - - /* Undoes C style string escaping, and optionally prefixes it. */ - - pl = strlen_ptr(prefix); - - r = new(char, pl+length+1); - if (!r) - return -ENOMEM; - - if (prefix) - memcpy(r, prefix, pl); - - for (f = s, t = r + pl; f < s + length; f++) { - size_t remaining; - bool eight_bit = false; - char32_t u; - int k; - - remaining = s + length - f; - assert(remaining > 0); - - if (*f != '\\') { - /* A literal, copy verbatim */ - *(t++) = *f; - continue; - } - - if (remaining == 1) { - if (flags & UNESCAPE_RELAX) { - /* A trailing backslash, copy verbatim */ - *(t++) = *f; - continue; - } - - free(r); - return -EINVAL; - } - - k = cunescape_one(f + 1, remaining - 1, &u, &eight_bit); - if (k < 0) { - if (flags & UNESCAPE_RELAX) { - /* Invalid escape code, let's take it literal then */ - *(t++) = '\\'; - continue; - } - - free(r); - return k; - } - - f += k; - if (eight_bit) - /* One byte? Set directly as specified */ - *(t++) = u; - else - /* Otherwise encode as multi-byte UTF-8 */ - t += utf8_encode_unichar(t, u); - } - - *t = 0; - - *ret = r; - return t - r; -} - -int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret) { - return cunescape_length_with_prefix(s, length, NULL, flags, ret); -} - -int cunescape(const char *s, UnescapeFlags flags, char **ret) { - return cunescape_length(s, strlen(s), flags, ret); -} - -char *xescape(const char *s, const char *bad) { - char *r, *t; - const char *f; - - /* Escapes all chars in bad, in addition to \ and all special - * chars, in \xFF style escaping. May be reversed with - * cunescape(). */ - - r = new(char, strlen(s) * 4 + 1); - if (!r) - return NULL; - - for (f = s, t = r; *f; f++) { - - if ((*f < ' ') || (*f >= 127) || - (*f == '\\') || strchr(bad, *f)) { - *(t++) = '\\'; - *(t++) = 'x'; - *(t++) = hexchar(*f >> 4); - *(t++) = hexchar(*f); - } else - *(t++) = *f; - } - - *t = 0; - - return r; -} - -char *octescape(const char *s, size_t len) { - char *r, *t; - const char *f; - - /* Escapes all chars in bad, in addition to \ and " chars, - * in \nnn style escaping. */ - - r = new(char, len * 4 + 1); - if (!r) - return NULL; - - for (f = s, t = r; f < s + len; f++) { - - if (*f < ' ' || *f >= 127 || IN_SET(*f, '\\', '"')) { - *(t++) = '\\'; - *(t++) = '0' + (*f >> 6); - *(t++) = '0' + ((*f >> 3) & 8); - *(t++) = '0' + (*f & 8); - } else - *(t++) = *f; - } - - *t = 0; - - return r; - -} - -static char *strcpy_backslash_escaped(char *t, const char *s, const char *bad, bool escape_tab_nl) { - assert(bad); - - for (; *s; s++) { - if (escape_tab_nl && IN_SET(*s, '\n', '\t')) { - *(t++) = '\\'; - *(t++) = *s == '\n' ? 'n' : 't'; - continue; - } - - if (*s == '\\' || strchr(bad, *s)) - *(t++) = '\\'; - - *(t++) = *s; - } - - return t; -} - -char *shell_escape(const char *s, const char *bad) { - char *r, *t; - - r = new(char, strlen(s)*2+1); - if (!r) - return NULL; - - t = strcpy_backslash_escaped(r, s, bad, false); - *t = 0; - - return r; -} - -char* shell_maybe_quote(const char *s, EscapeStyle style) { - const char *p; - char *r, *t; - - assert(s); - - /* Encloses a string in quotes if necessary to make it OK as a shell - * string. Note that we treat benign UTF-8 characters as needing - * escaping too, but that should be OK. */ - - for (p = s; *p; p++) - if (*p <= ' ' || - *p >= 127 || - strchr(SHELL_NEED_QUOTES, *p)) - break; - - if (!*p) - return strdup(s); - - r = new(char, (style == ESCAPE_POSIX) + 1 + strlen(s)*2 + 1 + 1); - if (!r) - return NULL; - - t = r; - if (style == ESCAPE_BACKSLASH) - *(t++) = '"'; - else if (style == ESCAPE_POSIX) { - *(t++) = '$'; - *(t++) = '\''; - } else - assert_not_reached("Bad EscapeStyle"); - - t = mempcpy(t, s, p - s); - - if (style == ESCAPE_BACKSLASH) - t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE, false); - else - t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE_POSIX, true); - - if (style == ESCAPE_BACKSLASH) - *(t++) = '"'; - else - *(t++) = '\''; - *t = 0; - - return r; -} diff --git a/src/systemd/src/basic/escape.h b/src/systemd/src/basic/escape.h deleted file mode 100644 index c612a7c0..00000000 --- a/src/systemd/src/basic/escape.h +++ /dev/null @@ -1,55 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <inttypes.h> -#include <stddef.h> -#include <stdint.h> -#include <sys/types.h> -#if 0 /* NM_IGNORED */ -#include <uchar.h> -#endif /* NM_IGNORED */ - -#include "string-util.h" -#include "missing.h" - -/* What characters are special in the shell? */ -/* must be escaped outside and inside double-quotes */ -#define SHELL_NEED_ESCAPE "\"\\`$" - -/* Those that can be escaped or double-quoted. - * - * Stricly speaking, ! does not need to be escaped, except in interactive - * mode, but let's be extra nice to the user and quote ! in case this - * output is ever used in interactive mode. */ -#define SHELL_NEED_QUOTES SHELL_NEED_ESCAPE GLOB_CHARS "'()<>|&;!" - -/* Note that we assume control characters would need to be escaped too in - * addition to the "special" characters listed here, if they appear in the - * string. Current users disallow control characters. Also '"' shall not - * be escaped. - */ -#define SHELL_NEED_ESCAPE_POSIX "\\\'" - -typedef enum UnescapeFlags { - UNESCAPE_RELAX = 1, -} UnescapeFlags; - -typedef enum EscapeStyle { - ESCAPE_BACKSLASH = 1, - ESCAPE_POSIX = 2, -} EscapeStyle; - -char *cescape(const char *s); -char *cescape_length(const char *s, size_t n); -int cescape_char(char c, char *buf); - -int cunescape(const char *s, UnescapeFlags flags, char **ret); -int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret); -int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret); -int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit); - -char *xescape(const char *s, const char *bad); -char *octescape(const char *s, size_t len); - -char *shell_escape(const char *s, const char *bad); -char* shell_maybe_quote(const char *s, EscapeStyle style); diff --git a/src/systemd/src/basic/ether-addr-util.c b/src/systemd/src/basic/ether-addr-util.c deleted file mode 100644 index ed92bc60..00000000 --- a/src/systemd/src/basic/ether-addr-util.c +++ /dev/null @@ -1,119 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <errno.h> -#include <net/ethernet.h> -#include <stdio.h> -#include <sys/types.h> - -#include "ether-addr-util.h" -#include "macro.h" -#include "string-util.h" - -char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]) { - assert(addr); - assert(buffer); - - /* Like ether_ntoa() but uses %02x instead of %x to print - * ethernet addresses, which makes them look less funny. Also, - * doesn't use a static buffer. */ - - sprintf(buffer, "%02x:%02x:%02x:%02x:%02x:%02x", - addr->ether_addr_octet[0], - addr->ether_addr_octet[1], - addr->ether_addr_octet[2], - addr->ether_addr_octet[3], - addr->ether_addr_octet[4], - addr->ether_addr_octet[5]); - - return buffer; -} - -int ether_addr_compare(const void *a, const void *b) { - assert(a); - assert(b); - - return memcmp(a, b, ETH_ALEN); -} - -static void ether_addr_hash_func(const void *p, struct siphash *state) { - siphash24_compress(p, sizeof(struct ether_addr), state); -} - -const struct hash_ops ether_addr_hash_ops = { - .hash = ether_addr_hash_func, - .compare = ether_addr_compare -}; - -int ether_addr_from_string(const char *s, struct ether_addr *ret) { - size_t pos = 0, n, field; - char sep = '\0'; - const char *hex = HEXDIGITS, *hexoff; - size_t x; - bool touched; - -#define parse_fields(v) \ - for (field = 0; field < ELEMENTSOF(v); field++) { \ - touched = false; \ - for (n = 0; n < (2 * sizeof(v[0])); n++) { \ - if (s[pos] == '\0') \ - break; \ - hexoff = strchr(hex, s[pos]); \ - if (!hexoff) \ - break; \ - assert(hexoff >= hex); \ - x = hexoff - hex; \ - if (x >= 16) \ - x -= 6; /* A-F */ \ - assert(x < 16); \ - touched = true; \ - v[field] <<= 4; \ - v[field] += x; \ - pos++; \ - } \ - if (!touched) \ - return -EINVAL; \ - if (field < (ELEMENTSOF(v)-1)) { \ - if (s[pos] != sep) \ - return -EINVAL; \ - else \ - pos++; \ - } \ - } - - assert(s); - assert(ret); - - s += strspn(s, WHITESPACE); - sep = s[strspn(s, hex)]; - - if (sep == '.') { - uint16_t shorts[3] = { 0 }; - - parse_fields(shorts); - - if (s[pos] != '\0') - return -EINVAL; - - for (n = 0; n < ELEMENTSOF(shorts); n++) { - ret->ether_addr_octet[2*n] = ((shorts[n] & (uint16_t)0xff00) >> 8); - ret->ether_addr_octet[2*n + 1] = (shorts[n] & (uint16_t)0x00ff); - } - - } else if (IN_SET(sep, ':', '-')) { - struct ether_addr out = ETHER_ADDR_NULL; - - parse_fields(out.ether_addr_octet); - - if (s[pos] != '\0') - return -EINVAL; - - for (n = 0; n < ELEMENTSOF(out.ether_addr_octet); n++) - ret->ether_addr_octet[n] = out.ether_addr_octet[n]; - - } else - return -EINVAL; - - return 0; -} diff --git a/src/systemd/src/basic/ether-addr-util.h b/src/systemd/src/basic/ether-addr-util.h deleted file mode 100644 index 3be03700..00000000 --- a/src/systemd/src/basic/ether-addr-util.h +++ /dev/null @@ -1,28 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <net/ethernet.h> -#include <stdbool.h> - -#include "hash-funcs.h" - -#define ETHER_ADDR_FORMAT_STR "%02X%02X%02X%02X%02X%02X" -#define ETHER_ADDR_FORMAT_VAL(x) (x).ether_addr_octet[0], (x).ether_addr_octet[1], (x).ether_addr_octet[2], (x).ether_addr_octet[3], (x).ether_addr_octet[4], (x).ether_addr_octet[5] - -#define ETHER_ADDR_TO_STRING_MAX (3*6) -char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]); - -int ether_addr_compare(const void *a, const void *b); -static inline bool ether_addr_equal(const struct ether_addr *a, const struct ether_addr *b) { - return ether_addr_compare(a, b) == 0; -} - -#define ETHER_ADDR_NULL ((const struct ether_addr){}) - -static inline bool ether_addr_is_null(const struct ether_addr *addr) { - return ether_addr_equal(addr, ÐER_ADDR_NULL); -} - -int ether_addr_from_string(const char *s, struct ether_addr *ret); - -extern const struct hash_ops ether_addr_hash_ops; diff --git a/src/systemd/src/basic/extract-word.c b/src/systemd/src/basic/extract-word.c deleted file mode 100644 index 404fe615..00000000 --- a/src/systemd/src/basic/extract-word.c +++ /dev/null @@ -1,289 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <errno.h> -#include <stdarg.h> -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <syslog.h> - -#include "alloc-util.h" -#include "escape.h" -#include "extract-word.h" -#include "log.h" -#include "macro.h" -#include "string-util.h" -#include "utf8.h" - -int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags) { - _cleanup_free_ char *s = NULL; - size_t allocated = 0, sz = 0; - char c; - int r; - - char quote = 0; /* 0 or ' or " */ - bool backslash = false; /* whether we've just seen a backslash */ - - assert(p); - assert(ret); - - /* Bail early if called after last value or with no input */ - if (!*p) - goto finish; - c = **p; - - if (!separators) - separators = WHITESPACE; - - /* Parses the first word of a string, and returns it in - * *ret. Removes all quotes in the process. When parsing fails - * (because of an uneven number of quotes or similar), leaves - * the pointer *p at the first invalid character. */ - - if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) - if (!GREEDY_REALLOC(s, allocated, sz+1)) - return -ENOMEM; - - for (;; (*p)++, c = **p) { - if (c == 0) - goto finish_force_terminate; - else if (strchr(separators, c)) { - if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) { - (*p)++; - goto finish_force_next; - } - } else { - /* We found a non-blank character, so we will always - * want to return a string (even if it is empty), - * allocate it here. */ - if (!GREEDY_REALLOC(s, allocated, sz+1)) - return -ENOMEM; - break; - } - } - - for (;; (*p)++, c = **p) { - if (backslash) { - if (!GREEDY_REALLOC(s, allocated, sz+7)) - return -ENOMEM; - - if (c == 0) { - if ((flags & EXTRACT_CUNESCAPE_RELAX) && - (!quote || flags & EXTRACT_RELAX)) { - /* If we find an unquoted trailing backslash and we're in - * EXTRACT_CUNESCAPE_RELAX mode, keep it verbatim in the - * output. - * - * Unbalanced quotes will only be allowed in EXTRACT_RELAX - * mode, EXTRACT_CUNESCAPE_RELAX mode does not allow them. - */ - s[sz++] = '\\'; - goto finish_force_terminate; - } - if (flags & EXTRACT_RELAX) - goto finish_force_terminate; - return -EINVAL; - } - - if (flags & EXTRACT_CUNESCAPE) { - bool eight_bit = false; - char32_t u; - - r = cunescape_one(*p, (size_t) -1, &u, &eight_bit); - if (r < 0) { - if (flags & EXTRACT_CUNESCAPE_RELAX) { - s[sz++] = '\\'; - s[sz++] = c; - } else - return -EINVAL; - } else { - (*p) += r - 1; - - if (eight_bit) - s[sz++] = u; - else - sz += utf8_encode_unichar(s + sz, u); - } - } else - s[sz++] = c; - - backslash = false; - - } else if (quote) { /* inside either single or double quotes */ - for (;; (*p)++, c = **p) { - if (c == 0) { - if (flags & EXTRACT_RELAX) - goto finish_force_terminate; - return -EINVAL; - } else if (c == quote) { /* found the end quote */ - quote = 0; - break; - } else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) { - backslash = true; - break; - } else { - if (!GREEDY_REALLOC(s, allocated, sz+2)) - return -ENOMEM; - - s[sz++] = c; - } - } - - } else { - for (;; (*p)++, c = **p) { - if (c == 0) - goto finish_force_terminate; - else if (IN_SET(c, '\'', '"') && (flags & EXTRACT_QUOTES)) { - quote = c; - break; - } else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) { - backslash = true; - break; - } else if (strchr(separators, c)) { - if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) { - (*p)++; - goto finish_force_next; - } - /* Skip additional coalesced separators. */ - for (;; (*p)++, c = **p) { - if (c == 0) - goto finish_force_terminate; - if (!strchr(separators, c)) - break; - } - goto finish; - - } else { - if (!GREEDY_REALLOC(s, allocated, sz+2)) - return -ENOMEM; - - s[sz++] = c; - } - } - } - } - -finish_force_terminate: - *p = NULL; -finish: - if (!s) { - *p = NULL; - *ret = NULL; - return 0; - } - -finish_force_next: - s[sz] = 0; - *ret = TAKE_PTR(s); - - return 1; -} - -#if 0 /* NM_IGNORED */ -int extract_first_word_and_warn( - const char **p, - char **ret, - const char *separators, - ExtractFlags flags, - const char *unit, - const char *filename, - unsigned line, - const char *rvalue) { - - /* Try to unquote it, if it fails, warn about it and try again - * but this time using EXTRACT_CUNESCAPE_RELAX to keep the - * backslashes verbatim in invalid escape sequences. */ - - const char *save; - int r; - - save = *p; - r = extract_first_word(p, ret, separators, flags); - if (r >= 0) - return r; - - if (r == -EINVAL && !(flags & EXTRACT_CUNESCAPE_RELAX)) { - - /* Retry it with EXTRACT_CUNESCAPE_RELAX. */ - *p = save; - r = extract_first_word(p, ret, separators, flags|EXTRACT_CUNESCAPE_RELAX); - if (r >= 0) { - /* It worked this time, hence it must have been an invalid escape sequence. */ - log_syntax(unit, LOG_WARNING, filename, line, EINVAL, "Ignoring unknown escape sequences: \"%s\"", *ret); - return r; - } - - /* If it's still EINVAL; then it must be unbalanced quoting, report this. */ - if (r == -EINVAL) - return log_syntax(unit, LOG_ERR, filename, line, r, "Unbalanced quoting, ignoring: \"%s\"", rvalue); - } - - /* Can be any error, report it */ - return log_syntax(unit, LOG_ERR, filename, line, r, "Unable to decode word \"%s\", ignoring: %m", rvalue); -} - -/* We pass ExtractFlags as unsigned int (to avoid undefined behaviour when passing - * an object that undergoes default argument promotion as an argument to va_start). - * Let's make sure that ExtractFlags fits into an unsigned int. */ -assert_cc(sizeof(enum ExtractFlags) <= sizeof(unsigned)); - -int extract_many_words(const char **p, const char *separators, unsigned flags, ...) { - va_list ap; - char **l; - int n = 0, i, c, r; - - /* Parses a number of words from a string, stripping any - * quotes if necessary. */ - - assert(p); - - /* Count how many words are expected */ - va_start(ap, flags); - for (;;) { - if (!va_arg(ap, char **)) - break; - n++; - } - va_end(ap); - - if (n <= 0) - return 0; - - /* Read all words into a temporary array */ - l = newa0(char*, n); - for (c = 0; c < n; c++) { - - r = extract_first_word(p, &l[c], separators, flags); - if (r < 0) { - int j; - - for (j = 0; j < c; j++) - free(l[j]); - - return r; - } - - if (r == 0) - break; - } - - /* If we managed to parse all words, return them in the passed - * in parameters */ - va_start(ap, flags); - for (i = 0; i < n; i++) { - char **v; - - v = va_arg(ap, char **); - assert(v); - - *v = l[i]; - } - va_end(ap); - - return c; -} -#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/extract-word.h b/src/systemd/src/basic/extract-word.h deleted file mode 100644 index 8c63b7c3..00000000 --- a/src/systemd/src/basic/extract-word.h +++ /dev/null @@ -1,17 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include "macro.h" - -typedef enum ExtractFlags { - EXTRACT_RELAX = 1, - EXTRACT_CUNESCAPE = 2, - EXTRACT_CUNESCAPE_RELAX = 4, - EXTRACT_QUOTES = 8, - EXTRACT_DONT_COALESCE_SEPARATORS = 16, - EXTRACT_RETAIN_ESCAPE = 32, -} ExtractFlags; - -int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags); -int extract_first_word_and_warn(const char **p, char **ret, const char *separators, ExtractFlags flags, const char *unit, const char *filename, unsigned line, const char *rvalue); -int extract_many_words(const char **p, const char *separators, unsigned flags, ...) _sentinel_; diff --git a/src/systemd/src/basic/fd-util.c b/src/systemd/src/basic/fd-util.c deleted file mode 100644 index 71babe2f..00000000 --- a/src/systemd/src/basic/fd-util.c +++ /dev/null @@ -1,959 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <errno.h> -#include <fcntl.h> -#include <sys/resource.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "copy.h" -#include "dirent-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "fs-util.h" -#include "io-util.h" -#include "macro.h" -#include "memfd-util.h" -#include "missing.h" -#include "parse-util.h" -#include "path-util.h" -#include "process-util.h" -#include "socket-util.h" -#include "stdio-util.h" -#include "util.h" - -int close_nointr(int fd) { - assert(fd >= 0); - - if (close(fd) >= 0) - return 0; - - /* - * Just ignore EINTR; a retry loop is the wrong thing to do on - * Linux. - * - * http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html - * https://bugzilla.gnome.org/show_bug.cgi?id=682819 - * http://utcc.utoronto.ca/~cks/space/blog/unix/CloseEINTR - * https://sites.google.com/site/michaelsafyan/software-engineering/checkforeintrwheninvokingclosethinkagain - */ - if (errno == EINTR) - return 0; - - return -errno; -} - -int safe_close(int fd) { - - /* - * Like close_nointr() but cannot fail. Guarantees errno is - * unchanged. Is a NOP with negative fds passed, and returns - * -1, so that it can be used in this syntax: - * - * fd = safe_close(fd); - */ - - if (fd >= 0) { - PROTECT_ERRNO; - - /* The kernel might return pretty much any error code - * via close(), but the fd will be closed anyway. The - * only condition we want to check for here is whether - * the fd was invalid at all... */ - - assert_se(close_nointr(fd) != -EBADF); - } - - return -1; -} - -void safe_close_pair(int p[]) { - assert(p); - - if (p[0] == p[1]) { - /* Special case pairs which use the same fd in both - * directions... */ - p[0] = p[1] = safe_close(p[0]); - return; - } - - p[0] = safe_close(p[0]); - p[1] = safe_close(p[1]); -} - -void close_many(const int fds[], size_t n_fd) { - size_t i; - - assert(fds || n_fd <= 0); - - for (i = 0; i < n_fd; i++) - safe_close(fds[i]); -} - -int fclose_nointr(FILE *f) { - assert(f); - - /* Same as close_nointr(), but for fclose() */ - - if (fclose(f) == 0) - return 0; - - if (errno == EINTR) - return 0; - - return -errno; -} - -FILE* safe_fclose(FILE *f) { - - /* Same as safe_close(), but for fclose() */ - - if (f) { - PROTECT_ERRNO; - - assert_se(fclose_nointr(f) != EBADF); - } - - return NULL; -} - -DIR* safe_closedir(DIR *d) { - - if (d) { - PROTECT_ERRNO; - - assert_se(closedir(d) >= 0 || errno != EBADF); - } - - return NULL; -} - -int fd_nonblock(int fd, bool nonblock) { - int flags, nflags; - - assert(fd >= 0); - - flags = fcntl(fd, F_GETFL, 0); - if (flags < 0) - return -errno; - - if (nonblock) - nflags = flags | O_NONBLOCK; - else - nflags = flags & ~O_NONBLOCK; - - if (nflags == flags) - return 0; - - if (fcntl(fd, F_SETFL, nflags) < 0) - return -errno; - - return 0; -} - -int fd_cloexec(int fd, bool cloexec) { - int flags, nflags; - - assert(fd >= 0); - - flags = fcntl(fd, F_GETFD, 0); - if (flags < 0) - return -errno; - - if (cloexec) - nflags = flags | FD_CLOEXEC; - else - nflags = flags & ~FD_CLOEXEC; - - if (nflags == flags) - return 0; - - if (fcntl(fd, F_SETFD, nflags) < 0) - return -errno; - - return 0; -} - -#if 0 /* NM_IGNORED */ -_pure_ static bool fd_in_set(int fd, const int fdset[], size_t n_fdset) { - size_t i; - - assert(n_fdset == 0 || fdset); - - for (i = 0; i < n_fdset; i++) - if (fdset[i] == fd) - return true; - - return false; -} - -int close_all_fds(const int except[], size_t n_except) { - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; - int r = 0; - - assert(n_except == 0 || except); - - d = opendir("/proc/self/fd"); - if (!d) { - struct rlimit rl; - int fd, max_fd; - - /* When /proc isn't available (for example in chroots) the fallback is brute forcing through the fd - * table */ - - assert_se(getrlimit(RLIMIT_NOFILE, &rl) >= 0); - - if (rl.rlim_max == 0) - return -EINVAL; - - /* Let's take special care if the resource limit is set to unlimited, or actually larger than the range - * of 'int'. Let's avoid implicit overflows. */ - max_fd = (rl.rlim_max == RLIM_INFINITY || rl.rlim_max > INT_MAX) ? INT_MAX : (int) (rl.rlim_max - 1); - - for (fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -1) { - int q; - - if (fd_in_set(fd, except, n_except)) - continue; - - q = close_nointr(fd); - if (q < 0 && q != -EBADF && r >= 0) - r = q; - } - - return r; - } - - FOREACH_DIRENT(de, d, return -errno) { - int fd = -1, q; - - if (safe_atoi(de->d_name, &fd) < 0) - /* Let's better ignore this, just in case */ - continue; - - if (fd < 3) - continue; - - if (fd == dirfd(d)) - continue; - - if (fd_in_set(fd, except, n_except)) - continue; - - q = close_nointr(fd); - if (q < 0 && q != -EBADF && r >= 0) /* Valgrind has its own FD and doesn't want to have it closed */ - r = q; - } - - return r; -} - -int same_fd(int a, int b) { - struct stat sta, stb; - pid_t pid; - int r, fa, fb; - - assert(a >= 0); - assert(b >= 0); - - /* Compares two file descriptors. Note that semantics are - * quite different depending on whether we have kcmp() or we - * don't. If we have kcmp() this will only return true for - * dup()ed file descriptors, but not otherwise. If we don't - * have kcmp() this will also return true for two fds of the same - * file, created by separate open() calls. Since we use this - * call mostly for filtering out duplicates in the fd store - * this difference hopefully doesn't matter too much. */ - - if (a == b) - return true; - - /* Try to use kcmp() if we have it. */ - pid = getpid_cached(); - r = kcmp(pid, pid, KCMP_FILE, a, b); - if (r == 0) - return true; - if (r > 0) - return false; - if (errno != ENOSYS) - return -errno; - - /* We don't have kcmp(), use fstat() instead. */ - if (fstat(a, &sta) < 0) - return -errno; - - if (fstat(b, &stb) < 0) - return -errno; - - if ((sta.st_mode & S_IFMT) != (stb.st_mode & S_IFMT)) - return false; - - /* We consider all device fds different, since two device fds - * might refer to quite different device contexts even though - * they share the same inode and backing dev_t. */ - - if (S_ISCHR(sta.st_mode) || S_ISBLK(sta.st_mode)) - return false; - - if (sta.st_dev != stb.st_dev || sta.st_ino != stb.st_ino) - return false; - - /* The fds refer to the same inode on disk, let's also check - * if they have the same fd flags. This is useful to - * distinguish the read and write side of a pipe created with - * pipe(). */ - fa = fcntl(a, F_GETFL); - if (fa < 0) - return -errno; - - fb = fcntl(b, F_GETFL); - if (fb < 0) - return -errno; - - return fa == fb; -} -#endif /* NM_IGNORED */ - -void cmsg_close_all(struct msghdr *mh) { - struct cmsghdr *cmsg; - - assert(mh); - - CMSG_FOREACH(cmsg, mh) - if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) - close_many((int*) CMSG_DATA(cmsg), (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int)); -} - -bool fdname_is_valid(const char *s) { - const char *p; - - /* Validates a name for $LISTEN_FDNAMES. We basically allow - * everything ASCII that's not a control character. Also, as - * special exception the ":" character is not allowed, as we - * use that as field separator in $LISTEN_FDNAMES. - * - * Note that the empty string is explicitly allowed - * here. However, we limit the length of the names to 255 - * characters. */ - - if (!s) - return false; - - for (p = s; *p; p++) { - if (*p < ' ') - return false; - if (*p >= 127) - return false; - if (*p == ':') - return false; - } - - return p - s < 256; -} - -int fd_get_path(int fd, char **ret) { - _cleanup_close_ int dir = -1; - char fdname[DECIMAL_STR_MAX(int)]; - int r; - - dir = open("/proc/self/fd/", O_CLOEXEC | O_DIRECTORY | O_PATH); - if (dir < 0) - /* /proc is not available or not set up properly, we're most likely - * in some chroot environment. */ - return errno == ENOENT ? -EOPNOTSUPP : -errno; - - xsprintf(fdname, "%i", fd); - - r = readlinkat_malloc(dir, fdname, ret); - if (r == -ENOENT) - /* If the file doesn't exist the fd is invalid */ - return -EBADF; - - return r; -} - -#if 0 /* NM_IGNORED */ -int move_fd(int from, int to, int cloexec) { - int r; - - /* Move fd 'from' to 'to', make sure FD_CLOEXEC remains equal if requested, and release the old fd. If - * 'cloexec' is passed as -1, the original FD_CLOEXEC is inherited for the new fd. If it is 0, it is turned - * off, if it is > 0 it is turned on. */ - - if (from < 0) - return -EBADF; - if (to < 0) - return -EBADF; - - if (from == to) { - - if (cloexec >= 0) { - r = fd_cloexec(to, cloexec); - if (r < 0) - return r; - } - - return to; - } - - if (cloexec < 0) { - int fl; - - fl = fcntl(from, F_GETFD, 0); - if (fl < 0) - return -errno; - - cloexec = !!(fl & FD_CLOEXEC); - } - - r = dup3(from, to, cloexec ? O_CLOEXEC : 0); - if (r < 0) - return -errno; - - assert(r == to); - - safe_close(from); - - return to; -} - -int acquire_data_fd(const void *data, size_t size, unsigned flags) { - - _cleanup_close_pair_ int pipefds[2] = { -1, -1 }; - char pattern[] = "/dev/shm/data-fd-XXXXXX"; - _cleanup_close_ int fd = -1; - int isz = 0, r; - ssize_t n; - off_t f; - - assert(data || size == 0); - - /* Acquire a read-only file descriptor that when read from returns the specified data. This is much more - * complex than I wish it was. But here's why: - * - * a) First we try to use memfds. They are the best option, as we can seal them nicely to make them - * read-only. Unfortunately they require kernel 3.17, and – at the time of writing – we still support 3.14. - * - * b) Then, we try classic pipes. They are the second best options, as we can close the writing side, retaining - * a nicely read-only fd in the reading side. However, they are by default quite small, and unprivileged - * clients can only bump their size to a system-wide limit, which might be quite low. - * - * c) Then, we try an O_TMPFILE file in /dev/shm (that dir is the only suitable one known to exist from - * earliest boot on). To make it read-only we open the fd a second time with O_RDONLY via - * /proc/self/<fd>. Unfortunately O_TMPFILE is not available on older kernels on tmpfs. - * - * d) Finally, we try creating a regular file in /dev/shm, which we then delete. - * - * It sucks a bit that depending on the situation we return very different objects here, but that's Linux I - * figure. */ - - if (size == 0 && ((flags & ACQUIRE_NO_DEV_NULL) == 0)) { - /* As a special case, return /dev/null if we have been called for an empty data block */ - r = open("/dev/null", O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (r < 0) - return -errno; - - return r; - } - - if ((flags & ACQUIRE_NO_MEMFD) == 0) { - fd = memfd_new("data-fd"); - if (fd < 0) - goto try_pipe; - - n = write(fd, data, size); - if (n < 0) - return -errno; - if ((size_t) n != size) - return -EIO; - - f = lseek(fd, 0, SEEK_SET); - if (f != 0) - return -errno; - - r = memfd_set_sealed(fd); - if (r < 0) - return r; - - return TAKE_FD(fd); - } - -try_pipe: - if ((flags & ACQUIRE_NO_PIPE) == 0) { - if (pipe2(pipefds, O_CLOEXEC|O_NONBLOCK) < 0) - return -errno; - - isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0); - if (isz < 0) - return -errno; - - if ((size_t) isz < size) { - isz = (int) size; - if (isz < 0 || (size_t) isz != size) - return -E2BIG; - - /* Try to bump the pipe size */ - (void) fcntl(pipefds[1], F_SETPIPE_SZ, isz); - - /* See if that worked */ - isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0); - if (isz < 0) - return -errno; - - if ((size_t) isz < size) - goto try_dev_shm; - } - - n = write(pipefds[1], data, size); - if (n < 0) - return -errno; - if ((size_t) n != size) - return -EIO; - - (void) fd_nonblock(pipefds[0], false); - - return TAKE_FD(pipefds[0]); - } - -try_dev_shm: - if ((flags & ACQUIRE_NO_TMPFILE) == 0) { - fd = open("/dev/shm", O_RDWR|O_TMPFILE|O_CLOEXEC, 0500); - if (fd < 0) - goto try_dev_shm_without_o_tmpfile; - - n = write(fd, data, size); - if (n < 0) - return -errno; - if ((size_t) n != size) - return -EIO; - - /* Let's reopen the thing, in order to get an O_RDONLY fd for the original O_RDWR one */ - return fd_reopen(fd, O_RDONLY|O_CLOEXEC); - } - -try_dev_shm_without_o_tmpfile: - if ((flags & ACQUIRE_NO_REGULAR) == 0) { - fd = mkostemp_safe(pattern); - if (fd < 0) - return fd; - - n = write(fd, data, size); - if (n < 0) { - r = -errno; - goto unlink_and_return; - } - if ((size_t) n != size) { - r = -EIO; - goto unlink_and_return; - } - - /* Let's reopen the thing, in order to get an O_RDONLY fd for the original O_RDWR one */ - r = open(pattern, O_RDONLY|O_CLOEXEC); - if (r < 0) - r = -errno; - - unlink_and_return: - (void) unlink(pattern); - return r; - } - - return -EOPNOTSUPP; -} - -/* When the data is smaller or equal to 64K, try to place the copy in a memfd/pipe */ -#define DATA_FD_MEMORY_LIMIT (64U*1024U) - -/* If memfd/pipe didn't work out, then let's use a file in /tmp up to a size of 1M. If it's large than that use /var/tmp instead. */ -#define DATA_FD_TMP_LIMIT (1024U*1024U) - -int fd_duplicate_data_fd(int fd) { - - _cleanup_close_ int copy_fd = -1, tmp_fd = -1; - _cleanup_free_ void *remains = NULL; - size_t remains_size = 0; - const char *td; - struct stat st; - int r; - - /* Creates a 'data' fd from the specified source fd, containing all the same data in a read-only fashion, but - * independent of it (i.e. the source fd can be closed and unmounted after this call succeeded). Tries to be - * somewhat smart about where to place the data. In the best case uses a memfd(). If memfd() are not supported - * uses a pipe instead. For larger data will use an unlinked file in /tmp, and for even larger data one in - * /var/tmp. */ - - if (fstat(fd, &st) < 0) - return -errno; - - /* For now, let's only accept regular files, sockets, pipes and char devices */ - if (S_ISDIR(st.st_mode)) - return -EISDIR; - if (S_ISLNK(st.st_mode)) - return -ELOOP; - if (!S_ISREG(st.st_mode) && !S_ISSOCK(st.st_mode) && !S_ISFIFO(st.st_mode) && !S_ISCHR(st.st_mode)) - return -EBADFD; - - /* If we have reason to believe the data is bounded in size, then let's use memfds or pipes as backing fd. Note - * that we use the reported regular file size only as a hint, given that there are plenty special files in - * /proc and /sys which report a zero file size but can be read from. */ - - if (!S_ISREG(st.st_mode) || st.st_size < DATA_FD_MEMORY_LIMIT) { - - /* Try a memfd first */ - copy_fd = memfd_new("data-fd"); - if (copy_fd >= 0) { - off_t f; - - r = copy_bytes(fd, copy_fd, DATA_FD_MEMORY_LIMIT, 0); - if (r < 0) - return r; - - f = lseek(copy_fd, 0, SEEK_SET); - if (f != 0) - return -errno; - - if (r == 0) { - /* Did it fit into the limit? If so, we are done. */ - r = memfd_set_sealed(copy_fd); - if (r < 0) - return r; - - return TAKE_FD(copy_fd); - } - - /* Hmm, pity, this didn't fit. Let's fall back to /tmp then, see below */ - - } else { - _cleanup_(close_pairp) int pipefds[2] = { -1, -1 }; - int isz; - - /* If memfds aren't available, use a pipe. Set O_NONBLOCK so that we will get EAGAIN rather - * then block indefinitely when we hit the pipe size limit */ - - if (pipe2(pipefds, O_CLOEXEC|O_NONBLOCK) < 0) - return -errno; - - isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0); - if (isz < 0) - return -errno; - - /* Try to enlarge the pipe size if necessary */ - if ((size_t) isz < DATA_FD_MEMORY_LIMIT) { - - (void) fcntl(pipefds[1], F_SETPIPE_SZ, DATA_FD_MEMORY_LIMIT); - - isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0); - if (isz < 0) - return -errno; - } - - if ((size_t) isz >= DATA_FD_MEMORY_LIMIT) { - - r = copy_bytes_full(fd, pipefds[1], DATA_FD_MEMORY_LIMIT, 0, &remains, &remains_size); - if (r < 0 && r != -EAGAIN) - return r; /* If we get EAGAIN it could be because of the source or because of - * the destination fd, we can't know, as sendfile() and friends won't - * tell us. Hence, treat this as reason to fall back, just to be - * sure. */ - if (r == 0) { - /* Everything fit in, yay! */ - (void) fd_nonblock(pipefds[0], false); - - return TAKE_FD(pipefds[0]); - } - - /* Things didn't fit in. But we read data into the pipe, let's remember that, so that - * when writing the new file we incorporate this first. */ - copy_fd = TAKE_FD(pipefds[0]); - } - } - } - - /* If we have reason to believe this will fit fine in /tmp, then use that as first fallback. */ - if ((!S_ISREG(st.st_mode) || st.st_size < DATA_FD_TMP_LIMIT) && - (DATA_FD_MEMORY_LIMIT + remains_size) < DATA_FD_TMP_LIMIT) { - off_t f; - - tmp_fd = open_tmpfile_unlinkable(NULL /* NULL as directory means /tmp */, O_RDWR|O_CLOEXEC); - if (tmp_fd < 0) - return tmp_fd; - - if (copy_fd >= 0) { - /* If we tried a memfd/pipe first and it ended up being too large, then copy this into the - * temporary file first. */ - - r = copy_bytes(copy_fd, tmp_fd, UINT64_MAX, 0); - if (r < 0) - return r; - - assert(r == 0); - } - - if (remains_size > 0) { - /* If there were remaining bytes (i.e. read into memory, but not written out yet) from the - * failed copy operation, let's flush them out next. */ - - r = loop_write(tmp_fd, remains, remains_size, false); - if (r < 0) - return r; - } - - r = copy_bytes(fd, tmp_fd, DATA_FD_TMP_LIMIT - DATA_FD_MEMORY_LIMIT - remains_size, COPY_REFLINK); - if (r < 0) - return r; - if (r == 0) - goto finish; /* Yay, it fit in */ - - /* It didn't fit in. Let's not forget to use what we already used */ - f = lseek(tmp_fd, 0, SEEK_SET); - if (f != 0) - return -errno; - - safe_close(copy_fd); - copy_fd = TAKE_FD(tmp_fd); - - remains = mfree(remains); - remains_size = 0; - } - - /* As last fallback use /var/tmp */ - r = var_tmp_dir(&td); - if (r < 0) - return r; - - tmp_fd = open_tmpfile_unlinkable(td, O_RDWR|O_CLOEXEC); - if (tmp_fd < 0) - return tmp_fd; - - if (copy_fd >= 0) { - /* If we tried a memfd/pipe first, or a file in /tmp, and it ended up being too large, than copy this - * into the temporary file first. */ - r = copy_bytes(copy_fd, tmp_fd, UINT64_MAX, COPY_REFLINK); - if (r < 0) - return r; - - assert(r == 0); - } - - if (remains_size > 0) { - /* Then, copy in any read but not yet written bytes. */ - r = loop_write(tmp_fd, remains, remains_size, false); - if (r < 0) - return r; - } - - /* Copy in the rest */ - r = copy_bytes(fd, tmp_fd, UINT64_MAX, COPY_REFLINK); - if (r < 0) - return r; - - assert(r == 0); - -finish: - /* Now convert the O_RDWR file descriptor into an O_RDONLY one (and as side effect seek to the beginning of the - * file again */ - - return fd_reopen(tmp_fd, O_RDONLY|O_CLOEXEC); -} -#endif /* NM_IGNORED */ - -int fd_move_above_stdio(int fd) { - int flags, copy; - PROTECT_ERRNO; - - /* Moves the specified file descriptor if possible out of the range [0…2], i.e. the range of - * stdin/stdout/stderr. If it can't be moved outside of this range the original file descriptor is - * returned. This call is supposed to be used for long-lasting file descriptors we allocate in our code that - * might get loaded into foreign code, and where we want ensure our fds are unlikely used accidentally as - * stdin/stdout/stderr of unrelated code. - * - * Note that this doesn't fix any real bugs, it just makes it less likely that our code will be affected by - * buggy code from others that mindlessly invokes 'fprintf(stderr, …' or similar in places where stderr has - * been closed before. - * - * This function is written in a "best-effort" and "least-impact" style. This means whenever we encounter an - * error we simply return the original file descriptor, and we do not touch errno. */ - - if (fd < 0 || fd > 2) - return fd; - - flags = fcntl(fd, F_GETFD, 0); - if (flags < 0) - return fd; - - if (flags & FD_CLOEXEC) - copy = fcntl(fd, F_DUPFD_CLOEXEC, 3); - else - copy = fcntl(fd, F_DUPFD, 3); - if (copy < 0) - return fd; - - assert(copy > 2); - - (void) close(fd); - return copy; -} - -#if 0 /* NM_IGNORED */ -int rearrange_stdio(int original_input_fd, int original_output_fd, int original_error_fd) { - - int fd[3] = { /* Put together an array of fds we work on */ - original_input_fd, - original_output_fd, - original_error_fd - }; - - int r, i, - null_fd = -1, /* if we open /dev/null, we store the fd to it here */ - copy_fd[3] = { -1, -1, -1 }; /* This contains all fds we duplicate here temporarily, and hence need to close at the end */ - bool null_readable, null_writable; - - /* Sets up stdin, stdout, stderr with the three file descriptors passed in. If any of the descriptors is - * specified as -1 it will be connected with /dev/null instead. If any of the file descriptors is passed as - * itself (e.g. stdin as STDIN_FILENO) it is left unmodified, but the O_CLOEXEC bit is turned off should it be - * on. - * - * Note that if any of the passed file descriptors are > 2 they will be closed — both on success and on - * failure! Thus, callers should assume that when this function returns the input fds are invalidated. - * - * Note that when this function fails stdin/stdout/stderr might remain half set up! - * - * O_CLOEXEC is turned off for all three file descriptors (which is how it should be for - * stdin/stdout/stderr). */ - - null_readable = original_input_fd < 0; - null_writable = original_output_fd < 0 || original_error_fd < 0; - - /* First step, open /dev/null once, if we need it */ - if (null_readable || null_writable) { - - /* Let's open this with O_CLOEXEC first, and convert it to non-O_CLOEXEC when we move the fd to the final position. */ - null_fd = open("/dev/null", (null_readable && null_writable ? O_RDWR : - null_readable ? O_RDONLY : O_WRONLY) | O_CLOEXEC); - if (null_fd < 0) { - r = -errno; - goto finish; - } - - /* If this fd is in the 0…2 range, let's move it out of it */ - if (null_fd < 3) { - int copy; - - copy = fcntl(null_fd, F_DUPFD_CLOEXEC, 3); /* Duplicate this with O_CLOEXEC set */ - if (copy < 0) { - r = -errno; - goto finish; - } - - safe_close(null_fd); - null_fd = copy; - } - } - - /* Let's assemble fd[] with the fds to install in place of stdin/stdout/stderr */ - for (i = 0; i < 3; i++) { - - if (fd[i] < 0) - fd[i] = null_fd; /* A negative parameter means: connect this one to /dev/null */ - else if (fd[i] != i && fd[i] < 3) { - /* This fd is in the 0…2 territory, but not at its intended place, move it out of there, so that we can work there. */ - copy_fd[i] = fcntl(fd[i], F_DUPFD_CLOEXEC, 3); /* Duplicate this with O_CLOEXEC set */ - if (copy_fd[i] < 0) { - r = -errno; - goto finish; - } - - fd[i] = copy_fd[i]; - } - } - - /* At this point we now have the fds to use in fd[], and they are all above the stdio range, so that we - * have freedom to move them around. If the fds already were at the right places then the specific fds are - * -1. Let's now move them to the right places. This is the point of no return. */ - for (i = 0; i < 3; i++) { - - if (fd[i] == i) { - - /* fd is already in place, but let's make sure O_CLOEXEC is off */ - r = fd_cloexec(i, false); - if (r < 0) - goto finish; - - } else { - assert(fd[i] > 2); - - if (dup2(fd[i], i) < 0) { /* Turns off O_CLOEXEC on the new fd. */ - r = -errno; - goto finish; - } - } - } - - r = 0; - -finish: - /* Close the original fds, but only if they were outside of the stdio range. Also, properly check for the same - * fd passed in multiple times. */ - safe_close_above_stdio(original_input_fd); - if (original_output_fd != original_input_fd) - safe_close_above_stdio(original_output_fd); - if (original_error_fd != original_input_fd && original_error_fd != original_output_fd) - safe_close_above_stdio(original_error_fd); - - /* Close the copies we moved > 2 */ - for (i = 0; i < 3; i++) - safe_close(copy_fd[i]); - - /* Close our null fd, if it's > 2 */ - safe_close_above_stdio(null_fd); - - return r; -} - -int fd_reopen(int fd, int flags) { - char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; - int new_fd; - - /* Reopens the specified fd with new flags. This is useful for convert an O_PATH fd into a regular one, or to - * turn O_RDWR fds into O_RDONLY fds. - * - * This doesn't work on sockets (since they cannot be open()ed, ever). - * - * This implicitly resets the file read index to 0. */ - - xsprintf(procfs_path, "/proc/self/fd/%i", fd); - new_fd = open(procfs_path, flags); - if (new_fd < 0) - return -errno; - - return new_fd; -} - -int read_nr_open(void) { - _cleanup_free_ char *nr_open = NULL; - int r; - - /* Returns the kernel's current fd limit, either by reading it of /proc/sys if that works, or using the - * hard-coded default compiled-in value of current kernels (1M) if not. This call will never fail. */ - - r = read_one_line_file("/proc/sys/fs/nr_open", &nr_open); - if (r < 0) - log_debug_errno(r, "Failed to read /proc/sys/fs/nr_open, ignoring: %m"); - else { - int v; - - r = safe_atoi(nr_open, &v); - if (r < 0) - log_debug_errno(r, "Failed to parse /proc/sys/fs/nr_open value '%s', ignoring: %m", nr_open); - else - return v; - } - - /* If we fail, fallback to the hard-coded kernel limit of 1024 * 1024. */ - return 1024 * 1024; -} -#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/fd-util.h b/src/systemd/src/basic/fd-util.h deleted file mode 100644 index 00303a7e..00000000 --- a/src/systemd/src/basic/fd-util.h +++ /dev/null @@ -1,110 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <dirent.h> -#include <stdbool.h> -#include <stdio.h> -#include <sys/socket.h> - -#include "macro.h" - -/* Make sure we can distinguish fd 0 and NULL */ -#define FD_TO_PTR(fd) INT_TO_PTR((fd)+1) -#define PTR_TO_FD(p) (PTR_TO_INT(p)-1) - -int close_nointr(int fd); -int safe_close(int fd); -void safe_close_pair(int p[]); - -static inline int safe_close_above_stdio(int fd) { - if (fd < 3) /* Don't close stdin/stdout/stderr, but still invalidate the fd by returning -1 */ - return -1; - - return safe_close(fd); -} - -void close_many(const int fds[], size_t n_fd); - -int fclose_nointr(FILE *f); -FILE* safe_fclose(FILE *f); -DIR* safe_closedir(DIR *f); - -static inline void closep(int *fd) { - safe_close(*fd); -} - -static inline void close_pairp(int (*p)[2]) { - safe_close_pair(*p); -} - -static inline void fclosep(FILE **f) { - safe_fclose(*f); -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, pclose); -DEFINE_TRIVIAL_CLEANUP_FUNC(DIR*, closedir); - -#define _cleanup_close_ _cleanup_(closep) -#define _cleanup_fclose_ _cleanup_(fclosep) -#define _cleanup_pclose_ _cleanup_(pclosep) -#define _cleanup_closedir_ _cleanup_(closedirp) -#define _cleanup_close_pair_ _cleanup_(close_pairp) - -int fd_nonblock(int fd, bool nonblock); -int fd_cloexec(int fd, bool cloexec); - -int close_all_fds(const int except[], size_t n_except); - -int same_fd(int a, int b); - -void cmsg_close_all(struct msghdr *mh); - -bool fdname_is_valid(const char *s); - -int fd_get_path(int fd, char **ret); - -int move_fd(int from, int to, int cloexec); - -enum { - ACQUIRE_NO_DEV_NULL = 1 << 0, - ACQUIRE_NO_MEMFD = 1 << 1, - ACQUIRE_NO_PIPE = 1 << 2, - ACQUIRE_NO_TMPFILE = 1 << 3, - ACQUIRE_NO_REGULAR = 1 << 4, -}; - -int acquire_data_fd(const void *data, size_t size, unsigned flags); - -int fd_duplicate_data_fd(int fd); - -/* Hint: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5 */ -/* The kernel sends e.g., EHOSTUNREACH or ENONET to userspace in some ICMP error cases. - * See the icmp_err_convert[] in net/ipv4/icmp.c in the kernel sources */ -#define ERRNO_IS_DISCONNECT(r) \ - IN_SET(r, \ - ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE, \ - ENETUNREACH, EHOSTUNREACH, ENOPROTOOPT, EHOSTDOWN, ENONET) - -/* Resource exhaustion, could be our fault or general system trouble */ -#define ERRNO_IS_RESOURCE(r) \ - IN_SET(r, ENOMEM, EMFILE, ENFILE) - -int fd_move_above_stdio(int fd); - -int rearrange_stdio(int original_input_fd, int original_output_fd, int original_error_fd); - -static inline int make_null_stdio(void) { - return rearrange_stdio(-1, -1, -1); -} - -/* Like TAKE_PTR() but for file descriptors, resetting them to -1 */ -#define TAKE_FD(fd) \ - ({ \ - int _fd_ = (fd); \ - (fd) = -1; \ - _fd_; \ - }) - -int fd_reopen(int fd, int flags); - -int read_nr_open(void); diff --git a/src/systemd/src/basic/fileio.c b/src/systemd/src/basic/fileio.c deleted file mode 100644 index 3978c3a7..00000000 --- a/src/systemd/src/basic/fileio.c +++ /dev/null @@ -1,1671 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <errno.h> -#include <fcntl.h> -#include <limits.h> -#include <stdarg.h> -#include <stdint.h> -#include <stdio_ext.h> -#include <stdlib.h> -#include <string.h> -#include <sys/mman.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "ctype.h" -#include "def.h" -#include "env-util.h" -#include "escape.h" -#include "fd-util.h" -#include "fileio.h" -#include "fs-util.h" -#include "hexdecoct.h" -#include "log.h" -#include "macro.h" -#include "missing.h" -#include "parse-util.h" -#include "path-util.h" -#include "process-util.h" -#include "random-util.h" -#include "stdio-util.h" -#include "string-util.h" -#include "strv.h" -#include "time-util.h" -#include "umask-util.h" -#include "utf8.h" - -#define READ_FULL_BYTES_MAX (4U*1024U*1024U) - -#if 0 /* NM_IGNORED */ -int write_string_stream_ts( - FILE *f, - const char *line, - WriteStringFileFlags flags, - struct timespec *ts) { - - bool needs_nl; - int r; - - assert(f); - assert(line); - - if (ferror(f)) - return -EIO; - - needs_nl = !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) && !endswith(line, "\n"); - - if (needs_nl && (flags & WRITE_STRING_FILE_DISABLE_BUFFER)) { - /* If STDIO buffering was disabled, then let's append the newline character to the string itself, so - * that the write goes out in one go, instead of two */ - - line = strjoina(line, "\n"); - needs_nl = false; - } - - if (fputs(line, f) == EOF) - return -errno; - - if (needs_nl) - if (fputc('\n', f) == EOF) - return -errno; - - if (flags & WRITE_STRING_FILE_SYNC) - r = fflush_sync_and_check(f); - else - r = fflush_and_check(f); - if (r < 0) - return r; - - if (ts) { - struct timespec twice[2] = {*ts, *ts}; - - if (futimens(fileno(f), twice) < 0) - return -errno; - } - - return 0; -} - -static int write_string_file_atomic( - const char *fn, - const char *line, - WriteStringFileFlags flags, - struct timespec *ts) { - - _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *p = NULL; - int r; - - assert(fn); - assert(line); - - r = fopen_temporary(fn, &f, &p); - if (r < 0) - return r; - - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - (void) fchmod_umask(fileno(f), 0644); - - r = write_string_stream_ts(f, line, flags, ts); - if (r < 0) - goto fail; - - if (rename(p, fn) < 0) { - r = -errno; - goto fail; - } - - return 0; - -fail: - (void) unlink(p); - return r; -} - -int write_string_file_ts( - const char *fn, - const char *line, - WriteStringFileFlags flags, - struct timespec *ts) { - - _cleanup_fclose_ FILE *f = NULL; - int q, r; - - assert(fn); - assert(line); - - /* We don't know how to verify whether the file contents was already on-disk. */ - assert(!((flags & WRITE_STRING_FILE_VERIFY_ON_FAILURE) && (flags & WRITE_STRING_FILE_SYNC))); - - if (flags & WRITE_STRING_FILE_ATOMIC) { - assert(flags & WRITE_STRING_FILE_CREATE); - - r = write_string_file_atomic(fn, line, flags, ts); - if (r < 0) - goto fail; - - return r; - } else - assert(!ts); - - if (flags & WRITE_STRING_FILE_CREATE) { - f = fopen(fn, "we"); - if (!f) { - r = -errno; - goto fail; - } - } else { - int fd; - - /* We manually build our own version of fopen(..., "we") that - * works without O_CREAT */ - fd = open(fn, O_WRONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) { - r = -errno; - goto fail; - } - - f = fdopen(fd, "we"); - if (!f) { - r = -errno; - safe_close(fd); - goto fail; - } - } - - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - - if (flags & WRITE_STRING_FILE_DISABLE_BUFFER) - setvbuf(f, NULL, _IONBF, 0); - - r = write_string_stream_ts(f, line, flags, ts); - if (r < 0) - goto fail; - - return 0; - -fail: - if (!(flags & WRITE_STRING_FILE_VERIFY_ON_FAILURE)) - return r; - - f = safe_fclose(f); - - /* OK, the operation failed, but let's see if the right - * contents in place already. If so, eat up the error. */ - - q = verify_file(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE)); - if (q <= 0) - return r; - - return 0; -} - -int write_string_filef( - const char *fn, - WriteStringFileFlags flags, - const char *format, ...) { - - _cleanup_free_ char *p = NULL; - va_list ap; - int r; - - va_start(ap, format); - r = vasprintf(&p, format, ap); - va_end(ap); - - if (r < 0) - return -ENOMEM; - - return write_string_file(fn, p, flags); -} - -int read_one_line_file(const char *fn, char **line) { - _cleanup_fclose_ FILE *f = NULL; - int r; - - assert(fn); - assert(line); - - f = fopen(fn, "re"); - if (!f) - return -errno; - - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - - r = read_line(f, LONG_LINE_MAX, line); - return r < 0 ? r : 0; -} - -int verify_file(const char *fn, const char *blob, bool accept_extra_nl) { - _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *buf = NULL; - size_t l, k; - - assert(fn); - assert(blob); - - l = strlen(blob); - - if (accept_extra_nl && endswith(blob, "\n")) - accept_extra_nl = false; - - buf = malloc(l + accept_extra_nl + 1); - if (!buf) - return -ENOMEM; - - f = fopen(fn, "re"); - if (!f) - return -errno; - - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - - /* We try to read one byte more than we need, so that we know whether we hit eof */ - errno = 0; - k = fread(buf, 1, l + accept_extra_nl + 1, f); - if (ferror(f)) - return errno > 0 ? -errno : -EIO; - - if (k != l && k != l + accept_extra_nl) - return 0; - if (memcmp(buf, blob, l) != 0) - return 0; - if (k > l && buf[l] != '\n') - return 0; - - return 1; -} -#endif /* NM_IGNORED */ - -int read_full_stream(FILE *f, char **contents, size_t *size) { - _cleanup_free_ char *buf = NULL; - struct stat st; - size_t n, l; - int fd; - - assert(f); - assert(contents); - - n = LINE_MAX; - - fd = fileno(f); - if (fd >= 0) { /* If the FILE* object is backed by an fd (as opposed to memory or such, see fmemopen(), let's - * optimize our buffering) */ - - if (fstat(fileno(f), &st) < 0) - return -errno; - - if (S_ISREG(st.st_mode)) { - - /* Safety check */ - if (st.st_size > READ_FULL_BYTES_MAX) - return -E2BIG; - - /* Start with the right file size, but be prepared for files from /proc which generally report a file - * size of 0. Note that we increase the size to read here by one, so that the first read attempt - * already makes us notice the EOF. */ - if (st.st_size > 0) - n = st.st_size + 1; - } - } - - l = 0; - for (;;) { - char *t; - size_t k; - - t = realloc(buf, n + 1); - if (!t) - return -ENOMEM; - - buf = t; - errno = 0; - k = fread(buf + l, 1, n - l, f); - if (k > 0) - l += k; - - if (ferror(f)) - return errno > 0 ? -errno : -EIO; - - if (feof(f)) - break; - - /* We aren't expecting fread() to return a short read outside - * of (error && eof), assert buffer is full and enlarge buffer. - */ - assert(l == n); - - /* Safety check */ - if (n >= READ_FULL_BYTES_MAX) - return -E2BIG; - - n = MIN(n * 2, READ_FULL_BYTES_MAX); - } - - buf[l] = 0; - *contents = TAKE_PTR(buf); - - if (size) - *size = l; - - return 0; -} - -int read_full_file(const char *fn, char **contents, size_t *size) { - _cleanup_fclose_ FILE *f = NULL; - - assert(fn); - assert(contents); - - f = fopen(fn, "re"); - if (!f) - return -errno; - - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - - return read_full_stream(f, contents, size); -} - -static int parse_env_file_internal( - FILE *f, - const char *fname, - const char *newline, - int (*push) (const char *filename, unsigned line, - const char *key, char *value, void *userdata, int *n_pushed), - void *userdata, - int *n_pushed) { - - size_t key_alloc = 0, n_key = 0, value_alloc = 0, n_value = 0, last_value_whitespace = (size_t) -1, last_key_whitespace = (size_t) -1; - _cleanup_free_ char *contents = NULL, *key = NULL, *value = NULL; - unsigned line = 1; - char *p; - int r; - - enum { - PRE_KEY, - KEY, - PRE_VALUE, - VALUE, - VALUE_ESCAPE, - SINGLE_QUOTE_VALUE, - SINGLE_QUOTE_VALUE_ESCAPE, - DOUBLE_QUOTE_VALUE, - DOUBLE_QUOTE_VALUE_ESCAPE, - COMMENT, - COMMENT_ESCAPE - } state = PRE_KEY; - - assert(newline); - - if (f) - r = read_full_stream(f, &contents, NULL); - else - r = read_full_file(fname, &contents, NULL); - if (r < 0) - return r; - - for (p = contents; *p; p++) { - char c = *p; - - switch (state) { - - case PRE_KEY: - if (strchr(COMMENTS, c)) - state = COMMENT; - else if (!strchr(WHITESPACE, c)) { - state = KEY; - last_key_whitespace = (size_t) -1; - - if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) - return -ENOMEM; - - key[n_key++] = c; - } - break; - - case KEY: - if (strchr(newline, c)) { - state = PRE_KEY; - line++; - n_key = 0; - } else if (c == '=') { - state = PRE_VALUE; - last_value_whitespace = (size_t) -1; - } else { - if (!strchr(WHITESPACE, c)) - last_key_whitespace = (size_t) -1; - else if (last_key_whitespace == (size_t) -1) - last_key_whitespace = n_key; - - if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) - return -ENOMEM; - - key[n_key++] = c; - } - - break; - - case PRE_VALUE: - if (strchr(newline, c)) { - state = PRE_KEY; - line++; - key[n_key] = 0; - - if (value) - value[n_value] = 0; - - /* strip trailing whitespace from key */ - if (last_key_whitespace != (size_t) -1) - key[last_key_whitespace] = 0; - - r = push(fname, line, key, value, userdata, n_pushed); - if (r < 0) - return r; - - n_key = 0; - value = NULL; - value_alloc = n_value = 0; - - } else if (c == '\'') - state = SINGLE_QUOTE_VALUE; - else if (c == '\"') - state = DOUBLE_QUOTE_VALUE; - else if (c == '\\') - state = VALUE_ESCAPE; - else if (!strchr(WHITESPACE, c)) { - state = VALUE; - - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) - return -ENOMEM; - - value[n_value++] = c; - } - - break; - - case VALUE: - if (strchr(newline, c)) { - state = PRE_KEY; - line++; - - key[n_key] = 0; - - if (value) - value[n_value] = 0; - - /* Chomp off trailing whitespace from value */ - if (last_value_whitespace != (size_t) -1) - value[last_value_whitespace] = 0; - - /* strip trailing whitespace from key */ - if (last_key_whitespace != (size_t) -1) - key[last_key_whitespace] = 0; - - r = push(fname, line, key, value, userdata, n_pushed); - if (r < 0) - return r; - - n_key = 0; - value = NULL; - value_alloc = n_value = 0; - - } else if (c == '\\') { - state = VALUE_ESCAPE; - last_value_whitespace = (size_t) -1; - } else { - if (!strchr(WHITESPACE, c)) - last_value_whitespace = (size_t) -1; - else if (last_value_whitespace == (size_t) -1) - last_value_whitespace = n_value; - - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) - return -ENOMEM; - - value[n_value++] = c; - } - - break; - - case VALUE_ESCAPE: - state = VALUE; - - if (!strchr(newline, c)) { - /* Escaped newlines we eat up entirely */ - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) - return -ENOMEM; - - value[n_value++] = c; - } - break; - - case SINGLE_QUOTE_VALUE: - if (c == '\'') - state = PRE_VALUE; - else if (c == '\\') - state = SINGLE_QUOTE_VALUE_ESCAPE; - else { - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) - return -ENOMEM; - - value[n_value++] = c; - } - - break; - - case SINGLE_QUOTE_VALUE_ESCAPE: - state = SINGLE_QUOTE_VALUE; - - if (!strchr(newline, c)) { - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) - return -ENOMEM; - - value[n_value++] = c; - } - break; - - case DOUBLE_QUOTE_VALUE: - if (c == '\"') - state = PRE_VALUE; - else if (c == '\\') - state = DOUBLE_QUOTE_VALUE_ESCAPE; - else { - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) - return -ENOMEM; - - value[n_value++] = c; - } - - break; - - case DOUBLE_QUOTE_VALUE_ESCAPE: - state = DOUBLE_QUOTE_VALUE; - - if (!strchr(newline, c)) { - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) - return -ENOMEM; - - value[n_value++] = c; - } - break; - - case COMMENT: - if (c == '\\') - state = COMMENT_ESCAPE; - else if (strchr(newline, c)) { - state = PRE_KEY; - line++; - } - break; - - case COMMENT_ESCAPE: - state = COMMENT; - break; - } - } - - if (IN_SET(state, - PRE_VALUE, - VALUE, - VALUE_ESCAPE, - SINGLE_QUOTE_VALUE, - SINGLE_QUOTE_VALUE_ESCAPE, - DOUBLE_QUOTE_VALUE, - DOUBLE_QUOTE_VALUE_ESCAPE)) { - - key[n_key] = 0; - - if (value) - value[n_value] = 0; - - if (state == VALUE) - if (last_value_whitespace != (size_t) -1) - value[last_value_whitespace] = 0; - - /* strip trailing whitespace from key */ - if (last_key_whitespace != (size_t) -1) - key[last_key_whitespace] = 0; - - r = push(fname, line, key, value, userdata, n_pushed); - if (r < 0) - return r; - - value = NULL; - } - - return 0; -} - -static int check_utf8ness_and_warn( - const char *filename, unsigned line, - const char *key, char *value) { - - if (!utf8_is_valid(key)) { - _cleanup_free_ char *p = NULL; - - p = utf8_escape_invalid(key); - log_error("%s:%u: invalid UTF-8 in key '%s', ignoring.", strna(filename), line, p); - return -EINVAL; - } - - if (value && !utf8_is_valid(value)) { - _cleanup_free_ char *p = NULL; - - p = utf8_escape_invalid(value); - log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, p); - return -EINVAL; - } - - return 0; -} - -static int parse_env_file_push( - const char *filename, unsigned line, - const char *key, char *value, - void *userdata, - int *n_pushed) { - - const char *k; - va_list aq, *ap = userdata; - int r; - - r = check_utf8ness_and_warn(filename, line, key, value); - if (r < 0) - return r; - - va_copy(aq, *ap); - - while ((k = va_arg(aq, const char *))) { - char **v; - - v = va_arg(aq, char **); - - if (streq(key, k)) { - va_end(aq); - free(*v); - *v = value; - - if (n_pushed) - (*n_pushed)++; - - return 1; - } - } - - va_end(aq); - free(value); - - return 0; -} - -int parse_env_filev( - FILE *f, - const char *fname, - const char *newline, - va_list ap) { - - int r, n_pushed = 0; - va_list aq; - - if (!newline) - newline = NEWLINE; - - va_copy(aq, ap); - r = parse_env_file_internal(f, fname, newline, parse_env_file_push, &aq, &n_pushed); - va_end(aq); - if (r < 0) - return r; - - return n_pushed; -} - -int parse_env_file( - FILE *f, - const char *fname, - const char *newline, - ...) { - - va_list ap; - int r; - - va_start(ap, newline); - r = parse_env_filev(f, fname, newline, ap); - va_end(ap); - - return r; -} - -#if 0 /* NM_IGNORED */ -static int load_env_file_push( - const char *filename, unsigned line, - const char *key, char *value, - void *userdata, - int *n_pushed) { - char ***m = userdata; - char *p; - int r; - - r = check_utf8ness_and_warn(filename, line, key, value); - if (r < 0) - return r; - - p = strjoin(key, "=", value); - if (!p) - return -ENOMEM; - - r = strv_env_replace(m, p); - if (r < 0) { - free(p); - return r; - } - - if (n_pushed) - (*n_pushed)++; - - free(value); - return 0; -} - -int load_env_file(FILE *f, const char *fname, const char *newline, char ***rl) { - char **m = NULL; - int r; - - if (!newline) - newline = NEWLINE; - - r = parse_env_file_internal(f, fname, newline, load_env_file_push, &m, NULL); - if (r < 0) { - strv_free(m); - return r; - } - - *rl = m; - return 0; -} - -static int load_env_file_push_pairs( - const char *filename, unsigned line, - const char *key, char *value, - void *userdata, - int *n_pushed) { - char ***m = userdata; - int r; - - r = check_utf8ness_and_warn(filename, line, key, value); - if (r < 0) - return r; - - r = strv_extend(m, key); - if (r < 0) - return -ENOMEM; - - if (!value) { - r = strv_extend(m, ""); - if (r < 0) - return -ENOMEM; - } else { - r = strv_push(m, value); - if (r < 0) - return r; - } - - if (n_pushed) - (*n_pushed)++; - - return 0; -} - -int load_env_file_pairs(FILE *f, const char *fname, const char *newline, char ***rl) { - char **m = NULL; - int r; - - if (!newline) - newline = NEWLINE; - - r = parse_env_file_internal(f, fname, newline, load_env_file_push_pairs, &m, NULL); - if (r < 0) { - strv_free(m); - return r; - } - - *rl = m; - return 0; -} - -static int merge_env_file_push( - const char *filename, unsigned line, - const char *key, char *value, - void *userdata, - int *n_pushed) { - - char ***env = userdata; - char *expanded_value; - - assert(env); - - if (!value) { - log_error("%s:%u: invalid syntax (around \"%s\"), ignoring.", strna(filename), line, key); - return 0; - } - - if (!env_name_is_valid(key)) { - log_error("%s:%u: invalid variable name \"%s\", ignoring.", strna(filename), line, key); - free(value); - return 0; - } - - expanded_value = replace_env(value, *env, - REPLACE_ENV_USE_ENVIRONMENT| - REPLACE_ENV_ALLOW_BRACELESS| - REPLACE_ENV_ALLOW_EXTENDED); - if (!expanded_value) - return -ENOMEM; - - free_and_replace(value, expanded_value); - - return load_env_file_push(filename, line, key, value, env, n_pushed); -} - -int merge_env_file( - char ***env, - FILE *f, - const char *fname) { - - /* NOTE: this function supports braceful and braceless variable expansions, - * plus "extended" substitutions, unlike other exported parsing functions. - */ - - return parse_env_file_internal(f, fname, NEWLINE, merge_env_file_push, env, NULL); -} - -static void write_env_var(FILE *f, const char *v) { - const char *p; - - p = strchr(v, '='); - if (!p) { - /* Fallback */ - fputs_unlocked(v, f); - fputc_unlocked('\n', f); - return; - } - - p++; - fwrite_unlocked(v, 1, p-v, f); - - if (string_has_cc(p, NULL) || chars_intersect(p, WHITESPACE SHELL_NEED_QUOTES)) { - fputc_unlocked('\"', f); - - for (; *p; p++) { - if (strchr(SHELL_NEED_ESCAPE, *p)) - fputc_unlocked('\\', f); - - fputc_unlocked(*p, f); - } - - fputc_unlocked('\"', f); - } else - fputs_unlocked(p, f); - - fputc_unlocked('\n', f); -} - -int write_env_file(const char *fname, char **l) { - _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *p = NULL; - char **i; - int r; - - assert(fname); - - r = fopen_temporary(fname, &f, &p); - if (r < 0) - return r; - - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - (void) fchmod_umask(fileno(f), 0644); - - STRV_FOREACH(i, l) - write_env_var(f, *i); - - r = fflush_and_check(f); - if (r >= 0) { - if (rename(p, fname) >= 0) - return 0; - - r = -errno; - } - - unlink(p); - return r; -} - -int executable_is_script(const char *path, char **interpreter) { - _cleanup_free_ char *line = NULL; - size_t len; - char *ans; - int r; - - assert(path); - - r = read_one_line_file(path, &line); - if (r == -ENOBUFS) /* First line overly long? if so, then it's not a script */ - return 0; - if (r < 0) - return r; - - if (!startswith(line, "#!")) - return 0; - - ans = strstrip(line + 2); - len = strcspn(ans, " \t"); - - if (len == 0) - return 0; - - ans = strndup(ans, len); - if (!ans) - return -ENOMEM; - - *interpreter = ans; - return 1; -} -#endif /* NM_IGNORED */ - -/** - * Retrieve one field from a file like /proc/self/status. pattern - * should not include whitespace or the delimiter (':'). pattern matches only - * the beginning of a line. Whitespace before ':' is skipped. Whitespace and - * zeros after the ':' will be skipped. field must be freed afterwards. - * terminator specifies the terminating characters of the field value (not - * included in the value). - */ -int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field) { - _cleanup_free_ char *status = NULL; - char *t, *f; - size_t len; - int r; - - assert(terminator); - assert(filename); - assert(pattern); - assert(field); - - r = read_full_file(filename, &status, NULL); - if (r < 0) - return r; - - t = status; - - do { - bool pattern_ok; - - do { - t = strstr(t, pattern); - if (!t) - return -ENOENT; - - /* Check that pattern occurs in beginning of line. */ - pattern_ok = (t == status || t[-1] == '\n'); - - t += strlen(pattern); - - } while (!pattern_ok); - - t += strspn(t, " \t"); - if (!*t) - return -ENOENT; - - } while (*t != ':'); - - t++; - - if (*t) { - t += strspn(t, " \t"); - - /* Also skip zeros, because when this is used for - * capabilities, we don't want the zeros. This way the - * same capability set always maps to the same string, - * irrespective of the total capability set size. For - * other numbers it shouldn't matter. */ - t += strspn(t, "0"); - /* Back off one char if there's nothing but whitespace - and zeros */ - if (!*t || isspace(*t)) - t--; - } - - len = strcspn(t, terminator); - - f = strndup(t, len); - if (!f) - return -ENOMEM; - - *field = f; - return 0; -} - -DIR *xopendirat(int fd, const char *name, int flags) { - int nfd; - DIR *d; - - assert(!(flags & O_CREAT)); - - nfd = openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|flags, 0); - if (nfd < 0) - return NULL; - - d = fdopendir(nfd); - if (!d) { - safe_close(nfd); - return NULL; - } - - return d; -} - -#if 0 /* NM_IGNORED */ -static int search_and_fopen_internal(const char *path, const char *mode, const char *root, char **search, FILE **_f) { - char **i; - - assert(path); - assert(mode); - assert(_f); - - if (!path_strv_resolve_uniq(search, root)) - return -ENOMEM; - - STRV_FOREACH(i, search) { - _cleanup_free_ char *p = NULL; - FILE *f; - - if (root) - p = strjoin(root, *i, "/", path); - else - p = strjoin(*i, "/", path); - if (!p) - return -ENOMEM; - - f = fopen(p, mode); - if (f) { - *_f = f; - return 0; - } - - if (errno != ENOENT) - return -errno; - } - - return -ENOENT; -} - -int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f) { - _cleanup_strv_free_ char **copy = NULL; - - assert(path); - assert(mode); - assert(_f); - - if (path_is_absolute(path)) { - FILE *f; - - f = fopen(path, mode); - if (f) { - *_f = f; - return 0; - } - - return -errno; - } - - copy = strv_copy((char**) search); - if (!copy) - return -ENOMEM; - - return search_and_fopen_internal(path, mode, root, copy, _f); -} - -int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f) { - _cleanup_strv_free_ char **s = NULL; - - if (path_is_absolute(path)) { - FILE *f; - - f = fopen(path, mode); - if (f) { - *_f = f; - return 0; - } - - return -errno; - } - - s = strv_split_nulstr(search); - if (!s) - return -ENOMEM; - - return search_and_fopen_internal(path, mode, root, s, _f); -} -#endif /* NM_IGNORED */ - -int fopen_temporary(const char *path, FILE **_f, char **_temp_path) { - FILE *f; - char *t; - int r, fd; - - assert(path); - assert(_f); - assert(_temp_path); - - r = tempfn_xxxxxx(path, NULL, &t); - if (r < 0) - return r; - - fd = mkostemp_safe(t); - if (fd < 0) { - free(t); - return -errno; - } - - f = fdopen(fd, "we"); - if (!f) { - unlink_noerrno(t); - free(t); - safe_close(fd); - return -errno; - } - - *_f = f; - *_temp_path = t; - - return 0; -} - -int fflush_and_check(FILE *f) { - assert(f); - - errno = 0; - fflush(f); - - if (ferror(f)) - return errno > 0 ? -errno : -EIO; - - return 0; -} - -#if 0 /* NM_IGNORED */ -int fflush_sync_and_check(FILE *f) { - int r; - - assert(f); - - r = fflush_and_check(f); - if (r < 0) - return r; - - if (fsync(fileno(f)) < 0) - return -errno; - - r = fsync_directory_of_file(fileno(f)); - if (r < 0) - return r; - - return 0; -} -#endif /* NM_IGNORED */ - -/* This is much like mkostemp() but is subject to umask(). */ -int mkostemp_safe(char *pattern) { - _cleanup_umask_ mode_t u = 0; - int fd; - - assert(pattern); - - u = umask(077); - - fd = mkostemp(pattern, O_CLOEXEC); - if (fd < 0) - return -errno; - - return fd; -} - -int tempfn_xxxxxx(const char *p, const char *extra, char **ret) { - const char *fn; - char *t; - - assert(ret); - - if (isempty(p)) - return -EINVAL; - if (path_equal(p, "/")) - return -EINVAL; - - /* - * Turns this: - * /foo/bar/waldo - * - * Into this: - * /foo/bar/.#<extra>waldoXXXXXX - */ - - fn = basename(p); - if (!filename_is_valid(fn)) - return -EINVAL; - - extra = strempty(extra); - - t = new(char, strlen(p) + 2 + strlen(extra) + 6 + 1); - if (!t) - return -ENOMEM; - - strcpy(stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn), "XXXXXX"); - - *ret = path_simplify(t, false); - return 0; -} - -#if 0 /* NM_IGNORED */ -int tempfn_random(const char *p, const char *extra, char **ret) { - const char *fn; - char *t, *x; - uint64_t u; - unsigned i; - - assert(ret); - - if (isempty(p)) - return -EINVAL; - if (path_equal(p, "/")) - return -EINVAL; - - /* - * Turns this: - * /foo/bar/waldo - * - * Into this: - * /foo/bar/.#<extra>waldobaa2a261115984a9 - */ - - fn = basename(p); - if (!filename_is_valid(fn)) - return -EINVAL; - - extra = strempty(extra); - - t = new(char, strlen(p) + 2 + strlen(extra) + 16 + 1); - if (!t) - return -ENOMEM; - - x = stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn); - - u = random_u64(); - for (i = 0; i < 16; i++) { - *(x++) = hexchar(u & 0xF); - u >>= 4; - } - - *x = 0; - - *ret = path_simplify(t, false); - return 0; -} - -int tempfn_random_child(const char *p, const char *extra, char **ret) { - char *t, *x; - uint64_t u; - unsigned i; - int r; - - assert(ret); - - /* Turns this: - * /foo/bar/waldo - * Into this: - * /foo/bar/waldo/.#<extra>3c2b6219aa75d7d0 - */ - - if (!p) { - r = tmp_dir(&p); - if (r < 0) - return r; - } - - extra = strempty(extra); - - t = new(char, strlen(p) + 3 + strlen(extra) + 16 + 1); - if (!t) - return -ENOMEM; - - if (isempty(p)) - x = stpcpy(stpcpy(t, ".#"), extra); - else - x = stpcpy(stpcpy(stpcpy(t, p), "/.#"), extra); - - u = random_u64(); - for (i = 0; i < 16; i++) { - *(x++) = hexchar(u & 0xF); - u >>= 4; - } - - *x = 0; - - *ret = path_simplify(t, false); - return 0; -} - -int write_timestamp_file_atomic(const char *fn, usec_t n) { - char ln[DECIMAL_STR_MAX(n)+2]; - - /* Creates a "timestamp" file, that contains nothing but a - * usec_t timestamp, formatted in ASCII. */ - - if (n <= 0 || n >= USEC_INFINITY) - return -ERANGE; - - xsprintf(ln, USEC_FMT "\n", n); - - return write_string_file(fn, ln, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC); -} - -int read_timestamp_file(const char *fn, usec_t *ret) { - _cleanup_free_ char *ln = NULL; - uint64_t t; - int r; - - r = read_one_line_file(fn, &ln); - if (r < 0) - return r; - - r = safe_atou64(ln, &t); - if (r < 0) - return r; - - if (t <= 0 || t >= (uint64_t) USEC_INFINITY) - return -ERANGE; - - *ret = (usec_t) t; - return 0; -} -#endif /* NM_IGNORED */ - -int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space) { - int r; - - assert(s); - - /* Outputs the specified string with fputs(), but optionally prefixes it with a separator. The *space parameter - * when specified shall initially point to a boolean variable initialized to false. It is set to true after the - * first invocation. This call is supposed to be use in loops, where a separator shall be inserted between each - * element, but not before the first one. */ - - if (!f) - f = stdout; - - if (space) { - if (!separator) - separator = " "; - - if (*space) { - r = fputs(separator, f); - if (r < 0) - return r; - } - - *space = true; - } - - return fputs(s, f); -} - -#if 0 /* NM_IGNORED */ -int open_tmpfile_unlinkable(const char *directory, int flags) { - char *p; - int fd, r; - - if (!directory) { - r = tmp_dir(&directory); - if (r < 0) - return r; - } else if (isempty(directory)) - return -EINVAL; - - /* Returns an unlinked temporary file that cannot be linked into the file system anymore */ - - /* Try O_TMPFILE first, if it is supported */ - fd = open(directory, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR); - if (fd >= 0) - return fd; - - /* Fall back to unguessable name + unlinking */ - p = strjoina(directory, "/systemd-tmp-XXXXXX"); - - fd = mkostemp_safe(p); - if (fd < 0) - return fd; - - (void) unlink(p); - - return fd; -} - -int open_tmpfile_linkable(const char *target, int flags, char **ret_path) { - _cleanup_free_ char *tmp = NULL; - int r, fd; - - assert(target); - assert(ret_path); - - /* Don't allow O_EXCL, as that has a special meaning for O_TMPFILE */ - assert((flags & O_EXCL) == 0); - - /* Creates a temporary file, that shall be renamed to "target" later. If possible, this uses O_TMPFILE – in - * which case "ret_path" will be returned as NULL. If not possible a the tempoary path name used is returned in - * "ret_path". Use link_tmpfile() below to rename the result after writing the file in full. */ - - fd = open_parent(target, O_TMPFILE|flags, 0640); - if (fd >= 0) { - *ret_path = NULL; - return fd; - } - - log_debug_errno(fd, "Failed to use O_TMPFILE for %s: %m", target); - - r = tempfn_random(target, NULL, &tmp); - if (r < 0) - return r; - - fd = open(tmp, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|flags, 0640); - if (fd < 0) - return -errno; - - *ret_path = TAKE_PTR(tmp); - - return fd; -} - -int open_serialization_fd(const char *ident) { - int fd = -1; - - fd = memfd_create(ident, MFD_CLOEXEC); - if (fd < 0) { - const char *path; - - path = getpid_cached() == 1 ? "/run/systemd" : "/tmp"; - fd = open_tmpfile_unlinkable(path, O_RDWR|O_CLOEXEC); - if (fd < 0) - return fd; - - log_debug("Serializing %s to %s.", ident, path); - } else - log_debug("Serializing %s to memfd.", ident); - - return fd; -} - -int link_tmpfile(int fd, const char *path, const char *target) { - - assert(fd >= 0); - assert(target); - - /* Moves a temporary file created with open_tmpfile() above into its final place. if "path" is NULL an fd - * created with O_TMPFILE is assumed, and linkat() is used. Otherwise it is assumed O_TMPFILE is not supported - * on the directory, and renameat2() is used instead. - * - * Note that in both cases we will not replace existing files. This is because linkat() does not support this - * operation currently (renameat2() does), and there is no nice way to emulate this. */ - - if (path) { - if (rename_noreplace(AT_FDCWD, path, AT_FDCWD, target) < 0) - return -errno; - } else { - char proc_fd_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1]; - - xsprintf(proc_fd_path, "/proc/self/fd/%i", fd); - - if (linkat(AT_FDCWD, proc_fd_path, AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0) - return -errno; - } - - return 0; -} - -int read_nul_string(FILE *f, char **ret) { - _cleanup_free_ char *x = NULL; - size_t allocated = 0, n = 0; - - assert(f); - assert(ret); - - /* Reads a NUL-terminated string from the specified file. */ - - for (;;) { - int c; - - if (!GREEDY_REALLOC(x, allocated, n+2)) - return -ENOMEM; - - c = fgetc(f); - if (c == 0) /* Terminate at NUL byte */ - break; - if (c == EOF) { - if (ferror(f)) - return -errno; - break; /* Terminate at EOF */ - } - - x[n++] = (char) c; - } - - if (x) - x[n] = 0; - else { - x = new0(char, 1); - if (!x) - return -ENOMEM; - } - - *ret = TAKE_PTR(x); - - return 0; -} - -int mkdtemp_malloc(const char *template, char **ret) { - _cleanup_free_ char *p = NULL; - int r; - - assert(ret); - - if (template) - p = strdup(template); - else { - const char *tmp; - - r = tmp_dir(&tmp); - if (r < 0) - return r; - - p = strjoin(tmp, "/XXXXXX"); - } - if (!p) - return -ENOMEM; - - if (!mkdtemp(p)) - return -errno; - - *ret = TAKE_PTR(p); - return 0; -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, funlockfile); - -int read_line(FILE *f, size_t limit, char **ret) { - _cleanup_free_ char *buffer = NULL; - size_t n = 0, allocated = 0, count = 0; - - assert(f); - - /* Something like a bounded version of getline(). - * - * Considers EOF, \n and \0 end of line delimiters, and does not include these delimiters in the string - * returned. - * - * Returns the number of bytes read from the files (i.e. including delimiters — this hence usually differs from - * the number of characters in the returned string). When EOF is hit, 0 is returned. - * - * The input parameter limit is the maximum numbers of characters in the returned string, i.e. excluding - * delimiters. If the limit is hit we fail and return -ENOBUFS. - * - * If a line shall be skipped ret may be initialized as NULL. */ - - if (ret) { - if (!GREEDY_REALLOC(buffer, allocated, 1)) - return -ENOMEM; - } - - { - _unused_ _cleanup_(funlockfilep) FILE *flocked = f; - flockfile(f); - - for (;;) { - int c; - - if (n >= limit) - return -ENOBUFS; - - errno = 0; - c = fgetc_unlocked(f); - if (c == EOF) { - /* if we read an error, and have no data to return, then propagate the error */ - if (ferror_unlocked(f) && n == 0) - return errno > 0 ? -errno : -EIO; - - break; - } - - count++; - - if (IN_SET(c, '\n', 0)) /* Reached a delimiter */ - break; - - if (ret) { - if (!GREEDY_REALLOC(buffer, allocated, n + 2)) - return -ENOMEM; - - buffer[n] = (char) c; - } - - n++; - } - } - - if (ret) { - buffer[n] = 0; - - *ret = TAKE_PTR(buffer); - } - - return (int) count; -} -#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/fileio.h b/src/systemd/src/basic/fileio.h deleted file mode 100644 index 77e6206e..00000000 --- a/src/systemd/src/basic/fileio.h +++ /dev/null @@ -1,96 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <dirent.h> -#include <stdbool.h> -#include <stddef.h> -#include <stdio.h> -#include <sys/types.h> - -#include "macro.h" -#include "time-util.h" - -typedef enum { - WRITE_STRING_FILE_CREATE = 1 << 0, - WRITE_STRING_FILE_ATOMIC = 1 << 1, - WRITE_STRING_FILE_AVOID_NEWLINE = 1 << 2, - WRITE_STRING_FILE_VERIFY_ON_FAILURE = 1 << 3, - WRITE_STRING_FILE_SYNC = 1 << 4, - WRITE_STRING_FILE_DISABLE_BUFFER = 1 << 5, - - /* And before you wonder, why write_string_file_atomic_label_ts() is a separate function instead of just one - more flag here: it's about linking: we don't want to pull -lselinux into all users of write_string_file() - and friends. */ - -} WriteStringFileFlags; - -int write_string_stream_ts(FILE *f, const char *line, WriteStringFileFlags flags, struct timespec *ts); -static inline int write_string_stream(FILE *f, const char *line, WriteStringFileFlags flags) { - return write_string_stream_ts(f, line, flags, NULL); -} -int write_string_file_ts(const char *fn, const char *line, WriteStringFileFlags flags, struct timespec *ts); -static inline int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags) { - return write_string_file_ts(fn, line, flags, NULL); -} - -int write_string_filef(const char *fn, WriteStringFileFlags flags, const char *format, ...) _printf_(3, 4); - -int read_one_line_file(const char *fn, char **line); -int read_full_file(const char *fn, char **contents, size_t *size); -int read_full_stream(FILE *f, char **contents, size_t *size); - -int verify_file(const char *fn, const char *blob, bool accept_extra_nl); - -int parse_env_filev(FILE *f, const char *fname, const char *separator, va_list ap); -int parse_env_file(FILE *f, const char *fname, const char *separator, ...) _sentinel_; -int load_env_file(FILE *f, const char *fname, const char *separator, char ***l); -int load_env_file_pairs(FILE *f, const char *fname, const char *separator, char ***l); - -int merge_env_file(char ***env, FILE *f, const char *fname); - -int write_env_file(const char *fname, char **l); - -int executable_is_script(const char *path, char **interpreter); - -int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field); - -DIR *xopendirat(int dirfd, const char *name, int flags); - -int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f); -int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f); - -#define FOREACH_LINE(line, f, on_error) \ - for (;;) \ - if (!fgets(line, sizeof(line), f)) { \ - if (ferror(f)) { \ - on_error; \ - } \ - break; \ - } else - -int fflush_and_check(FILE *f); -int fflush_sync_and_check(FILE *f); - -int fopen_temporary(const char *path, FILE **_f, char **_temp_path); -int mkostemp_safe(char *pattern); - -int tempfn_xxxxxx(const char *p, const char *extra, char **ret); -int tempfn_random(const char *p, const char *extra, char **ret); -int tempfn_random_child(const char *p, const char *extra, char **ret); - -int write_timestamp_file_atomic(const char *fn, usec_t n); -int read_timestamp_file(const char *fn, usec_t *ret); - -int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space); - -int open_tmpfile_unlinkable(const char *directory, int flags); -int open_tmpfile_linkable(const char *target, int flags, char **ret_path); -int open_serialization_fd(const char *ident); - -int link_tmpfile(int fd, const char *path, const char *target); - -int read_nul_string(FILE *f, char **ret); - -int mkdtemp_malloc(const char *template, char **ret); - -int read_line(FILE *f, size_t limit, char **ret); diff --git a/src/systemd/src/basic/fs-util.c b/src/systemd/src/basic/fs-util.c deleted file mode 100644 index 8c19e9c7..00000000 --- a/src/systemd/src/basic/fs-util.c +++ /dev/null @@ -1,1256 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <errno.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/stat.h> -#include <linux/magic.h> -#include <time.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "dirent-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "fs-util.h" -#include "log.h" -#include "macro.h" -#include "missing.h" -#include "mkdir.h" -#include "parse-util.h" -#include "path-util.h" -#include "process-util.h" -#include "stat-util.h" -#include "stdio-util.h" -#include "string-util.h" -#include "strv.h" -#include "time-util.h" -#include "user-util.h" -#include "util.h" - -int unlink_noerrno(const char *path) { - PROTECT_ERRNO; - int r; - - r = unlink(path); - if (r < 0) - return -errno; - - return 0; -} - -#if 0 /* NM_IGNORED */ -int rmdir_parents(const char *path, const char *stop) { - size_t l; - int r = 0; - - assert(path); - assert(stop); - - l = strlen(path); - - /* Skip trailing slashes */ - while (l > 0 && path[l-1] == '/') - l--; - - while (l > 0) { - char *t; - - /* Skip last component */ - while (l > 0 && path[l-1] != '/') - l--; - - /* Skip trailing slashes */ - while (l > 0 && path[l-1] == '/') - l--; - - if (l <= 0) - break; - - t = strndup(path, l); - if (!t) - return -ENOMEM; - - if (path_startswith(stop, t)) { - free(t); - return 0; - } - - r = rmdir(t); - free(t); - - if (r < 0) - if (errno != ENOENT) - return -errno; - } - - return 0; -} - -int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) { - struct stat buf; - int ret; - - ret = renameat2(olddirfd, oldpath, newdirfd, newpath, RENAME_NOREPLACE); - if (ret >= 0) - return 0; - - /* renameat2() exists since Linux 3.15, btrfs added support for it later. - * If it is not implemented, fallback to another method. */ - if (!IN_SET(errno, EINVAL, ENOSYS)) - return -errno; - - /* The link()/unlink() fallback does not work on directories. But - * renameat() without RENAME_NOREPLACE gives the same semantics on - * directories, except when newpath is an *empty* directory. This is - * good enough. */ - ret = fstatat(olddirfd, oldpath, &buf, AT_SYMLINK_NOFOLLOW); - if (ret >= 0 && S_ISDIR(buf.st_mode)) { - ret = renameat(olddirfd, oldpath, newdirfd, newpath); - return ret >= 0 ? 0 : -errno; - } - - /* If it is not a directory, use the link()/unlink() fallback. */ - ret = linkat(olddirfd, oldpath, newdirfd, newpath, 0); - if (ret < 0) - return -errno; - - ret = unlinkat(olddirfd, oldpath, 0); - if (ret < 0) { - /* backup errno before the following unlinkat() alters it */ - ret = errno; - (void) unlinkat(newdirfd, newpath, 0); - errno = ret; - return -errno; - } - - return 0; -} -#endif /* NM_IGNORED */ - -int readlinkat_malloc(int fd, const char *p, char **ret) { - size_t l = 100; - int r; - - assert(p); - assert(ret); - - for (;;) { - char *c; - ssize_t n; - - c = new(char, l); - if (!c) - return -ENOMEM; - - n = readlinkat(fd, p, c, l-1); - if (n < 0) { - r = -errno; - free(c); - return r; - } - - if ((size_t) n < l-1) { - c[n] = 0; - *ret = c; - return 0; - } - - free(c); - l *= 2; - } -} - -int readlink_malloc(const char *p, char **ret) { - return readlinkat_malloc(AT_FDCWD, p, ret); -} - -#if 0 /* NM_IGNORED */ -int readlink_value(const char *p, char **ret) { - _cleanup_free_ char *link = NULL; - char *value; - int r; - - r = readlink_malloc(p, &link); - if (r < 0) - return r; - - value = basename(link); - if (!value) - return -ENOENT; - - value = strdup(value); - if (!value) - return -ENOMEM; - - *ret = value; - - return 0; -} - -int readlink_and_make_absolute(const char *p, char **r) { - _cleanup_free_ char *target = NULL; - char *k; - int j; - - assert(p); - assert(r); - - j = readlink_malloc(p, &target); - if (j < 0) - return j; - - k = file_in_same_dir(p, target); - if (!k) - return -ENOMEM; - - *r = k; - return 0; -} - -int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) { - assert(path); - - /* Under the assumption that we are running privileged we - * first change the access mode and only then hand out - * ownership to avoid a window where access is too open. */ - - if (mode != MODE_INVALID) - if (chmod(path, mode) < 0) - return -errno; - - if (uid != UID_INVALID || gid != GID_INVALID) - if (chown(path, uid, gid) < 0) - return -errno; - - return 0; -} - -int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) { - /* Under the assumption that we are running privileged we - * first change the access mode and only then hand out - * ownership to avoid a window where access is too open. */ - - if (mode != MODE_INVALID) - if (fchmod(fd, mode) < 0) - return -errno; - - if (uid != UID_INVALID || gid != GID_INVALID) - if (fchown(fd, uid, gid) < 0) - return -errno; - - return 0; -} -#endif /* NM_IGNORED */ - -int fchmod_umask(int fd, mode_t m) { - mode_t u; - int r; - - u = umask(0777); - r = fchmod(fd, m & (~u)) < 0 ? -errno : 0; - umask(u); - - return r; -} - -#if 0 /* NM_IGNORED */ -int fchmod_opath(int fd, mode_t m) { - char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; - - /* This function operates also on fd that might have been opened with - * O_PATH. Indeed fchmodat() doesn't have the AT_EMPTY_PATH flag like - * fchownat() does. */ - - xsprintf(procfs_path, "/proc/self/fd/%i", fd); - - if (chmod(procfs_path, m) < 0) - return -errno; - - return 0; -} - -int fd_warn_permissions(const char *path, int fd) { - struct stat st; - - if (fstat(fd, &st) < 0) - return -errno; - - if (st.st_mode & 0111) - log_warning("Configuration file %s is marked executable. Please remove executable permission bits. Proceeding anyway.", path); - - if (st.st_mode & 0002) - log_warning("Configuration file %s is marked world-writable. Please remove world writability permission bits. Proceeding anyway.", path); - - if (getpid_cached() == 1 && (st.st_mode & 0044) != 0044) - log_warning("Configuration file %s is marked world-inaccessible. This has no effect as configuration data is accessible via APIs without restrictions. Proceeding anyway.", path); - - return 0; -} - -int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) { - char fdpath[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; - _cleanup_close_ int fd = -1; - int r, ret = 0; - - assert(path); - - /* Note that touch_file() does not follow symlinks: if invoked on an existing symlink, then it is the symlink - * itself which is updated, not its target - * - * Returns the first error we encounter, but tries to apply as much as possible. */ - - if (parents) - (void) mkdir_parents(path, 0755); - - /* Initially, we try to open the node with O_PATH, so that we get a reference to the node. This is useful in - * case the path refers to an existing device or socket node, as we can open it successfully in all cases, and - * won't trigger any driver magic or so. */ - fd = open(path, O_PATH|O_CLOEXEC|O_NOFOLLOW); - if (fd < 0) { - if (errno != ENOENT) - return -errno; - - /* if the node doesn't exist yet, we create it, but with O_EXCL, so that we only create a regular file - * here, and nothing else */ - fd = open(path, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, IN_SET(mode, 0, MODE_INVALID) ? 0644 : mode); - if (fd < 0) - return -errno; - } - - /* Let's make a path from the fd, and operate on that. With this logic, we can adjust the access mode, - * ownership and time of the file node in all cases, even if the fd refers to an O_PATH object — which is - * something fchown(), fchmod(), futimensat() don't allow. */ - xsprintf(fdpath, "/proc/self/fd/%i", fd); - - if (mode != MODE_INVALID) - if (chmod(fdpath, mode) < 0) - ret = -errno; - - if (uid_is_valid(uid) || gid_is_valid(gid)) - if (chown(fdpath, uid, gid) < 0 && ret >= 0) - ret = -errno; - - if (stamp != USEC_INFINITY) { - struct timespec ts[2]; - - timespec_store(&ts[0], stamp); - ts[1] = ts[0]; - r = utimensat(AT_FDCWD, fdpath, ts, 0); - } else - r = utimensat(AT_FDCWD, fdpath, NULL, 0); - if (r < 0 && ret >= 0) - return -errno; - - return ret; -} - -int touch(const char *path) { - return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID); -} - -int symlink_idempotent(const char *from, const char *to) { - int r; - - assert(from); - assert(to); - - if (symlink(from, to) < 0) { - _cleanup_free_ char *p = NULL; - - if (errno != EEXIST) - return -errno; - - r = readlink_malloc(to, &p); - if (r == -EINVAL) /* Not a symlink? In that case return the original error we encountered: -EEXIST */ - return -EEXIST; - if (r < 0) /* Any other error? In that case propagate it as is */ - return r; - - if (!streq(p, from)) /* Not the symlink we want it to be? In that case, propagate the original -EEXIST */ - return -EEXIST; - } - - return 0; -} - -int symlink_atomic(const char *from, const char *to) { - _cleanup_free_ char *t = NULL; - int r; - - assert(from); - assert(to); - - r = tempfn_random(to, NULL, &t); - if (r < 0) - return r; - - if (symlink(from, t) < 0) - return -errno; - - if (rename(t, to) < 0) { - unlink_noerrno(t); - return -errno; - } - - return 0; -} - -int mknod_atomic(const char *path, mode_t mode, dev_t dev) { - _cleanup_free_ char *t = NULL; - int r; - - assert(path); - - r = tempfn_random(path, NULL, &t); - if (r < 0) - return r; - - if (mknod(t, mode, dev) < 0) - return -errno; - - if (rename(t, path) < 0) { - unlink_noerrno(t); - return -errno; - } - - return 0; -} - -int mkfifo_atomic(const char *path, mode_t mode) { - _cleanup_free_ char *t = NULL; - int r; - - assert(path); - - r = tempfn_random(path, NULL, &t); - if (r < 0) - return r; - - if (mkfifo(t, mode) < 0) - return -errno; - - if (rename(t, path) < 0) { - unlink_noerrno(t); - return -errno; - } - - return 0; -} - -int mkfifoat_atomic(int dirfd, const char *path, mode_t mode) { - _cleanup_free_ char *t = NULL; - int r; - - assert(path); - - if (path_is_absolute(path)) - return mkfifo_atomic(path, mode); - - /* We're only interested in the (random) filename. */ - r = tempfn_random_child("", NULL, &t); - if (r < 0) - return r; - - if (mkfifoat(dirfd, t, mode) < 0) - return -errno; - - if (renameat(dirfd, t, dirfd, path) < 0) { - unlink_noerrno(t); - return -errno; - } - - return 0; -} - -int get_files_in_directory(const char *path, char ***list) { - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; - size_t bufsize = 0, n = 0; - _cleanup_strv_free_ char **l = NULL; - - assert(path); - - /* Returns all files in a directory in *list, and the number - * of files as return value. If list is NULL returns only the - * number. */ - - d = opendir(path); - if (!d) - return -errno; - - FOREACH_DIRENT_ALL(de, d, return -errno) { - dirent_ensure_type(d, de); - - if (!dirent_is_file(de)) - continue; - - if (list) { - /* one extra slot is needed for the terminating NULL */ - if (!GREEDY_REALLOC(l, bufsize, n + 2)) - return -ENOMEM; - - l[n] = strdup(de->d_name); - if (!l[n]) - return -ENOMEM; - - l[++n] = NULL; - } else - n++; - } - - if (list) - *list = TAKE_PTR(l); - - return n; -} - -static int getenv_tmp_dir(const char **ret_path) { - const char *n; - int r, ret = 0; - - assert(ret_path); - - /* We use the same order of environment variables python uses in tempfile.gettempdir(): - * https://docs.python.org/3/library/tempfile.html#tempfile.gettempdir */ - FOREACH_STRING(n, "TMPDIR", "TEMP", "TMP") { - const char *e; - - e = secure_getenv(n); - if (!e) - continue; - if (!path_is_absolute(e)) { - r = -ENOTDIR; - goto next; - } - if (!path_is_normalized(e)) { - r = -EPERM; - goto next; - } - - r = is_dir(e, true); - if (r < 0) - goto next; - if (r == 0) { - r = -ENOTDIR; - goto next; - } - - *ret_path = e; - return 1; - - next: - /* Remember first error, to make this more debuggable */ - if (ret >= 0) - ret = r; - } - - if (ret < 0) - return ret; - - *ret_path = NULL; - return ret; -} - -static int tmp_dir_internal(const char *def, const char **ret) { - const char *e; - int r, k; - - assert(def); - assert(ret); - - r = getenv_tmp_dir(&e); - if (r > 0) { - *ret = e; - return 0; - } - - k = is_dir(def, true); - if (k == 0) - k = -ENOTDIR; - if (k < 0) - return r < 0 ? r : k; - - *ret = def; - return 0; -} - -int var_tmp_dir(const char **ret) { - - /* Returns the location for "larger" temporary files, that is backed by physical storage if available, and thus - * even might survive a boot: /var/tmp. If $TMPDIR (or related environment variables) are set, its value is - * returned preferably however. Note that both this function and tmp_dir() below are affected by $TMPDIR, - * making it a variable that overrides all temporary file storage locations. */ - - return tmp_dir_internal("/var/tmp", ret); -} - -int tmp_dir(const char **ret) { - - /* Similar to var_tmp_dir() above, but returns the location for "smaller" temporary files, which is usually - * backed by an in-memory file system: /tmp. */ - - return tmp_dir_internal("/tmp", ret); -} - -int unlink_or_warn(const char *filename) { - if (unlink(filename) < 0 && errno != ENOENT) - /* If the file doesn't exist and the fs simply was read-only (in which - * case unlink() returns EROFS even if the file doesn't exist), don't - * complain */ - if (errno != EROFS || access(filename, F_OK) >= 0) - return log_error_errno(errno, "Failed to remove \"%s\": %m", filename); - - return 0; -} -#endif /* NM_IGNORED */ - -int inotify_add_watch_fd(int fd, int what, uint32_t mask) { - char path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1]; - int r; - - /* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */ - xsprintf(path, "/proc/self/fd/%i", what); - - r = inotify_add_watch(fd, path, mask); - if (r < 0) - return -errno; - - return r; -} - -#if 0 /* NM_IGNORED */ -static bool safe_transition(const struct stat *a, const struct stat *b) { - /* Returns true if the transition from a to b is safe, i.e. that we never transition from unprivileged to - * privileged files or directories. Why bother? So that unprivileged code can't symlink to privileged files - * making us believe we read something safe even though it isn't safe in the specific context we open it in. */ - - if (a->st_uid == 0) /* Transitioning from privileged to unprivileged is always fine */ - return true; - - return a->st_uid == b->st_uid; /* Otherwise we need to stay within the same UID */ -} - -int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret) { - _cleanup_free_ char *buffer = NULL, *done = NULL, *root = NULL; - _cleanup_close_ int fd = -1; - unsigned max_follow = CHASE_SYMLINKS_MAX; /* how many symlinks to follow before giving up and returning ELOOP */ - struct stat previous_stat; - bool exists = true; - char *todo; - int r; - - assert(path); - - /* Either the file may be missing, or we return an fd to the final object, but both make no sense */ - if (FLAGS_SET(flags, CHASE_NONEXISTENT | CHASE_OPEN)) - return -EINVAL; - - if (FLAGS_SET(flags, CHASE_STEP | CHASE_OPEN)) - return -EINVAL; - - if (isempty(path)) - return -EINVAL; - - /* This is a lot like canonicalize_file_name(), but takes an additional "root" parameter, that allows following - * symlinks relative to a root directory, instead of the root of the host. - * - * Note that "root" primarily matters if we encounter an absolute symlink. It is also used when following - * relative symlinks to ensure they cannot be used to "escape" the root directory. The path parameter passed is - * assumed to be already prefixed by it, except if the CHASE_PREFIX_ROOT flag is set, in which case it is first - * prefixed accordingly. - * - * Algorithmically this operates on two path buffers: "done" are the components of the path we already - * processed and resolved symlinks, "." and ".." of. "todo" are the components of the path we still need to - * process. On each iteration, we move one component from "todo" to "done", processing it's special meaning - * each time. The "todo" path always starts with at least one slash, the "done" path always ends in no - * slash. We always keep an O_PATH fd to the component we are currently processing, thus keeping lookup races - * at a minimum. - * - * Suggested usage: whenever you want to canonicalize a path, use this function. Pass the absolute path you got - * as-is: fully qualified and relative to your host's root. Optionally, specify the root parameter to tell this - * function what to do when encountering a symlink with an absolute path as directory: prefix it by the - * specified path. - * - * There are three ways to invoke this function: - * - * 1. Without CHASE_STEP or CHASE_OPEN: in this case the path is resolved and the normalized path is returned - * in `ret`. The return value is < 0 on error. If CHASE_NONEXISTENT is also set 0 is returned if the file - * doesn't exist, > 0 otherwise. If CHASE_NONEXISTENT is not set >= 0 is returned if the destination was - * found, -ENOENT if it doesn't. - * - * 2. With CHASE_OPEN: in this case the destination is opened after chasing it as O_PATH and this file - * descriptor is returned as return value. This is useful to open files relative to some root - * directory. Note that the returned O_PATH file descriptors must be converted into a regular one (using - * fd_reopen() or such) before it can be used for reading/writing. CHASE_OPEN may not be combined with - * CHASE_NONEXISTENT. - * - * 3. With CHASE_STEP: in this case only a single step of the normalization is executed, i.e. only the first - * symlink or ".." component of the path is resolved, and the resulting path is returned. This is useful if - * a caller wants to trace the a path through the file system verbosely. Returns < 0 on error, > 0 if the - * path is fully normalized, and == 0 for each normalization step. This may be combined with - * CHASE_NONEXISTENT, in which case 1 is returned when a component is not found. - * - * */ - - /* A root directory of "/" or "" is identical to none */ - if (empty_or_root(original_root)) - original_root = NULL; - - if (!original_root && !ret && (flags & (CHASE_NONEXISTENT|CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_OPEN|CHASE_STEP)) == CHASE_OPEN) { - /* Shortcut the CHASE_OPEN case if the caller isn't interested in the actual path and has no root set - * and doesn't care about any of the other special features we provide either. */ - r = open(path, O_PATH|O_CLOEXEC|((flags & CHASE_NOFOLLOW) ? O_NOFOLLOW : 0)); - if (r < 0) - return -errno; - - return r; - } - - if (original_root) { - r = path_make_absolute_cwd(original_root, &root); - if (r < 0) - return r; - - if (flags & CHASE_PREFIX_ROOT) { - - /* We don't support relative paths in combination with a root directory */ - if (!path_is_absolute(path)) - return -EINVAL; - - path = prefix_roota(root, path); - } - } - - r = path_make_absolute_cwd(path, &buffer); - if (r < 0) - return r; - - fd = open("/", O_CLOEXEC|O_NOFOLLOW|O_PATH); - if (fd < 0) - return -errno; - - if (flags & CHASE_SAFE) { - if (fstat(fd, &previous_stat) < 0) - return -errno; - } - - todo = buffer; - for (;;) { - _cleanup_free_ char *first = NULL; - _cleanup_close_ int child = -1; - struct stat st; - size_t n, m; - - /* Determine length of first component in the path */ - n = strspn(todo, "/"); /* The slashes */ - m = n + strcspn(todo + n, "/"); /* The entire length of the component */ - - /* Extract the first component. */ - first = strndup(todo, m); - if (!first) - return -ENOMEM; - - todo += m; - - /* Empty? Then we reached the end. */ - if (isempty(first)) - break; - - /* Just a single slash? Then we reached the end. */ - if (path_equal(first, "/")) { - /* Preserve the trailing slash */ - - if (flags & CHASE_TRAIL_SLASH) - if (!strextend(&done, "/", NULL)) - return -ENOMEM; - - break; - } - - /* Just a dot? Then let's eat this up. */ - if (path_equal(first, "/.")) - continue; - - /* Two dots? Then chop off the last bit of what we already found out. */ - if (path_equal(first, "/..")) { - _cleanup_free_ char *parent = NULL; - _cleanup_close_ int fd_parent = -1; - - /* If we already are at the top, then going up will not change anything. This is in-line with - * how the kernel handles this. */ - if (empty_or_root(done)) - continue; - - parent = dirname_malloc(done); - if (!parent) - return -ENOMEM; - - /* Don't allow this to leave the root dir. */ - if (root && - path_startswith(done, root) && - !path_startswith(parent, root)) - continue; - - free_and_replace(done, parent); - - if (flags & CHASE_STEP) - goto chased_one; - - fd_parent = openat(fd, "..", O_CLOEXEC|O_NOFOLLOW|O_PATH); - if (fd_parent < 0) - return -errno; - - if (flags & CHASE_SAFE) { - if (fstat(fd_parent, &st) < 0) - return -errno; - - if (!safe_transition(&previous_stat, &st)) - return -EPERM; - - previous_stat = st; - } - - safe_close(fd); - fd = TAKE_FD(fd_parent); - - continue; - } - - /* Otherwise let's see what this is. */ - child = openat(fd, first + n, O_CLOEXEC|O_NOFOLLOW|O_PATH); - if (child < 0) { - - if (errno == ENOENT && - (flags & CHASE_NONEXISTENT) && - (isempty(todo) || path_is_normalized(todo))) { - - /* If CHASE_NONEXISTENT is set, and the path does not exist, then that's OK, return - * what we got so far. But don't allow this if the remaining path contains "../ or "./" - * or something else weird. */ - - /* If done is "/", as first also contains slash at the head, then remove this redundant slash. */ - if (streq_ptr(done, "/")) - *done = '\0'; - - if (!strextend(&done, first, todo, NULL)) - return -ENOMEM; - - exists = false; - break; - } - - return -errno; - } - - if (fstat(child, &st) < 0) - return -errno; - if ((flags & CHASE_SAFE) && - !safe_transition(&previous_stat, &st)) - return -EPERM; - - previous_stat = st; - - if ((flags & CHASE_NO_AUTOFS) && - fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0) - return -EREMOTE; - - if (S_ISLNK(st.st_mode) && !((flags & CHASE_NOFOLLOW) && isempty(todo))) { - char *joined; - - _cleanup_free_ char *destination = NULL; - - /* This is a symlink, in this case read the destination. But let's make sure we don't follow - * symlinks without bounds. */ - if (--max_follow <= 0) - return -ELOOP; - - r = readlinkat_malloc(fd, first + n, &destination); - if (r < 0) - return r; - if (isempty(destination)) - return -EINVAL; - - if (path_is_absolute(destination)) { - - /* An absolute destination. Start the loop from the beginning, but use the root - * directory as base. */ - - safe_close(fd); - fd = open(root ?: "/", O_CLOEXEC|O_NOFOLLOW|O_PATH); - if (fd < 0) - return -errno; - - if (flags & CHASE_SAFE) { - if (fstat(fd, &st) < 0) - return -errno; - - if (!safe_transition(&previous_stat, &st)) - return -EPERM; - - previous_stat = st; - } - - free(done); - - /* Note that we do not revalidate the root, we take it as is. */ - if (isempty(root)) - done = NULL; - else { - done = strdup(root); - if (!done) - return -ENOMEM; - } - - /* Prefix what's left to do with what we just read, and start the loop again, but - * remain in the current directory. */ - joined = strjoin(destination, todo); - } else - joined = strjoin("/", destination, todo); - if (!joined) - return -ENOMEM; - - free(buffer); - todo = buffer = joined; - - if (flags & CHASE_STEP) - goto chased_one; - - continue; - } - - /* If this is not a symlink, then let's just add the name we read to what we already verified. */ - if (!done) - done = TAKE_PTR(first); - else { - /* If done is "/", as first also contains slash at the head, then remove this redundant slash. */ - if (streq(done, "/")) - *done = '\0'; - - if (!strextend(&done, first, NULL)) - return -ENOMEM; - } - - /* And iterate again, but go one directory further down. */ - safe_close(fd); - fd = TAKE_FD(child); - } - - if (!done) { - /* Special case, turn the empty string into "/", to indicate the root directory. */ - done = strdup("/"); - if (!done) - return -ENOMEM; - } - - if (ret) - *ret = TAKE_PTR(done); - - if (flags & CHASE_OPEN) { - /* Return the O_PATH fd we currently are looking to the caller. It can translate it to a proper fd by - * opening /proc/self/fd/xyz. */ - - assert(fd >= 0); - return TAKE_FD(fd); - } - - if (flags & CHASE_STEP) - return 1; - - return exists; - -chased_one: - if (ret) { - char *c; - - c = strjoin(strempty(done), todo); - if (!c) - return -ENOMEM; - - *ret = c; - } - - return 0; -} - -int chase_symlinks_and_open( - const char *path, - const char *root, - unsigned chase_flags, - int open_flags, - char **ret_path) { - - _cleanup_close_ int path_fd = -1; - _cleanup_free_ char *p = NULL; - int r; - - if (chase_flags & CHASE_NONEXISTENT) - return -EINVAL; - - if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) { - /* Shortcut this call if none of the special features of this call are requested */ - r = open(path, open_flags); - if (r < 0) - return -errno; - - return r; - } - - path_fd = chase_symlinks(path, root, chase_flags|CHASE_OPEN, ret_path ? &p : NULL); - if (path_fd < 0) - return path_fd; - - r = fd_reopen(path_fd, open_flags); - if (r < 0) - return r; - - if (ret_path) - *ret_path = TAKE_PTR(p); - - return r; -} - -int chase_symlinks_and_opendir( - const char *path, - const char *root, - unsigned chase_flags, - char **ret_path, - DIR **ret_dir) { - - char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; - _cleanup_close_ int path_fd = -1; - _cleanup_free_ char *p = NULL; - DIR *d; - - if (!ret_dir) - return -EINVAL; - if (chase_flags & CHASE_NONEXISTENT) - return -EINVAL; - - if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) { - /* Shortcut this call if none of the special features of this call are requested */ - d = opendir(path); - if (!d) - return -errno; - - *ret_dir = d; - return 0; - } - - path_fd = chase_symlinks(path, root, chase_flags|CHASE_OPEN, ret_path ? &p : NULL); - if (path_fd < 0) - return path_fd; - - xsprintf(procfs_path, "/proc/self/fd/%i", path_fd); - d = opendir(procfs_path); - if (!d) - return -errno; - - if (ret_path) - *ret_path = TAKE_PTR(p); - - *ret_dir = d; - return 0; -} - -int chase_symlinks_and_stat( - const char *path, - const char *root, - unsigned chase_flags, - char **ret_path, - struct stat *ret_stat) { - - _cleanup_close_ int path_fd = -1; - _cleanup_free_ char *p = NULL; - - assert(path); - assert(ret_stat); - - if (chase_flags & CHASE_NONEXISTENT) - return -EINVAL; - - if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) { - /* Shortcut this call if none of the special features of this call are requested */ - if (stat(path, ret_stat) < 0) - return -errno; - - return 1; - } - - path_fd = chase_symlinks(path, root, chase_flags|CHASE_OPEN, ret_path ? &p : NULL); - if (path_fd < 0) - return path_fd; - - if (fstat(path_fd, ret_stat) < 0) - return -errno; - - if (ret_path) - *ret_path = TAKE_PTR(p); - - if (chase_flags & CHASE_OPEN) - return TAKE_FD(path_fd); - - return 1; -} - -int access_fd(int fd, int mode) { - char p[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1]; - int r; - - /* Like access() but operates on an already open fd */ - - xsprintf(p, "/proc/self/fd/%i", fd); - r = access(p, mode); - if (r < 0) - return -errno; - - return r; -} - -void unlink_tempfilep(char (*p)[]) { - /* If the file is created with mkstemp(), it will (almost always) - * change the suffix. Treat this as a sign that the file was - * successfully created. We ignore both the rare case where the - * original suffix is used and unlink failures. */ - if (!endswith(*p, ".XXXXXX")) - (void) unlink_noerrno(*p); -} - -int unlinkat_deallocate(int fd, const char *name, int flags) { - _cleanup_close_ int truncate_fd = -1; - struct stat st; - off_t l, bs; - - /* Operates like unlinkat() but also deallocates the file contents if it is a regular file and there's no other - * link to it. This is useful to ensure that other processes that might have the file open for reading won't be - * able to keep the data pinned on disk forever. This call is particular useful whenever we execute clean-up - * jobs ("vacuuming"), where we want to make sure the data is really gone and the disk space released and - * returned to the free pool. - * - * Deallocation is preferably done by FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE (👊) if supported, which means - * the file won't change size. That's a good thing since we shouldn't needlessly trigger SIGBUS in other - * programs that have mmap()ed the file. (The assumption here is that changing file contents to all zeroes - * underneath those programs is the better choice than simply triggering SIGBUS in them which truncation does.) - * However if hole punching is not implemented in the kernel or file system we'll fall back to normal file - * truncation (🔪), as our goal of deallocating the data space trumps our goal of being nice to readers (💐). - * - * Note that we attempt deallocation, but failure to succeed with that is not considered fatal, as long as the - * primary job – to delete the file – is accomplished. */ - - if ((flags & AT_REMOVEDIR) == 0) { - truncate_fd = openat(fd, name, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|O_NONBLOCK); - if (truncate_fd < 0) { - - /* If this failed because the file doesn't exist propagate the error right-away. Also, - * AT_REMOVEDIR wasn't set, and we tried to open the file for writing, which means EISDIR is - * returned when this is a directory but we are not supposed to delete those, hence propagate - * the error right-away too. */ - if (IN_SET(errno, ENOENT, EISDIR)) - return -errno; - - if (errno != ELOOP) /* don't complain if this is a symlink */ - log_debug_errno(errno, "Failed to open file '%s' for deallocation, ignoring: %m", name); - } - } - - if (unlinkat(fd, name, flags) < 0) - return -errno; - - if (truncate_fd < 0) /* Don't have a file handle, can't do more ☹️ */ - return 0; - - if (fstat(truncate_fd, &st) < 0) { - log_debug_errno(errno, "Failed to stat file '%s' for deallocation, ignoring.", name); - return 0; - } - - if (!S_ISREG(st.st_mode) || st.st_blocks == 0 || st.st_nlink > 0) - return 0; - - /* If this is a regular file, it actually took up space on disk and there are no other links it's time to - * punch-hole/truncate this to release the disk space. */ - - bs = MAX(st.st_blksize, 512); - l = DIV_ROUND_UP(st.st_size, bs) * bs; /* Round up to next block size */ - - if (fallocate(truncate_fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE, 0, l) >= 0) - return 0; /* Successfully punched a hole! 😊 */ - - /* Fall back to truncation */ - if (ftruncate(truncate_fd, 0) < 0) { - log_debug_errno(errno, "Failed to truncate file to 0, ignoring: %m"); - return 0; - } - - return 0; -} - -int fsync_directory_of_file(int fd) { - _cleanup_free_ char *path = NULL; - _cleanup_close_ int dfd = -1; - int r; - - r = fd_verify_regular(fd); - if (r < 0) - return r; - - r = fd_get_path(fd, &path); - if (r < 0) { - log_debug_errno(r, "Failed to query /proc/self/fd/%d%s: %m", - fd, - r == -EOPNOTSUPP ? ", ignoring" : ""); - - if (r == -EOPNOTSUPP) - /* If /proc is not available, we're most likely running in some - * chroot environment, and syncing the directory is not very - * important in that case. Let's just silently do nothing. */ - return 0; - - return r; - } - - if (!path_is_absolute(path)) - return -EINVAL; - - dfd = open_parent(path, O_CLOEXEC, 0); - if (dfd < 0) - return dfd; - - if (fsync(dfd) < 0) - return -errno; - - return 0; -} - -int open_parent(const char *path, int flags, mode_t mode) { - _cleanup_free_ char *parent = NULL; - int fd; - - if (isempty(path)) - return -EINVAL; - if (path_equal(path, "/")) /* requesting the parent of the root dir is fishy, let's prohibit that */ - return -EINVAL; - - parent = dirname_malloc(path); - if (!parent) - return -ENOMEM; - - /* Let's insist on O_DIRECTORY since the parent of a file or directory is a directory. Except if we open an - * O_TMPFILE file, because in that case we are actually create a regular file below the parent directory. */ - - if ((flags & O_PATH) == O_PATH) - flags |= O_DIRECTORY; - else if ((flags & O_TMPFILE) != O_TMPFILE) - flags |= O_DIRECTORY|O_RDONLY; - - fd = open(parent, flags, mode); - if (fd < 0) - return -errno; - - return fd; -} -#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/fs-util.h b/src/systemd/src/basic/fs-util.h deleted file mode 100644 index 4b656258..00000000 --- a/src/systemd/src/basic/fs-util.h +++ /dev/null @@ -1,109 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <dirent.h> -#include <fcntl.h> -#include <limits.h> -#include <stdbool.h> -#include <stdint.h> -#include <sys/inotify.h> -#include <sys/types.h> -#include <unistd.h> - -#include "time-util.h" -#include "util.h" - -int unlink_noerrno(const char *path); - -int rmdir_parents(const char *path, const char *stop); - -int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath); - -int readlinkat_malloc(int fd, const char *p, char **ret); -int readlink_malloc(const char *p, char **r); -int readlink_value(const char *p, char **ret); -int readlink_and_make_absolute(const char *p, char **r); - -int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid); -int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid); - -int fchmod_umask(int fd, mode_t mode); -int fchmod_opath(int fd, mode_t m); - -int fd_warn_permissions(const char *path, int fd); - -#define laccess(path, mode) faccessat(AT_FDCWD, (path), (mode), AT_SYMLINK_NOFOLLOW) - -int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode); -int touch(const char *path); - -int symlink_idempotent(const char *from, const char *to); - -int symlink_atomic(const char *from, const char *to); -int mknod_atomic(const char *path, mode_t mode, dev_t dev); -int mkfifo_atomic(const char *path, mode_t mode); -int mkfifoat_atomic(int dir_fd, const char *path, mode_t mode); - -int get_files_in_directory(const char *path, char ***list); - -int tmp_dir(const char **ret); -int var_tmp_dir(const char **ret); - -int unlink_or_warn(const char *filename); - -#define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX + 1) - -#define FOREACH_INOTIFY_EVENT(e, buffer, sz) \ - for ((e) = &buffer.ev; \ - (uint8_t*) (e) < (uint8_t*) (buffer.raw) + (sz); \ - (e) = (struct inotify_event*) ((uint8_t*) (e) + sizeof(struct inotify_event) + (e)->len)) - -union inotify_event_buffer { - struct inotify_event ev; - uint8_t raw[INOTIFY_EVENT_MAX]; -}; - -int inotify_add_watch_fd(int fd, int what, uint32_t mask); - -enum { - CHASE_PREFIX_ROOT = 1 << 0, /* If set, the specified path will be prefixed by the specified root before beginning the iteration */ - CHASE_NONEXISTENT = 1 << 1, /* If set, it's OK if the path doesn't actually exist. */ - CHASE_NO_AUTOFS = 1 << 2, /* If set, return -EREMOTE if autofs mount point found */ - CHASE_SAFE = 1 << 3, /* If set, return EPERM if we ever traverse from unprivileged to privileged files or directories */ - CHASE_OPEN = 1 << 4, /* If set, return an O_PATH object to the final component */ - CHASE_TRAIL_SLASH = 1 << 5, /* If set, any trailing slash will be preserved */ - CHASE_STEP = 1 << 6, /* If set, just execute a single step of the normalization */ - CHASE_NOFOLLOW = 1 << 7, /* Only valid with CHASE_OPEN: when the path's right-most component refers to symlink return O_PATH fd of the symlink, rather than following it. */ -}; - -/* How many iterations to execute before returning -ELOOP */ -#define CHASE_SYMLINKS_MAX 32 - -int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret); - -int chase_symlinks_and_open(const char *path, const char *root, unsigned chase_flags, int open_flags, char **ret_path); -int chase_symlinks_and_opendir(const char *path, const char *root, unsigned chase_flags, char **ret_path, DIR **ret_dir); -int chase_symlinks_and_stat(const char *path, const char *root, unsigned chase_flags, char **ret_path, struct stat *ret_stat); - -/* Useful for usage with _cleanup_(), removes a directory and frees the pointer */ -static inline void rmdir_and_free(char *p) { - PROTECT_ERRNO; - (void) rmdir(p); - free(p); -} -DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rmdir_and_free); - -static inline void unlink_and_free(char *p) { - (void) unlink_noerrno(p); - free(p); -} -DEFINE_TRIVIAL_CLEANUP_FUNC(char*, unlink_and_free); - -int access_fd(int fd, int mode); - -void unlink_tempfilep(char (*p)[]); -int unlinkat_deallocate(int fd, const char *name, int flags); - -int fsync_directory_of_file(int fd); - -int open_parent(const char *path, int flags, mode_t mode); diff --git a/src/systemd/src/basic/hash-funcs.c b/src/systemd/src/basic/hash-funcs.c deleted file mode 100644 index fc2c4f78..00000000 --- a/src/systemd/src/basic/hash-funcs.c +++ /dev/null @@ -1,120 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <string.h> - -#include "hash-funcs.h" -#include "path-util.h" - -void string_hash_func(const void *p, struct siphash *state) { - siphash24_compress(p, strlen(p) + 1, state); -} - -#if 0 /* NM_IGNORED */ -int string_compare_func(const void *a, const void *b) { - return strcmp(a, b); -} - -const struct hash_ops string_hash_ops = { - .hash = string_hash_func, - .compare = string_compare_func -}; - -void path_hash_func(const void *p, struct siphash *state) { - const char *q = p; - size_t n; - - assert(q); - assert(state); - - /* Calculates a hash for a path in a way this duplicate inner slashes don't make a differences, and also - * whether there's a trailing slash or not. This fits well with the semantics of path_compare(), which does - * similar checks and also doesn't care for trailing slashes. Note that relative and absolute paths (i.e. those - * which begin in a slash or not) will hash differently though. */ - - n = strspn(q, "/"); - if (n > 0) { /* Eat up initial slashes, and add one "/" to the hash for all of them */ - siphash24_compress(q, 1, state); - q += n; - } - - for (;;) { - /* Determine length of next component */ - n = strcspn(q, "/"); - if (n == 0) /* Reached the end? */ - break; - - /* Add this component to the hash and skip over it */ - siphash24_compress(q, n, state); - q += n; - - /* How many slashes follow this component? */ - n = strspn(q, "/"); - if (q[n] == 0) /* Is this a trailing slash? If so, we are at the end, and don't care about the slashes anymore */ - break; - - /* We are not add the end yet. Hash exactly one slash for all of the ones we just encountered. */ - siphash24_compress(q, 1, state); - q += n; - } -} - -int path_compare_func(const void *a, const void *b) { - return path_compare(a, b); -} - -const struct hash_ops path_hash_ops = { - .hash = path_hash_func, - .compare = path_compare_func -}; -#endif /* NM_IGNORED */ - -void trivial_hash_func(const void *p, struct siphash *state) { - siphash24_compress(&p, sizeof(p), state); -} - -int trivial_compare_func(const void *a, const void *b) { - return CMP(a, b); -} - -const struct hash_ops trivial_hash_ops = { - .hash = trivial_hash_func, - .compare = trivial_compare_func -}; - -void uint64_hash_func(const void *p, struct siphash *state) { - siphash24_compress(p, sizeof(uint64_t), state); -} - -int uint64_compare_func(const void *_a, const void *_b) { - uint64_t a, b; - a = *(const uint64_t*) _a; - b = *(const uint64_t*) _b; - return CMP(a, b); -} - -const struct hash_ops uint64_hash_ops = { - .hash = uint64_hash_func, - .compare = uint64_compare_func -}; - -#if 0 /* NM_IGNORED */ -#if SIZEOF_DEV_T != 8 -void devt_hash_func(const void *p, struct siphash *state) { - siphash24_compress(p, sizeof(dev_t), state); -} - -int devt_compare_func(const void *_a, const void *_b) { - dev_t a, b; - a = *(const dev_t*) _a; - b = *(const dev_t*) _b; - return CMP(a, b); -} - -const struct hash_ops devt_hash_ops = { - .hash = devt_hash_func, - .compare = devt_compare_func -}; -#endif -#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/hash-funcs.h b/src/systemd/src/basic/hash-funcs.h deleted file mode 100644 index fa45cfe2..00000000 --- a/src/systemd/src/basic/hash-funcs.h +++ /dev/null @@ -1,45 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include "macro.h" -#include "siphash24.h" - -typedef void (*hash_func_t)(const void *p, struct siphash *state); -typedef int (*compare_func_t)(const void *a, const void *b); - -struct hash_ops { - hash_func_t hash; - compare_func_t compare; -}; - -void string_hash_func(const void *p, struct siphash *state); -int string_compare_func(const void *a, const void *b) _pure_; -extern const struct hash_ops string_hash_ops; - -void path_hash_func(const void *p, struct siphash *state); -int path_compare_func(const void *a, const void *b) _pure_; -extern const struct hash_ops path_hash_ops; - -/* This will compare the passed pointers directly, and will not dereference them. This is hence not useful for strings - * or suchlike. */ -void trivial_hash_func(const void *p, struct siphash *state); -int trivial_compare_func(const void *a, const void *b) _const_; -extern const struct hash_ops trivial_hash_ops; - -/* 32bit values we can always just embed in the pointer itself, but in order to support 32bit archs we need store 64bit - * values indirectly, since they don't fit in a pointer. */ -void uint64_hash_func(const void *p, struct siphash *state); -int uint64_compare_func(const void *a, const void *b) _pure_; -extern const struct hash_ops uint64_hash_ops; - -/* On some archs dev_t is 32bit, and on others 64bit. And sometimes it's 64bit on 32bit archs, and sometimes 32bit on - * 64bit archs. Yuck! */ -#if SIZEOF_DEV_T != 8 -void devt_hash_func(const void *p, struct siphash *state) _pure_; -int devt_compare_func(const void *a, const void *b) _pure_; -extern const struct hash_ops devt_hash_ops; -#else -#define devt_hash_func uint64_hash_func -#define devt_compare_func uint64_compare_func -#define devt_hash_ops uint64_hash_ops -#endif diff --git a/src/systemd/src/basic/hashmap.c b/src/systemd/src/basic/hashmap.c deleted file mode 100644 index cfddeafe..00000000 --- a/src/systemd/src/basic/hashmap.c +++ /dev/null @@ -1,1988 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <errno.h> -#include <stdint.h> -#include <stdlib.h> -#include <string.h> - -#include "alloc-util.h" -#include "env-util.h" -#include "fileio.h" -#include "hashmap.h" -#include "macro.h" -#include "mempool.h" -#include "process-util.h" -#include "random-util.h" -#include "set.h" -#include "siphash24.h" -#include "string-util.h" -#include "strv.h" -#include "util.h" - -#if ENABLE_DEBUG_HASHMAP -#include <pthread.h> -#include "list.h" -#endif - -/* - * Implementation of hashmaps. - * Addressing: open - * - uses less RAM compared to closed addressing (chaining), because - * our entries are small (especially in Sets, which tend to contain - * the majority of entries in systemd). - * Collision resolution: Robin Hood - * - tends to equalize displacement of entries from their optimal buckets. - * Probe sequence: linear - * - though theoretically worse than random probing/uniform hashing/double - * hashing, it is good for cache locality. - * - * References: - * Celis, P. 1986. Robin Hood Hashing. - * Ph.D. Dissertation. University of Waterloo, Waterloo, Ont., Canada, Canada. - * https://cs.uwaterloo.ca/research/tr/1986/CS-86-14.pdf - * - The results are derived for random probing. Suggests deletion with - * tombstones and two mean-centered search methods. None of that works - * well for linear probing. - * - * Janson, S. 2005. Individual displacements for linear probing hashing with different insertion policies. - * ACM Trans. Algorithms 1, 2 (October 2005), 177-213. - * DOI=10.1145/1103963.1103964 http://doi.acm.org/10.1145/1103963.1103964 - * http://www.math.uu.se/~svante/papers/sj157.pdf - * - Applies to Robin Hood with linear probing. Contains remarks on - * the unsuitability of mean-centered search with linear probing. - * - * Viola, A. 2005. Exact distribution of individual displacements in linear probing hashing. - * ACM Trans. Algorithms 1, 2 (October 2005), 214-242. - * DOI=10.1145/1103963.1103965 http://doi.acm.org/10.1145/1103963.1103965 - * - Similar to Janson. Note that Viola writes about C_{m,n} (number of probes - * in a successful search), and Janson writes about displacement. C = d + 1. - * - * Goossaert, E. 2013. Robin Hood hashing: backward shift deletion. - * http://codecapsule.com/2013/11/17/robin-hood-hashing-backward-shift-deletion/ - * - Explanation of backward shift deletion with pictures. - * - * Khuong, P. 2013. The Other Robin Hood Hashing. - * http://www.pvk.ca/Blog/2013/11/26/the-other-robin-hood-hashing/ - * - Short summary of random vs. linear probing, and tombstones vs. backward shift. - */ - -/* - * XXX Ideas for improvement: - * For unordered hashmaps, randomize iteration order, similarly to Perl: - * http://blog.booking.com/hardening-perls-hash-function.html - */ - -/* INV_KEEP_FREE = 1 / (1 - max_load_factor) - * e.g. 1 / (1 - 0.8) = 5 ... keep one fifth of the buckets free. */ -#define INV_KEEP_FREE 5U - -/* Fields common to entries of all hashmap/set types */ -struct hashmap_base_entry { - const void *key; -}; - -/* Entry types for specific hashmap/set types - * hashmap_base_entry must be at the beginning of each entry struct. */ - -struct plain_hashmap_entry { - struct hashmap_base_entry b; - void *value; -}; - -struct ordered_hashmap_entry { - struct plain_hashmap_entry p; - unsigned iterate_next, iterate_previous; -}; - -struct set_entry { - struct hashmap_base_entry b; -}; - -/* In several functions it is advantageous to have the hash table extended - * virtually by a couple of additional buckets. We reserve special index values - * for these "swap" buckets. */ -#define _IDX_SWAP_BEGIN (UINT_MAX - 3) -#define IDX_PUT (_IDX_SWAP_BEGIN + 0) -#define IDX_TMP (_IDX_SWAP_BEGIN + 1) -#define _IDX_SWAP_END (_IDX_SWAP_BEGIN + 2) - -#define IDX_FIRST (UINT_MAX - 1) /* special index for freshly initialized iterators */ -#define IDX_NIL UINT_MAX /* special index value meaning "none" or "end" */ - -assert_cc(IDX_FIRST == _IDX_SWAP_END); -assert_cc(IDX_FIRST == _IDX_ITERATOR_FIRST); - -/* Storage space for the "swap" buckets. - * All entry types can fit into a ordered_hashmap_entry. */ -struct swap_entries { - struct ordered_hashmap_entry e[_IDX_SWAP_END - _IDX_SWAP_BEGIN]; -}; - -/* Distance from Initial Bucket */ -typedef uint8_t dib_raw_t; -#define DIB_RAW_OVERFLOW ((dib_raw_t)0xfdU) /* indicates DIB value is greater than representable */ -#define DIB_RAW_REHASH ((dib_raw_t)0xfeU) /* entry yet to be rehashed during in-place resize */ -#define DIB_RAW_FREE ((dib_raw_t)0xffU) /* a free bucket */ -#define DIB_RAW_INIT ((char)DIB_RAW_FREE) /* a byte to memset a DIB store with when initializing */ - -#define DIB_FREE UINT_MAX - -#if ENABLE_DEBUG_HASHMAP -struct hashmap_debug_info { - LIST_FIELDS(struct hashmap_debug_info, debug_list); - unsigned max_entries; /* high watermark of n_entries */ - - /* who allocated this hashmap */ - int line; - const char *file; - const char *func; - - /* fields to detect modification while iterating */ - unsigned put_count; /* counts puts into the hashmap */ - unsigned rem_count; /* counts removals from hashmap */ - unsigned last_rem_idx; /* remembers last removal index */ -}; - -/* Tracks all existing hashmaps. Get at it from gdb. See sd_dump_hashmaps.py */ -static LIST_HEAD(struct hashmap_debug_info, hashmap_debug_list); -static pthread_mutex_t hashmap_debug_list_mutex = PTHREAD_MUTEX_INITIALIZER; - -#define HASHMAP_DEBUG_FIELDS struct hashmap_debug_info debug; - -#else /* !ENABLE_DEBUG_HASHMAP */ -#define HASHMAP_DEBUG_FIELDS -#endif /* ENABLE_DEBUG_HASHMAP */ - -enum HashmapType { - HASHMAP_TYPE_PLAIN, - HASHMAP_TYPE_ORDERED, - HASHMAP_TYPE_SET, - _HASHMAP_TYPE_MAX -}; - -struct _packed_ indirect_storage { - void *storage; /* where buckets and DIBs are stored */ - uint8_t hash_key[HASH_KEY_SIZE]; /* hash key; changes during resize */ - - unsigned n_entries; /* number of stored entries */ - unsigned n_buckets; /* number of buckets */ - - unsigned idx_lowest_entry; /* Index below which all buckets are free. - Makes "while(hashmap_steal_first())" loops - O(n) instead of O(n^2) for unordered hashmaps. */ - uint8_t _pad[3]; /* padding for the whole HashmapBase */ - /* The bitfields in HashmapBase complete the alignment of the whole thing. */ -}; - -struct direct_storage { - /* This gives us 39 bytes on 64bit, or 35 bytes on 32bit. - * That's room for 4 set_entries + 4 DIB bytes + 3 unused bytes on 64bit, - * or 7 set_entries + 7 DIB bytes + 0 unused bytes on 32bit. */ - uint8_t storage[sizeof(struct indirect_storage)]; -}; - -#define DIRECT_BUCKETS(entry_t) \ - (sizeof(struct direct_storage) / (sizeof(entry_t) + sizeof(dib_raw_t))) - -/* We should be able to store at least one entry directly. */ -assert_cc(DIRECT_BUCKETS(struct ordered_hashmap_entry) >= 1); - -/* We have 3 bits for n_direct_entries. */ -assert_cc(DIRECT_BUCKETS(struct set_entry) < (1 << 3)); - -/* Hashmaps with directly stored entries all use this shared hash key. - * It's no big deal if the key is guessed, because there can be only - * a handful of directly stored entries in a hashmap. When a hashmap - * outgrows direct storage, it gets its own key for indirect storage. */ -static uint8_t shared_hash_key[HASH_KEY_SIZE]; -static bool shared_hash_key_initialized; - -/* Fields that all hashmap/set types must have */ -struct HashmapBase { - const struct hash_ops *hash_ops; /* hash and compare ops to use */ - - union _packed_ { - struct indirect_storage indirect; /* if has_indirect */ - struct direct_storage direct; /* if !has_indirect */ - }; - - enum HashmapType type:2; /* HASHMAP_TYPE_* */ - bool has_indirect:1; /* whether indirect storage is used */ - unsigned n_direct_entries:3; /* Number of entries in direct storage. - * Only valid if !has_indirect. */ - bool from_pool:1; /* whether was allocated from mempool */ - bool dirty:1; /* whether dirtied since last iterated_cache_get() */ - bool cached:1; /* whether this hashmap is being cached */ - HASHMAP_DEBUG_FIELDS /* optional hashmap_debug_info */ -}; - -/* Specific hash types - * HashmapBase must be at the beginning of each hashmap struct. */ - -struct Hashmap { - struct HashmapBase b; -}; - -struct OrderedHashmap { - struct HashmapBase b; - unsigned iterate_list_head, iterate_list_tail; -}; - -struct Set { - struct HashmapBase b; -}; - -typedef struct CacheMem { - const void **ptr; - size_t n_populated, n_allocated; - bool active:1; -} CacheMem; - -struct IteratedCache { - HashmapBase *hashmap; - CacheMem keys, values; -}; - -DEFINE_MEMPOOL(hashmap_pool, Hashmap, 8); -DEFINE_MEMPOOL(ordered_hashmap_pool, OrderedHashmap, 8); -/* No need for a separate Set pool */ -assert_cc(sizeof(Hashmap) == sizeof(Set)); - -struct hashmap_type_info { - size_t head_size; - size_t entry_size; - struct mempool *mempool; - unsigned n_direct_buckets; -}; - -static const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX] = { - [HASHMAP_TYPE_PLAIN] = { - .head_size = sizeof(Hashmap), - .entry_size = sizeof(struct plain_hashmap_entry), - .mempool = &hashmap_pool, - .n_direct_buckets = DIRECT_BUCKETS(struct plain_hashmap_entry), - }, - [HASHMAP_TYPE_ORDERED] = { - .head_size = sizeof(OrderedHashmap), - .entry_size = sizeof(struct ordered_hashmap_entry), - .mempool = &ordered_hashmap_pool, - .n_direct_buckets = DIRECT_BUCKETS(struct ordered_hashmap_entry), - }, - [HASHMAP_TYPE_SET] = { - .head_size = sizeof(Set), - .entry_size = sizeof(struct set_entry), - .mempool = &hashmap_pool, - .n_direct_buckets = DIRECT_BUCKETS(struct set_entry), - }, -}; - -#if VALGRIND -__attribute__((destructor)) static void cleanup_pools(void) { - _cleanup_free_ char *t = NULL; - int r; - - /* Be nice to valgrind */ - - /* The pool is only allocated by the main thread, but the memory can - * be passed to other threads. Let's clean up if we are the main thread - * and no other threads are live. */ - if (!is_main_thread()) - return; - - r = get_proc_field("/proc/self/status", "Threads", WHITESPACE, &t); - if (r < 0 || !streq(t, "1")) - return; - - mempool_drop(&hashmap_pool); - mempool_drop(&ordered_hashmap_pool); -} -#endif - -static unsigned n_buckets(HashmapBase *h) { - return h->has_indirect ? h->indirect.n_buckets - : hashmap_type_info[h->type].n_direct_buckets; -} - -static unsigned n_entries(HashmapBase *h) { - return h->has_indirect ? h->indirect.n_entries - : h->n_direct_entries; -} - -static void n_entries_inc(HashmapBase *h) { - if (h->has_indirect) - h->indirect.n_entries++; - else - h->n_direct_entries++; -} - -static void n_entries_dec(HashmapBase *h) { - if (h->has_indirect) - h->indirect.n_entries--; - else - h->n_direct_entries--; -} - -static void *storage_ptr(HashmapBase *h) { - return h->has_indirect ? h->indirect.storage - : h->direct.storage; -} - -static uint8_t *hash_key(HashmapBase *h) { - return h->has_indirect ? h->indirect.hash_key - : shared_hash_key; -} - -static unsigned base_bucket_hash(HashmapBase *h, const void *p) { - struct siphash state; - uint64_t hash; - - siphash24_init(&state, hash_key(h)); - - h->hash_ops->hash(p, &state); - - hash = siphash24_finalize(&state); - - return (unsigned) (hash % n_buckets(h)); -} -#define bucket_hash(h, p) base_bucket_hash(HASHMAP_BASE(h), p) - -static inline void base_set_dirty(HashmapBase *h) { - h->dirty = true; -} -#define hashmap_set_dirty(h) base_set_dirty(HASHMAP_BASE(h)) - -static void get_hash_key(uint8_t hash_key[HASH_KEY_SIZE], bool reuse_is_ok) { - static uint8_t current[HASH_KEY_SIZE]; - static bool current_initialized = false; - - /* Returns a hash function key to use. In order to keep things - * fast we will not generate a new key each time we allocate a - * new hash table. Instead, we'll just reuse the most recently - * generated one, except if we never generated one or when we - * are rehashing an entire hash table because we reached a - * fill level */ - - if (!current_initialized || !reuse_is_ok) { - random_bytes(current, sizeof(current)); - current_initialized = true; - } - - memcpy(hash_key, current, sizeof(current)); -} - -static struct hashmap_base_entry *bucket_at(HashmapBase *h, unsigned idx) { - return (struct hashmap_base_entry*) - ((uint8_t*) storage_ptr(h) + idx * hashmap_type_info[h->type].entry_size); -} - -static struct plain_hashmap_entry *plain_bucket_at(Hashmap *h, unsigned idx) { - return (struct plain_hashmap_entry*) bucket_at(HASHMAP_BASE(h), idx); -} - -static struct ordered_hashmap_entry *ordered_bucket_at(OrderedHashmap *h, unsigned idx) { - return (struct ordered_hashmap_entry*) bucket_at(HASHMAP_BASE(h), idx); -} - -static struct set_entry *set_bucket_at(Set *h, unsigned idx) { - return (struct set_entry*) bucket_at(HASHMAP_BASE(h), idx); -} - -static struct ordered_hashmap_entry *bucket_at_swap(struct swap_entries *swap, unsigned idx) { - return &swap->e[idx - _IDX_SWAP_BEGIN]; -} - -/* Returns a pointer to the bucket at index idx. - * Understands real indexes and swap indexes, hence "_virtual". */ -static struct hashmap_base_entry *bucket_at_virtual(HashmapBase *h, struct swap_entries *swap, - unsigned idx) { - if (idx < _IDX_SWAP_BEGIN) - return bucket_at(h, idx); - - if (idx < _IDX_SWAP_END) - return &bucket_at_swap(swap, idx)->p.b; - - assert_not_reached("Invalid index"); -} - -static dib_raw_t *dib_raw_ptr(HashmapBase *h) { - return (dib_raw_t*) - ((uint8_t*) storage_ptr(h) + hashmap_type_info[h->type].entry_size * n_buckets(h)); -} - -static unsigned bucket_distance(HashmapBase *h, unsigned idx, unsigned from) { - return idx >= from ? idx - from - : n_buckets(h) + idx - from; -} - -static unsigned bucket_calculate_dib(HashmapBase *h, unsigned idx, dib_raw_t raw_dib) { - unsigned initial_bucket; - - if (raw_dib == DIB_RAW_FREE) - return DIB_FREE; - - if (_likely_(raw_dib < DIB_RAW_OVERFLOW)) - return raw_dib; - - /* - * Having an overflow DIB value is very unlikely. The hash function - * would have to be bad. For example, in a table of size 2^24 filled - * to load factor 0.9 the maximum observed DIB is only about 60. - * In theory (assuming I used Maxima correctly), for an infinite size - * hash table with load factor 0.8 the probability of a given entry - * having DIB > 40 is 1.9e-8. - * This returns the correct DIB value by recomputing the hash value in - * the unlikely case. XXX Hitting this case could be a hint to rehash. - */ - initial_bucket = bucket_hash(h, bucket_at(h, idx)->key); - return bucket_distance(h, idx, initial_bucket); -} - -static void bucket_set_dib(HashmapBase *h, unsigned idx, unsigned dib) { - dib_raw_ptr(h)[idx] = dib != DIB_FREE ? MIN(dib, DIB_RAW_OVERFLOW) : DIB_RAW_FREE; -} - -static unsigned skip_free_buckets(HashmapBase *h, unsigned idx) { - dib_raw_t *dibs; - - dibs = dib_raw_ptr(h); - - for ( ; idx < n_buckets(h); idx++) - if (dibs[idx] != DIB_RAW_FREE) - return idx; - - return IDX_NIL; -} - -static void bucket_mark_free(HashmapBase *h, unsigned idx) { - memzero(bucket_at(h, idx), hashmap_type_info[h->type].entry_size); - bucket_set_dib(h, idx, DIB_FREE); -} - -static void bucket_move_entry(HashmapBase *h, struct swap_entries *swap, - unsigned from, unsigned to) { - struct hashmap_base_entry *e_from, *e_to; - - assert(from != to); - - e_from = bucket_at_virtual(h, swap, from); - e_to = bucket_at_virtual(h, swap, to); - - memcpy(e_to, e_from, hashmap_type_info[h->type].entry_size); - - if (h->type == HASHMAP_TYPE_ORDERED) { - OrderedHashmap *lh = (OrderedHashmap*) h; - struct ordered_hashmap_entry *le, *le_to; - - le_to = (struct ordered_hashmap_entry*) e_to; - - if (le_to->iterate_next != IDX_NIL) { - le = (struct ordered_hashmap_entry*) - bucket_at_virtual(h, swap, le_to->iterate_next); - le->iterate_previous = to; - } - - if (le_to->iterate_previous != IDX_NIL) { - le = (struct ordered_hashmap_entry*) - bucket_at_virtual(h, swap, le_to->iterate_previous); - le->iterate_next = to; - } - - if (lh->iterate_list_head == from) - lh->iterate_list_head = to; - if (lh->iterate_list_tail == from) - lh->iterate_list_tail = to; - } -} - -static unsigned next_idx(HashmapBase *h, unsigned idx) { - return (idx + 1U) % n_buckets(h); -} - -static unsigned prev_idx(HashmapBase *h, unsigned idx) { - return (n_buckets(h) + idx - 1U) % n_buckets(h); -} - -static void *entry_value(HashmapBase *h, struct hashmap_base_entry *e) { - switch (h->type) { - - case HASHMAP_TYPE_PLAIN: - case HASHMAP_TYPE_ORDERED: - return ((struct plain_hashmap_entry*)e)->value; - - case HASHMAP_TYPE_SET: - return (void*) e->key; - - default: - assert_not_reached("Unknown hashmap type"); - } -} - -static void base_remove_entry(HashmapBase *h, unsigned idx) { - unsigned left, right, prev, dib; - dib_raw_t raw_dib, *dibs; - - dibs = dib_raw_ptr(h); - assert(dibs[idx] != DIB_RAW_FREE); - -#if ENABLE_DEBUG_HASHMAP - h->debug.rem_count++; - h->debug.last_rem_idx = idx; -#endif - - left = idx; - /* Find the stop bucket ("right"). It is either free or has DIB == 0. */ - for (right = next_idx(h, left); ; right = next_idx(h, right)) { - raw_dib = dibs[right]; - if (IN_SET(raw_dib, 0, DIB_RAW_FREE)) - break; - - /* The buckets are not supposed to be all occupied and with DIB > 0. - * That would mean we could make everyone better off by shifting them - * backward. This scenario is impossible. */ - assert(left != right); - } - - if (h->type == HASHMAP_TYPE_ORDERED) { - OrderedHashmap *lh = (OrderedHashmap*) h; - struct ordered_hashmap_entry *le = ordered_bucket_at(lh, idx); - - if (le->iterate_next != IDX_NIL) - ordered_bucket_at(lh, le->iterate_next)->iterate_previous = le->iterate_previous; - else - lh->iterate_list_tail = le->iterate_previous; - - if (le->iterate_previous != IDX_NIL) - ordered_bucket_at(lh, le->iterate_previous)->iterate_next = le->iterate_next; - else - lh->iterate_list_head = le->iterate_next; - } - - /* Now shift all buckets in the interval (left, right) one step backwards */ - for (prev = left, left = next_idx(h, left); left != right; - prev = left, left = next_idx(h, left)) { - dib = bucket_calculate_dib(h, left, dibs[left]); - assert(dib != 0); - bucket_move_entry(h, NULL, left, prev); - bucket_set_dib(h, prev, dib - 1); - } - - bucket_mark_free(h, prev); - n_entries_dec(h); - base_set_dirty(h); -} -#define remove_entry(h, idx) base_remove_entry(HASHMAP_BASE(h), idx) - -static unsigned hashmap_iterate_in_insertion_order(OrderedHashmap *h, Iterator *i) { - struct ordered_hashmap_entry *e; - unsigned idx; - - assert(h); - assert(i); - - if (i->idx == IDX_NIL) - goto at_end; - - if (i->idx == IDX_FIRST && h->iterate_list_head == IDX_NIL) - goto at_end; - - if (i->idx == IDX_FIRST) { - idx = h->iterate_list_head; - e = ordered_bucket_at(h, idx); - } else { - idx = i->idx; - e = ordered_bucket_at(h, idx); - /* - * We allow removing the current entry while iterating, but removal may cause - * a backward shift. The next entry may thus move one bucket to the left. - * To detect when it happens, we remember the key pointer of the entry we were - * going to iterate next. If it does not match, there was a backward shift. - */ - if (e->p.b.key != i->next_key) { - idx = prev_idx(HASHMAP_BASE(h), idx); - e = ordered_bucket_at(h, idx); - } - assert(e->p.b.key == i->next_key); - } - -#if ENABLE_DEBUG_HASHMAP - i->prev_idx = idx; -#endif - - if (e->iterate_next != IDX_NIL) { - struct ordered_hashmap_entry *n; - i->idx = e->iterate_next; - n = ordered_bucket_at(h, i->idx); - i->next_key = n->p.b.key; - } else - i->idx = IDX_NIL; - - return idx; - -at_end: - i->idx = IDX_NIL; - return IDX_NIL; -} - -static unsigned hashmap_iterate_in_internal_order(HashmapBase *h, Iterator *i) { - unsigned idx; - - assert(h); - assert(i); - - if (i->idx == IDX_NIL) - goto at_end; - - if (i->idx == IDX_FIRST) { - /* fast forward to the first occupied bucket */ - if (h->has_indirect) { - i->idx = skip_free_buckets(h, h->indirect.idx_lowest_entry); - h->indirect.idx_lowest_entry = i->idx; - } else - i->idx = skip_free_buckets(h, 0); - - if (i->idx == IDX_NIL) - goto at_end; - } else { - struct hashmap_base_entry *e; - - assert(i->idx > 0); - - e = bucket_at(h, i->idx); - /* - * We allow removing the current entry while iterating, but removal may cause - * a backward shift. The next entry may thus move one bucket to the left. - * To detect when it happens, we remember the key pointer of the entry we were - * going to iterate next. If it does not match, there was a backward shift. - */ - if (e->key != i->next_key) - e = bucket_at(h, --i->idx); - - assert(e->key == i->next_key); - } - - idx = i->idx; -#if ENABLE_DEBUG_HASHMAP - i->prev_idx = idx; -#endif - - i->idx = skip_free_buckets(h, i->idx + 1); - if (i->idx != IDX_NIL) - i->next_key = bucket_at(h, i->idx)->key; - else - i->idx = IDX_NIL; - - return idx; - -at_end: - i->idx = IDX_NIL; - return IDX_NIL; -} - -static unsigned hashmap_iterate_entry(HashmapBase *h, Iterator *i) { - if (!h) { - i->idx = IDX_NIL; - return IDX_NIL; - } - -#if ENABLE_DEBUG_HASHMAP - if (i->idx == IDX_FIRST) { - i->put_count = h->debug.put_count; - i->rem_count = h->debug.rem_count; - } else { - /* While iterating, must not add any new entries */ - assert(i->put_count == h->debug.put_count); - /* ... or remove entries other than the current one */ - assert(i->rem_count == h->debug.rem_count || - (i->rem_count == h->debug.rem_count - 1 && - i->prev_idx == h->debug.last_rem_idx)); - /* Reset our removals counter */ - i->rem_count = h->debug.rem_count; - } -#endif - - return h->type == HASHMAP_TYPE_ORDERED ? hashmap_iterate_in_insertion_order((OrderedHashmap*) h, i) - : hashmap_iterate_in_internal_order(h, i); -} - -bool internal_hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key) { - struct hashmap_base_entry *e; - void *data; - unsigned idx; - - idx = hashmap_iterate_entry(h, i); - if (idx == IDX_NIL) { - if (value) - *value = NULL; - if (key) - *key = NULL; - - return false; - } - - e = bucket_at(h, idx); - data = entry_value(h, e); - if (value) - *value = data; - if (key) - *key = e->key; - - return true; -} - -bool set_iterate(Set *s, Iterator *i, void **value) { - return internal_hashmap_iterate(HASHMAP_BASE(s), i, value, NULL); -} - -#define HASHMAP_FOREACH_IDX(idx, h, i) \ - for ((i) = ITERATOR_FIRST, (idx) = hashmap_iterate_entry((h), &(i)); \ - (idx != IDX_NIL); \ - (idx) = hashmap_iterate_entry((h), &(i))) - -IteratedCache *internal_hashmap_iterated_cache_new(HashmapBase *h) { - IteratedCache *cache; - - assert(h); - assert(!h->cached); - - if (h->cached) - return NULL; - - cache = new0(IteratedCache, 1); - if (!cache) - return NULL; - - cache->hashmap = h; - h->cached = true; - - return cache; -} - -static void reset_direct_storage(HashmapBase *h) { - const struct hashmap_type_info *hi = &hashmap_type_info[h->type]; - void *p; - - assert(!h->has_indirect); - - p = mempset(h->direct.storage, 0, hi->entry_size * hi->n_direct_buckets); - memset(p, DIB_RAW_INIT, sizeof(dib_raw_t) * hi->n_direct_buckets); -} - -static bool use_pool(void) { - static int b = -1; - - if (!is_main_thread()) - return false; - - if (b < 0) - b = getenv_bool("SYSTEMD_MEMPOOL") != 0; - - return b; -} - -static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enum HashmapType type HASHMAP_DEBUG_PARAMS) { - HashmapBase *h; - const struct hashmap_type_info *hi = &hashmap_type_info[type]; - bool up; - - up = use_pool(); - - h = up ? mempool_alloc0_tile(hi->mempool) : malloc0(hi->head_size); - if (!h) - return NULL; - - h->type = type; - h->from_pool = up; - h->hash_ops = hash_ops ? hash_ops : &trivial_hash_ops; - - if (type == HASHMAP_TYPE_ORDERED) { - OrderedHashmap *lh = (OrderedHashmap*)h; - lh->iterate_list_head = lh->iterate_list_tail = IDX_NIL; - } - - reset_direct_storage(h); - - if (!shared_hash_key_initialized) { - random_bytes(shared_hash_key, sizeof(shared_hash_key)); - shared_hash_key_initialized= true; - } - -#if ENABLE_DEBUG_HASHMAP - h->debug.func = func; - h->debug.file = file; - h->debug.line = line; - assert_se(pthread_mutex_lock(&hashmap_debug_list_mutex) == 0); - LIST_PREPEND(debug_list, hashmap_debug_list, &h->debug); - assert_se(pthread_mutex_unlock(&hashmap_debug_list_mutex) == 0); -#endif - - return h; -} - -Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return (Hashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS); -} - -OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return (OrderedHashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS); -} - -Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return (Set*) hashmap_base_new(hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS); -} - -static int hashmap_base_ensure_allocated(HashmapBase **h, const struct hash_ops *hash_ops, - enum HashmapType type HASHMAP_DEBUG_PARAMS) { - HashmapBase *q; - - assert(h); - - if (*h) - return 0; - - q = hashmap_base_new(hash_ops, type HASHMAP_DEBUG_PASS_ARGS); - if (!q) - return -ENOMEM; - - *h = q; - return 0; -} - -int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS); -} - -int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS); -} - -int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return hashmap_base_ensure_allocated((HashmapBase**)s, hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS); -} - -static void hashmap_free_no_clear(HashmapBase *h) { - assert(!h->has_indirect); - assert(!h->n_direct_entries); - -#if ENABLE_DEBUG_HASHMAP - assert_se(pthread_mutex_lock(&hashmap_debug_list_mutex) == 0); - LIST_REMOVE(debug_list, hashmap_debug_list, &h->debug); - assert_se(pthread_mutex_unlock(&hashmap_debug_list_mutex) == 0); -#endif - - if (h->from_pool) { - /* Ensure that the object didn't get migrated between threads. */ - assert_se(is_main_thread()); - mempool_free_tile(hashmap_type_info[h->type].mempool, h); - } else - free(h); -} - -HashmapBase *internal_hashmap_free(HashmapBase *h) { - - /* Free the hashmap, but nothing in it */ - - if (h) { - internal_hashmap_clear(h); - hashmap_free_no_clear(h); - } - - return NULL; -} - -HashmapBase *internal_hashmap_free_free(HashmapBase *h) { - - /* Free the hashmap and all data objects in it, but not the - * keys */ - - if (h) { - internal_hashmap_clear_free(h); - hashmap_free_no_clear(h); - } - - return NULL; -} - -Hashmap *hashmap_free_free_free(Hashmap *h) { - - /* Free the hashmap and all data and key objects in it */ - - if (h) { - hashmap_clear_free_free(h); - hashmap_free_no_clear(HASHMAP_BASE(h)); - } - - return NULL; -} - -void internal_hashmap_clear(HashmapBase *h) { - if (!h) - return; - - if (h->has_indirect) { - free(h->indirect.storage); - h->has_indirect = false; - } - - h->n_direct_entries = 0; - reset_direct_storage(h); - - if (h->type == HASHMAP_TYPE_ORDERED) { - OrderedHashmap *lh = (OrderedHashmap*) h; - lh->iterate_list_head = lh->iterate_list_tail = IDX_NIL; - } - - base_set_dirty(h); -} - -void internal_hashmap_clear_free(HashmapBase *h) { - unsigned idx; - - if (!h) - return; - - for (idx = skip_free_buckets(h, 0); idx != IDX_NIL; - idx = skip_free_buckets(h, idx + 1)) - free(entry_value(h, bucket_at(h, idx))); - - internal_hashmap_clear(h); -} - -void hashmap_clear_free_free(Hashmap *h) { - unsigned idx; - - if (!h) - return; - - for (idx = skip_free_buckets(HASHMAP_BASE(h), 0); idx != IDX_NIL; - idx = skip_free_buckets(HASHMAP_BASE(h), idx + 1)) { - struct plain_hashmap_entry *e = plain_bucket_at(h, idx); - free((void*)e->b.key); - free(e->value); - } - - internal_hashmap_clear(HASHMAP_BASE(h)); -} - -static int resize_buckets(HashmapBase *h, unsigned entries_add); - -/* - * Finds an empty bucket to put an entry into, starting the scan at 'idx'. - * Performs Robin Hood swaps as it goes. The entry to put must be placed - * by the caller into swap slot IDX_PUT. - * If used for in-place resizing, may leave a displaced entry in swap slot - * IDX_PUT. Caller must rehash it next. - * Returns: true if it left a displaced entry to rehash next in IDX_PUT, - * false otherwise. - */ -static bool hashmap_put_robin_hood(HashmapBase *h, unsigned idx, - struct swap_entries *swap) { - dib_raw_t raw_dib, *dibs; - unsigned dib, distance; - -#if ENABLE_DEBUG_HASHMAP - h->debug.put_count++; -#endif - - dibs = dib_raw_ptr(h); - - for (distance = 0; ; distance++) { - raw_dib = dibs[idx]; - if (IN_SET(raw_dib, DIB_RAW_FREE, DIB_RAW_REHASH)) { - if (raw_dib == DIB_RAW_REHASH) - bucket_move_entry(h, swap, idx, IDX_TMP); - - if (h->has_indirect && h->indirect.idx_lowest_entry > idx) - h->indirect.idx_lowest_entry = idx; - - bucket_set_dib(h, idx, distance); - bucket_move_entry(h, swap, IDX_PUT, idx); - if (raw_dib == DIB_RAW_REHASH) { - bucket_move_entry(h, swap, IDX_TMP, IDX_PUT); - return true; - } - - return false; - } - - dib = bucket_calculate_dib(h, idx, raw_dib); - - if (dib < distance) { - /* Found a wealthier entry. Go Robin Hood! */ - bucket_set_dib(h, idx, distance); - - /* swap the entries */ - bucket_move_entry(h, swap, idx, IDX_TMP); - bucket_move_entry(h, swap, IDX_PUT, idx); - bucket_move_entry(h, swap, IDX_TMP, IDX_PUT); - - distance = dib; - } - - idx = next_idx(h, idx); - } -} - -/* - * Puts an entry into a hashmap, boldly - no check whether key already exists. - * The caller must place the entry (only its key and value, not link indexes) - * in swap slot IDX_PUT. - * Caller must ensure: the key does not exist yet in the hashmap. - * that resize is not needed if !may_resize. - * Returns: 1 if entry was put successfully. - * -ENOMEM if may_resize==true and resize failed with -ENOMEM. - * Cannot return -ENOMEM if !may_resize. - */ -static int hashmap_base_put_boldly(HashmapBase *h, unsigned idx, - struct swap_entries *swap, bool may_resize) { - struct ordered_hashmap_entry *new_entry; - int r; - - assert(idx < n_buckets(h)); - - new_entry = bucket_at_swap(swap, IDX_PUT); - - if (may_resize) { - r = resize_buckets(h, 1); - if (r < 0) - return r; - if (r > 0) - idx = bucket_hash(h, new_entry->p.b.key); - } - assert(n_entries(h) < n_buckets(h)); - - if (h->type == HASHMAP_TYPE_ORDERED) { - OrderedHashmap *lh = (OrderedHashmap*) h; - - new_entry->iterate_next = IDX_NIL; - new_entry->iterate_previous = lh->iterate_list_tail; - - if (lh->iterate_list_tail != IDX_NIL) { - struct ordered_hashmap_entry *old_tail; - - old_tail = ordered_bucket_at(lh, lh->iterate_list_tail); - assert(old_tail->iterate_next == IDX_NIL); - old_tail->iterate_next = IDX_PUT; - } - - lh->iterate_list_tail = IDX_PUT; - if (lh->iterate_list_head == IDX_NIL) - lh->iterate_list_head = IDX_PUT; - } - - assert_se(hashmap_put_robin_hood(h, idx, swap) == false); - - n_entries_inc(h); -#if ENABLE_DEBUG_HASHMAP - h->debug.max_entries = MAX(h->debug.max_entries, n_entries(h)); -#endif - - base_set_dirty(h); - - return 1; -} -#define hashmap_put_boldly(h, idx, swap, may_resize) \ - hashmap_base_put_boldly(HASHMAP_BASE(h), idx, swap, may_resize) - -/* - * Returns 0 if resize is not needed. - * 1 if successfully resized. - * -ENOMEM on allocation failure. - */ -static int resize_buckets(HashmapBase *h, unsigned entries_add) { - struct swap_entries swap; - void *new_storage; - dib_raw_t *old_dibs, *new_dibs; - const struct hashmap_type_info *hi; - unsigned idx, optimal_idx; - unsigned old_n_buckets, new_n_buckets, n_rehashed, new_n_entries; - uint8_t new_shift; - bool rehash_next; - - assert(h); - - hi = &hashmap_type_info[h->type]; - new_n_entries = n_entries(h) + entries_add; - - /* overflow? */ - if (_unlikely_(new_n_entries < entries_add)) - return -ENOMEM; - - /* For direct storage we allow 100% load, because it's tiny. */ - if (!h->has_indirect && new_n_entries <= hi->n_direct_buckets) - return 0; - - /* - * Load factor = n/m = 1 - (1/INV_KEEP_FREE). - * From it follows: m = n + n/(INV_KEEP_FREE - 1) - */ - new_n_buckets = new_n_entries + new_n_entries / (INV_KEEP_FREE - 1); - /* overflow? */ - if (_unlikely_(new_n_buckets < new_n_entries)) - return -ENOMEM; - - if (_unlikely_(new_n_buckets > UINT_MAX / (hi->entry_size + sizeof(dib_raw_t)))) - return -ENOMEM; - - old_n_buckets = n_buckets(h); - - if (_likely_(new_n_buckets <= old_n_buckets)) - return 0; - - new_shift = log2u_round_up(MAX( - new_n_buckets * (hi->entry_size + sizeof(dib_raw_t)), - 2 * sizeof(struct direct_storage))); - - /* Realloc storage (buckets and DIB array). */ - new_storage = realloc(h->has_indirect ? h->indirect.storage : NULL, - 1U << new_shift); - if (!new_storage) - return -ENOMEM; - - /* Must upgrade direct to indirect storage. */ - if (!h->has_indirect) { - memcpy(new_storage, h->direct.storage, - old_n_buckets * (hi->entry_size + sizeof(dib_raw_t))); - h->indirect.n_entries = h->n_direct_entries; - h->indirect.idx_lowest_entry = 0; - h->n_direct_entries = 0; - } - - /* Get a new hash key. If we've just upgraded to indirect storage, - * allow reusing a previously generated key. It's still a different key - * from the shared one that we used for direct storage. */ - get_hash_key(h->indirect.hash_key, !h->has_indirect); - - h->has_indirect = true; - h->indirect.storage = new_storage; - h->indirect.n_buckets = (1U << new_shift) / - (hi->entry_size + sizeof(dib_raw_t)); - - old_dibs = (dib_raw_t*)((uint8_t*) new_storage + hi->entry_size * old_n_buckets); - new_dibs = dib_raw_ptr(h); - - /* - * Move the DIB array to the new place, replacing valid DIB values with - * DIB_RAW_REHASH to indicate all of the used buckets need rehashing. - * Note: Overlap is not possible, because we have at least doubled the - * number of buckets and dib_raw_t is smaller than any entry type. - */ - for (idx = 0; idx < old_n_buckets; idx++) { - assert(old_dibs[idx] != DIB_RAW_REHASH); - new_dibs[idx] = old_dibs[idx] == DIB_RAW_FREE ? DIB_RAW_FREE - : DIB_RAW_REHASH; - } - - /* Zero the area of newly added entries (including the old DIB area) */ - memzero(bucket_at(h, old_n_buckets), - (n_buckets(h) - old_n_buckets) * hi->entry_size); - - /* The upper half of the new DIB array needs initialization */ - memset(&new_dibs[old_n_buckets], DIB_RAW_INIT, - (n_buckets(h) - old_n_buckets) * sizeof(dib_raw_t)); - - /* Rehash entries that need it */ - n_rehashed = 0; - for (idx = 0; idx < old_n_buckets; idx++) { - if (new_dibs[idx] != DIB_RAW_REHASH) - continue; - - optimal_idx = bucket_hash(h, bucket_at(h, idx)->key); - - /* - * Not much to do if by luck the entry hashes to its current - * location. Just set its DIB. - */ - if (optimal_idx == idx) { - new_dibs[idx] = 0; - n_rehashed++; - continue; - } - - new_dibs[idx] = DIB_RAW_FREE; - bucket_move_entry(h, &swap, idx, IDX_PUT); - /* bucket_move_entry does not clear the source */ - memzero(bucket_at(h, idx), hi->entry_size); - - do { - /* - * Find the new bucket for the current entry. This may make - * another entry homeless and load it into IDX_PUT. - */ - rehash_next = hashmap_put_robin_hood(h, optimal_idx, &swap); - n_rehashed++; - - /* Did the current entry displace another one? */ - if (rehash_next) - optimal_idx = bucket_hash(h, bucket_at_swap(&swap, IDX_PUT)->p.b.key); - } while (rehash_next); - } - - assert(n_rehashed == n_entries(h)); - - return 1; -} - -/* - * Finds an entry with a matching key - * Returns: index of the found entry, or IDX_NIL if not found. - */ -static unsigned base_bucket_scan(HashmapBase *h, unsigned idx, const void *key) { - struct hashmap_base_entry *e; - unsigned dib, distance; - dib_raw_t *dibs = dib_raw_ptr(h); - - assert(idx < n_buckets(h)); - - for (distance = 0; ; distance++) { - if (dibs[idx] == DIB_RAW_FREE) - return IDX_NIL; - - dib = bucket_calculate_dib(h, idx, dibs[idx]); - - if (dib < distance) - return IDX_NIL; - if (dib == distance) { - e = bucket_at(h, idx); - if (h->hash_ops->compare(e->key, key) == 0) - return idx; - } - - idx = next_idx(h, idx); - } -} -#define bucket_scan(h, idx, key) base_bucket_scan(HASHMAP_BASE(h), idx, key) - -int hashmap_put(Hashmap *h, const void *key, void *value) { - struct swap_entries swap; - struct plain_hashmap_entry *e; - unsigned hash, idx; - - assert(h); - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx != IDX_NIL) { - e = plain_bucket_at(h, idx); - if (e->value == value) - return 0; - return -EEXIST; - } - - e = &bucket_at_swap(&swap, IDX_PUT)->p; - e->b.key = key; - e->value = value; - return hashmap_put_boldly(h, hash, &swap, true); -} - -int set_put(Set *s, const void *key) { - struct swap_entries swap; - struct hashmap_base_entry *e; - unsigned hash, idx; - - assert(s); - - hash = bucket_hash(s, key); - idx = bucket_scan(s, hash, key); - if (idx != IDX_NIL) - return 0; - - e = &bucket_at_swap(&swap, IDX_PUT)->p.b; - e->key = key; - return hashmap_put_boldly(s, hash, &swap, true); -} - -int hashmap_replace(Hashmap *h, const void *key, void *value) { - struct swap_entries swap; - struct plain_hashmap_entry *e; - unsigned hash, idx; - - assert(h); - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx != IDX_NIL) { - e = plain_bucket_at(h, idx); -#if ENABLE_DEBUG_HASHMAP - /* Although the key is equal, the key pointer may have changed, - * and this would break our assumption for iterating. So count - * this operation as incompatible with iteration. */ - if (e->b.key != key) { - h->b.debug.put_count++; - h->b.debug.rem_count++; - h->b.debug.last_rem_idx = idx; - } -#endif - e->b.key = key; - e->value = value; - hashmap_set_dirty(h); - - return 0; - } - - e = &bucket_at_swap(&swap, IDX_PUT)->p; - e->b.key = key; - e->value = value; - return hashmap_put_boldly(h, hash, &swap, true); -} - -int hashmap_update(Hashmap *h, const void *key, void *value) { - struct plain_hashmap_entry *e; - unsigned hash, idx; - - assert(h); - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx == IDX_NIL) - return -ENOENT; - - e = plain_bucket_at(h, idx); - e->value = value; - hashmap_set_dirty(h); - - return 0; -} - -void *internal_hashmap_get(HashmapBase *h, const void *key) { - struct hashmap_base_entry *e; - unsigned hash, idx; - - if (!h) - return NULL; - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx == IDX_NIL) - return NULL; - - e = bucket_at(h, idx); - return entry_value(h, e); -} - -void *hashmap_get2(Hashmap *h, const void *key, void **key2) { - struct plain_hashmap_entry *e; - unsigned hash, idx; - - if (!h) - return NULL; - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx == IDX_NIL) - return NULL; - - e = plain_bucket_at(h, idx); - if (key2) - *key2 = (void*) e->b.key; - - return e->value; -} - -bool internal_hashmap_contains(HashmapBase *h, const void *key) { - unsigned hash; - - if (!h) - return false; - - hash = bucket_hash(h, key); - return bucket_scan(h, hash, key) != IDX_NIL; -} - -void *internal_hashmap_remove(HashmapBase *h, const void *key) { - struct hashmap_base_entry *e; - unsigned hash, idx; - void *data; - - if (!h) - return NULL; - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx == IDX_NIL) - return NULL; - - e = bucket_at(h, idx); - data = entry_value(h, e); - remove_entry(h, idx); - - return data; -} - -void *hashmap_remove2(Hashmap *h, const void *key, void **rkey) { - struct plain_hashmap_entry *e; - unsigned hash, idx; - void *data; - - if (!h) { - if (rkey) - *rkey = NULL; - return NULL; - } - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx == IDX_NIL) { - if (rkey) - *rkey = NULL; - return NULL; - } - - e = plain_bucket_at(h, idx); - data = e->value; - if (rkey) - *rkey = (void*) e->b.key; - - remove_entry(h, idx); - - return data; -} - -int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value) { - struct swap_entries swap; - struct plain_hashmap_entry *e; - unsigned old_hash, new_hash, idx; - - if (!h) - return -ENOENT; - - old_hash = bucket_hash(h, old_key); - idx = bucket_scan(h, old_hash, old_key); - if (idx == IDX_NIL) - return -ENOENT; - - new_hash = bucket_hash(h, new_key); - if (bucket_scan(h, new_hash, new_key) != IDX_NIL) - return -EEXIST; - - remove_entry(h, idx); - - e = &bucket_at_swap(&swap, IDX_PUT)->p; - e->b.key = new_key; - e->value = value; - assert_se(hashmap_put_boldly(h, new_hash, &swap, false) == 1); - - return 0; -} - -int set_remove_and_put(Set *s, const void *old_key, const void *new_key) { - struct swap_entries swap; - struct hashmap_base_entry *e; - unsigned old_hash, new_hash, idx; - - if (!s) - return -ENOENT; - - old_hash = bucket_hash(s, old_key); - idx = bucket_scan(s, old_hash, old_key); - if (idx == IDX_NIL) - return -ENOENT; - - new_hash = bucket_hash(s, new_key); - if (bucket_scan(s, new_hash, new_key) != IDX_NIL) - return -EEXIST; - - remove_entry(s, idx); - - e = &bucket_at_swap(&swap, IDX_PUT)->p.b; - e->key = new_key; - assert_se(hashmap_put_boldly(s, new_hash, &swap, false) == 1); - - return 0; -} - -int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value) { - struct swap_entries swap; - struct plain_hashmap_entry *e; - unsigned old_hash, new_hash, idx_old, idx_new; - - if (!h) - return -ENOENT; - - old_hash = bucket_hash(h, old_key); - idx_old = bucket_scan(h, old_hash, old_key); - if (idx_old == IDX_NIL) - return -ENOENT; - - old_key = bucket_at(HASHMAP_BASE(h), idx_old)->key; - - new_hash = bucket_hash(h, new_key); - idx_new = bucket_scan(h, new_hash, new_key); - if (idx_new != IDX_NIL) - if (idx_old != idx_new) { - remove_entry(h, idx_new); - /* Compensate for a possible backward shift. */ - if (old_key != bucket_at(HASHMAP_BASE(h), idx_old)->key) - idx_old = prev_idx(HASHMAP_BASE(h), idx_old); - assert(old_key == bucket_at(HASHMAP_BASE(h), idx_old)->key); - } - - remove_entry(h, idx_old); - - e = &bucket_at_swap(&swap, IDX_PUT)->p; - e->b.key = new_key; - e->value = value; - assert_se(hashmap_put_boldly(h, new_hash, &swap, false) == 1); - - return 0; -} - -void *hashmap_remove_value(Hashmap *h, const void *key, void *value) { - struct plain_hashmap_entry *e; - unsigned hash, idx; - - if (!h) - return NULL; - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx == IDX_NIL) - return NULL; - - e = plain_bucket_at(h, idx); - if (e->value != value) - return NULL; - - remove_entry(h, idx); - - return value; -} - -static unsigned find_first_entry(HashmapBase *h) { - Iterator i = ITERATOR_FIRST; - - if (!h || !n_entries(h)) - return IDX_NIL; - - return hashmap_iterate_entry(h, &i); -} - -void *internal_hashmap_first(HashmapBase *h) { - unsigned idx; - - idx = find_first_entry(h); - if (idx == IDX_NIL) - return NULL; - - return entry_value(h, bucket_at(h, idx)); -} - -void *internal_hashmap_first_key(HashmapBase *h) { - struct hashmap_base_entry *e; - unsigned idx; - - idx = find_first_entry(h); - if (idx == IDX_NIL) - return NULL; - - e = bucket_at(h, idx); - return (void*) e->key; -} - -void *internal_hashmap_steal_first(HashmapBase *h) { - struct hashmap_base_entry *e; - void *data; - unsigned idx; - - idx = find_first_entry(h); - if (idx == IDX_NIL) - return NULL; - - e = bucket_at(h, idx); - data = entry_value(h, e); - remove_entry(h, idx); - - return data; -} - -void *internal_hashmap_steal_first_key(HashmapBase *h) { - struct hashmap_base_entry *e; - void *key; - unsigned idx; - - idx = find_first_entry(h); - if (idx == IDX_NIL) - return NULL; - - e = bucket_at(h, idx); - key = (void*) e->key; - remove_entry(h, idx); - - return key; -} - -unsigned internal_hashmap_size(HashmapBase *h) { - - if (!h) - return 0; - - return n_entries(h); -} - -unsigned internal_hashmap_buckets(HashmapBase *h) { - - if (!h) - return 0; - - return n_buckets(h); -} - -int internal_hashmap_merge(Hashmap *h, Hashmap *other) { - Iterator i; - unsigned idx; - - assert(h); - - HASHMAP_FOREACH_IDX(idx, HASHMAP_BASE(other), i) { - struct plain_hashmap_entry *pe = plain_bucket_at(other, idx); - int r; - - r = hashmap_put(h, pe->b.key, pe->value); - if (r < 0 && r != -EEXIST) - return r; - } - - return 0; -} - -int set_merge(Set *s, Set *other) { - Iterator i; - unsigned idx; - - assert(s); - - HASHMAP_FOREACH_IDX(idx, HASHMAP_BASE(other), i) { - struct set_entry *se = set_bucket_at(other, idx); - int r; - - r = set_put(s, se->b.key); - if (r < 0) - return r; - } - - return 0; -} - -int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add) { - int r; - - assert(h); - - r = resize_buckets(h, entries_add); - if (r < 0) - return r; - - return 0; -} - -/* - * The same as hashmap_merge(), but every new item from other is moved to h. - * Keys already in h are skipped and stay in other. - * Returns: 0 on success. - * -ENOMEM on alloc failure, in which case no move has been done. - */ -int internal_hashmap_move(HashmapBase *h, HashmapBase *other) { - struct swap_entries swap; - struct hashmap_base_entry *e, *n; - Iterator i; - unsigned idx; - int r; - - assert(h); - - if (!other) - return 0; - - assert(other->type == h->type); - - /* - * This reserves buckets for the worst case, where none of other's - * entries are yet present in h. This is preferable to risking - * an allocation failure in the middle of the moving and having to - * rollback or return a partial result. - */ - r = resize_buckets(h, n_entries(other)); - if (r < 0) - return r; - - HASHMAP_FOREACH_IDX(idx, other, i) { - unsigned h_hash; - - e = bucket_at(other, idx); - h_hash = bucket_hash(h, e->key); - if (bucket_scan(h, h_hash, e->key) != IDX_NIL) - continue; - - n = &bucket_at_swap(&swap, IDX_PUT)->p.b; - n->key = e->key; - if (h->type != HASHMAP_TYPE_SET) - ((struct plain_hashmap_entry*) n)->value = - ((struct plain_hashmap_entry*) e)->value; - assert_se(hashmap_put_boldly(h, h_hash, &swap, false) == 1); - - remove_entry(other, idx); - } - - return 0; -} - -int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key) { - struct swap_entries swap; - unsigned h_hash, other_hash, idx; - struct hashmap_base_entry *e, *n; - int r; - - assert(h); - - h_hash = bucket_hash(h, key); - if (bucket_scan(h, h_hash, key) != IDX_NIL) - return -EEXIST; - - if (!other) - return -ENOENT; - - assert(other->type == h->type); - - other_hash = bucket_hash(other, key); - idx = bucket_scan(other, other_hash, key); - if (idx == IDX_NIL) - return -ENOENT; - - e = bucket_at(other, idx); - - n = &bucket_at_swap(&swap, IDX_PUT)->p.b; - n->key = e->key; - if (h->type != HASHMAP_TYPE_SET) - ((struct plain_hashmap_entry*) n)->value = - ((struct plain_hashmap_entry*) e)->value; - r = hashmap_put_boldly(h, h_hash, &swap, true); - if (r < 0) - return r; - - remove_entry(other, idx); - return 0; -} - -HashmapBase *internal_hashmap_copy(HashmapBase *h) { - HashmapBase *copy; - int r; - - assert(h); - - copy = hashmap_base_new(h->hash_ops, h->type HASHMAP_DEBUG_SRC_ARGS); - if (!copy) - return NULL; - - switch (h->type) { - case HASHMAP_TYPE_PLAIN: - case HASHMAP_TYPE_ORDERED: - r = hashmap_merge((Hashmap*)copy, (Hashmap*)h); - break; - case HASHMAP_TYPE_SET: - r = set_merge((Set*)copy, (Set*)h); - break; - default: - assert_not_reached("Unknown hashmap type"); - } - - if (r < 0) { - internal_hashmap_free(copy); - return NULL; - } - - return copy; -} - -char **internal_hashmap_get_strv(HashmapBase *h) { - char **sv; - Iterator i; - unsigned idx, n; - - sv = new(char*, n_entries(h)+1); - if (!sv) - return NULL; - - n = 0; - HASHMAP_FOREACH_IDX(idx, h, i) - sv[n++] = entry_value(h, bucket_at(h, idx)); - sv[n] = NULL; - - return sv; -} - -void *ordered_hashmap_next(OrderedHashmap *h, const void *key) { - struct ordered_hashmap_entry *e; - unsigned hash, idx; - - if (!h) - return NULL; - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx == IDX_NIL) - return NULL; - - e = ordered_bucket_at(h, idx); - if (e->iterate_next == IDX_NIL) - return NULL; - return ordered_bucket_at(h, e->iterate_next)->p.value; -} - -int set_consume(Set *s, void *value) { - int r; - - assert(s); - assert(value); - - r = set_put(s, value); - if (r <= 0) - free(value); - - return r; -} - -int set_put_strdup(Set *s, const char *p) { - char *c; - - assert(s); - assert(p); - - if (set_contains(s, (char*) p)) - return 0; - - c = strdup(p); - if (!c) - return -ENOMEM; - - return set_consume(s, c); -} - -int set_put_strdupv(Set *s, char **l) { - int n = 0, r; - char **i; - - assert(s); - - STRV_FOREACH(i, l) { - r = set_put_strdup(s, *i); - if (r < 0) - return r; - - n += r; - } - - return n; -} - -int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags flags) { - const char *p = v; - int r; - - assert(s); - assert(v); - - for (;;) { - char *word; - - r = extract_first_word(&p, &word, separators, flags); - if (r <= 0) - return r; - - r = set_consume(s, word); - if (r < 0) - return r; - } -} - -/* expand the cachemem if needed, return true if newly (re)activated. */ -static int cachemem_maintain(CacheMem *mem, unsigned size) { - assert(mem); - - if (!GREEDY_REALLOC(mem->ptr, mem->n_allocated, size)) { - if (size > 0) - return -ENOMEM; - } - - if (!mem->active) { - mem->active = true; - return true; - } - - return false; -} - -int iterated_cache_get(IteratedCache *cache, const void ***res_keys, const void ***res_values, unsigned *res_n_entries) { - bool sync_keys = false, sync_values = false; - unsigned size; - int r; - - assert(cache); - assert(cache->hashmap); - - size = n_entries(cache->hashmap); - - if (res_keys) { - r = cachemem_maintain(&cache->keys, size); - if (r < 0) - return r; - - sync_keys = r; - } else - cache->keys.active = false; - - if (res_values) { - r = cachemem_maintain(&cache->values, size); - if (r < 0) - return r; - - sync_values = r; - } else - cache->values.active = false; - - if (cache->hashmap->dirty) { - if (cache->keys.active) - sync_keys = true; - if (cache->values.active) - sync_values = true; - - cache->hashmap->dirty = false; - } - - if (sync_keys || sync_values) { - unsigned i, idx; - Iterator iter; - - i = 0; - HASHMAP_FOREACH_IDX(idx, cache->hashmap, iter) { - struct hashmap_base_entry *e; - - e = bucket_at(cache->hashmap, idx); - - if (sync_keys) - cache->keys.ptr[i] = e->key; - if (sync_values) - cache->values.ptr[i] = entry_value(cache->hashmap, e); - i++; - } - } - - if (res_keys) - *res_keys = cache->keys.ptr; - if (res_values) - *res_values = cache->values.ptr; - if (res_n_entries) - *res_n_entries = size; - - return 0; -} - -IteratedCache *iterated_cache_free(IteratedCache *cache) { - if (cache) { - free(cache->keys.ptr); - free(cache->values.ptr); - free(cache); - } - - return NULL; -} diff --git a/src/systemd/src/basic/hashmap.h b/src/systemd/src/basic/hashmap.h deleted file mode 100644 index 274afb39..00000000 --- a/src/systemd/src/basic/hashmap.h +++ /dev/null @@ -1,393 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <limits.h> -#include <stdbool.h> -#include <stddef.h> - -#include "hash-funcs.h" -#include "macro.h" -#include "util.h" - -/* - * A hash table implementation. As a minor optimization a NULL hashmap object - * will be treated as empty hashmap for all read operations. That way it is not - * necessary to instantiate an object for each Hashmap use. - * - * If ENABLE_DEBUG_HASHMAP is defined (by configuring with --enable-debug=hashmap), - * the implemention will: - * - store extra data for debugging and statistics (see tools/gdb-sd_dump_hashmaps.py) - * - perform extra checks for invalid use of iterators - */ - -#define HASH_KEY_SIZE 16 - -/* The base type for all hashmap and set types. Many functions in the - * implementation take (HashmapBase*) parameters and are run-time polymorphic, - * though the API is not meant to be polymorphic (do not call functions - * internal_*() directly). */ -typedef struct HashmapBase HashmapBase; - -/* Specific hashmap/set types */ -typedef struct Hashmap Hashmap; /* Maps keys to values */ -typedef struct OrderedHashmap OrderedHashmap; /* Like Hashmap, but also remembers entry insertion order */ -typedef struct Set Set; /* Stores just keys */ - -typedef struct IteratedCache IteratedCache; /* Caches the iterated order of one of the above */ - -/* Ideally the Iterator would be an opaque struct, but it is instantiated - * by hashmap users, so the definition has to be here. Do not use its fields - * directly. */ -typedef struct { - unsigned idx; /* index of an entry to be iterated next */ - const void *next_key; /* expected value of that entry's key pointer */ -#if ENABLE_DEBUG_HASHMAP - unsigned put_count; /* hashmap's put_count recorded at start of iteration */ - unsigned rem_count; /* hashmap's rem_count in previous iteration */ - unsigned prev_idx; /* idx in previous iteration */ -#endif -} Iterator; - -#define _IDX_ITERATOR_FIRST (UINT_MAX - 1) -#define ITERATOR_FIRST ((Iterator) { .idx = _IDX_ITERATOR_FIRST, .next_key = NULL }) - -/* Macros for type checking */ -#define PTR_COMPATIBLE_WITH_HASHMAP_BASE(h) \ - (__builtin_types_compatible_p(typeof(h), HashmapBase*) || \ - __builtin_types_compatible_p(typeof(h), Hashmap*) || \ - __builtin_types_compatible_p(typeof(h), OrderedHashmap*) || \ - __builtin_types_compatible_p(typeof(h), Set*)) - -#define PTR_COMPATIBLE_WITH_PLAIN_HASHMAP(h) \ - (__builtin_types_compatible_p(typeof(h), Hashmap*) || \ - __builtin_types_compatible_p(typeof(h), OrderedHashmap*)) \ - -#define HASHMAP_BASE(h) \ - __builtin_choose_expr(PTR_COMPATIBLE_WITH_HASHMAP_BASE(h), \ - (HashmapBase*)(h), \ - (void)0) - -#define PLAIN_HASHMAP(h) \ - __builtin_choose_expr(PTR_COMPATIBLE_WITH_PLAIN_HASHMAP(h), \ - (Hashmap*)(h), \ - (void)0) - -#if ENABLE_DEBUG_HASHMAP -# define HASHMAP_DEBUG_PARAMS , const char *func, const char *file, int line -# define HASHMAP_DEBUG_SRC_ARGS , __func__, __FILE__, __LINE__ -# define HASHMAP_DEBUG_PASS_ARGS , func, file, line -#else -# define HASHMAP_DEBUG_PARAMS -# define HASHMAP_DEBUG_SRC_ARGS -# define HASHMAP_DEBUG_PASS_ARGS -#endif - -Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define hashmap_new(ops) internal_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) -#define ordered_hashmap_new(ops) internal_ordered_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) - -HashmapBase *internal_hashmap_free(HashmapBase *h); -static inline Hashmap *hashmap_free(Hashmap *h) { - return (void*)internal_hashmap_free(HASHMAP_BASE(h)); -} -static inline OrderedHashmap *ordered_hashmap_free(OrderedHashmap *h) { - return (void*)internal_hashmap_free(HASHMAP_BASE(h)); -} - -HashmapBase *internal_hashmap_free_free(HashmapBase *h); -static inline Hashmap *hashmap_free_free(Hashmap *h) { - return (void*)internal_hashmap_free_free(HASHMAP_BASE(h)); -} -static inline OrderedHashmap *ordered_hashmap_free_free(OrderedHashmap *h) { - return (void*)internal_hashmap_free_free(HASHMAP_BASE(h)); -} - -Hashmap *hashmap_free_free_free(Hashmap *h); -static inline OrderedHashmap *ordered_hashmap_free_free_free(OrderedHashmap *h) { - return (void*)hashmap_free_free_free(PLAIN_HASHMAP(h)); -} - -IteratedCache *iterated_cache_free(IteratedCache *cache); -int iterated_cache_get(IteratedCache *cache, const void ***res_keys, const void ***res_values, unsigned *res_n_entries); - -HashmapBase *internal_hashmap_copy(HashmapBase *h); -static inline Hashmap *hashmap_copy(Hashmap *h) { - return (Hashmap*) internal_hashmap_copy(HASHMAP_BASE(h)); -} -static inline OrderedHashmap *ordered_hashmap_copy(OrderedHashmap *h) { - return (OrderedHashmap*) internal_hashmap_copy(HASHMAP_BASE(h)); -} - -int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define hashmap_ensure_allocated(h, ops) internal_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) -#define ordered_hashmap_ensure_allocated(h, ops) internal_ordered_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) - -IteratedCache *internal_hashmap_iterated_cache_new(HashmapBase *h); -static inline IteratedCache *hashmap_iterated_cache_new(Hashmap *h) { - return (IteratedCache*) internal_hashmap_iterated_cache_new(HASHMAP_BASE(h)); -} -static inline IteratedCache *ordered_hashmap_iterated_cache_new(OrderedHashmap *h) { - return (IteratedCache*) internal_hashmap_iterated_cache_new(HASHMAP_BASE(h)); -} - -int hashmap_put(Hashmap *h, const void *key, void *value); -static inline int ordered_hashmap_put(OrderedHashmap *h, const void *key, void *value) { - return hashmap_put(PLAIN_HASHMAP(h), key, value); -} - -int hashmap_update(Hashmap *h, const void *key, void *value); -static inline int ordered_hashmap_update(OrderedHashmap *h, const void *key, void *value) { - return hashmap_update(PLAIN_HASHMAP(h), key, value); -} - -int hashmap_replace(Hashmap *h, const void *key, void *value); -static inline int ordered_hashmap_replace(OrderedHashmap *h, const void *key, void *value) { - return hashmap_replace(PLAIN_HASHMAP(h), key, value); -} - -void *internal_hashmap_get(HashmapBase *h, const void *key); -static inline void *hashmap_get(Hashmap *h, const void *key) { - return internal_hashmap_get(HASHMAP_BASE(h), key); -} -static inline void *ordered_hashmap_get(OrderedHashmap *h, const void *key) { - return internal_hashmap_get(HASHMAP_BASE(h), key); -} - -void *hashmap_get2(Hashmap *h, const void *key, void **rkey); -static inline void *ordered_hashmap_get2(OrderedHashmap *h, const void *key, void **rkey) { - return hashmap_get2(PLAIN_HASHMAP(h), key, rkey); -} - -bool internal_hashmap_contains(HashmapBase *h, const void *key); -static inline bool hashmap_contains(Hashmap *h, const void *key) { - return internal_hashmap_contains(HASHMAP_BASE(h), key); -} -static inline bool ordered_hashmap_contains(OrderedHashmap *h, const void *key) { - return internal_hashmap_contains(HASHMAP_BASE(h), key); -} - -void *internal_hashmap_remove(HashmapBase *h, const void *key); -static inline void *hashmap_remove(Hashmap *h, const void *key) { - return internal_hashmap_remove(HASHMAP_BASE(h), key); -} -static inline void *ordered_hashmap_remove(OrderedHashmap *h, const void *key) { - return internal_hashmap_remove(HASHMAP_BASE(h), key); -} - -void *hashmap_remove2(Hashmap *h, const void *key, void **rkey); -static inline void *ordered_hashmap_remove2(OrderedHashmap *h, const void *key, void **rkey) { - return hashmap_remove2(PLAIN_HASHMAP(h), key, rkey); -} - -void *hashmap_remove_value(Hashmap *h, const void *key, void *value); -static inline void *ordered_hashmap_remove_value(OrderedHashmap *h, const void *key, void *value) { - return hashmap_remove_value(PLAIN_HASHMAP(h), key, value); -} - -int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value); -static inline int ordered_hashmap_remove_and_put(OrderedHashmap *h, const void *old_key, const void *new_key, void *value) { - return hashmap_remove_and_put(PLAIN_HASHMAP(h), old_key, new_key, value); -} - -int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value); -static inline int ordered_hashmap_remove_and_replace(OrderedHashmap *h, const void *old_key, const void *new_key, void *value) { - return hashmap_remove_and_replace(PLAIN_HASHMAP(h), old_key, new_key, value); -} - -/* Since merging data from a OrderedHashmap into a Hashmap or vice-versa - * should just work, allow this by having looser type-checking here. */ -int internal_hashmap_merge(Hashmap *h, Hashmap *other); -#define hashmap_merge(h, other) internal_hashmap_merge(PLAIN_HASHMAP(h), PLAIN_HASHMAP(other)) -#define ordered_hashmap_merge(h, other) hashmap_merge(h, other) - -int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add); -static inline int hashmap_reserve(Hashmap *h, unsigned entries_add) { - return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add); -} -static inline int ordered_hashmap_reserve(OrderedHashmap *h, unsigned entries_add) { - return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add); -} - -int internal_hashmap_move(HashmapBase *h, HashmapBase *other); -/* Unlike hashmap_merge, hashmap_move does not allow mixing the types. */ -static inline int hashmap_move(Hashmap *h, Hashmap *other) { - return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other)); -} -static inline int ordered_hashmap_move(OrderedHashmap *h, OrderedHashmap *other) { - return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other)); -} - -int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key); -static inline int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) { - return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key); -} -static inline int ordered_hashmap_move_one(OrderedHashmap *h, OrderedHashmap *other, const void *key) { - return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key); -} - -unsigned internal_hashmap_size(HashmapBase *h) _pure_; -static inline unsigned hashmap_size(Hashmap *h) { - return internal_hashmap_size(HASHMAP_BASE(h)); -} -static inline unsigned ordered_hashmap_size(OrderedHashmap *h) { - return internal_hashmap_size(HASHMAP_BASE(h)); -} - -static inline bool hashmap_isempty(Hashmap *h) { - return hashmap_size(h) == 0; -} -static inline bool ordered_hashmap_isempty(OrderedHashmap *h) { - return ordered_hashmap_size(h) == 0; -} - -unsigned internal_hashmap_buckets(HashmapBase *h) _pure_; -static inline unsigned hashmap_buckets(Hashmap *h) { - return internal_hashmap_buckets(HASHMAP_BASE(h)); -} -static inline unsigned ordered_hashmap_buckets(OrderedHashmap *h) { - return internal_hashmap_buckets(HASHMAP_BASE(h)); -} - -bool internal_hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key); -static inline bool hashmap_iterate(Hashmap *h, Iterator *i, void **value, const void **key) { - return internal_hashmap_iterate(HASHMAP_BASE(h), i, value, key); -} -static inline bool ordered_hashmap_iterate(OrderedHashmap *h, Iterator *i, void **value, const void **key) { - return internal_hashmap_iterate(HASHMAP_BASE(h), i, value, key); -} - -void internal_hashmap_clear(HashmapBase *h); -static inline void hashmap_clear(Hashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h)); -} -static inline void ordered_hashmap_clear(OrderedHashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h)); -} - -void internal_hashmap_clear_free(HashmapBase *h); -static inline void hashmap_clear_free(Hashmap *h) { - internal_hashmap_clear_free(HASHMAP_BASE(h)); -} -static inline void ordered_hashmap_clear_free(OrderedHashmap *h) { - internal_hashmap_clear_free(HASHMAP_BASE(h)); -} - -void hashmap_clear_free_free(Hashmap *h); -static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) { - hashmap_clear_free_free(PLAIN_HASHMAP(h)); -} - -/* - * Note about all *_first*() functions - * - * For plain Hashmaps and Sets the order of entries is undefined. - * The functions find whatever entry is first in the implementation - * internal order. - * - * Only for OrderedHashmaps the order is well defined and finding - * the first entry is O(1). - */ - -void *internal_hashmap_steal_first(HashmapBase *h); -static inline void *hashmap_steal_first(Hashmap *h) { - return internal_hashmap_steal_first(HASHMAP_BASE(h)); -} -static inline void *ordered_hashmap_steal_first(OrderedHashmap *h) { - return internal_hashmap_steal_first(HASHMAP_BASE(h)); -} - -void *internal_hashmap_steal_first_key(HashmapBase *h); -static inline void *hashmap_steal_first_key(Hashmap *h) { - return internal_hashmap_steal_first_key(HASHMAP_BASE(h)); -} -static inline void *ordered_hashmap_steal_first_key(OrderedHashmap *h) { - return internal_hashmap_steal_first_key(HASHMAP_BASE(h)); -} - -void *internal_hashmap_first_key(HashmapBase *h) _pure_; -static inline void *hashmap_first_key(Hashmap *h) { - return internal_hashmap_first_key(HASHMAP_BASE(h)); -} -static inline void *ordered_hashmap_first_key(OrderedHashmap *h) { - return internal_hashmap_first_key(HASHMAP_BASE(h)); -} - -void *internal_hashmap_first(HashmapBase *h) _pure_; -static inline void *hashmap_first(Hashmap *h) { - return internal_hashmap_first(HASHMAP_BASE(h)); -} -static inline void *ordered_hashmap_first(OrderedHashmap *h) { - return internal_hashmap_first(HASHMAP_BASE(h)); -} - -#define hashmap_clear_with_destructor(_s, _f) \ - ({ \ - void *_item; \ - while ((_item = hashmap_steal_first(_s))) \ - _f(_item); \ - }) -#define hashmap_free_with_destructor(_s, _f) \ - ({ \ - hashmap_clear_with_destructor(_s, _f); \ - hashmap_free(_s); \ - }) -#define ordered_hashmap_clear_with_destructor(_s, _f) \ - ({ \ - void *_item; \ - while ((_item = ordered_hashmap_steal_first(_s))) \ - _f(_item); \ - }) -#define ordered_hashmap_free_with_destructor(_s, _f) \ - ({ \ - ordered_hashmap_clear_with_destructor(_s, _f); \ - ordered_hashmap_free(_s); \ - }) - -/* no hashmap_next */ -void *ordered_hashmap_next(OrderedHashmap *h, const void *key); - -char **internal_hashmap_get_strv(HashmapBase *h); -static inline char **hashmap_get_strv(Hashmap *h) { - return internal_hashmap_get_strv(HASHMAP_BASE(h)); -} -static inline char **ordered_hashmap_get_strv(OrderedHashmap *h) { - return internal_hashmap_get_strv(HASHMAP_BASE(h)); -} - -/* - * Hashmaps are iterated in unpredictable order. - * OrderedHashmaps are an exception to this. They are iterated in the order - * the entries were inserted. - * It is safe to remove the current entry. - */ -#define HASHMAP_FOREACH(e, h, i) \ - for ((i) = ITERATOR_FIRST; hashmap_iterate((h), &(i), (void**)&(e), NULL); ) - -#define ORDERED_HASHMAP_FOREACH(e, h, i) \ - for ((i) = ITERATOR_FIRST; ordered_hashmap_iterate((h), &(i), (void**)&(e), NULL); ) - -#define HASHMAP_FOREACH_KEY(e, k, h, i) \ - for ((i) = ITERATOR_FIRST; hashmap_iterate((h), &(i), (void**)&(e), (const void**) &(k)); ) - -#define ORDERED_HASHMAP_FOREACH_KEY(e, k, h, i) \ - for ((i) = ITERATOR_FIRST; ordered_hashmap_iterate((h), &(i), (void**)&(e), (const void**) &(k)); ) - -DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free); -DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free); -DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free_free); -DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free); -DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free); -DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free_free); - -#define _cleanup_hashmap_free_ _cleanup_(hashmap_freep) -#define _cleanup_hashmap_free_free_ _cleanup_(hashmap_free_freep) -#define _cleanup_hashmap_free_free_free_ _cleanup_(hashmap_free_free_freep) -#define _cleanup_ordered_hashmap_free_ _cleanup_(ordered_hashmap_freep) -#define _cleanup_ordered_hashmap_free_free_ _cleanup_(ordered_hashmap_free_freep) -#define _cleanup_ordered_hashmap_free_free_free_ _cleanup_(ordered_hashmap_free_free_freep) - -DEFINE_TRIVIAL_CLEANUP_FUNC(IteratedCache*, iterated_cache_free); - -#define _cleanup_iterated_cache_free_ _cleanup_(iterated_cache_freep) diff --git a/src/systemd/src/basic/hexdecoct.c b/src/systemd/src/basic/hexdecoct.c deleted file mode 100644 index 09f2a9e7..00000000 --- a/src/systemd/src/basic/hexdecoct.c +++ /dev/null @@ -1,821 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <ctype.h> -#include <errno.h> -#include <stdint.h> -#include <stdlib.h> - -#include "alloc-util.h" -#include "hexdecoct.h" -#include "macro.h" -#include "string-util.h" -#include "util.h" - -char octchar(int x) { - return '0' + (x & 7); -} - -int unoctchar(char c) { - - if (c >= '0' && c <= '7') - return c - '0'; - - return -EINVAL; -} - -char decchar(int x) { - return '0' + (x % 10); -} - -int undecchar(char c) { - - if (c >= '0' && c <= '9') - return c - '0'; - - return -EINVAL; -} - -char hexchar(int x) { - static const char table[16] = "0123456789abcdef"; - - return table[x & 15]; -} - -int unhexchar(char c) { - - if (c >= '0' && c <= '9') - return c - '0'; - - if (c >= 'a' && c <= 'f') - return c - 'a' + 10; - - if (c >= 'A' && c <= 'F') - return c - 'A' + 10; - - return -EINVAL; -} - -char *hexmem(const void *p, size_t l) { - const uint8_t *x; - char *r, *z; - - z = r = new(char, l * 2 + 1); - if (!r) - return NULL; - - for (x = p; x < (const uint8_t*) p + l; x++) { - *(z++) = hexchar(*x >> 4); - *(z++) = hexchar(*x & 15); - } - - *z = 0; - return r; -} - -static int unhex_next(const char **p, size_t *l) { - int r; - - assert(p); - assert(l); - - /* Find the next non-whitespace character, and decode it. We - * greedily skip all preceeding and all following whitespace. */ - - for (;;) { - if (*l == 0) - return -EPIPE; - - if (!strchr(WHITESPACE, **p)) - break; - - /* Skip leading whitespace */ - (*p)++, (*l)--; - } - - r = unhexchar(**p); - if (r < 0) - return r; - - for (;;) { - (*p)++, (*l)--; - - if (*l == 0 || !strchr(WHITESPACE, **p)) - break; - - /* Skip following whitespace */ - } - - return r; -} - -int unhexmem(const char *p, size_t l, void **ret, size_t *ret_len) { - _cleanup_free_ uint8_t *buf = NULL; - const char *x; - uint8_t *z; - - assert(ret); - assert(ret_len); - assert(p || l == 0); - - if (l == (size_t) -1) - l = strlen(p); - - /* Note that the calculation of memory size is an upper boundary, as we ignore whitespace while decoding */ - buf = malloc((l + 1) / 2 + 1); - if (!buf) - return -ENOMEM; - - for (x = p, z = buf;;) { - int a, b; - - a = unhex_next(&x, &l); - if (a == -EPIPE) /* End of string */ - break; - if (a < 0) - return a; - - b = unhex_next(&x, &l); - if (b < 0) - return b; - - *(z++) = (uint8_t) a << 4 | (uint8_t) b; - } - - *z = 0; - - *ret_len = (size_t) (z - buf); - *ret = TAKE_PTR(buf); - - return 0; -} - -#if 0 /* NM_IGNORED */ -/* https://tools.ietf.org/html/rfc4648#section-6 - * Notice that base32hex differs from base32 in the alphabet it uses. - * The distinction is that the base32hex representation preserves the - * order of the underlying data when compared as bytestrings, this is - * useful when representing NSEC3 hashes, as one can then verify the - * order of hashes directly from their representation. */ -char base32hexchar(int x) { - static const char table[32] = "0123456789" - "ABCDEFGHIJKLMNOPQRSTUV"; - - return table[x & 31]; -} - -int unbase32hexchar(char c) { - unsigned offset; - - if (c >= '0' && c <= '9') - return c - '0'; - - offset = '9' - '0' + 1; - - if (c >= 'A' && c <= 'V') - return c - 'A' + offset; - - return -EINVAL; -} - -char *base32hexmem(const void *p, size_t l, bool padding) { - char *r, *z; - const uint8_t *x; - size_t len; - - assert(p || l == 0); - - if (padding) - /* five input bytes makes eight output bytes, padding is added so we must round up */ - len = 8 * (l + 4) / 5; - else { - /* same, but round down as there is no padding */ - len = 8 * l / 5; - - switch (l % 5) { - case 4: - len += 7; - break; - case 3: - len += 5; - break; - case 2: - len += 4; - break; - case 1: - len += 2; - break; - } - } - - z = r = malloc(len + 1); - if (!r) - return NULL; - - for (x = p; x < (const uint8_t*) p + (l / 5) * 5; x += 5) { - /* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ - * x[3] == QQQQQQQQ; x[4] == WWWWWWWW */ - *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */ - *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */ - *(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */ - *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4); /* 000YZZZZ */ - *(z++) = base32hexchar((x[2] & 15) << 1 | x[3] >> 7); /* 000ZZZZQ */ - *(z++) = base32hexchar((x[3] & 127) >> 2); /* 000QQQQQ */ - *(z++) = base32hexchar((x[3] & 3) << 3 | x[4] >> 5); /* 000QQWWW */ - *(z++) = base32hexchar((x[4] & 31)); /* 000WWWWW */ - } - - switch (l % 5) { - case 4: - *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */ - *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */ - *(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */ - *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4); /* 000YZZZZ */ - *(z++) = base32hexchar((x[2] & 15) << 1 | x[3] >> 7); /* 000ZZZZQ */ - *(z++) = base32hexchar((x[3] & 127) >> 2); /* 000QQQQQ */ - *(z++) = base32hexchar((x[3] & 3) << 3); /* 000QQ000 */ - if (padding) - *(z++) = '='; - - break; - - case 3: - *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */ - *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */ - *(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */ - *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4); /* 000YZZZZ */ - *(z++) = base32hexchar((x[2] & 15) << 1); /* 000ZZZZ0 */ - if (padding) { - *(z++) = '='; - *(z++) = '='; - *(z++) = '='; - } - - break; - - case 2: - *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */ - *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */ - *(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */ - *(z++) = base32hexchar((x[1] & 1) << 4); /* 000Y0000 */ - if (padding) { - *(z++) = '='; - *(z++) = '='; - *(z++) = '='; - *(z++) = '='; - } - - break; - - case 1: - *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */ - *(z++) = base32hexchar((x[0] & 7) << 2); /* 000XXX00 */ - if (padding) { - *(z++) = '='; - *(z++) = '='; - *(z++) = '='; - *(z++) = '='; - *(z++) = '='; - *(z++) = '='; - } - - break; - } - - *z = 0; - return r; -} - -int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *_len) { - _cleanup_free_ uint8_t *r = NULL; - int a, b, c, d, e, f, g, h; - uint8_t *z; - const char *x; - size_t len; - unsigned pad = 0; - - assert(p || l == 0); - assert(mem); - assert(_len); - - if (l == (size_t) -1) - l = strlen(p); - - /* padding ensures any base32hex input has input divisible by 8 */ - if (padding && l % 8 != 0) - return -EINVAL; - - if (padding) { - /* strip the padding */ - while (l > 0 && p[l - 1] == '=' && pad < 7) { - pad++; - l--; - } - } - - /* a group of eight input bytes needs five output bytes, in case of - * padding we need to add some extra bytes */ - len = (l / 8) * 5; - - switch (l % 8) { - case 7: - len += 4; - break; - case 5: - len += 3; - break; - case 4: - len += 2; - break; - case 2: - len += 1; - break; - case 0: - break; - default: - return -EINVAL; - } - - z = r = malloc(len + 1); - if (!r) - return -ENOMEM; - - for (x = p; x < p + (l / 8) * 8; x += 8) { - /* a == 000XXXXX; b == 000YYYYY; c == 000ZZZZZ; d == 000WWWWW - * e == 000SSSSS; f == 000QQQQQ; g == 000VVVVV; h == 000RRRRR */ - a = unbase32hexchar(x[0]); - if (a < 0) - return -EINVAL; - - b = unbase32hexchar(x[1]); - if (b < 0) - return -EINVAL; - - c = unbase32hexchar(x[2]); - if (c < 0) - return -EINVAL; - - d = unbase32hexchar(x[3]); - if (d < 0) - return -EINVAL; - - e = unbase32hexchar(x[4]); - if (e < 0) - return -EINVAL; - - f = unbase32hexchar(x[5]); - if (f < 0) - return -EINVAL; - - g = unbase32hexchar(x[6]); - if (g < 0) - return -EINVAL; - - h = unbase32hexchar(x[7]); - if (h < 0) - return -EINVAL; - - *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */ - *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */ - *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1; /* WWWWSSSS */ - *(z++) = (uint8_t) e << 7 | (uint8_t) f << 2 | (uint8_t) g >> 3; /* SQQQQQVV */ - *(z++) = (uint8_t) g << 5 | (uint8_t) h; /* VVVRRRRR */ - } - - switch (l % 8) { - case 7: - a = unbase32hexchar(x[0]); - if (a < 0) - return -EINVAL; - - b = unbase32hexchar(x[1]); - if (b < 0) - return -EINVAL; - - c = unbase32hexchar(x[2]); - if (c < 0) - return -EINVAL; - - d = unbase32hexchar(x[3]); - if (d < 0) - return -EINVAL; - - e = unbase32hexchar(x[4]); - if (e < 0) - return -EINVAL; - - f = unbase32hexchar(x[5]); - if (f < 0) - return -EINVAL; - - g = unbase32hexchar(x[6]); - if (g < 0) - return -EINVAL; - - /* g == 000VV000 */ - if (g & 7) - return -EINVAL; - - *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */ - *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */ - *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1; /* WWWWSSSS */ - *(z++) = (uint8_t) e << 7 | (uint8_t) f << 2 | (uint8_t) g >> 3; /* SQQQQQVV */ - - break; - case 5: - a = unbase32hexchar(x[0]); - if (a < 0) - return -EINVAL; - - b = unbase32hexchar(x[1]); - if (b < 0) - return -EINVAL; - - c = unbase32hexchar(x[2]); - if (c < 0) - return -EINVAL; - - d = unbase32hexchar(x[3]); - if (d < 0) - return -EINVAL; - - e = unbase32hexchar(x[4]); - if (e < 0) - return -EINVAL; - - /* e == 000SSSS0 */ - if (e & 1) - return -EINVAL; - - *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */ - *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */ - *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1; /* WWWWSSSS */ - - break; - case 4: - a = unbase32hexchar(x[0]); - if (a < 0) - return -EINVAL; - - b = unbase32hexchar(x[1]); - if (b < 0) - return -EINVAL; - - c = unbase32hexchar(x[2]); - if (c < 0) - return -EINVAL; - - d = unbase32hexchar(x[3]); - if (d < 0) - return -EINVAL; - - /* d == 000W0000 */ - if (d & 15) - return -EINVAL; - - *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */ - *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */ - - break; - case 2: - a = unbase32hexchar(x[0]); - if (a < 0) - return -EINVAL; - - b = unbase32hexchar(x[1]); - if (b < 0) - return -EINVAL; - - /* b == 000YYY00 */ - if (b & 3) - return -EINVAL; - - *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */ - - break; - case 0: - break; - default: - return -EINVAL; - } - - *z = 0; - - *mem = TAKE_PTR(r); - *_len = len; - - return 0; -} - -/* https://tools.ietf.org/html/rfc4648#section-4 */ -char base64char(int x) { - static const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789+/"; - return table[x & 63]; -} - -int unbase64char(char c) { - unsigned offset; - - if (c >= 'A' && c <= 'Z') - return c - 'A'; - - offset = 'Z' - 'A' + 1; - - if (c >= 'a' && c <= 'z') - return c - 'a' + offset; - - offset += 'z' - 'a' + 1; - - if (c >= '0' && c <= '9') - return c - '0' + offset; - - offset += '9' - '0' + 1; - - if (c == '+') - return offset; - - offset++; - - if (c == '/') - return offset; - - return -EINVAL; -} - -ssize_t base64mem(const void *p, size_t l, char **out) { - char *r, *z; - const uint8_t *x; - - assert(p || l == 0); - assert(out); - - /* three input bytes makes four output bytes, padding is added so we must round up */ - z = r = malloc(4 * (l + 2) / 3 + 1); - if (!r) - return -ENOMEM; - - for (x = p; x < (const uint8_t*) p + (l / 3) * 3; x += 3) { - /* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ */ - *(z++) = base64char(x[0] >> 2); /* 00XXXXXX */ - *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */ - *(z++) = base64char((x[1] & 15) << 2 | x[2] >> 6); /* 00YYYYZZ */ - *(z++) = base64char(x[2] & 63); /* 00ZZZZZZ */ - } - - switch (l % 3) { - case 2: - *(z++) = base64char(x[0] >> 2); /* 00XXXXXX */ - *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */ - *(z++) = base64char((x[1] & 15) << 2); /* 00YYYY00 */ - *(z++) = '='; - - break; - case 1: - *(z++) = base64char(x[0] >> 2); /* 00XXXXXX */ - *(z++) = base64char((x[0] & 3) << 4); /* 00XX0000 */ - *(z++) = '='; - *(z++) = '='; - - break; - } - - *z = 0; - *out = r; - return z - r; -} - -static int base64_append_width( - char **prefix, int plen, - const char *sep, int indent, - const void *p, size_t l, - int width) { - - _cleanup_free_ char *x = NULL; - char *t, *s; - ssize_t slen, len, avail; - int line, lines; - - len = base64mem(p, l, &x); - if (len <= 0) - return len; - - lines = DIV_ROUND_UP(len, width); - - slen = strlen_ptr(sep); - t = realloc(*prefix, plen + 1 + slen + (indent + width + 1) * lines); - if (!t) - return -ENOMEM; - - memcpy_safe(t + plen, sep, slen); - - for (line = 0, s = t + plen + slen, avail = len; line < lines; line++) { - int act = MIN(width, avail); - - if (line > 0 || sep) { - memset(s, ' ', indent); - s += indent; - } - - memcpy(s, x + width * line, act); - s += act; - *(s++) = line < lines - 1 ? '\n' : '\0'; - avail -= act; - } - assert(avail == 0); - - *prefix = t; - return 0; -} - -int base64_append( - char **prefix, int plen, - const void *p, size_t l, - int indent, int width) { - - if (plen > width / 2 || plen + indent > width) - /* leave indent on the left, keep last column free */ - return base64_append_width(prefix, plen, "\n", indent, p, l, width - indent - 1); - else - /* leave plen on the left, keep last column free */ - return base64_append_width(prefix, plen, NULL, plen, p, l, width - plen - 1); -} - -static int unbase64_next(const char **p, size_t *l) { - int ret; - - assert(p); - assert(l); - - /* Find the next non-whitespace character, and decode it. If we find padding, we return it as INT_MAX. We - * greedily skip all preceeding and all following whitespace. */ - - for (;;) { - if (*l == 0) - return -EPIPE; - - if (!strchr(WHITESPACE, **p)) - break; - - /* Skip leading whitespace */ - (*p)++, (*l)--; - } - - if (**p == '=') - ret = INT_MAX; /* return padding as INT_MAX */ - else { - ret = unbase64char(**p); - if (ret < 0) - return ret; - } - - for (;;) { - (*p)++, (*l)--; - - if (*l == 0) - break; - if (!strchr(WHITESPACE, **p)) - break; - - /* Skip following whitespace */ - } - - return ret; -} - -int unbase64mem(const char *p, size_t l, void **ret, size_t *ret_size) { - _cleanup_free_ uint8_t *buf = NULL; - const char *x; - uint8_t *z; - size_t len; - - assert(p || l == 0); - assert(ret); - assert(ret_size); - - if (l == (size_t) -1) - l = strlen(p); - - /* A group of four input bytes needs three output bytes, in case of padding we need to add two or three extra - * bytes. Note that this calculation is an upper boundary, as we ignore whitespace while decoding */ - len = (l / 4) * 3 + (l % 4 != 0 ? (l % 4) - 1 : 0); - - buf = malloc(len + 1); - if (!buf) - return -ENOMEM; - - for (x = p, z = buf;;) { - int a, b, c, d; /* a == 00XXXXXX; b == 00YYYYYY; c == 00ZZZZZZ; d == 00WWWWWW */ - - a = unbase64_next(&x, &l); - if (a == -EPIPE) /* End of string */ - break; - if (a < 0) - return a; - if (a == INT_MAX) /* Padding is not allowed at the beginning of a 4ch block */ - return -EINVAL; - - b = unbase64_next(&x, &l); - if (b < 0) - return b; - if (b == INT_MAX) /* Padding is not allowed at the second character of a 4ch block either */ - return -EINVAL; - - c = unbase64_next(&x, &l); - if (c < 0) - return c; - - d = unbase64_next(&x, &l); - if (d < 0) - return d; - - if (c == INT_MAX) { /* Padding at the third character */ - - if (d != INT_MAX) /* If the third character is padding, the fourth must be too */ - return -EINVAL; - - /* b == 00YY0000 */ - if (b & 15) - return -EINVAL; - - if (l > 0) /* Trailing rubbish? */ - return -ENAMETOOLONG; - - *(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */ - break; - } - - if (d == INT_MAX) { - /* c == 00ZZZZ00 */ - if (c & 3) - return -EINVAL; - - if (l > 0) /* Trailing rubbish? */ - return -ENAMETOOLONG; - - *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */ - *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */ - break; - } - - *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */ - *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */ - *(z++) = (uint8_t) c << 6 | (uint8_t) d; /* ZZWWWWWW */ - } - - *z = 0; - - *ret_size = (size_t) (z - buf); - *ret = TAKE_PTR(buf); - - return 0; -} - -void hexdump(FILE *f, const void *p, size_t s) { - const uint8_t *b = p; - unsigned n = 0; - - assert(b || s == 0); - - if (!f) - f = stdout; - - while (s > 0) { - size_t i; - - fprintf(f, "%04x ", n); - - for (i = 0; i < 16; i++) { - - if (i >= s) - fputs(" ", f); - else - fprintf(f, "%02x ", b[i]); - - if (i == 7) - fputc(' ', f); - } - - fputc(' ', f); - - for (i = 0; i < 16; i++) { - - if (i >= s) - fputc(' ', f); - else - fputc(isprint(b[i]) ? (char) b[i] : '.', f); - } - - fputc('\n', f); - - if (s < 16) - break; - - n += 16; - b += 16; - s -= 16; - } -} -#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/hexdecoct.h b/src/systemd/src/basic/hexdecoct.h deleted file mode 100644 index 9477d16e..00000000 --- a/src/systemd/src/basic/hexdecoct.h +++ /dev/null @@ -1,38 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <stdbool.h> -#include <stddef.h> -#include <stdio.h> -#include <sys/types.h> - -#include "macro.h" - -char octchar(int x) _const_; -int unoctchar(char c) _const_; - -char decchar(int x) _const_; -int undecchar(char c) _const_; - -char hexchar(int x) _const_; -int unhexchar(char c) _const_; - -char *hexmem(const void *p, size_t l); -int unhexmem(const char *p, size_t l, void **mem, size_t *len); - -char base32hexchar(int x) _const_; -int unbase32hexchar(char c) _const_; - -char base64char(int x) _const_; -int unbase64char(char c) _const_; - -char *base32hexmem(const void *p, size_t l, bool padding); -int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *len); - -ssize_t base64mem(const void *p, size_t l, char **out); -int base64_append(char **prefix, int plen, - const void *p, size_t l, - int margin, int width); -int unbase64mem(const char *p, size_t l, void **mem, size_t *len); - -void hexdump(FILE *f, const void *p, size_t s); diff --git a/src/systemd/src/basic/hostname-util.c b/src/systemd/src/basic/hostname-util.c deleted file mode 100644 index 85708394..00000000 --- a/src/systemd/src/basic/hostname-util.c +++ /dev/null @@ -1,295 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <errno.h> -#include <limits.h> -#include <stdio.h> -#include <string.h> -#include <sys/utsname.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "def.h" -#include "fd-util.h" -#include "fileio.h" -#include "hostname-util.h" -#include "macro.h" -#include "string-util.h" - -#if 0 /* NM_IGNORED */ -bool hostname_is_set(void) { - struct utsname u; - - assert_se(uname(&u) >= 0); - - if (isempty(u.nodename)) - return false; - - /* This is the built-in kernel default host name */ - if (streq(u.nodename, "(none)")) - return false; - - return true; -} - -char* gethostname_malloc(void) { - struct utsname u; - - /* This call tries to return something useful, either the actual hostname - * or it makes something up. The only reason it might fail is OOM. - * It might even return "localhost" if that's set. */ - - assert_se(uname(&u) >= 0); - - if (isempty(u.nodename) || streq(u.nodename, "(none)")) - return strdup(FALLBACK_HOSTNAME); - - return strdup(u.nodename); -} -#endif /* NM_IGNORED */ - -int gethostname_strict(char **ret) { - struct utsname u; - char *k; - - /* This call will rather fail than make up a name. It will not return "localhost" either. */ - - assert_se(uname(&u) >= 0); - - if (isempty(u.nodename)) - return -ENXIO; - - if (streq(u.nodename, "(none)")) - return -ENXIO; - - if (is_localhost(u.nodename)) - return -ENXIO; - - k = strdup(u.nodename); - if (!k) - return -ENOMEM; - - *ret = k; - return 0; -} - -static bool hostname_valid_char(char c) { - return - (c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z') || - (c >= '0' && c <= '9') || - IN_SET(c, '-', '_', '.'); -} - -/** - * Check if s looks like a valid host name or FQDN. This does not do - * full DNS validation, but only checks if the name is composed of - * allowed characters and the length is not above the maximum allowed - * by Linux (c.f. dns_name_is_valid()). Trailing dot is allowed if - * allow_trailing_dot is true and at least two components are present - * in the name. Note that due to the restricted charset and length - * this call is substantially more conservative than - * dns_name_is_valid(). - */ -bool hostname_is_valid(const char *s, bool allow_trailing_dot) { - unsigned n_dots = 0; - const char *p; - bool dot; - - if (isempty(s)) - return false; - - /* Doesn't accept empty hostnames, hostnames with - * leading dots, and hostnames with multiple dots in a - * sequence. Also ensures that the length stays below - * HOST_NAME_MAX. */ - - for (p = s, dot = true; *p; p++) { - if (*p == '.') { - if (dot) - return false; - - dot = true; - n_dots++; - } else { - if (!hostname_valid_char(*p)) - return false; - - dot = false; - } - } - - if (dot && (n_dots < 2 || !allow_trailing_dot)) - return false; - - if (p-s > HOST_NAME_MAX) /* Note that HOST_NAME_MAX is 64 on - * Linux, but DNS allows domain names - * up to 255 characters */ - return false; - - return true; -} - -char* hostname_cleanup(char *s) { - char *p, *d; - bool dot; - - assert(s); - - strshorten(s, HOST_NAME_MAX); - - for (p = s, d = s, dot = true; *p; p++) { - if (*p == '.') { - if (dot) - continue; - - *(d++) = '.'; - dot = true; - } else if (hostname_valid_char(*p)) { - *(d++) = *p; - dot = false; - } - } - - if (dot && d > s) - d[-1] = 0; - else - *d = 0; - - return s; -} - -bool is_localhost(const char *hostname) { - assert(hostname); - - /* This tries to identify local host and domain names - * described in RFC6761 plus the redhatism of localdomain */ - - return strcaseeq(hostname, "localhost") || - strcaseeq(hostname, "localhost.") || - strcaseeq(hostname, "localhost.localdomain") || - strcaseeq(hostname, "localhost.localdomain.") || - endswith_no_case(hostname, ".localhost") || - endswith_no_case(hostname, ".localhost.") || - endswith_no_case(hostname, ".localhost.localdomain") || - endswith_no_case(hostname, ".localhost.localdomain."); -} - -#if 0 /* NM_IGNORED */ -bool is_gateway_hostname(const char *hostname) { - assert(hostname); - - /* This tries to identify the valid syntaxes for the our - * synthetic "gateway" host. */ - - return - strcaseeq(hostname, "_gateway") || strcaseeq(hostname, "_gateway.") -#if ENABLE_COMPAT_GATEWAY_HOSTNAME - || strcaseeq(hostname, "gateway") || strcaseeq(hostname, "gateway.") -#endif - ; -} - -int sethostname_idempotent(const char *s) { - char buf[HOST_NAME_MAX + 1] = {}; - - assert(s); - - if (gethostname(buf, sizeof(buf)) < 0) - return -errno; - - if (streq(buf, s)) - return 0; - - if (sethostname(s, strlen(s)) < 0) - return -errno; - - return 1; -} - -int shorten_overlong(const char *s, char **ret) { - char *h, *p; - - /* Shorten an overlong name to HOST_NAME_MAX or to the first dot, - * whatever comes earlier. */ - - assert(s); - - h = strdup(s); - if (!h) - return -ENOMEM; - - if (hostname_is_valid(h, false)) { - *ret = h; - return 0; - } - - p = strchr(h, '.'); - if (p) - *p = 0; - - strshorten(h, HOST_NAME_MAX); - - if (!hostname_is_valid(h, false)) { - free(h); - return -EDOM; - } - - *ret = h; - return 1; -} - -int read_etc_hostname_stream(FILE *f, char **ret) { - int r; - - assert(f); - assert(ret); - - for (;;) { - _cleanup_free_ char *line = NULL; - char *p; - - r = read_line(f, LONG_LINE_MAX, &line); - if (r < 0) - return r; - if (r == 0) /* EOF without any hostname? the file is empty, let's treat that exactly like no file at all: ENOENT */ - return -ENOENT; - - p = strstrip(line); - - /* File may have empty lines or comments, ignore them */ - if (!IN_SET(*p, '\0', '#')) { - char *copy; - - hostname_cleanup(p); /* normalize the hostname */ - - if (!hostname_is_valid(p, true)) /* check that the hostname we return is valid */ - return -EBADMSG; - - copy = strdup(p); - if (!copy) - return -ENOMEM; - - *ret = copy; - return 0; - } - } -} - -int read_etc_hostname(const char *path, char **ret) { - _cleanup_fclose_ FILE *f = NULL; - - assert(ret); - - if (!path) - path = "/etc/hostname"; - - f = fopen(path, "re"); - if (!f) - return -errno; - - return read_etc_hostname_stream(f, ret); - -} -#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/hostname-util.h b/src/systemd/src/basic/hostname-util.h deleted file mode 100644 index 74948172..00000000 --- a/src/systemd/src/basic/hostname-util.h +++ /dev/null @@ -1,27 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <stdbool.h> -#include <stdio.h> - -#include "macro.h" - -bool hostname_is_set(void); - -char* gethostname_malloc(void); -int gethostname_strict(char **ret); - -bool hostname_is_valid(const char *s, bool allow_trailing_dot) _pure_; -char* hostname_cleanup(char *s); - -#define machine_name_is_valid(s) hostname_is_valid(s, false) - -bool is_localhost(const char *hostname); -bool is_gateway_hostname(const char *hostname); - -int sethostname_idempotent(const char *s); - -int shorten_overlong(const char *s, char **ret); - -int read_etc_hostname_stream(FILE *f, char **ret); -int read_etc_hostname(const char *path, char **ret); diff --git a/src/systemd/src/basic/in-addr-util.c b/src/systemd/src/basic/in-addr-util.c deleted file mode 100644 index 19d0db25..00000000 --- a/src/systemd/src/basic/in-addr-util.c +++ /dev/null @@ -1,602 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <arpa/inet.h> -#include <endian.h> -#include <errno.h> -#include <net/if.h> -#include <stdint.h> -#include <stdlib.h> - -#include "alloc-util.h" -#include "in-addr-util.h" -#include "macro.h" -#include "parse-util.h" -#include "util.h" - -bool in4_addr_is_null(const struct in_addr *a) { - assert(a); - - return a->s_addr == 0; -} - -int in_addr_is_null(int family, const union in_addr_union *u) { - assert(u); - - if (family == AF_INET) - return in4_addr_is_null(&u->in); - - if (family == AF_INET6) - return IN6_IS_ADDR_UNSPECIFIED(&u->in6); - - return -EAFNOSUPPORT; -} - -bool in4_addr_is_link_local(const struct in_addr *a) { - assert(a); - - return (be32toh(a->s_addr) & UINT32_C(0xFFFF0000)) == (UINT32_C(169) << 24 | UINT32_C(254) << 16); -} - -int in_addr_is_link_local(int family, const union in_addr_union *u) { - assert(u); - - if (family == AF_INET) - return in4_addr_is_link_local(&u->in); - - if (family == AF_INET6) - return IN6_IS_ADDR_LINKLOCAL(&u->in6); - - return -EAFNOSUPPORT; -} - -int in_addr_is_multicast(int family, const union in_addr_union *u) { - assert(u); - - if (family == AF_INET) - return IN_MULTICAST(be32toh(u->in.s_addr)); - - if (family == AF_INET6) - return IN6_IS_ADDR_MULTICAST(&u->in6); - - return -EAFNOSUPPORT; -} - -bool in4_addr_is_localhost(const struct in_addr *a) { - assert(a); - - /* All of 127.x.x.x is localhost. */ - return (be32toh(a->s_addr) & UINT32_C(0xFF000000)) == UINT32_C(127) << 24; -} - -int in_addr_is_localhost(int family, const union in_addr_union *u) { - assert(u); - - if (family == AF_INET) - return in4_addr_is_localhost(&u->in); - - if (family == AF_INET6) - return IN6_IS_ADDR_LOOPBACK(&u->in6); - - return -EAFNOSUPPORT; -} - -int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b) { - assert(a); - assert(b); - - if (family == AF_INET) - return a->in.s_addr == b->in.s_addr; - - if (family == AF_INET6) - return - a->in6.s6_addr32[0] == b->in6.s6_addr32[0] && - a->in6.s6_addr32[1] == b->in6.s6_addr32[1] && - a->in6.s6_addr32[2] == b->in6.s6_addr32[2] && - a->in6.s6_addr32[3] == b->in6.s6_addr32[3]; - - return -EAFNOSUPPORT; -} - -int in_addr_prefix_intersect( - int family, - const union in_addr_union *a, - unsigned aprefixlen, - const union in_addr_union *b, - unsigned bprefixlen) { - - unsigned m; - - assert(a); - assert(b); - - /* Checks whether there are any addresses that are in both - * networks */ - - m = MIN(aprefixlen, bprefixlen); - - if (family == AF_INET) { - uint32_t x, nm; - - x = be32toh(a->in.s_addr ^ b->in.s_addr); - nm = (m == 0) ? 0 : 0xFFFFFFFFUL << (32 - m); - - return (x & nm) == 0; - } - - if (family == AF_INET6) { - unsigned i; - - if (m > 128) - m = 128; - - for (i = 0; i < 16; i++) { - uint8_t x, nm; - - x = a->in6.s6_addr[i] ^ b->in6.s6_addr[i]; - - if (m < 8) - nm = 0xFF << (8 - m); - else - nm = 0xFF; - - if ((x & nm) != 0) - return 0; - - if (m > 8) - m -= 8; - else - m = 0; - } - - return 1; - } - - return -EAFNOSUPPORT; -} - -int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen) { - assert(u); - - /* Increases the network part of an address by one. Returns - * positive it that succeeds, or 0 if this overflows. */ - - if (prefixlen <= 0) - return 0; - - if (family == AF_INET) { - uint32_t c, n; - - if (prefixlen > 32) - prefixlen = 32; - - c = be32toh(u->in.s_addr); - n = c + (1UL << (32 - prefixlen)); - if (n < c) - return 0; - n &= 0xFFFFFFFFUL << (32 - prefixlen); - - u->in.s_addr = htobe32(n); - return 1; - } - - if (family == AF_INET6) { - struct in6_addr add = {}, result; - uint8_t overflow = 0; - unsigned i; - - if (prefixlen > 128) - prefixlen = 128; - - /* First calculate what we have to add */ - add.s6_addr[(prefixlen-1) / 8] = 1 << (7 - (prefixlen-1) % 8); - - for (i = 16; i > 0; i--) { - unsigned j = i - 1; - - result.s6_addr[j] = u->in6.s6_addr[j] + add.s6_addr[j] + overflow; - overflow = (result.s6_addr[j] < u->in6.s6_addr[j]); - } - - if (overflow) - return 0; - - u->in6 = result; - return 1; - } - - return -EAFNOSUPPORT; -} - -int in_addr_to_string(int family, const union in_addr_union *u, char **ret) { - char *x; - size_t l; - - assert(u); - assert(ret); - - if (family == AF_INET) - l = INET_ADDRSTRLEN; - else if (family == AF_INET6) - l = INET6_ADDRSTRLEN; - else - return -EAFNOSUPPORT; - - x = new(char, l); - if (!x) - return -ENOMEM; - - errno = 0; - if (!inet_ntop(family, u, x, l)) { - free(x); - return errno > 0 ? -errno : -EINVAL; - } - - *ret = x; - return 0; -} - -int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret) { - size_t l; - char *x; - int r; - - assert(u); - assert(ret); - - /* Much like in_addr_to_string(), but optionally appends the zone interface index to the address, to properly - * handle IPv6 link-local addresses. */ - - if (family != AF_INET6) - goto fallback; - if (ifindex <= 0) - goto fallback; - - r = in_addr_is_link_local(family, u); - if (r < 0) - return r; - if (r == 0) - goto fallback; - - l = INET6_ADDRSTRLEN + 1 + DECIMAL_STR_MAX(ifindex) + 1; - x = new(char, l); - if (!x) - return -ENOMEM; - - errno = 0; - if (!inet_ntop(family, u, x, l)) { - free(x); - return errno > 0 ? -errno : -EINVAL; - } - - sprintf(strchr(x, 0), "%%%i", ifindex); - *ret = x; - - return 0; - -fallback: - return in_addr_to_string(family, u, ret); -} - -int in_addr_from_string(int family, const char *s, union in_addr_union *ret) { - union in_addr_union buffer; - assert(s); - - if (!IN_SET(family, AF_INET, AF_INET6)) - return -EAFNOSUPPORT; - - errno = 0; - if (inet_pton(family, s, ret ?: &buffer) <= 0) - return errno > 0 ? -errno : -EINVAL; - - return 0; -} - -int in_addr_from_string_auto(const char *s, int *ret_family, union in_addr_union *ret) { - int r; - - assert(s); - - r = in_addr_from_string(AF_INET, s, ret); - if (r >= 0) { - if (ret_family) - *ret_family = AF_INET; - return 0; - } - - r = in_addr_from_string(AF_INET6, s, ret); - if (r >= 0) { - if (ret_family) - *ret_family = AF_INET6; - return 0; - } - - return -EINVAL; -} - -#if 0 /* NM_IGNORED */ -int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex) { - const char *suffix; - int r, ifi = 0; - - assert(s); - assert(family); - assert(ret); - - /* Similar to in_addr_from_string_auto() but also parses an optionally appended IPv6 zone suffix ("scope id") - * if one is found. */ - - suffix = strchr(s, '%'); - if (suffix) { - - if (ifindex) { - /* If we shall return the interface index, try to parse it */ - r = parse_ifindex(suffix + 1, &ifi); - if (r < 0) { - unsigned u; - - u = if_nametoindex(suffix + 1); - if (u <= 0) - return -errno; - - ifi = (int) u; - } - } - - s = strndupa(s, suffix - s); - } - - r = in_addr_from_string_auto(s, family, ret); - if (r < 0) - return r; - - if (ifindex) - *ifindex = ifi; - - return r; -} -#endif /* NM_IGNORED */ - -unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr) { - assert(addr); - - return 32 - u32ctz(be32toh(addr->s_addr)); -} - -struct in_addr* in4_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen) { - assert(addr); - assert(prefixlen <= 32); - - /* Shifting beyond 32 is not defined, handle this specially. */ - if (prefixlen == 0) - addr->s_addr = 0; - else - addr->s_addr = htobe32((0xffffffff << (32 - prefixlen)) & 0xffffffff); - - return addr; -} - -int in4_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen) { - uint8_t msb_octet = *(uint8_t*) addr; - - /* addr may not be aligned, so make sure we only access it byte-wise */ - - assert(addr); - assert(prefixlen); - - if (msb_octet < 128) - /* class A, leading bits: 0 */ - *prefixlen = 8; - else if (msb_octet < 192) - /* class B, leading bits 10 */ - *prefixlen = 16; - else if (msb_octet < 224) - /* class C, leading bits 110 */ - *prefixlen = 24; - else - /* class D or E, no default prefixlen */ - return -ERANGE; - - return 0; -} - -int in4_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask) { - unsigned char prefixlen; - int r; - - assert(addr); - assert(mask); - - r = in4_addr_default_prefixlen(addr, &prefixlen); - if (r < 0) - return r; - - in4_addr_prefixlen_to_netmask(mask, prefixlen); - return 0; -} - -#if 0 /* NM_IGNORED */ -int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen) { - assert(addr); - - if (family == AF_INET) { - struct in_addr mask; - - if (!in4_addr_prefixlen_to_netmask(&mask, prefixlen)) - return -EINVAL; - - addr->in.s_addr &= mask.s_addr; - return 0; - } - - if (family == AF_INET6) { - unsigned i; - - for (i = 0; i < 16; i++) { - uint8_t mask; - - if (prefixlen >= 8) { - mask = 0xFF; - prefixlen -= 8; - } else { - mask = 0xFF << (8 - prefixlen); - prefixlen = 0; - } - - addr->in6.s6_addr[i] &= mask; - } - - return 0; - } - - return -EAFNOSUPPORT; -} - -int in_addr_prefix_covers(int family, - const union in_addr_union *prefix, - unsigned char prefixlen, - const union in_addr_union *address) { - - union in_addr_union masked_prefix, masked_address; - int r; - - assert(prefix); - assert(address); - - masked_prefix = *prefix; - r = in_addr_mask(family, &masked_prefix, prefixlen); - if (r < 0) - return r; - - masked_address = *address; - r = in_addr_mask(family, &masked_address, prefixlen); - if (r < 0) - return r; - - return in_addr_equal(family, &masked_prefix, &masked_address); -} - -int in_addr_parse_prefixlen(int family, const char *p, unsigned char *ret) { - uint8_t u; - int r; - - if (!IN_SET(family, AF_INET, AF_INET6)) - return -EAFNOSUPPORT; - - r = safe_atou8(p, &u); - if (r < 0) - return r; - - if (u > FAMILY_ADDRESS_SIZE(family) * 8) - return -ERANGE; - - *ret = u; - return 0; -} - -int in_addr_prefix_from_string( - const char *p, - int family, - union in_addr_union *ret_prefix, - unsigned char *ret_prefixlen) { - - union in_addr_union buffer; - const char *e, *l; - unsigned char k; - int r; - - assert(p); - - if (!IN_SET(family, AF_INET, AF_INET6)) - return -EAFNOSUPPORT; - - e = strchr(p, '/'); - if (e) - l = strndupa(p, e - p); - else - l = p; - - r = in_addr_from_string(family, l, &buffer); - if (r < 0) - return r; - - if (e) { - r = in_addr_parse_prefixlen(family, e+1, &k); - if (r < 0) - return r; - } else - k = FAMILY_ADDRESS_SIZE(family) * 8; - - if (ret_prefix) - *ret_prefix = buffer; - if (ret_prefixlen) - *ret_prefixlen = k; - - return 0; -} - -int in_addr_prefix_from_string_auto( - const char *p, - int *ret_family, - union in_addr_union *ret_prefix, - unsigned char *ret_prefixlen) { - - union in_addr_union buffer; - const char *e, *l; - unsigned char k; - int family, r; - - assert(p); - - e = strchr(p, '/'); - if (e) - l = strndupa(p, e - p); - else - l = p; - - r = in_addr_from_string_auto(l, &family, &buffer); - if (r < 0) - return r; - - if (e) { - r = in_addr_parse_prefixlen(family, e+1, &k); - if (r < 0) - return r; - } else - k = FAMILY_ADDRESS_SIZE(family) * 8; - - if (ret_family) - *ret_family = family; - if (ret_prefix) - *ret_prefix = buffer; - if (ret_prefixlen) - *ret_prefixlen = k; - - return 0; - -} - -void in_addr_data_hash_func(const void *p, struct siphash *state) { - const struct in_addr_data *a = p; - - siphash24_compress(&a->family, sizeof(a->family), state); - siphash24_compress(&a->address, FAMILY_ADDRESS_SIZE(a->family), state); -} - -int in_addr_data_compare_func(const void *a, const void *b) { - const struct in_addr_data *x = a, *y = b; - int r; - - r = CMP(x->family, y->family); - if (r != 0) - return r; - - return memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family)); -} - -const struct hash_ops in_addr_data_hash_ops = { - .hash = in_addr_data_hash_func, - .compare = in_addr_data_compare_func, -}; -#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/in-addr-util.h b/src/systemd/src/basic/in-addr-util.h deleted file mode 100644 index e4be30dc..00000000 --- a/src/systemd/src/basic/in-addr-util.h +++ /dev/null @@ -1,60 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <netinet/in.h> -#include <stddef.h> -#include <sys/socket.h> - -#include "hash-funcs.h" -#include "macro.h" -#include "util.h" - -union in_addr_union { - struct in_addr in; - struct in6_addr in6; -}; - -struct in_addr_data { - int family; - union in_addr_union address; -}; - -bool in4_addr_is_null(const struct in_addr *a); -int in_addr_is_null(int family, const union in_addr_union *u); - -int in_addr_is_multicast(int family, const union in_addr_union *u); - -bool in4_addr_is_link_local(const struct in_addr *a); -int in_addr_is_link_local(int family, const union in_addr_union *u); - -bool in4_addr_is_localhost(const struct in_addr *a); -int in_addr_is_localhost(int family, const union in_addr_union *u); - -int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b); -int in_addr_prefix_intersect(int family, const union in_addr_union *a, unsigned aprefixlen, const union in_addr_union *b, unsigned bprefixlen); -int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen); -int in_addr_to_string(int family, const union in_addr_union *u, char **ret); -int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret); -int in_addr_from_string(int family, const char *s, union in_addr_union *ret); -int in_addr_from_string_auto(const char *s, int *ret_family, union in_addr_union *ret); -int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex); -unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr); -struct in_addr* in4_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen); -int in4_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen); -int in4_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask); -int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen); -int in_addr_prefix_covers(int family, const union in_addr_union *prefix, unsigned char prefixlen, const union in_addr_union *address); -int in_addr_parse_prefixlen(int family, const char *p, unsigned char *ret); -int in_addr_prefix_from_string(const char *p, int family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen); -int in_addr_prefix_from_string_auto(const char *p, int *ret_family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen); - -static inline size_t FAMILY_ADDRESS_SIZE(int family) { - assert(IN_SET(family, AF_INET, AF_INET6)); - return family == AF_INET6 ? 16 : 4; -} - -#define IN_ADDR_NULL ((union in_addr_union) {}) - -void in_addr_data_hash_func(const void *p, struct siphash *state); -int in_addr_data_compare_func(const void *a, const void *b); -extern const struct hash_ops in_addr_data_hash_ops; diff --git a/src/systemd/src/basic/io-util.c b/src/systemd/src/basic/io-util.c deleted file mode 100644 index 6f46c944..00000000 --- a/src/systemd/src/basic/io-util.c +++ /dev/null @@ -1,256 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <errno.h> -#include <limits.h> -#include <poll.h> -#include <stdio.h> -#include <time.h> -#include <unistd.h> - -#include "io-util.h" -#include "time-util.h" - -int flush_fd(int fd) { - struct pollfd pollfd = { - .fd = fd, - .events = POLLIN, - }; - int count = 0; - - /* Read from the specified file descriptor, until POLLIN is not set anymore, throwing away everything - * read. Note that some file descriptors (notable IP sockets) will trigger POLLIN even when no data can be read - * (due to IP packet checksum mismatches), hence this function is only safe to be non-blocking if the fd used - * was set to non-blocking too. */ - - for (;;) { - char buf[LINE_MAX]; - ssize_t l; - int r; - - r = poll(&pollfd, 1, 0); - if (r < 0) { - if (errno == EINTR) - continue; - - return -errno; - - } else if (r == 0) - return count; - - l = read(fd, buf, sizeof(buf)); - if (l < 0) { - - if (errno == EINTR) - continue; - - if (errno == EAGAIN) - return count; - - return -errno; - } else if (l == 0) - return count; - - count += (int) l; - } -} - -ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) { - uint8_t *p = buf; - ssize_t n = 0; - - assert(fd >= 0); - assert(buf); - - /* If called with nbytes == 0, let's call read() at least - * once, to validate the operation */ - - if (nbytes > (size_t) SSIZE_MAX) - return -EINVAL; - - do { - ssize_t k; - - k = read(fd, p, nbytes); - if (k < 0) { - if (errno == EINTR) - continue; - - if (errno == EAGAIN && do_poll) { - - /* We knowingly ignore any return value here, - * and expect that any error/EOF is reported - * via read() */ - - (void) fd_wait_for_event(fd, POLLIN, USEC_INFINITY); - continue; - } - - return n > 0 ? n : -errno; - } - - if (k == 0) - return n; - - assert((size_t) k <= nbytes); - - p += k; - nbytes -= k; - n += k; - } while (nbytes > 0); - - return n; -} - -int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll) { - ssize_t n; - - n = loop_read(fd, buf, nbytes, do_poll); - if (n < 0) - return (int) n; - if ((size_t) n != nbytes) - return -EIO; - - return 0; -} - -int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) { - const uint8_t *p = buf; - - assert(fd >= 0); - assert(buf); - - if (_unlikely_(nbytes > (size_t) SSIZE_MAX)) - return -EINVAL; - - do { - ssize_t k; - - k = write(fd, p, nbytes); - if (k < 0) { - if (errno == EINTR) - continue; - - if (errno == EAGAIN && do_poll) { - /* We knowingly ignore any return value here, - * and expect that any error/EOF is reported - * via write() */ - - (void) fd_wait_for_event(fd, POLLOUT, USEC_INFINITY); - continue; - } - - return -errno; - } - - if (_unlikely_(nbytes > 0 && k == 0)) /* Can't really happen */ - return -EIO; - - assert((size_t) k <= nbytes); - - p += k; - nbytes -= k; - } while (nbytes > 0); - - return 0; -} - -int pipe_eof(int fd) { - struct pollfd pollfd = { - .fd = fd, - .events = POLLIN|POLLHUP, - }; - - int r; - - r = poll(&pollfd, 1, 0); - if (r < 0) - return -errno; - - if (r == 0) - return 0; - - return pollfd.revents & POLLHUP; -} - -int fd_wait_for_event(int fd, int event, usec_t t) { - - struct pollfd pollfd = { - .fd = fd, - .events = event, - }; - - struct timespec ts; - int r; - - r = ppoll(&pollfd, 1, t == USEC_INFINITY ? NULL : timespec_store(&ts, t), NULL); - if (r < 0) - return -errno; - if (r == 0) - return 0; - - return pollfd.revents; -} - -static size_t nul_length(const uint8_t *p, size_t sz) { - size_t n = 0; - - while (sz > 0) { - if (*p != 0) - break; - - n++; - p++; - sz--; - } - - return n; -} - -ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length) { - const uint8_t *q, *w, *e; - ssize_t l; - - q = w = p; - e = q + sz; - while (q < e) { - size_t n; - - n = nul_length(q, e - q); - - /* If there are more than the specified run length of - * NUL bytes, or if this is the beginning or the end - * of the buffer, then seek instead of write */ - if ((n > run_length) || - (n > 0 && q == p) || - (n > 0 && q + n >= e)) { - if (q > w) { - l = write(fd, w, q - w); - if (l < 0) - return -errno; - if (l != q -w) - return -EIO; - } - - if (lseek(fd, n, SEEK_CUR) == (off_t) -1) - return -errno; - - q += n; - w = q; - } else if (n > 0) - q += n; - else - q++; - } - - if (q > w) { - l = write(fd, w, q - w); - if (l < 0) - return -errno; - if (l != q - w) - return -EIO; - } - - return q - (const uint8_t*) p; -} diff --git a/src/systemd/src/basic/io-util.h b/src/systemd/src/basic/io-util.h deleted file mode 100644 index ed189b58..00000000 --- a/src/systemd/src/basic/io-util.h +++ /dev/null @@ -1,73 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> -#include <sys/types.h> -#include <sys/uio.h> - -#include "macro.h" -#include "time-util.h" - -int flush_fd(int fd); - -ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll); -int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll); -int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll); - -int pipe_eof(int fd); - -int fd_wait_for_event(int fd, int event, usec_t timeout); - -ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length); - -static inline size_t IOVEC_TOTAL_SIZE(const struct iovec *i, size_t n) { - size_t j, r = 0; - - for (j = 0; j < n; j++) - r += i[j].iov_len; - - return r; -} - -static inline size_t IOVEC_INCREMENT(struct iovec *i, size_t n, size_t k) { - size_t j; - - for (j = 0; j < n; j++) { - size_t sub; - - if (_unlikely_(k <= 0)) - break; - - sub = MIN(i[j].iov_len, k); - i[j].iov_len -= sub; - i[j].iov_base = (uint8_t*) i[j].iov_base + sub; - k -= sub; - } - - return k; -} - -static inline bool FILE_SIZE_VALID(uint64_t l) { - /* ftruncate() and friends take an unsigned file size, but actually cannot deal with file sizes larger than - * 2^63 since the kernel internally handles it as signed value. This call allows checking for this early. */ - - return (l >> 63) == 0; -} - -static inline bool FILE_SIZE_VALID_OR_INFINITY(uint64_t l) { - - /* Same as above, but allows one extra value: -1 as indication for infinity. */ - - if (l == (uint64_t) -1) - return true; - - return FILE_SIZE_VALID(l); - -} - -#define IOVEC_INIT(base, len) { .iov_base = (base), .iov_len = (len) } -#define IOVEC_MAKE(base, len) (struct iovec) IOVEC_INIT(base, len) -#define IOVEC_INIT_STRING(string) IOVEC_INIT((char*) string, strlen(string)) -#define IOVEC_MAKE_STRING(string) (struct iovec) IOVEC_INIT_STRING(string) diff --git a/src/systemd/src/basic/list.h b/src/systemd/src/basic/list.h deleted file mode 100644 index 643e0bea..00000000 --- a/src/systemd/src/basic/list.h +++ /dev/null @@ -1,169 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -/* The head of the linked list. Use this in the structure that shall - * contain the head of the linked list */ -#define LIST_HEAD(t,name) \ - t *name - -/* The pointers in the linked list's items. Use this in the item structure */ -#define LIST_FIELDS(t,name) \ - t *name##_next, *name##_prev - -/* Initialize the list's head */ -#define LIST_HEAD_INIT(head) \ - do { \ - (head) = NULL; } \ - while (false) - -/* Initialize a list item */ -#define LIST_INIT(name,item) \ - do { \ - typeof(*(item)) *_item = (item); \ - assert(_item); \ - _item->name##_prev = _item->name##_next = NULL; \ - } while (false) - -/* Prepend an item to the list */ -#define LIST_PREPEND(name,head,item) \ - do { \ - typeof(*(head)) **_head = &(head), *_item = (item); \ - assert(_item); \ - if ((_item->name##_next = *_head)) \ - _item->name##_next->name##_prev = _item; \ - _item->name##_prev = NULL; \ - *_head = _item; \ - } while (false) - -/* Append an item to the list */ -#define LIST_APPEND(name,head,item) \ - do { \ - typeof(*(head)) *_tail; \ - LIST_FIND_TAIL(name,head,_tail); \ - LIST_INSERT_AFTER(name,head,_tail,item); \ - } while (false) - -/* Remove an item from the list */ -#define LIST_REMOVE(name,head,item) \ - do { \ - typeof(*(head)) **_head = &(head), *_item = (item); \ - assert(_item); \ - if (_item->name##_next) \ - _item->name##_next->name##_prev = _item->name##_prev; \ - if (_item->name##_prev) \ - _item->name##_prev->name##_next = _item->name##_next; \ - else { \ - assert(*_head == _item); \ - *_head = _item->name##_next; \ - } \ - _item->name##_next = _item->name##_prev = NULL; \ - } while (false) - -/* Find the head of the list */ -#define LIST_FIND_HEAD(name,item,head) \ - do { \ - typeof(*(item)) *_item = (item); \ - if (!_item) \ - (head) = NULL; \ - else { \ - while (_item->name##_prev) \ - _item = _item->name##_prev; \ - (head) = _item; \ - } \ - } while (false) - -/* Find the tail of the list */ -#define LIST_FIND_TAIL(name,item,tail) \ - do { \ - typeof(*(item)) *_item = (item); \ - if (!_item) \ - (tail) = NULL; \ - else { \ - while (_item->name##_next) \ - _item = _item->name##_next; \ - (tail) = _item; \ - } \ - } while (false) - -/* Insert an item after another one (a = where, b = what) */ -#define LIST_INSERT_AFTER(name,head,a,b) \ - do { \ - typeof(*(head)) **_head = &(head), *_a = (a), *_b = (b); \ - assert(_b); \ - if (!_a) { \ - if ((_b->name##_next = *_head)) \ - _b->name##_next->name##_prev = _b; \ - _b->name##_prev = NULL; \ - *_head = _b; \ - } else { \ - if ((_b->name##_next = _a->name##_next)) \ - _b->name##_next->name##_prev = _b; \ - _b->name##_prev = _a; \ - _a->name##_next = _b; \ - } \ - } while (false) - -/* Insert an item before another one (a = where, b = what) */ -#define LIST_INSERT_BEFORE(name,head,a,b) \ - do { \ - typeof(*(head)) **_head = &(head), *_a = (a), *_b = (b); \ - assert(_b); \ - if (!_a) { \ - if (!*_head) { \ - _b->name##_next = NULL; \ - _b->name##_prev = NULL; \ - *_head = _b; \ - } else { \ - typeof(*(head)) *_tail = (head); \ - while (_tail->name##_next) \ - _tail = _tail->name##_next; \ - _b->name##_next = NULL; \ - _b->name##_prev = _tail; \ - _tail->name##_next = _b; \ - } \ - } else { \ - if ((_b->name##_prev = _a->name##_prev)) \ - _b->name##_prev->name##_next = _b; \ - else \ - *_head = _b; \ - _b->name##_next = _a; \ - _a->name##_prev = _b; \ - } \ - } while (false) - -#define LIST_JUST_US(name,item) \ - (!(item)->name##_prev && !(item)->name##_next) \ - -#define LIST_FOREACH(name,i,head) \ - for ((i) = (head); (i); (i) = (i)->name##_next) - -#define LIST_FOREACH_SAFE(name,i,n,head) \ - for ((i) = (head); (i) && (((n) = (i)->name##_next), 1); (i) = (n)) - -#define LIST_FOREACH_BEFORE(name,i,p) \ - for ((i) = (p)->name##_prev; (i); (i) = (i)->name##_prev) - -#define LIST_FOREACH_AFTER(name,i,p) \ - for ((i) = (p)->name##_next; (i); (i) = (i)->name##_next) - -/* Iterate through all the members of the list p is included in, but skip over p */ -#define LIST_FOREACH_OTHERS(name,i,p) \ - for (({ \ - (i) = (p); \ - while ((i) && (i)->name##_prev) \ - (i) = (i)->name##_prev; \ - if ((i) == (p)) \ - (i) = (p)->name##_next; \ - }); \ - (i); \ - (i) = (i)->name##_next == (p) ? (p)->name##_next : (i)->name##_next) - -/* Loop starting from p->next until p->prev. - p can be adjusted meanwhile. */ -#define LIST_LOOP_BUT_ONE(name,i,head,p) \ - for ((i) = (p)->name##_next ? (p)->name##_next : (head); \ - (i) != (p); \ - (i) = (i)->name##_next ? (i)->name##_next : (head)) - -#define LIST_IS_EMPTY(head) \ - (!(head)) diff --git a/src/systemd/src/basic/log.h b/src/systemd/src/basic/log.h deleted file mode 100644 index 28edd572..00000000 --- a/src/systemd/src/basic/log.h +++ /dev/null @@ -1,326 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <stdarg.h> -#include <stdbool.h> -#include <stdlib.h> -#include <syslog.h> - -#include "macro.h" - -/* Some structures we reference but don't want to pull in headers for */ -struct iovec; -struct signalfd_siginfo; - -typedef enum LogRealm { - LOG_REALM_SYSTEMD, - LOG_REALM_UDEV, - _LOG_REALM_MAX, -} LogRealm; - -#ifndef LOG_REALM -# define LOG_REALM LOG_REALM_SYSTEMD -#endif - -typedef enum LogTarget{ - LOG_TARGET_CONSOLE, - LOG_TARGET_CONSOLE_PREFIXED, - LOG_TARGET_KMSG, - LOG_TARGET_JOURNAL, - LOG_TARGET_JOURNAL_OR_KMSG, - LOG_TARGET_SYSLOG, - LOG_TARGET_SYSLOG_OR_KMSG, - LOG_TARGET_AUTO, /* console if stderr is tty, JOURNAL_OR_KMSG otherwise */ - LOG_TARGET_NULL, - _LOG_TARGET_MAX, - _LOG_TARGET_INVALID = -1 -} LogTarget; - -#define LOG_REALM_PLUS_LEVEL(realm, level) \ - ((realm) << 10 | (level)) -#define LOG_REALM_REMOVE_LEVEL(realm_level) \ - ((realm_level >> 10)) - -void log_set_target(LogTarget target); -void log_set_max_level_realm(LogRealm realm, int level); -#define log_set_max_level(level) \ - log_set_max_level_realm(LOG_REALM, (level)) - -void log_set_facility(int facility); - -int log_set_target_from_string(const char *e); -int log_set_max_level_from_string_realm(LogRealm realm, const char *e); -#define log_set_max_level_from_string(e) \ - log_set_max_level_from_string_realm(LOG_REALM, (e)) - -void log_show_color(bool b); -bool log_get_show_color(void) _pure_; -void log_show_location(bool b); -bool log_get_show_location(void) _pure_; - -int log_show_color_from_string(const char *e); -int log_show_location_from_string(const char *e); - -LogTarget log_get_target(void) _pure_; -#if 0 /* NM_IGNORED */ -int log_get_max_level_realm(LogRealm realm) _pure_; -#endif /* NM_IGNORED */ -#define log_get_max_level() \ - log_get_max_level_realm(LOG_REALM) - -/* Functions below that open and close logs or configure logging based on the - * environment should not be called from library code — this is always a job - * for the application itself. - */ - -int log_open(void); -void log_close(void); -void log_forget_fds(void); - -void log_parse_environment_realm(LogRealm realm); -#define log_parse_environment() \ - log_parse_environment_realm(LOG_REALM) - -#if 0 /* NM_IGNORED */ -int log_dispatch_internal( - int level, - int error, - const char *file, - int line, - const char *func, - const char *object_field, - const char *object, - const char *extra, - const char *extra_field, - char *buffer); - -int log_internal_realm( - int level, - int error, - const char *file, - int line, - const char *func, - const char *format, ...) _printf_(6,7); -#endif /* NM_IGNORED */ -#define log_internal(level, ...) \ - log_internal_realm(LOG_REALM_PLUS_LEVEL(LOG_REALM, (level)), __VA_ARGS__) - -#if 0 /* NM_IGNORED */ -int log_internalv_realm( - int level, - int error, - const char *file, - int line, - const char *func, - const char *format, - va_list ap) _printf_(6,0); -#define log_internalv(level, ...) \ - log_internalv_realm(LOG_REALM_PLUS_LEVEL(LOG_REALM, (level)), __VA_ARGS__) - -/* Realm is fixed to LOG_REALM_SYSTEMD for those */ -int log_object_internal( - int level, - int error, - const char *file, - int line, - const char *func, - const char *object_field, - const char *object, - const char *extra_field, - const char *extra, - const char *format, ...) _printf_(10,11); - -int log_struct_internal( - int level, - int error, - const char *file, - int line, - const char *func, - const char *format, ...) _printf_(6,0) _sentinel_; - -int log_oom_internal( - LogRealm realm, - const char *file, - int line, - const char *func); - -int log_format_iovec( - struct iovec *iovec, - size_t iovec_len, - size_t *n, - bool newline_separator, - int error, - const char *format, - va_list ap) _printf_(6, 0); - -int log_struct_iovec_internal( - int level, - int error, - const char *file, - int line, - const char *func, - const struct iovec *input_iovec, - size_t n_input_iovec); - -/* This modifies the buffer passed! */ -int log_dump_internal( - int level, - int error, - const char *file, - int line, - const char *func, - char *buffer); - -/* Logging for various assertions */ -_noreturn_ void log_assert_failed_realm( - LogRealm realm, - const char *text, - const char *file, - int line, - const char *func); -#define log_assert_failed(text, ...) \ - log_assert_failed_realm(LOG_REALM, (text), __VA_ARGS__) - -_noreturn_ void log_assert_failed_unreachable_realm( - LogRealm realm, - const char *text, - const char *file, - int line, - const char *func); -#define log_assert_failed_unreachable(text, ...) \ - log_assert_failed_unreachable_realm(LOG_REALM, (text), __VA_ARGS__) - -void log_assert_failed_return_realm( - LogRealm realm, - const char *text, - const char *file, - int line, - const char *func); -#define log_assert_failed_return(text, ...) \ - log_assert_failed_return_realm(LOG_REALM, (text), __VA_ARGS__) - -#define log_dispatch(level, error, buffer) \ - log_dispatch_internal(level, error, __FILE__, __LINE__, __func__, NULL, NULL, NULL, NULL, buffer) -#endif /* NM_IGNORED */ - -/* Logging with level */ -#define log_full_errno_realm(realm, level, error, ...) \ - ({ \ - int _level = (level), _e = (error), _realm = (realm); \ - (log_get_max_level_realm(_realm) >= LOG_PRI(_level)) \ - ? log_internal_realm(LOG_REALM_PLUS_LEVEL(_realm, _level), _e, \ - __FILE__, __LINE__, __func__, __VA_ARGS__) \ - : -abs(_e); \ - }) - -#define log_full_errno(level, error, ...) \ - log_full_errno_realm(LOG_REALM, (level), (error), __VA_ARGS__) - -#define log_full(level, ...) log_full_errno((level), 0, __VA_ARGS__) - -int log_emergency_level(void); - -/* Normal logging */ -#define log_debug(...) log_full(LOG_DEBUG, __VA_ARGS__) -#define log_info(...) log_full(LOG_INFO, __VA_ARGS__) -#define log_notice(...) log_full(LOG_NOTICE, __VA_ARGS__) -#define log_warning(...) log_full(LOG_WARNING, __VA_ARGS__) -#define log_error(...) log_full(LOG_ERR, __VA_ARGS__) -#define log_emergency(...) log_full(log_emergency_level(), __VA_ARGS__) - -/* Logging triggered by an errno-like error */ -#define log_debug_errno(error, ...) log_full_errno(LOG_DEBUG, error, __VA_ARGS__) -#define log_info_errno(error, ...) log_full_errno(LOG_INFO, error, __VA_ARGS__) -#define log_notice_errno(error, ...) log_full_errno(LOG_NOTICE, error, __VA_ARGS__) -#define log_warning_errno(error, ...) log_full_errno(LOG_WARNING, error, __VA_ARGS__) -#define log_error_errno(error, ...) log_full_errno(LOG_ERR, error, __VA_ARGS__) -#define log_emergency_errno(error, ...) log_full_errno(log_emergency_level(), error, __VA_ARGS__) - -#ifdef LOG_TRACE -# define log_trace(...) log_debug(__VA_ARGS__) -#else -# define log_trace(...) do {} while (0) -#endif - -/* Structured logging */ -#define log_struct_errno(level, error, ...) \ - log_struct_internal(LOG_REALM_PLUS_LEVEL(LOG_REALM, level), \ - error, __FILE__, __LINE__, __func__, __VA_ARGS__, NULL) -#define log_struct(level, ...) log_struct_errno(level, 0, __VA_ARGS__) - -#define log_struct_iovec_errno(level, error, iovec, n_iovec) \ - log_struct_iovec_internal(LOG_REALM_PLUS_LEVEL(LOG_REALM, level), \ - error, __FILE__, __LINE__, __func__, iovec, n_iovec) -#define log_struct_iovec(level, iovec, n_iovec) log_struct_iovec_errno(level, 0, iovec, n_iovec) - -/* This modifies the buffer passed! */ -#define log_dump(level, buffer) \ - log_dump_internal(LOG_REALM_PLUS_LEVEL(LOG_REALM, level), \ - 0, __FILE__, __LINE__, __func__, buffer) - -#define log_oom() log_oom_internal(LOG_REALM, __FILE__, __LINE__, __func__) - -bool log_on_console(void) _pure_; - -const char *log_target_to_string(LogTarget target) _const_; -LogTarget log_target_from_string(const char *s) _pure_; - -/* Helper to prepare various field for structured logging */ -#define LOG_MESSAGE(fmt, ...) "MESSAGE=" fmt, ##__VA_ARGS__ - -void log_received_signal(int level, const struct signalfd_siginfo *si); - -/* If turned on, any requests for a log target involving "syslog" will be implicitly upgraded to the equivalent journal target */ -void log_set_upgrade_syslog_to_journal(bool b); - -/* If turned on, and log_open() is called, we'll not use STDERR_FILENO for logging ever, but rather open /dev/console */ -void log_set_always_reopen_console(bool b); - -/* If turned on, we'll open the log stream implicitly if needed on each individual log call. This is normally not - * desired as we want to reuse our logging streams. It is useful however */ -void log_set_open_when_needed(bool b); - -/* If turned on, then we'll never use IPC-based logging, i.e. never log to syslog or the journal. We'll only log to - * stderr, the console or kmsg */ -void log_set_prohibit_ipc(bool b); - -int log_dup_console(void); - -int log_syntax_internal( - const char *unit, - int level, - const char *config_file, - unsigned config_line, - int error, - const char *file, - int line, - const char *func, - const char *format, ...) _printf_(9, 10); - -int log_syntax_invalid_utf8_internal( - const char *unit, - int level, - const char *config_file, - unsigned config_line, - const char *file, - int line, - const char *func, - const char *rvalue); - -#define log_syntax(unit, level, config_file, config_line, error, ...) \ - ({ \ - int _level = (level), _e = (error); \ - (log_get_max_level() >= LOG_PRI(_level)) \ - ? log_syntax_internal(unit, _level, config_file, config_line, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \ - : -abs(_e); \ - }) - -#define log_syntax_invalid_utf8(unit, level, config_file, config_line, rvalue) \ - ({ \ - int _level = (level); \ - (log_get_max_level() >= LOG_PRI(_level)) \ - ? log_syntax_invalid_utf8_internal(unit, _level, config_file, config_line, __FILE__, __LINE__, __func__, rvalue) \ - : -EINVAL; \ - }) - -#define DEBUG_LOGGING _unlikely_(log_get_max_level() >= LOG_DEBUG) diff --git a/src/systemd/src/basic/macro.h b/src/systemd/src/basic/macro.h deleted file mode 100644 index 22193fe3..00000000 --- a/src/systemd/src/basic/macro.h +++ /dev/null @@ -1,476 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <inttypes.h> -#include <stdbool.h> -#include <sys/param.h> -#include <sys/sysmacros.h> -#include <sys/types.h> - -#define _printf_(a, b) __attribute__ ((format (printf, a, b))) -#ifdef __clang__ -# define _alloc_(...) -#else -# define _alloc_(...) __attribute__ ((alloc_size(__VA_ARGS__))) -#endif -#define _sentinel_ __attribute__ ((sentinel)) -#define _unused_ __attribute__ ((unused)) -#define _destructor_ __attribute__ ((destructor)) -#define _pure_ __attribute__ ((pure)) -#define _const_ __attribute__ ((const)) -#define _deprecated_ __attribute__ ((deprecated)) -#define _packed_ __attribute__ ((packed)) -#define _malloc_ __attribute__ ((malloc)) -#define _weak_ __attribute__ ((weak)) -#define _likely_(x) (__builtin_expect(!!(x), 1)) -#define _unlikely_(x) (__builtin_expect(!!(x), 0)) -#define _public_ __attribute__ ((visibility("default"))) -#define _hidden_ __attribute__ ((visibility("hidden"))) -#define _weakref_(x) __attribute__((weakref(#x))) -#define _alignas_(x) __attribute__((aligned(__alignof(x)))) -#define _cleanup_(x) __attribute__((cleanup(x))) -#if __GNUC__ >= 7 -#define _fallthrough_ __attribute__((fallthrough)) -#else -#define _fallthrough_ -#endif -/* Define C11 noreturn without <stdnoreturn.h> and even on older gcc - * compiler versions */ -#ifndef _noreturn_ -#if __STDC_VERSION__ >= 201112L -#define _noreturn_ _Noreturn -#else -#define _noreturn_ __attribute__((noreturn)) -#endif -#endif - -#if !defined(HAS_FEATURE_MEMORY_SANITIZER) -# if defined(__has_feature) -# if __has_feature(memory_sanitizer) -# define HAS_FEATURE_MEMORY_SANITIZER 1 -# endif -# endif -# if !defined(HAS_FEATURE_MEMORY_SANITIZER) -# define HAS_FEATURE_MEMORY_SANITIZER 0 -# endif -#endif - -#if (defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) || defined (__clang__) -/* Temporarily disable some warnings */ -#define DISABLE_WARNING_DECLARATION_AFTER_STATEMENT \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wdeclaration-after-statement\"") - -#define DISABLE_WARNING_FORMAT_NONLITERAL \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wformat-nonliteral\"") - -#define DISABLE_WARNING_MISSING_PROTOTYPES \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wmissing-prototypes\"") - -#define DISABLE_WARNING_NONNULL \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wnonnull\"") - -#define DISABLE_WARNING_SHADOW \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wshadow\"") - -#define DISABLE_WARNING_INCOMPATIBLE_POINTER_TYPES \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wincompatible-pointer-types\"") - -#define REENABLE_WARNING \ - _Pragma("GCC diagnostic pop") -#else -#define DISABLE_WARNING_DECLARATION_AFTER_STATEMENT -#define DISABLE_WARNING_FORMAT_NONLITERAL -#define DISABLE_WARNING_MISSING_PROTOTYPES -#define DISABLE_WARNING_NONNULL -#define DISABLE_WARNING_SHADOW -#define REENABLE_WARNING -#endif - -/* automake test harness */ -#define EXIT_TEST_SKIP 77 - -#define XSTRINGIFY(x) #x -#define STRINGIFY(x) XSTRINGIFY(x) - -#define XCONCATENATE(x, y) x ## y -#define CONCATENATE(x, y) XCONCATENATE(x, y) - -#define UNIQ_T(x, uniq) CONCATENATE(__unique_prefix_, CONCATENATE(x, uniq)) -#define UNIQ __COUNTER__ - -/* builtins */ -#if __SIZEOF_INT__ == 4 -#define BUILTIN_FFS_U32(x) __builtin_ffs(x); -#elif __SIZEOF_LONG__ == 4 -#define BUILTIN_FFS_U32(x) __builtin_ffsl(x); -#else -#error "neither int nor long are four bytes long?!?" -#endif - -/* Rounds up */ - -#define ALIGN4(l) (((l) + 3) & ~3) -#define ALIGN8(l) (((l) + 7) & ~7) - -#if __SIZEOF_POINTER__ == 8 -#define ALIGN(l) ALIGN8(l) -#elif __SIZEOF_POINTER__ == 4 -#define ALIGN(l) ALIGN4(l) -#else -#error "Wut? Pointers are neither 4 nor 8 bytes long?" -#endif - -#define ALIGN_PTR(p) ((void*) ALIGN((unsigned long) (p))) -#define ALIGN4_PTR(p) ((void*) ALIGN4((unsigned long) (p))) -#define ALIGN8_PTR(p) ((void*) ALIGN8((unsigned long) (p))) - -static inline size_t ALIGN_TO(size_t l, size_t ali) { - return ((l + ali - 1) & ~(ali - 1)); -} - -#define ALIGN_TO_PTR(p, ali) ((void*) ALIGN_TO((unsigned long) (p), (ali))) - -/* align to next higher power-of-2 (except for: 0 => 0, overflow => 0) */ -static inline unsigned long ALIGN_POWER2(unsigned long u) { - /* clz(0) is undefined */ - if (u == 1) - return 1; - - /* left-shift overflow is undefined */ - if (__builtin_clzl(u - 1UL) < 1) - return 0; - - return 1UL << (sizeof(u) * 8 - __builtin_clzl(u - 1UL)); -} - -#ifndef __COVERITY__ -# define VOID_0 ((void)0) -#else -# define VOID_0 ((void*)0) -#endif - -#define ELEMENTSOF(x) \ - (__builtin_choose_expr( \ - !__builtin_types_compatible_p(typeof(x), typeof(&*(x))), \ - sizeof(x)/sizeof((x)[0]), \ - VOID_0)) - -/* - * STRLEN - return the length of a string literal, minus the trailing NUL byte. - * Contrary to strlen(), this is a constant expression. - * @x: a string literal. - */ -#define STRLEN(x) (sizeof(""x"") - 1) - -/* - * container_of - cast a member of a structure out to the containing structure - * @ptr: the pointer to the member. - * @type: the type of the container struct this is embedded in. - * @member: the name of the member within the struct. - */ -#define container_of(ptr, type, member) __container_of(UNIQ, (ptr), type, member) -#define __container_of(uniq, ptr, type, member) \ - ({ \ - const typeof( ((type*)0)->member ) *UNIQ_T(A, uniq) = (ptr); \ - (type*)( (char *)UNIQ_T(A, uniq) - offsetof(type, member) ); \ - }) - -#undef MAX -#define MAX(a, b) __MAX(UNIQ, (a), UNIQ, (b)) -#define __MAX(aq, a, bq, b) \ - ({ \ - const typeof(a) UNIQ_T(A, aq) = (a); \ - const typeof(b) UNIQ_T(B, bq) = (b); \ - UNIQ_T(A, aq) > UNIQ_T(B, bq) ? UNIQ_T(A, aq) : UNIQ_T(B, bq); \ - }) - -/* evaluates to (void) if _A or _B are not constant or of different types */ -#define CONST_MAX(_A, _B) \ - (__builtin_choose_expr( \ - __builtin_constant_p(_A) && \ - __builtin_constant_p(_B) && \ - __builtin_types_compatible_p(typeof(_A), typeof(_B)), \ - ((_A) > (_B)) ? (_A) : (_B), \ - VOID_0)) - -/* takes two types and returns the size of the larger one */ -#define MAXSIZE(A, B) (sizeof(union _packed_ { typeof(A) a; typeof(B) b; })) - -#define MAX3(x, y, z) \ - ({ \ - const typeof(x) _c = MAX(x, y); \ - MAX(_c, z); \ - }) - -#undef MIN -#define MIN(a, b) __MIN(UNIQ, (a), UNIQ, (b)) -#define __MIN(aq, a, bq, b) \ - ({ \ - const typeof(a) UNIQ_T(A, aq) = (a); \ - const typeof(b) UNIQ_T(B, bq) = (b); \ - UNIQ_T(A, aq) < UNIQ_T(B, bq) ? UNIQ_T(A, aq) : UNIQ_T(B, bq); \ - }) - -#define MIN3(x, y, z) \ - ({ \ - const typeof(x) _c = MIN(x, y); \ - MIN(_c, z); \ - }) - -#define LESS_BY(a, b) __LESS_BY(UNIQ, (a), UNIQ, (b)) -#define __LESS_BY(aq, a, bq, b) \ - ({ \ - const typeof(a) UNIQ_T(A, aq) = (a); \ - const typeof(b) UNIQ_T(B, bq) = (b); \ - UNIQ_T(A, aq) > UNIQ_T(B, bq) ? UNIQ_T(A, aq) - UNIQ_T(B, bq) : 0; \ - }) - -#define CMP(a, b) __CMP(UNIQ, (a), UNIQ, (b)) -#define __CMP(aq, a, bq, b) \ - ({ \ - const typeof(a) UNIQ_T(A, aq) = (a); \ - const typeof(b) UNIQ_T(B, bq) = (b); \ - UNIQ_T(A, aq) < UNIQ_T(B, bq) ? -1 : \ - UNIQ_T(A, aq) > UNIQ_T(B, bq) ? 1 : 0; \ - }) - -#undef CLAMP -#define CLAMP(x, low, high) __CLAMP(UNIQ, (x), UNIQ, (low), UNIQ, (high)) -#define __CLAMP(xq, x, lowq, low, highq, high) \ - ({ \ - const typeof(x) UNIQ_T(X, xq) = (x); \ - const typeof(low) UNIQ_T(LOW, lowq) = (low); \ - const typeof(high) UNIQ_T(HIGH, highq) = (high); \ - UNIQ_T(X, xq) > UNIQ_T(HIGH, highq) ? \ - UNIQ_T(HIGH, highq) : \ - UNIQ_T(X, xq) < UNIQ_T(LOW, lowq) ? \ - UNIQ_T(LOW, lowq) : \ - UNIQ_T(X, xq); \ - }) - -/* [(x + y - 1) / y] suffers from an integer overflow, even though the - * computation should be possible in the given type. Therefore, we use - * [x / y + !!(x % y)]. Note that on "Real CPUs" a division returns both the - * quotient and the remainder, so both should be equally fast. */ -#define DIV_ROUND_UP(_x, _y) \ - ({ \ - const typeof(_x) __x = (_x); \ - const typeof(_y) __y = (_y); \ - (__x / __y + !!(__x % __y)); \ - }) - -#ifdef __COVERITY__ - -/* Use special definitions of assertion macros in order to prevent - * false positives of ASSERT_SIDE_EFFECT on Coverity static analyzer - * for uses of assert_se() and assert_return(). - * - * These definitions make expression go through a (trivial) function - * call to ensure they are not discarded. Also use ! or !! to ensure - * the boolean expressions are seen as such. - * - * This technique has been described and recommended in: - * https://community.synopsys.com/s/question/0D534000046Yuzb/suppressing-assertsideeffect-for-functions-that-allow-for-sideeffects - */ - -extern void __coverity_panic__(void); - -static inline int __coverity_check__(int condition) { - return condition; -} - -#define assert_message_se(expr, message) \ - do { \ - if (__coverity_check__(!(expr))) \ - __coverity_panic__(); \ - } while (false) - -#define assert_log(expr, message) __coverity_check__(!!(expr)) - -#else /* ! __COVERITY__ */ - -#define assert_message_se(expr, message) \ - do { \ - if (_unlikely_(!(expr))) \ - log_assert_failed(message, __FILE__, __LINE__, __PRETTY_FUNCTION__); \ - } while (false) - -#define assert_log(expr, message) ((_likely_(expr)) \ - ? (true) \ - : (log_assert_failed_return(message, __FILE__, __LINE__, __PRETTY_FUNCTION__), false)) - -#endif /* __COVERITY__ */ - -#define assert_se(expr) assert_message_se(expr, #expr) - -/* We override the glibc assert() here. */ -#undef assert -#ifdef NDEBUG -#define assert(expr) do {} while (false) -#else -#define assert(expr) assert_message_se(expr, #expr) -#endif - -#define assert_not_reached(t) \ - do { \ - log_assert_failed_unreachable(t, __FILE__, __LINE__, __PRETTY_FUNCTION__); \ - } while (false) - -#if defined(static_assert) -/* static_assert() is sometimes defined in a way that trips up - * -Wdeclaration-after-statement, hence let's temporarily turn off - * this warning around it. */ -#define assert_cc(expr) \ - DISABLE_WARNING_DECLARATION_AFTER_STATEMENT; \ - static_assert(expr, #expr); \ - REENABLE_WARNING -#else -#define assert_cc(expr) \ - DISABLE_WARNING_DECLARATION_AFTER_STATEMENT; \ - struct CONCATENATE(_assert_struct_, __COUNTER__) { \ - char x[(expr) ? 0 : -1]; \ - }; \ - REENABLE_WARNING -#endif - -#define assert_return(expr, r) \ - do { \ - if (!assert_log(expr, #expr)) \ - return (r); \ - } while (false) - -#define assert_return_errno(expr, r, err) \ - do { \ - if (!assert_log(expr, #expr)) { \ - errno = err; \ - return (r); \ - } \ - } while (false) - -#define PTR_TO_INT(p) ((int) ((intptr_t) (p))) -#define INT_TO_PTR(u) ((void *) ((intptr_t) (u))) -#define PTR_TO_UINT(p) ((unsigned int) ((uintptr_t) (p))) -#define UINT_TO_PTR(u) ((void *) ((uintptr_t) (u))) - -#define PTR_TO_LONG(p) ((long) ((intptr_t) (p))) -#define LONG_TO_PTR(u) ((void *) ((intptr_t) (u))) -#define PTR_TO_ULONG(p) ((unsigned long) ((uintptr_t) (p))) -#define ULONG_TO_PTR(u) ((void *) ((uintptr_t) (u))) - -#define PTR_TO_INT32(p) ((int32_t) ((intptr_t) (p))) -#define INT32_TO_PTR(u) ((void *) ((intptr_t) (u))) -#define PTR_TO_UINT32(p) ((uint32_t) ((uintptr_t) (p))) -#define UINT32_TO_PTR(u) ((void *) ((uintptr_t) (u))) - -#define PTR_TO_INT64(p) ((int64_t) ((intptr_t) (p))) -#define INT64_TO_PTR(u) ((void *) ((intptr_t) (u))) -#define PTR_TO_UINT64(p) ((uint64_t) ((uintptr_t) (p))) -#define UINT64_TO_PTR(u) ((void *) ((uintptr_t) (u))) - -#define PTR_TO_SIZE(p) ((size_t) ((uintptr_t) (p))) -#define SIZE_TO_PTR(u) ((void *) ((uintptr_t) (u))) - -#define CHAR_TO_STR(x) ((char[2]) { x, 0 }) - -#define char_array_0(x) x[sizeof(x)-1] = 0; - -/* Returns the number of chars needed to format variables of the - * specified type as a decimal string. Adds in extra space for a - * negative '-' prefix (hence works correctly on signed - * types). Includes space for the trailing NUL. */ -#define DECIMAL_STR_MAX(type) \ - (2+(sizeof(type) <= 1 ? 3 : \ - sizeof(type) <= 2 ? 5 : \ - sizeof(type) <= 4 ? 10 : \ - sizeof(type) <= 8 ? 20 : sizeof(int[-2*(sizeof(type) > 8)]))) - -#define DECIMAL_STR_WIDTH(x) \ - ({ \ - typeof(x) _x_ = (x); \ - unsigned ans = 1; \ - while ((_x_ /= 10) != 0) \ - ans++; \ - ans; \ - }) - -#define SET_FLAG(v, flag, b) \ - (v) = (b) ? ((v) | (flag)) : ((v) & ~(flag)) -#define FLAGS_SET(v, flags) \ - (((v) & (flags)) == (flags)) - -#define CASE_F(X) case X: -#define CASE_F_1(CASE, X) CASE_F(X) -#define CASE_F_2(CASE, X, ...) CASE(X) CASE_F_1(CASE, __VA_ARGS__) -#define CASE_F_3(CASE, X, ...) CASE(X) CASE_F_2(CASE, __VA_ARGS__) -#define CASE_F_4(CASE, X, ...) CASE(X) CASE_F_3(CASE, __VA_ARGS__) -#define CASE_F_5(CASE, X, ...) CASE(X) CASE_F_4(CASE, __VA_ARGS__) -#define CASE_F_6(CASE, X, ...) CASE(X) CASE_F_5(CASE, __VA_ARGS__) -#define CASE_F_7(CASE, X, ...) CASE(X) CASE_F_6(CASE, __VA_ARGS__) -#define CASE_F_8(CASE, X, ...) CASE(X) CASE_F_7(CASE, __VA_ARGS__) -#define CASE_F_9(CASE, X, ...) CASE(X) CASE_F_8(CASE, __VA_ARGS__) -#define CASE_F_10(CASE, X, ...) CASE(X) CASE_F_9(CASE, __VA_ARGS__) -#define CASE_F_11(CASE, X, ...) CASE(X) CASE_F_10(CASE, __VA_ARGS__) -#define CASE_F_12(CASE, X, ...) CASE(X) CASE_F_11(CASE, __VA_ARGS__) -#define CASE_F_13(CASE, X, ...) CASE(X) CASE_F_12(CASE, __VA_ARGS__) -#define CASE_F_14(CASE, X, ...) CASE(X) CASE_F_13(CASE, __VA_ARGS__) -#define CASE_F_15(CASE, X, ...) CASE(X) CASE_F_14(CASE, __VA_ARGS__) -#define CASE_F_16(CASE, X, ...) CASE(X) CASE_F_15(CASE, __VA_ARGS__) -#define CASE_F_17(CASE, X, ...) CASE(X) CASE_F_16(CASE, __VA_ARGS__) -#define CASE_F_18(CASE, X, ...) CASE(X) CASE_F_17(CASE, __VA_ARGS__) -#define CASE_F_19(CASE, X, ...) CASE(X) CASE_F_18(CASE, __VA_ARGS__) -#define CASE_F_20(CASE, X, ...) CASE(X) CASE_F_19(CASE, __VA_ARGS__) - -#define GET_CASE_F(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,NAME,...) NAME -#define FOR_EACH_MAKE_CASE(...) \ - GET_CASE_F(__VA_ARGS__,CASE_F_20,CASE_F_19,CASE_F_18,CASE_F_17,CASE_F_16,CASE_F_15,CASE_F_14,CASE_F_13,CASE_F_12,CASE_F_11, \ - CASE_F_10,CASE_F_9,CASE_F_8,CASE_F_7,CASE_F_6,CASE_F_5,CASE_F_4,CASE_F_3,CASE_F_2,CASE_F_1) \ - (CASE_F,__VA_ARGS__) - -#define IN_SET(x, ...) \ - ({ \ - bool _found = false; \ - /* If the build breaks in the line below, you need to extend the case macros */ \ - static _unused_ char _static_assert__macros_need_to_be_extended[20 - sizeof((int[]){__VA_ARGS__})/sizeof(int)]; \ - switch(x) { \ - FOR_EACH_MAKE_CASE(__VA_ARGS__) \ - _found = true; \ - break; \ - default: \ - break; \ - } \ - _found; \ - }) - -#define SWAP_TWO(x, y) do { \ - typeof(x) _t = (x); \ - (x) = (y); \ - (y) = (_t); \ - } while (false) - -/* Define C11 thread_local attribute even on older gcc compiler - * version */ -#ifndef thread_local -/* - * Don't break on glibc < 2.16 that doesn't define __STDC_NO_THREADS__ - * see http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53769 - */ -#if __STDC_VERSION__ >= 201112L && !(defined(__STDC_NO_THREADS__) || (defined(__GNU_LIBRARY__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 16)) -#define thread_local _Thread_local -#else -#define thread_local __thread -#endif -#endif - -#define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func) \ - static inline void func##p(type *p) { \ - if (*p) \ - func(*p); \ - } - -#include "log.h" diff --git a/src/systemd/src/basic/mempool.c b/src/systemd/src/basic/mempool.c deleted file mode 100644 index dd1b1e5f..00000000 --- a/src/systemd/src/basic/mempool.c +++ /dev/null @@ -1,87 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <stdint.h> -#include <stdlib.h> - -#include "macro.h" -#include "mempool.h" -#include "util.h" - -struct pool { - struct pool *next; - size_t n_tiles; - size_t n_used; -}; - -void* mempool_alloc_tile(struct mempool *mp) { - size_t i; - - /* When a tile is released we add it to the list and simply - * place the next pointer at its offset 0. */ - - assert(mp->tile_size >= sizeof(void*)); - assert(mp->at_least > 0); - - if (mp->freelist) { - void *r; - - r = mp->freelist; - mp->freelist = * (void**) mp->freelist; - return r; - } - - if (_unlikely_(!mp->first_pool) || - _unlikely_(mp->first_pool->n_used >= mp->first_pool->n_tiles)) { - size_t size, n; - struct pool *p; - - n = mp->first_pool ? mp->first_pool->n_tiles : 0; - n = MAX(mp->at_least, n * 2); - size = PAGE_ALIGN(ALIGN(sizeof(struct pool)) + n*mp->tile_size); - n = (size - ALIGN(sizeof(struct pool))) / mp->tile_size; - - p = malloc(size); - if (!p) - return NULL; - - p->next = mp->first_pool; - p->n_tiles = n; - p->n_used = 0; - - mp->first_pool = p; - } - - i = mp->first_pool->n_used++; - - return ((uint8_t*) mp->first_pool) + ALIGN(sizeof(struct pool)) + i*mp->tile_size; -} - -void* mempool_alloc0_tile(struct mempool *mp) { - void *p; - - p = mempool_alloc_tile(mp); - if (p) - memzero(p, mp->tile_size); - return p; -} - -void mempool_free_tile(struct mempool *mp, void *p) { - * (void**) p = mp->freelist; - mp->freelist = p; -} - -#if VALGRIND - -void mempool_drop(struct mempool *mp) { - struct pool *p = mp->first_pool; - while (p) { - struct pool *n; - n = p->next; - free(p); - p = n; - } -} - -#endif diff --git a/src/systemd/src/basic/mempool.h b/src/systemd/src/basic/mempool.h deleted file mode 100644 index 4098535c..00000000 --- a/src/systemd/src/basic/mempool.h +++ /dev/null @@ -1,27 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <stddef.h> - -struct pool; - -struct mempool { - struct pool *first_pool; - void *freelist; - size_t tile_size; - unsigned at_least; -}; - -void* mempool_alloc_tile(struct mempool *mp); -void* mempool_alloc0_tile(struct mempool *mp); -void mempool_free_tile(struct mempool *mp, void *p); - -#define DEFINE_MEMPOOL(pool_name, tile_type, alloc_at_least) \ -static struct mempool pool_name = { \ - .tile_size = sizeof(tile_type), \ - .at_least = alloc_at_least, \ -} - -#if VALGRIND -void mempool_drop(struct mempool *mp); -#endif diff --git a/src/systemd/src/basic/parse-util.c b/src/systemd/src/basic/parse-util.c deleted file mode 100644 index b0ccce28..00000000 --- a/src/systemd/src/basic/parse-util.c +++ /dev/null @@ -1,749 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <errno.h> -#include <inttypes.h> -#include <locale.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/socket.h> - -#include "alloc-util.h" -#include "errno-list.h" -#include "extract-word.h" -#include "locale-util.h" -#include "macro.h" -#include "missing.h" -#include "parse-util.h" -#include "process-util.h" -#include "string-util.h" - -int parse_boolean(const char *v) { - assert(v); - - if (streq(v, "1") || strcaseeq(v, "yes") || strcaseeq(v, "y") || strcaseeq(v, "true") || strcaseeq(v, "t") || strcaseeq(v, "on")) - return 1; - else if (streq(v, "0") || strcaseeq(v, "no") || strcaseeq(v, "n") || strcaseeq(v, "false") || strcaseeq(v, "f") || strcaseeq(v, "off")) - return 0; - - return -EINVAL; -} - -#if 0 /* NM_IGNORED */ -int parse_pid(const char *s, pid_t* ret_pid) { - unsigned long ul = 0; - pid_t pid; - int r; - - assert(s); - assert(ret_pid); - - r = safe_atolu(s, &ul); - if (r < 0) - return r; - - pid = (pid_t) ul; - - if ((unsigned long) pid != ul) - return -ERANGE; - - if (!pid_is_valid(pid)) - return -ERANGE; - - *ret_pid = pid; - return 0; -} - -int parse_mode(const char *s, mode_t *ret) { - char *x; - long l; - - assert(s); - assert(ret); - - s += strspn(s, WHITESPACE); - if (s[0] == '-') - return -ERANGE; - - errno = 0; - l = strtol(s, &x, 8); - if (errno > 0) - return -errno; - if (!x || x == s || *x != 0) - return -EINVAL; - if (l < 0 || l > 07777) - return -ERANGE; - - *ret = (mode_t) l; - return 0; -} - -int parse_ifindex(const char *s, int *ret) { - int ifi, r; - - r = safe_atoi(s, &ifi); - if (r < 0) - return r; - if (ifi <= 0) - return -EINVAL; - - *ret = ifi; - return 0; -} - -int parse_mtu(int family, const char *s, uint32_t *ret) { - uint64_t u; - size_t m; - int r; - - r = parse_size(s, 1024, &u); - if (r < 0) - return r; - - if (u > UINT32_MAX) - return -ERANGE; - - if (family == AF_INET6) - m = IPV6_MIN_MTU; /* This is 1280 */ - else - m = IPV4_MIN_MTU; /* For all other protocols, including 'unspecified' we assume the IPv4 minimal MTU */ - - if (u < m) - return -ERANGE; - - *ret = (uint32_t) u; - return 0; -} - -int parse_size(const char *t, uint64_t base, uint64_t *size) { - - /* Soo, sometimes we want to parse IEC binary suffixes, and - * sometimes SI decimal suffixes. This function can parse - * both. Which one is the right way depends on the - * context. Wikipedia suggests that SI is customary for - * hardware metrics and network speeds, while IEC is - * customary for most data sizes used by software and volatile - * (RAM) memory. Hence be careful which one you pick! - * - * In either case we use just K, M, G as suffix, and not Ki, - * Mi, Gi or so (as IEC would suggest). That's because that's - * frickin' ugly. But this means you really need to make sure - * to document which base you are parsing when you use this - * call. */ - - struct table { - const char *suffix; - unsigned long long factor; - }; - - static const struct table iec[] = { - { "E", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL*1024ULL }, - { "P", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL }, - { "T", 1024ULL*1024ULL*1024ULL*1024ULL }, - { "G", 1024ULL*1024ULL*1024ULL }, - { "M", 1024ULL*1024ULL }, - { "K", 1024ULL }, - { "B", 1ULL }, - { "", 1ULL }, - }; - - static const struct table si[] = { - { "E", 1000ULL*1000ULL*1000ULL*1000ULL*1000ULL*1000ULL }, - { "P", 1000ULL*1000ULL*1000ULL*1000ULL*1000ULL }, - { "T", 1000ULL*1000ULL*1000ULL*1000ULL }, - { "G", 1000ULL*1000ULL*1000ULL }, - { "M", 1000ULL*1000ULL }, - { "K", 1000ULL }, - { "B", 1ULL }, - { "", 1ULL }, - }; - - const struct table *table; - const char *p; - unsigned long long r = 0; - unsigned n_entries, start_pos = 0; - - assert(t); - assert(IN_SET(base, 1000, 1024)); - assert(size); - - if (base == 1000) { - table = si; - n_entries = ELEMENTSOF(si); - } else { - table = iec; - n_entries = ELEMENTSOF(iec); - } - - p = t; - do { - unsigned long long l, tmp; - double frac = 0; - char *e; - unsigned i; - - p += strspn(p, WHITESPACE); - - errno = 0; - l = strtoull(p, &e, 10); - if (errno > 0) - return -errno; - if (e == p) - return -EINVAL; - if (*p == '-') - return -ERANGE; - - if (*e == '.') { - e++; - - /* strtoull() itself would accept space/+/- */ - if (*e >= '0' && *e <= '9') { - unsigned long long l2; - char *e2; - - l2 = strtoull(e, &e2, 10); - if (errno > 0) - return -errno; - - /* Ignore failure. E.g. 10.M is valid */ - frac = l2; - for (; e < e2; e++) - frac /= 10; - } - } - - e += strspn(e, WHITESPACE); - - for (i = start_pos; i < n_entries; i++) - if (startswith(e, table[i].suffix)) - break; - - if (i >= n_entries) - return -EINVAL; - - if (l + (frac > 0) > ULLONG_MAX / table[i].factor) - return -ERANGE; - - tmp = l * table[i].factor + (unsigned long long) (frac * table[i].factor); - if (tmp > ULLONG_MAX - r) - return -ERANGE; - - r += tmp; - if ((unsigned long long) (uint64_t) r != r) - return -ERANGE; - - p = e + strlen(table[i].suffix); - - start_pos = i + 1; - - } while (*p); - - *size = r; - - return 0; -} - -int parse_range(const char *t, unsigned *lower, unsigned *upper) { - _cleanup_free_ char *word = NULL; - unsigned l, u; - int r; - - assert(lower); - assert(upper); - - /* Extract the lower bound. */ - r = extract_first_word(&t, &word, "-", EXTRACT_DONT_COALESCE_SEPARATORS); - if (r < 0) - return r; - if (r == 0) - return -EINVAL; - - r = safe_atou(word, &l); - if (r < 0) - return r; - - /* Check for the upper bound and extract it if needed */ - if (!t) - /* Single number with no dashes. */ - u = l; - else if (!*t) - /* Trailing dash is an error. */ - return -EINVAL; - else { - r = safe_atou(t, &u); - if (r < 0) - return r; - } - - *lower = l; - *upper = u; - return 0; -} - -int parse_errno(const char *t) { - int r, e; - - assert(t); - - r = errno_from_name(t); - if (r > 0) - return r; - - r = safe_atoi(t, &e); - if (r < 0) - return r; - - /* 0 is also allowed here */ - if (!errno_is_valid(e) && e != 0) - return -ERANGE; - - return e; -} - -int parse_syscall_and_errno(const char *in, char **name, int *error) { - _cleanup_free_ char *n = NULL; - char *p; - int e = -1; - - assert(in); - assert(name); - assert(error); - - /* - * This parse "syscall:errno" like "uname:EILSEQ", "@sync:255". - * If errno is omitted, then error is set to -1. - * Empty syscall name is not allowed. - * Here, we do not check that the syscall name is valid or not. - */ - - p = strchr(in, ':'); - if (p) { - e = parse_errno(p + 1); - if (e < 0) - return e; - - n = strndup(in, p - in); - } else - n = strdup(in); - - if (!n) - return -ENOMEM; - - if (isempty(n)) - return -EINVAL; - - *error = e; - *name = TAKE_PTR(n); - - return 0; -} - -char *format_bytes(char *buf, size_t l, uint64_t t) { - unsigned i; - - /* This only does IEC units so far */ - - static const struct { - const char *suffix; - uint64_t factor; - } table[] = { - { "E", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) }, - { "P", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) }, - { "T", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) }, - { "G", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) }, - { "M", UINT64_C(1024)*UINT64_C(1024) }, - { "K", UINT64_C(1024) }, - }; - - if (t == (uint64_t) -1) - return NULL; - - for (i = 0; i < ELEMENTSOF(table); i++) { - - if (t >= table[i].factor) { - snprintf(buf, l, - "%" PRIu64 ".%" PRIu64 "%s", - t / table[i].factor, - ((t*UINT64_C(10)) / table[i].factor) % UINT64_C(10), - table[i].suffix); - - goto finish; - } - } - - snprintf(buf, l, "%" PRIu64 "B", t); - -finish: - buf[l-1] = 0; - return buf; - -} -#endif /* NM_IGNORED */ - -int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) { - char *x = NULL; - unsigned long l; - - assert(s); - assert(ret_u); - assert(base <= 16); - - /* strtoul() is happy to parse negative values, and silently - * converts them to unsigned values without generating an - * error. We want a clean error, hence let's look for the "-" - * prefix on our own, and generate an error. But let's do so - * only after strtoul() validated that the string is clean - * otherwise, so that we return EINVAL preferably over - * ERANGE. */ - - s += strspn(s, WHITESPACE); - - errno = 0; - l = strtoul(s, &x, base); - if (errno > 0) - return -errno; - if (!x || x == s || *x != 0) - return -EINVAL; - if (s[0] == '-') - return -ERANGE; - if ((unsigned long) (unsigned) l != l) - return -ERANGE; - - *ret_u = (unsigned) l; - return 0; -} - -int safe_atoi(const char *s, int *ret_i) { - char *x = NULL; - long l; - - assert(s); - assert(ret_i); - - errno = 0; - l = strtol(s, &x, 0); - if (errno > 0) - return -errno; - if (!x || x == s || *x != 0) - return -EINVAL; - if ((long) (int) l != l) - return -ERANGE; - - *ret_i = (int) l; - return 0; -} - -int safe_atollu(const char *s, long long unsigned *ret_llu) { - char *x = NULL; - unsigned long long l; - - assert(s); - assert(ret_llu); - - s += strspn(s, WHITESPACE); - - errno = 0; - l = strtoull(s, &x, 0); - if (errno > 0) - return -errno; - if (!x || x == s || *x != 0) - return -EINVAL; - if (*s == '-') - return -ERANGE; - - *ret_llu = l; - return 0; -} - -int safe_atolli(const char *s, long long int *ret_lli) { - char *x = NULL; - long long l; - - assert(s); - assert(ret_lli); - - errno = 0; - l = strtoll(s, &x, 0); - if (errno > 0) - return -errno; - if (!x || x == s || *x != 0) - return -EINVAL; - - *ret_lli = l; - return 0; -} - -int safe_atou8(const char *s, uint8_t *ret) { - char *x = NULL; - unsigned long l; - - assert(s); - assert(ret); - - s += strspn(s, WHITESPACE); - - errno = 0; - l = strtoul(s, &x, 0); - if (errno > 0) - return -errno; - if (!x || x == s || *x != 0) - return -EINVAL; - if (s[0] == '-') - return -ERANGE; - if ((unsigned long) (uint8_t) l != l) - return -ERANGE; - - *ret = (uint8_t) l; - return 0; -} - -int safe_atou16_full(const char *s, unsigned base, uint16_t *ret) { - char *x = NULL; - unsigned long l; - - assert(s); - assert(ret); - assert(base <= 16); - - s += strspn(s, WHITESPACE); - - errno = 0; - l = strtoul(s, &x, base); - if (errno > 0) - return -errno; - if (!x || x == s || *x != 0) - return -EINVAL; - if (s[0] == '-') - return -ERANGE; - if ((unsigned long) (uint16_t) l != l) - return -ERANGE; - - *ret = (uint16_t) l; - return 0; -} - -int safe_atoi16(const char *s, int16_t *ret) { - char *x = NULL; - long l; - - assert(s); - assert(ret); - - errno = 0; - l = strtol(s, &x, 0); - if (errno > 0) - return -errno; - if (!x || x == s || *x != 0) - return -EINVAL; - if ((long) (int16_t) l != l) - return -ERANGE; - - *ret = (int16_t) l; - return 0; -} - -#if 0 /* NM_IGNORED */ -int safe_atod(const char *s, double *ret_d) { - _cleanup_(freelocalep) locale_t loc = (locale_t) 0; - char *x = NULL; - double d = 0; - - assert(s); - assert(ret_d); - - loc = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0); - if (loc == (locale_t) 0) - return -errno; - - errno = 0; - d = strtod_l(s, &x, loc); - if (errno > 0) - return -errno; - if (!x || x == s || *x != 0) - return -EINVAL; - - *ret_d = (double) d; - return 0; -} - -int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) { - size_t i; - unsigned val = 0; - const char *s; - - s = *p; - - /* accept any number of digits, strtoull is limted to 19 */ - for (i=0; i < digits; i++,s++) { - if (*s < '0' || *s > '9') { - if (i == 0) - return -EINVAL; - - /* too few digits, pad with 0 */ - for (; i < digits; i++) - val *= 10; - - break; - } - - val *= 10; - val += *s - '0'; - } - - /* maybe round up */ - if (*s >= '5' && *s <= '9') - val++; - - s += strspn(s, DIGITS); - - *p = s; - *res = val; - - return 0; -} - -int parse_percent_unbounded(const char *p) { - const char *pc, *n; - int r, v; - - pc = endswith(p, "%"); - if (!pc) - return -EINVAL; - - n = strndupa(p, pc - p); - r = safe_atoi(n, &v); - if (r < 0) - return r; - if (v < 0) - return -ERANGE; - - return v; -} - -int parse_percent(const char *p) { - int v; - - v = parse_percent_unbounded(p); - if (v > 100) - return -ERANGE; - - return v; -} - -int parse_permille_unbounded(const char *p) { - const char *pc, *pm, *dot, *n; - int r, q, v; - - pm = endswith(p, "‰"); - if (pm) { - n = strndupa(p, pm - p); - r = safe_atoi(n, &v); - if (r < 0) - return r; - if (v < 0) - return -ERANGE; - } else { - pc = endswith(p, "%"); - if (!pc) - return -EINVAL; - - dot = memchr(p, '.', pc - p); - if (dot) { - if (dot + 2 != pc) - return -EINVAL; - if (dot[1] < '0' || dot[1] > '9') - return -EINVAL; - q = dot[1] - '0'; - n = strndupa(p, dot - p); - } else { - q = 0; - n = strndupa(p, pc - p); - } - r = safe_atoi(n, &v); - if (r < 0) - return r; - if (v < 0) - return -ERANGE; - if (v > (INT_MAX - q) / 10) - return -ERANGE; - - v = v * 10 + q; - } - - return v; -} - -int parse_permille(const char *p) { - int v; - - v = parse_permille_unbounded(p); - if (v > 1000) - return -ERANGE; - - return v; -} - -int parse_nice(const char *p, int *ret) { - int n, r; - - r = safe_atoi(p, &n); - if (r < 0) - return r; - - if (!nice_is_valid(n)) - return -ERANGE; - - *ret = n; - return 0; -} - -int parse_ip_port(const char *s, uint16_t *ret) { - uint16_t l; - int r; - - r = safe_atou16(s, &l); - if (r < 0) - return r; - - if (l == 0) - return -EINVAL; - - *ret = (uint16_t) l; - - return 0; -} - -int parse_dev(const char *s, dev_t *ret) { - unsigned x, y; - dev_t d; - - if (sscanf(s, "%u:%u", &x, &y) != 2) - return -EINVAL; - - d = makedev(x, y); - if ((unsigned) major(d) != x || (unsigned) minor(d) != y) - return -EINVAL; - - *ret = d; - return 0; -} - -int parse_oom_score_adjust(const char *s, int *ret) { - int r, v; - - assert(s); - assert(ret); - - r = safe_atoi(s, &v); - if (r < 0) - return r; - - if (v < OOM_SCORE_ADJ_MIN || v > OOM_SCORE_ADJ_MAX) - return -ERANGE; - - *ret = v; - return 0; -} -#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/parse-util.h b/src/systemd/src/basic/parse-util.h deleted file mode 100644 index f3267f4c..00000000 --- a/src/systemd/src/basic/parse-util.h +++ /dev/null @@ -1,119 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <inttypes.h> -#include <limits.h> -#include <stddef.h> -#include <stdint.h> -#include <sys/types.h> - -#include "macro.h" - -#define MODE_INVALID ((mode_t) -1) - -int parse_boolean(const char *v) _pure_; -int parse_dev(const char *s, dev_t *ret); -int parse_pid(const char *s, pid_t* ret_pid); -int parse_mode(const char *s, mode_t *ret); -int parse_ifindex(const char *s, int *ret); -int parse_mtu(int family, const char *s, uint32_t *ret); - -int parse_size(const char *t, uint64_t base, uint64_t *size); -int parse_range(const char *t, unsigned *lower, unsigned *upper); -int parse_errno(const char *t); -int parse_syscall_and_errno(const char *in, char **name, int *error); - -#define FORMAT_BYTES_MAX 8 -char *format_bytes(char *buf, size_t l, uint64_t t); - -int safe_atou_full(const char *s, unsigned base, unsigned *ret_u); - -static inline int safe_atou(const char *s, unsigned *ret_u) { - return safe_atou_full(s, 0, ret_u); -} - -int safe_atoi(const char *s, int *ret_i); -int safe_atollu(const char *s, unsigned long long *ret_u); -int safe_atolli(const char *s, long long int *ret_i); - -int safe_atou8(const char *s, uint8_t *ret); - -int safe_atou16_full(const char *s, unsigned base, uint16_t *ret); - -static inline int safe_atou16(const char *s, uint16_t *ret) { - return safe_atou16_full(s, 0, ret); -} - -static inline int safe_atoux16(const char *s, uint16_t *ret) { - return safe_atou16_full(s, 16, ret); -} - -int safe_atoi16(const char *s, int16_t *ret); - -static inline int safe_atou32(const char *s, uint32_t *ret_u) { - assert_cc(sizeof(uint32_t) == sizeof(unsigned)); - return safe_atou(s, (unsigned*) ret_u); -} - -static inline int safe_atoi32(const char *s, int32_t *ret_i) { - assert_cc(sizeof(int32_t) == sizeof(int)); - return safe_atoi(s, (int*) ret_i); -} - -static inline int safe_atou64(const char *s, uint64_t *ret_u) { - assert_cc(sizeof(uint64_t) == sizeof(unsigned long long)); - return safe_atollu(s, (unsigned long long*) ret_u); -} - -static inline int safe_atoi64(const char *s, int64_t *ret_i) { - assert_cc(sizeof(int64_t) == sizeof(long long int)); - return safe_atolli(s, (long long int*) ret_i); -} - -#if LONG_MAX == INT_MAX -static inline int safe_atolu(const char *s, unsigned long *ret_u) { - assert_cc(sizeof(unsigned long) == sizeof(unsigned)); - return safe_atou(s, (unsigned*) ret_u); -} -static inline int safe_atoli(const char *s, long int *ret_u) { - assert_cc(sizeof(long int) == sizeof(int)); - return safe_atoi(s, (int*) ret_u); -} -#else -static inline int safe_atolu(const char *s, unsigned long *ret_u) { - assert_cc(sizeof(unsigned long) == sizeof(unsigned long long)); - return safe_atollu(s, (unsigned long long*) ret_u); -} -static inline int safe_atoli(const char *s, long int *ret_u) { - assert_cc(sizeof(long int) == sizeof(long long int)); - return safe_atolli(s, (long long int*) ret_u); -} -#endif - -#if SIZE_MAX == UINT_MAX -static inline int safe_atozu(const char *s, size_t *ret_u) { - assert_cc(sizeof(size_t) == sizeof(unsigned)); - return safe_atou(s, (unsigned *) ret_u); -} -#else -static inline int safe_atozu(const char *s, size_t *ret_u) { - assert_cc(sizeof(size_t) == sizeof(long unsigned)); - return safe_atolu(s, ret_u); -} -#endif - -int safe_atod(const char *s, double *ret_d); - -int parse_fractional_part_u(const char **s, size_t digits, unsigned *res); - -int parse_percent_unbounded(const char *p); -int parse_percent(const char *p); - -int parse_permille_unbounded(const char *p); -int parse_permille(const char *p); - -int parse_nice(const char *p, int *ret); - -int parse_ip_port(const char *s, uint16_t *ret); - -int parse_oom_score_adjust(const char *s, int *ret); diff --git a/src/systemd/src/basic/path-util.c b/src/systemd/src/basic/path-util.c deleted file mode 100644 index 5202eae3..00000000 --- a/src/systemd/src/basic/path-util.c +++ /dev/null @@ -1,1062 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <errno.h> -#include <limits.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/stat.h> -#include <unistd.h> - -/* When we include libgen.h because we need dirname() we immediately - * undefine basename() since libgen.h defines it as a macro to the - * POSIX version which is really broken. We prefer GNU basename(). */ -#include <libgen.h> -#undef basename - -#include "alloc-util.h" -#include "extract-word.h" -#include "fs-util.h" -#include "glob-util.h" -#include "log.h" -#include "macro.h" -#include "missing.h" -#include "parse-util.h" -#include "path-util.h" -#include "stat-util.h" -#include "string-util.h" -#include "strv.h" -#include "time-util.h" -#include "utf8.h" - -bool path_is_absolute(const char *p) { - return p[0] == '/'; -} - -#if 0 /* NM_IGNORED */ -bool is_path(const char *p) { - return !!strchr(p, '/'); -} - -int path_split_and_make_absolute(const char *p, char ***ret) { - char **l; - int r; - - assert(p); - assert(ret); - - l = strv_split(p, ":"); - if (!l) - return -ENOMEM; - - r = path_strv_make_absolute_cwd(l); - if (r < 0) { - strv_free(l); - return r; - } - - *ret = l; - return r; -} - -char *path_make_absolute(const char *p, const char *prefix) { - assert(p); - - /* Makes every item in the list an absolute path by prepending - * the prefix, if specified and necessary */ - - if (path_is_absolute(p) || isempty(prefix)) - return strdup(p); - - if (endswith(prefix, "/")) - return strjoin(prefix, p); - else - return strjoin(prefix, "/", p); -} - -int safe_getcwd(char **ret) { - char *cwd; - - cwd = get_current_dir_name(); - if (!cwd) - return negative_errno(); - - /* Let's make sure the directory is really absolute, to protect us from the logic behind - * CVE-2018-1000001 */ - if (cwd[0] != '/') { - free(cwd); - return -ENOMEDIUM; - } - - *ret = cwd; - return 0; -} - -int path_make_absolute_cwd(const char *p, char **ret) { - char *c; - int r; - - assert(p); - assert(ret); - - /* Similar to path_make_absolute(), but prefixes with the - * current working directory. */ - - if (path_is_absolute(p)) - c = strdup(p); - else { - _cleanup_free_ char *cwd = NULL; - - r = safe_getcwd(&cwd); - if (r < 0) - return r; - - c = path_join(NULL, cwd, p); - } - if (!c) - return -ENOMEM; - - *ret = c; - return 0; -} - -int path_make_relative(const char *from_dir, const char *to_path, char **_r) { - char *f, *t, *r, *p; - unsigned n_parents = 0; - - assert(from_dir); - assert(to_path); - assert(_r); - - /* Strips the common part, and adds ".." elements as necessary. */ - - if (!path_is_absolute(from_dir) || !path_is_absolute(to_path)) - return -EINVAL; - - f = strdupa(from_dir); - t = strdupa(to_path); - - path_simplify(f, true); - path_simplify(t, true); - - /* Skip the common part. */ - for (;;) { - size_t a, b; - - f += *f == '/'; - t += *t == '/'; - - if (!*f) { - if (!*t) - /* from_dir equals to_path. */ - r = strdup("."); - else - /* from_dir is a parent directory of to_path. */ - r = strdup(t); - if (!r) - return -ENOMEM; - - *_r = r; - return 0; - } - - if (!*t) - break; - - a = strcspn(f, "/"); - b = strcspn(t, "/"); - - if (a != b || memcmp(f, t, a) != 0) - break; - - f += a; - t += b; - } - - /* If we're here, then "from_dir" has one or more elements that need to - * be replaced with "..". */ - - /* Count the number of necessary ".." elements. */ - for (; *f;) { - size_t w; - - w = strcspn(f, "/"); - - /* If this includes ".." we can't do a simple series of "..", refuse */ - if (w == 2 && f[0] == '.' && f[1] == '.') - return -EINVAL; - - /* Count number of elements */ - n_parents++; - - f += w; - f += *f == '/'; - } - - r = new(char, n_parents * 3 + strlen(t) + 1); - if (!r) - return -ENOMEM; - - for (p = r; n_parents > 0; n_parents--) - p = mempcpy(p, "../", 3); - - if (*t) - strcpy(p, t); - else - /* Remove trailing slash */ - *(--p) = 0; - - *_r = r; - return 0; -} - -int path_strv_make_absolute_cwd(char **l) { - char **s; - int r; - - /* Goes through every item in the string list and makes it - * absolute. This works in place and won't rollback any - * changes on failure. */ - - STRV_FOREACH(s, l) { - char *t; - - r = path_make_absolute_cwd(*s, &t); - if (r < 0) - return r; - - path_simplify(t, false); - free_and_replace(*s, t); - } - - return 0; -} - -char **path_strv_resolve(char **l, const char *root) { - char **s; - unsigned k = 0; - bool enomem = false; - int r; - - if (strv_isempty(l)) - return l; - - /* Goes through every item in the string list and canonicalize - * the path. This works in place and won't rollback any - * changes on failure. */ - - STRV_FOREACH(s, l) { - _cleanup_free_ char *orig = NULL; - char *t, *u; - - if (!path_is_absolute(*s)) { - free(*s); - continue; - } - - if (root) { - orig = *s; - t = prefix_root(root, orig); - if (!t) { - enomem = true; - continue; - } - } else - t = *s; - - r = chase_symlinks(t, root, 0, &u); - if (r == -ENOENT) { - if (root) { - u = TAKE_PTR(orig); - free(t); - } else - u = t; - } else if (r < 0) { - free(t); - - if (r == -ENOMEM) - enomem = true; - - continue; - } else if (root) { - char *x; - - free(t); - x = path_startswith(u, root); - if (x) { - /* restore the slash if it was lost */ - if (!startswith(x, "/")) - *(--x) = '/'; - - t = strdup(x); - free(u); - if (!t) { - enomem = true; - continue; - } - u = t; - } else { - /* canonicalized path goes outside of - * prefix, keep the original path instead */ - free_and_replace(u, orig); - } - } else - free(t); - - l[k++] = u; - } - - l[k] = NULL; - - if (enomem) - return NULL; - - return l; -} - -char **path_strv_resolve_uniq(char **l, const char *root) { - - if (strv_isempty(l)) - return l; - - if (!path_strv_resolve(l, root)) - return NULL; - - return strv_uniq(l); -} -#endif /* NM_IGNORED */ - -char *path_simplify(char *path, bool kill_dots) { - char *f, *t; - bool slash = false, ignore_slash = false, absolute; - - assert(path); - - /* Removes redundant inner and trailing slashes. Also removes unnecessary dots - * if kill_dots is true. Modifies the passed string in-place. - * - * ///foo//./bar/. becomes /foo/./bar/. (if kill_dots is false) - * ///foo//./bar/. becomes /foo/bar (if kill_dots is true) - * .//./foo//./bar/. becomes ./foo/bar (if kill_dots is false) - * .//./foo//./bar/. becomes foo/bar (if kill_dots is true) - */ - - absolute = path_is_absolute(path); - - f = path; - if (kill_dots && *f == '.' && IN_SET(f[1], 0, '/')) { - ignore_slash = true; - f++; - } - - for (t = path; *f; f++) { - - if (*f == '/') { - slash = true; - continue; - } - - if (slash) { - if (kill_dots && *f == '.' && IN_SET(f[1], 0, '/')) - continue; - - slash = false; - if (ignore_slash) - ignore_slash = false; - else - *(t++) = '/'; - } - - *(t++) = *f; - } - - /* Special rule, if we are talking of the root directory, a trailing slash is good */ - if (absolute && t == path) - *(t++) = '/'; - - *t = 0; - return path; -} - -#if 0 /* NM_IGNORED */ -char* path_startswith(const char *path, const char *prefix) { - assert(path); - assert(prefix); - - /* Returns a pointer to the start of the first component after the parts matched by - * the prefix, iff - * - both paths are absolute or both paths are relative, - * and - * - each component in prefix in turn matches a component in path at the same position. - * An empty string will be returned when the prefix and path are equivalent. - * - * Returns NULL otherwise. - */ - - if ((path[0] == '/') != (prefix[0] == '/')) - return NULL; - - for (;;) { - size_t a, b; - - path += strspn(path, "/"); - prefix += strspn(prefix, "/"); - - if (*prefix == 0) - return (char*) path; - - if (*path == 0) - return NULL; - - a = strcspn(path, "/"); - b = strcspn(prefix, "/"); - - if (a != b) - return NULL; - - if (memcmp(path, prefix, a) != 0) - return NULL; - - path += a; - prefix += b; - } -} -#endif /* NM_IGNORED */ - -int path_compare(const char *a, const char *b) { - int d; - - assert(a); - assert(b); - - /* A relative path and an abolute path must not compare as equal. - * Which one is sorted before the other does not really matter. - * Here a relative path is ordered before an absolute path. */ - d = (a[0] == '/') - (b[0] == '/'); - if (d != 0) - return d; - - for (;;) { - size_t j, k; - - a += strspn(a, "/"); - b += strspn(b, "/"); - - if (*a == 0 && *b == 0) - return 0; - - /* Order prefixes first: "/foo" before "/foo/bar" */ - if (*a == 0) - return -1; - if (*b == 0) - return 1; - - j = strcspn(a, "/"); - k = strcspn(b, "/"); - - /* Alphabetical sort: "/foo/aaa" before "/foo/b" */ - d = memcmp(a, b, MIN(j, k)); - if (d != 0) - return (d > 0) - (d < 0); /* sign of d */ - - /* Sort "/foo/a" before "/foo/aaa" */ - d = (j > k) - (j < k); /* sign of (j - k) */ - if (d != 0) - return d; - - a += j; - b += k; - } -} - -bool path_equal(const char *a, const char *b) { - return path_compare(a, b) == 0; -} - -#if 0 /* NM_IGNORED */ -bool path_equal_or_files_same(const char *a, const char *b, int flags) { - return path_equal(a, b) || files_same(a, b, flags) > 0; -} - -char* path_join(const char *root, const char *path, const char *rest) { - assert(path); - - if (!isempty(root)) - return strjoin(root, endswith(root, "/") ? "" : "/", - path[0] == '/' ? path+1 : path, - rest ? (endswith(path, "/") ? "" : "/") : NULL, - rest && rest[0] == '/' ? rest+1 : rest); - else - return strjoin(path, - rest ? (endswith(path, "/") ? "" : "/") : NULL, - rest && rest[0] == '/' ? rest+1 : rest); -} - -int find_binary(const char *name, char **ret) { - int last_error, r; - const char *p; - - assert(name); - - if (is_path(name)) { - if (access(name, X_OK) < 0) - return -errno; - - if (ret) { - r = path_make_absolute_cwd(name, ret); - if (r < 0) - return r; - } - - return 0; - } - - /** - * Plain getenv, not secure_getenv, because we want - * to actually allow the user to pick the binary. - */ - p = getenv("PATH"); - if (!p) - p = DEFAULT_PATH; - - last_error = -ENOENT; - - for (;;) { - _cleanup_free_ char *j = NULL, *element = NULL; - - r = extract_first_word(&p, &element, ":", EXTRACT_RELAX|EXTRACT_DONT_COALESCE_SEPARATORS); - if (r < 0) - return r; - if (r == 0) - break; - - if (!path_is_absolute(element)) - continue; - - j = strjoin(element, "/", name); - if (!j) - return -ENOMEM; - - if (access(j, X_OK) >= 0) { - /* Found it! */ - - if (ret) { - *ret = path_simplify(j, false); - j = NULL; - } - - return 0; - } - - last_error = -errno; - } - - return last_error; -} - -bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) { - bool changed = false; - const char* const* i; - - assert(timestamp); - - if (!paths) - return false; - - STRV_FOREACH(i, paths) { - struct stat stats; - usec_t u; - - if (stat(*i, &stats) < 0) - continue; - - u = timespec_load(&stats.st_mtim); - - /* first check */ - if (*timestamp >= u) - continue; - - log_debug("timestamp of '%s' changed", *i); - - /* update timestamp */ - if (update) { - *timestamp = u; - changed = true; - } else - return true; - } - - return changed; -} - -static int binary_is_good(const char *binary) { - _cleanup_free_ char *p = NULL, *d = NULL; - int r; - - r = find_binary(binary, &p); - if (r == -ENOENT) - return 0; - if (r < 0) - return r; - - /* An fsck that is linked to /bin/true is a non-existent - * fsck */ - - r = readlink_malloc(p, &d); - if (r == -EINVAL) /* not a symlink */ - return 1; - if (r < 0) - return r; - - return !PATH_IN_SET(d, "true" - "/bin/true", - "/usr/bin/true", - "/dev/null"); -} - -int fsck_exists(const char *fstype) { - const char *checker; - - assert(fstype); - - if (streq(fstype, "auto")) - return -EINVAL; - - checker = strjoina("fsck.", fstype); - return binary_is_good(checker); -} - -int mkfs_exists(const char *fstype) { - const char *mkfs; - - assert(fstype); - - if (streq(fstype, "auto")) - return -EINVAL; - - mkfs = strjoina("mkfs.", fstype); - return binary_is_good(mkfs); -} - -char *prefix_root(const char *root, const char *path) { - char *n, *p; - size_t l; - - /* If root is passed, prefixes path with it. Otherwise returns - * it as is. */ - - assert(path); - - /* First, drop duplicate prefixing slashes from the path */ - while (path[0] == '/' && path[1] == '/') - path++; - - if (empty_or_root(root)) - return strdup(path); - - l = strlen(root) + 1 + strlen(path) + 1; - - n = new(char, l); - if (!n) - return NULL; - - p = stpcpy(n, root); - - while (p > n && p[-1] == '/') - p--; - - if (path[0] != '/') - *(p++) = '/'; - - strcpy(p, path); - return n; -} - -int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg) { - char *p; - int r; - - /* - * This function is intended to be used in command line - * parsers, to handle paths that are passed in. It makes the - * path absolute, and reduces it to NULL if omitted or - * root (the latter optionally). - * - * NOTE THAT THIS WILL FREE THE PREVIOUS ARGUMENT POINTER ON - * SUCCESS! Hence, do not pass in uninitialized pointers. - */ - - if (isempty(path)) { - *arg = mfree(*arg); - return 0; - } - - r = path_make_absolute_cwd(path, &p); - if (r < 0) - return log_error_errno(r, "Failed to parse path \"%s\" and make it absolute: %m", path); - - path_simplify(p, false); - if (suppress_root && empty_or_root(p)) - p = mfree(p); - - free_and_replace(*arg, p); - - return 0; -} - -char* dirname_malloc(const char *path) { - char *d, *dir, *dir2; - - assert(path); - - d = strdup(path); - if (!d) - return NULL; - - dir = dirname(d); - assert(dir); - - if (dir == d) - return d; - - dir2 = strdup(dir); - free(d); - - return dir2; -} - -const char *last_path_component(const char *path) { - - /* Finds the last component of the path, preserving the optional trailing slash that signifies a directory. - * - * a/b/c → c - * a/b/c/ → c/ - * x → x - * x/ → x/ - * /y → y - * /y/ → y/ - * / → / - * // → / - * /foo/a → a - * /foo/a/ → a/ - * - * Also, the empty string is mapped to itself. - * - * This is different than basename(), which returns "" when a trailing slash is present. - */ - - unsigned l, k; - - l = k = strlen(path); - if (l == 0) /* special case — an empty string */ - return path; - - while (k > 0 && path[k-1] == '/') - k--; - - if (k == 0) /* the root directory */ - return path + l - 1; - - while (k > 0 && path[k-1] != '/') - k--; - - return path + k; -} -#endif /* NM_IGNORED */ - -bool filename_is_valid(const char *p) { - const char *e; - - if (isempty(p)) - return false; - - if (dot_or_dot_dot(p)) - return false; - - e = strchrnul(p, '/'); - if (*e != 0) - return false; - - if (e - p > FILENAME_MAX) - return false; - - return true; -} - -bool path_is_normalized(const char *p) { - - if (isempty(p)) - return false; - - if (dot_or_dot_dot(p)) - return false; - - if (startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../")) - return false; - - if (strlen(p)+1 > PATH_MAX) - return false; - - if (startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./")) - return false; - - if (strstr(p, "//")) - return false; - - return true; -} - -#if 0 /* NM_IGNORED */ -char *file_in_same_dir(const char *path, const char *filename) { - char *e, *ret; - size_t k; - - assert(path); - assert(filename); - - /* This removes the last component of path and appends - * filename, unless the latter is absolute anyway or the - * former isn't */ - - if (path_is_absolute(filename)) - return strdup(filename); - - e = strrchr(path, '/'); - if (!e) - return strdup(filename); - - k = strlen(filename); - ret = new(char, (e + 1 - path) + k + 1); - if (!ret) - return NULL; - - memcpy(mempcpy(ret, path, e + 1 - path), filename, k + 1); - return ret; -} - -bool hidden_or_backup_file(const char *filename) { - const char *p; - - assert(filename); - - if (filename[0] == '.' || - streq(filename, "lost+found") || - streq(filename, "aquota.user") || - streq(filename, "aquota.group") || - endswith(filename, "~")) - return true; - - p = strrchr(filename, '.'); - if (!p) - return false; - - /* Please, let's not add more entries to the list below. If external projects think it's a good idea to come up - * with always new suffixes and that everybody else should just adjust to that, then it really should be on - * them. Hence, in future, let's not add any more entries. Instead, let's ask those packages to instead adopt - * one of the generic suffixes/prefixes for hidden files or backups, possibly augmented with an additional - * string. Specifically: there's now: - * - * The generic suffixes "~" and ".bak" for backup files - * The generic prefix "." for hidden files - * - * Thus, if a new package manager "foopkg" wants its own set of ".foopkg-new", ".foopkg-old", ".foopkg-dist" - * or so registered, let's refuse that and ask them to use ".foopkg.new", ".foopkg.old" or ".foopkg~" instead. - */ - - return STR_IN_SET(p + 1, - "rpmnew", - "rpmsave", - "rpmorig", - "dpkg-old", - "dpkg-new", - "dpkg-tmp", - "dpkg-dist", - "dpkg-bak", - "dpkg-backup", - "dpkg-remove", - "ucf-new", - "ucf-old", - "ucf-dist", - "swp", - "bak", - "old", - "new"); -} - -bool is_device_path(const char *path) { - - /* Returns true on paths that likely refer to a device, either by path in sysfs or to something in /dev */ - - return PATH_STARTSWITH_SET(path, "/dev/", "/sys/"); -} - -bool valid_device_node_path(const char *path) { - - /* Some superficial checks whether the specified path is a valid device node path, all without looking at the - * actual device node. */ - - if (!PATH_STARTSWITH_SET(path, "/dev/", "/run/systemd/inaccessible/")) - return false; - - if (endswith(path, "/")) /* can't be a device node if it ends in a slash */ - return false; - - return path_is_normalized(path); -} - -bool valid_device_allow_pattern(const char *path) { - assert(path); - - /* Like valid_device_node_path(), but also allows full-subsystem expressions, like DeviceAllow= and DeviceDeny= - * accept it */ - - if (startswith(path, "block-") || - startswith(path, "char-")) - return true; - - return valid_device_node_path(path); -} - -int systemd_installation_has_version(const char *root, unsigned minimal_version) { - const char *pattern; - int r; - - /* Try to guess if systemd installation is later than the specified version. This - * is hacky and likely to yield false negatives, particularly if the installation - * is non-standard. False positives should be relatively rare. - */ - - NULSTR_FOREACH(pattern, - /* /lib works for systems without usr-merge, and for systems with a sane - * usr-merge, where /lib is a symlink to /usr/lib. /usr/lib is necessary - * for Gentoo which does a merge without making /lib a symlink. - */ - "lib/systemd/libsystemd-shared-*.so\0" - "lib64/systemd/libsystemd-shared-*.so\0" - "usr/lib/systemd/libsystemd-shared-*.so\0" - "usr/lib64/systemd/libsystemd-shared-*.so\0") { - - _cleanup_strv_free_ char **names = NULL; - _cleanup_free_ char *path = NULL; - char *c, **name; - - path = prefix_root(root, pattern); - if (!path) - return -ENOMEM; - - r = glob_extend(&names, path); - if (r == -ENOENT) - continue; - if (r < 0) - return r; - - assert_se(c = endswith(path, "*.so")); - *c = '\0'; /* truncate the glob part */ - - STRV_FOREACH(name, names) { - /* This is most likely to run only once, hence let's not optimize anything. */ - char *t, *t2; - unsigned version; - - t = startswith(*name, path); - if (!t) - continue; - - t2 = endswith(t, ".so"); - if (!t2) - continue; - - t2[0] = '\0'; /* truncate the suffix */ - - r = safe_atou(t, &version); - if (r < 0) { - log_debug_errno(r, "Found libsystemd shared at \"%s.so\", but failed to parse version: %m", *name); - continue; - } - - log_debug("Found libsystemd shared at \"%s.so\", version %u (%s).", - *name, version, - version >= minimal_version ? "OK" : "too old"); - if (version >= minimal_version) - return true; - } - } - - return false; -} -#endif /* NM_IGNORED */ - -bool dot_or_dot_dot(const char *path) { - if (!path) - return false; - if (path[0] != '.') - return false; - if (path[1] == 0) - return true; - if (path[1] != '.') - return false; - - return path[2] == 0; -} - -#if 0 /* NM_IGNORED */ -bool empty_or_root(const char *root) { - - /* For operations relative to some root directory, returns true if the specified root directory is redundant, - * i.e. either / or NULL or the empty string or any equivalent. */ - - if (!root) - return true; - - return root[strspn(root, "/")] == 0; -} - -int path_simplify_and_warn( - char *path, - unsigned flag, - const char *unit, - const char *filename, - unsigned line, - const char *lvalue) { - - bool absolute, fatal = flag & PATH_CHECK_FATAL; - - assert(!FLAGS_SET(flag, PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE)); - - if (!utf8_is_valid(path)) { - log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, path); - return -EINVAL; - } - - if (flag & (PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE)) { - absolute = path_is_absolute(path); - - if (!absolute && (flag & PATH_CHECK_ABSOLUTE)) { - log_syntax(unit, LOG_ERR, filename, line, 0, - "%s= path is not absolute%s: %s", - lvalue, fatal ? "" : ", ignoring", path); - return -EINVAL; - } - - if (absolute && (flag & PATH_CHECK_RELATIVE)) { - log_syntax(unit, LOG_ERR, filename, line, 0, - "%s= path is absolute%s: %s", - lvalue, fatal ? "" : ", ignoring", path); - return -EINVAL; - } - } - - path_simplify(path, true); - - if (!path_is_normalized(path)) { - log_syntax(unit, LOG_ERR, filename, line, 0, - "%s= path is not normalized%s: %s", - lvalue, fatal ? "" : ", ignoring", path); - return -EINVAL; - } - - return 0; -} -#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/path-util.h b/src/systemd/src/basic/path-util.h deleted file mode 100644 index 72f3ce36..00000000 --- a/src/systemd/src/basic/path-util.h +++ /dev/null @@ -1,175 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <alloca.h> -#include <stdbool.h> -#include <stddef.h> - -#include "macro.h" -#include "string-util.h" -#include "time-util.h" - -#if 0 /* NM_IGNORED */ -#define PATH_SPLIT_SBIN_BIN(x) x "sbin:" x "bin" -#define PATH_SPLIT_SBIN_BIN_NULSTR(x) x "sbin\0" x "bin\0" - -#define PATH_NORMAL_SBIN_BIN(x) x "bin" -#define PATH_NORMAL_SBIN_BIN_NULSTR(x) x "bin\0" - -#if HAVE_SPLIT_BIN -# define PATH_SBIN_BIN(x) PATH_SPLIT_SBIN_BIN(x) -# define PATH_SBIN_BIN_NULSTR(x) PATH_SPLIT_SBIN_BIN_NULSTR(x) -#else -# define PATH_SBIN_BIN(x) PATH_NORMAL_SBIN_BIN(x) -# define PATH_SBIN_BIN_NULSTR(x) PATH_NORMAL_SBIN_BIN_NULSTR(x) -#endif - -#define DEFAULT_PATH_NORMAL PATH_SBIN_BIN("/usr/local/") ":" PATH_SBIN_BIN("/usr/") -#define DEFAULT_PATH_NORMAL_NULSTR PATH_SBIN_BIN_NULSTR("/usr/local/") PATH_SBIN_BIN_NULSTR("/usr/") -#define DEFAULT_PATH_SPLIT_USR DEFAULT_PATH_NORMAL ":" PATH_SBIN_BIN("/") -#define DEFAULT_PATH_SPLIT_USR_NULSTR DEFAULT_PATH_NORMAL_NULSTR PATH_SBIN_BIN_NULSTR("/") -#define DEFAULT_PATH_COMPAT PATH_SPLIT_SBIN_BIN("/usr/local/") ":" PATH_SPLIT_SBIN_BIN("/usr/") ":" PATH_SPLIT_SBIN_BIN("/") - -#if HAVE_SPLIT_USR -# define DEFAULT_PATH DEFAULT_PATH_SPLIT_USR -# define DEFAULT_PATH_NULSTR DEFAULT_PATH_SPLIT_USR_NULSTR -#else -# define DEFAULT_PATH DEFAULT_PATH_NORMAL -# define DEFAULT_PATH_NULSTR DEFAULT_PATH_NORMAL_NULSTR -#endif -#endif /* NM_IGNORED */ - -bool is_path(const char *p) _pure_; -int path_split_and_make_absolute(const char *p, char ***ret); -bool path_is_absolute(const char *p) _pure_; -char* path_make_absolute(const char *p, const char *prefix); -int safe_getcwd(char **ret); -int path_make_absolute_cwd(const char *p, char **ret); -int path_make_relative(const char *from_dir, const char *to_path, char **_r); -char* path_startswith(const char *path, const char *prefix) _pure_; -int path_compare(const char *a, const char *b) _pure_; -bool path_equal(const char *a, const char *b) _pure_; -bool path_equal_or_files_same(const char *a, const char *b, int flags); -char* path_join(const char *root, const char *path, const char *rest); -char* path_simplify(char *path, bool kill_dots); - -static inline bool path_equal_ptr(const char *a, const char *b) { - return !!a == !!b && (!a || path_equal(a, b)); -} - -/* Note: the search terminates on the first NULL item. */ -#define PATH_IN_SET(p, ...) \ - ({ \ - char **_s; \ - bool _found = false; \ - STRV_FOREACH(_s, STRV_MAKE(__VA_ARGS__)) \ - if (path_equal(p, *_s)) { \ - _found = true; \ - break; \ - } \ - _found; \ - }) - -#define PATH_STARTSWITH_SET(p, ...) \ - ({ \ - char **s; \ - bool _found = false; \ - STRV_FOREACH(s, STRV_MAKE(__VA_ARGS__)) \ - if (path_startswith(p, *s)) { \ - _found = true; \ - break; \ - } \ - _found; \ - }) - -int path_strv_make_absolute_cwd(char **l); -char** path_strv_resolve(char **l, const char *root); -char** path_strv_resolve_uniq(char **l, const char *root); - -int find_binary(const char *name, char **filename); - -bool paths_check_timestamp(const char* const* paths, usec_t *paths_ts_usec, bool update); - -int fsck_exists(const char *fstype); -int mkfs_exists(const char *fstype); - -/* Iterates through the path prefixes of the specified path, going up - * the tree, to root. Also returns "" (and not "/"!) for the root - * directory. Excludes the specified directory itself */ -#define PATH_FOREACH_PREFIX(prefix, path) \ - for (char *_slash = ({ path_simplify(strcpy(prefix, path), false); streq(prefix, "/") ? NULL : strrchr(prefix, '/'); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/')) - -/* Same as PATH_FOREACH_PREFIX but also includes the specified path itself */ -#define PATH_FOREACH_PREFIX_MORE(prefix, path) \ - for (char *_slash = ({ path_simplify(strcpy(prefix, path), false); if (streq(prefix, "/")) prefix[0] = 0; strrchr(prefix, 0); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/')) - -char *prefix_root(const char *root, const char *path); - -/* Similar to prefix_root(), but returns an alloca() buffer, or - * possibly a const pointer into the path parameter */ -#define prefix_roota(root, path) \ - ({ \ - const char* _path = (path), *_root = (root), *_ret; \ - char *_p, *_n; \ - size_t _l; \ - while (_path[0] == '/' && _path[1] == '/') \ - _path ++; \ - if (empty_or_root(_root)) \ - _ret = _path; \ - else { \ - _l = strlen(_root) + 1 + strlen(_path) + 1; \ - _n = alloca(_l); \ - _p = stpcpy(_n, _root); \ - while (_p > _n && _p[-1] == '/') \ - _p--; \ - if (_path[0] != '/') \ - *(_p++) = '/'; \ - strcpy(_p, _path); \ - _ret = _n; \ - } \ - _ret; \ - }) - -int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg); - -char* dirname_malloc(const char *path); -const char *last_path_component(const char *path); - -bool filename_is_valid(const char *p) _pure_; -bool path_is_normalized(const char *p) _pure_; - -char *file_in_same_dir(const char *path, const char *filename); - -bool hidden_or_backup_file(const char *filename) _pure_; - -bool is_device_path(const char *path); - -bool valid_device_node_path(const char *path); -bool valid_device_allow_pattern(const char *path); - -int systemd_installation_has_version(const char *root, unsigned minimal_version); - -bool dot_or_dot_dot(const char *path); - -static inline const char *skip_dev_prefix(const char *p) { - const char *e; - - /* Drop any /dev prefix if there is any */ - - e = path_startswith(p, "/dev/"); - - return e ?: p; -} - -bool empty_or_root(const char *root); -static inline const char *empty_to_root(const char *path) { - return isempty(path) ? "/" : path; -} - -enum { - PATH_CHECK_FATAL = 1 << 0, /* If not set, then error message is appended with 'ignoring'. */ - PATH_CHECK_ABSOLUTE = 1 << 1, - PATH_CHECK_RELATIVE = 1 << 2, -}; - -int path_simplify_and_warn(char *path, unsigned flag, const char *unit, const char *filename, unsigned line, const char *lvalue); diff --git a/src/systemd/src/basic/prioq.c b/src/systemd/src/basic/prioq.c deleted file mode 100644 index 79900df3..00000000 --- a/src/systemd/src/basic/prioq.c +++ /dev/null @@ -1,303 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -/* - * Priority Queue - * The prioq object implements a priority queue. That is, it orders objects by - * their priority and allows O(1) access to the object with the highest - * priority. Insertion and removal are Θ(log n). Optionally, the caller can - * provide a pointer to an index which will be kept up-to-date by the prioq. - * - * The underlying algorithm used in this implementation is a Heap. - */ - -#include "nm-sd-adapt.h" - -#include <errno.h> -#include <stdlib.h> - -#include "alloc-util.h" -#include "hashmap.h" -#include "prioq.h" - -struct prioq_item { - void *data; - unsigned *idx; -}; - -struct Prioq { - compare_func_t compare_func; - unsigned n_items, n_allocated; - - struct prioq_item *items; -}; - -Prioq *prioq_new(compare_func_t compare_func) { - Prioq *q; - - q = new0(Prioq, 1); - if (!q) - return q; - - q->compare_func = compare_func; - return q; -} - -Prioq* prioq_free(Prioq *q) { - if (!q) - return NULL; - - free(q->items); - return mfree(q); -} - -int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func) { - assert(q); - - if (*q) - return 0; - - *q = prioq_new(compare_func); - if (!*q) - return -ENOMEM; - - return 0; -} - -static void swap(Prioq *q, unsigned j, unsigned k) { - void *saved_data; - unsigned *saved_idx; - - assert(q); - assert(j < q->n_items); - assert(k < q->n_items); - - assert(!q->items[j].idx || *(q->items[j].idx) == j); - assert(!q->items[k].idx || *(q->items[k].idx) == k); - - saved_data = q->items[j].data; - saved_idx = q->items[j].idx; - q->items[j].data = q->items[k].data; - q->items[j].idx = q->items[k].idx; - q->items[k].data = saved_data; - q->items[k].idx = saved_idx; - - if (q->items[j].idx) - *q->items[j].idx = j; - - if (q->items[k].idx) - *q->items[k].idx = k; -} - -static unsigned shuffle_up(Prioq *q, unsigned idx) { - assert(q); - - while (idx > 0) { - unsigned k; - - k = (idx-1)/2; - - if (q->compare_func(q->items[k].data, q->items[idx].data) <= 0) - break; - - swap(q, idx, k); - idx = k; - } - - return idx; -} - -static unsigned shuffle_down(Prioq *q, unsigned idx) { - assert(q); - - for (;;) { - unsigned j, k, s; - - k = (idx+1)*2; /* right child */ - j = k-1; /* left child */ - - if (j >= q->n_items) - break; - - if (q->compare_func(q->items[j].data, q->items[idx].data) < 0) - - /* So our left child is smaller than we are, let's - * remember this fact */ - s = j; - else - s = idx; - - if (k < q->n_items && - q->compare_func(q->items[k].data, q->items[s].data) < 0) - - /* So our right child is smaller than we are, let's - * remember this fact */ - s = k; - - /* s now points to the smallest of the three items */ - - if (s == idx) - /* No swap necessary, we're done */ - break; - - swap(q, idx, s); - idx = s; - } - - return idx; -} - -int prioq_put(Prioq *q, void *data, unsigned *idx) { - struct prioq_item *i; - unsigned k; - - assert(q); - - if (q->n_items >= q->n_allocated) { - unsigned n; - struct prioq_item *j; - - n = MAX((q->n_items+1) * 2, 16u); - j = reallocarray(q->items, n, sizeof(struct prioq_item)); - if (!j) - return -ENOMEM; - - q->items = j; - q->n_allocated = n; - } - - k = q->n_items++; - i = q->items + k; - i->data = data; - i->idx = idx; - - if (idx) - *idx = k; - - shuffle_up(q, k); - - return 0; -} - -static void remove_item(Prioq *q, struct prioq_item *i) { - struct prioq_item *l; - - assert(q); - assert(i); - - l = q->items + q->n_items - 1; - - if (i == l) - /* Last entry, let's just remove it */ - q->n_items--; - else { - unsigned k; - - /* Not last entry, let's replace the last entry with - * this one, and reshuffle */ - - k = i - q->items; - - i->data = l->data; - i->idx = l->idx; - if (i->idx) - *i->idx = k; - q->n_items--; - - k = shuffle_down(q, k); - shuffle_up(q, k); - } -} - -_pure_ static struct prioq_item* find_item(Prioq *q, void *data, unsigned *idx) { - struct prioq_item *i; - - assert(q); - - if (idx) { - if (*idx == PRIOQ_IDX_NULL || - *idx > q->n_items) - return NULL; - - i = q->items + *idx; - if (i->data != data) - return NULL; - - return i; - } else { - for (i = q->items; i < q->items + q->n_items; i++) - if (i->data == data) - return i; - return NULL; - } -} - -int prioq_remove(Prioq *q, void *data, unsigned *idx) { - struct prioq_item *i; - - if (!q) - return 0; - - i = find_item(q, data, idx); - if (!i) - return 0; - - remove_item(q, i); - return 1; -} - -int prioq_reshuffle(Prioq *q, void *data, unsigned *idx) { - struct prioq_item *i; - unsigned k; - - assert(q); - - i = find_item(q, data, idx); - if (!i) - return 0; - - k = i - q->items; - k = shuffle_down(q, k); - shuffle_up(q, k); - return 1; -} - -void *prioq_peek(Prioq *q) { - - if (!q) - return NULL; - - if (q->n_items <= 0) - return NULL; - - return q->items[0].data; -} - -void *prioq_pop(Prioq *q) { - void *data; - - if (!q) - return NULL; - - if (q->n_items <= 0) - return NULL; - - data = q->items[0].data; - remove_item(q, q->items); - return data; -} - -unsigned prioq_size(Prioq *q) { - - if (!q) - return 0; - - return q->n_items; -} - -bool prioq_isempty(Prioq *q) { - - if (!q) - return true; - - return q->n_items <= 0; -} diff --git a/src/systemd/src/basic/prioq.h b/src/systemd/src/basic/prioq.h deleted file mode 100644 index e0361752..00000000 --- a/src/systemd/src/basic/prioq.h +++ /dev/null @@ -1,25 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <stdbool.h> - -#include "hashmap.h" -#include "macro.h" - -typedef struct Prioq Prioq; - -#define PRIOQ_IDX_NULL ((unsigned) -1) - -Prioq *prioq_new(compare_func_t compare); -Prioq *prioq_free(Prioq *q); -int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func); - -int prioq_put(Prioq *q, void *data, unsigned *idx); -int prioq_remove(Prioq *q, void *data, unsigned *idx); -int prioq_reshuffle(Prioq *q, void *data, unsigned *idx); - -void *prioq_peek(Prioq *q) _pure_; -void *prioq_pop(Prioq *q); - -unsigned prioq_size(Prioq *q) _pure_; -bool prioq_isempty(Prioq *q) _pure_; diff --git a/src/systemd/src/basic/process-util.c b/src/systemd/src/basic/process-util.c deleted file mode 100644 index 1412f036..00000000 --- a/src/systemd/src/basic/process-util.c +++ /dev/null @@ -1,1505 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <ctype.h> -#include <errno.h> -#include <limits.h> -#include <linux/oom.h> -#include <sched.h> -#include <signal.h> -#include <stdbool.h> -#include <stdio.h> -#include <stdio_ext.h> -#include <stdlib.h> -#include <string.h> -#include <sys/mman.h> -#include <sys/mount.h> -#include <sys/personality.h> -#include <sys/prctl.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <syslog.h> -#include <unistd.h> -#if 0 /* NM_IGNORED */ -#if HAVE_VALGRIND_VALGRIND_H -#include <valgrind/valgrind.h> -#endif -#endif /* NM_IGNORED */ - -#include "alloc-util.h" -#include "architecture.h" -#include "escape.h" -#include "fd-util.h" -#include "fileio.h" -#include "fs-util.h" -#include "ioprio.h" -#include "log.h" -#include "macro.h" -#include "missing.h" -#include "process-util.h" -#include "raw-clone.h" -#include "signal-util.h" -#include "stat-util.h" -#include "string-table.h" -#include "string-util.h" -#include "terminal-util.h" -#include "user-util.h" -#include "util.h" - -#if 0 /* NM_IGNORED */ -int get_process_state(pid_t pid) { - const char *p; - char state; - int r; - _cleanup_free_ char *line = NULL; - - assert(pid >= 0); - - p = procfs_file_alloca(pid, "stat"); - - r = read_one_line_file(p, &line); - if (r == -ENOENT) - return -ESRCH; - if (r < 0) - return r; - - p = strrchr(line, ')'); - if (!p) - return -EIO; - - p++; - - if (sscanf(p, " %c", &state) != 1) - return -EIO; - - return (unsigned char) state; -} - -int get_process_comm(pid_t pid, char **ret) { - _cleanup_free_ char *escaped = NULL, *comm = NULL; - const char *p; - int r; - - assert(ret); - assert(pid >= 0); - - escaped = new(char, TASK_COMM_LEN); - if (!escaped) - return -ENOMEM; - - p = procfs_file_alloca(pid, "comm"); - - r = read_one_line_file(p, &comm); - if (r == -ENOENT) - return -ESRCH; - if (r < 0) - return r; - - /* Escape unprintable characters, just in case, but don't grow the string beyond the underlying size */ - cellescape(escaped, TASK_COMM_LEN, comm); - - *ret = TAKE_PTR(escaped); - return 0; -} - -int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) { - _cleanup_fclose_ FILE *f = NULL; - bool space = false; - char *k, *ans = NULL; - const char *p; - int c; - - assert(line); - assert(pid >= 0); - - /* Retrieves a process' command line. Replaces unprintable characters while doing so by whitespace (coalescing - * multiple sequential ones into one). If max_length is != 0 will return a string of the specified size at most - * (the trailing NUL byte does count towards the length here!), abbreviated with a "..." ellipsis. If - * comm_fallback is true and the process has no command line set (the case for kernel threads), or has a - * command line that resolves to the empty string will return the "comm" name of the process instead. - * - * Returns -ESRCH if the process doesn't exist, and -ENOENT if the process has no command line (and - * comm_fallback is false). Returns 0 and sets *line otherwise. */ - - p = procfs_file_alloca(pid, "cmdline"); - - f = fopen(p, "re"); - if (!f) { - if (errno == ENOENT) - return -ESRCH; - return -errno; - } - - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - - if (max_length == 1) { - - /* If there's only room for one byte, return the empty string */ - ans = new0(char, 1); - if (!ans) - return -ENOMEM; - - *line = ans; - return 0; - - } else if (max_length == 0) { - size_t len = 0, allocated = 0; - - while ((c = getc(f)) != EOF) { - - if (!GREEDY_REALLOC(ans, allocated, len+3)) { - free(ans); - return -ENOMEM; - } - - if (isprint(c)) { - if (space) { - ans[len++] = ' '; - space = false; - } - - ans[len++] = c; - } else if (len > 0) - space = true; - } - - if (len > 0) - ans[len] = '\0'; - else - ans = mfree(ans); - - } else { - bool dotdotdot = false; - size_t left; - - ans = new(char, max_length); - if (!ans) - return -ENOMEM; - - k = ans; - left = max_length; - while ((c = getc(f)) != EOF) { - - if (isprint(c)) { - - if (space) { - if (left <= 2) { - dotdotdot = true; - break; - } - - *(k++) = ' '; - left--; - space = false; - } - - if (left <= 1) { - dotdotdot = true; - break; - } - - *(k++) = (char) c; - left--; - } else if (k > ans) - space = true; - } - - if (dotdotdot) { - if (max_length <= 4) { - k = ans; - left = max_length; - } else { - k = ans + max_length - 4; - left = 4; - - /* Eat up final spaces */ - while (k > ans && isspace(k[-1])) { - k--; - left++; - } - } - - strncpy(k, "...", left-1); - k[left-1] = 0; - } else - *k = 0; - } - - /* Kernel threads have no argv[] */ - if (isempty(ans)) { - _cleanup_free_ char *t = NULL; - int h; - - free(ans); - - if (!comm_fallback) - return -ENOENT; - - h = get_process_comm(pid, &t); - if (h < 0) - return h; - - if (max_length == 0) - ans = strjoin("[", t, "]"); - else { - size_t l; - - l = strlen(t); - - if (l + 3 <= max_length) - ans = strjoin("[", t, "]"); - else if (max_length <= 6) { - - ans = new(char, max_length); - if (!ans) - return -ENOMEM; - - memcpy(ans, "[...]", max_length-1); - ans[max_length-1] = 0; - } else { - t[max_length - 6] = 0; - - /* Chop off final spaces */ - delete_trailing_chars(t, WHITESPACE); - - ans = strjoin("[", t, "...]"); - } - } - if (!ans) - return -ENOMEM; - } - - *line = ans; - return 0; -} - -int rename_process(const char name[]) { - static size_t mm_size = 0; - static char *mm = NULL; - bool truncated = false; - size_t l; - - /* This is a like a poor man's setproctitle(). It changes the comm field, argv[0], and also the glibc's - * internally used name of the process. For the first one a limit of 16 chars applies; to the second one in - * many cases one of 10 (i.e. length of "/sbin/init") — however if we have CAP_SYS_RESOURCES it is unbounded; - * to the third one 7 (i.e. the length of "systemd". If you pass a longer string it will likely be - * truncated. - * - * Returns 0 if a name was set but truncated, > 0 if it was set but not truncated. */ - - if (isempty(name)) - return -EINVAL; /* let's not confuse users unnecessarily with an empty name */ - - if (!is_main_thread()) - return -EPERM; /* Let's not allow setting the process name from other threads than the main one, as we - * cache things without locking, and we make assumptions that PR_SET_NAME sets the - * process name that isn't correct on any other threads */ - - l = strlen(name); - - /* First step, change the comm field. The main thread's comm is identical to the process comm. This means we - * can use PR_SET_NAME, which sets the thread name for the calling thread. */ - if (prctl(PR_SET_NAME, name) < 0) - log_debug_errno(errno, "PR_SET_NAME failed: %m"); - if (l >= TASK_COMM_LEN) /* Linux process names can be 15 chars at max */ - truncated = true; - - /* Second step, change glibc's ID of the process name. */ - if (program_invocation_name) { - size_t k; - - k = strlen(program_invocation_name); - strncpy(program_invocation_name, name, k); - if (l > k) - truncated = true; - } - - /* Third step, completely replace the argv[] array the kernel maintains for us. This requires privileges, but - * has the advantage that the argv[] array is exactly what we want it to be, and not filled up with zeros at - * the end. This is the best option for changing /proc/self/cmdline. */ - - /* Let's not bother with this if we don't have euid == 0. Strictly speaking we should check for the - * CAP_SYS_RESOURCE capability which is independent of the euid. In our own code the capability generally is - * present only for euid == 0, hence let's use this as quick bypass check, to avoid calling mmap() if - * PR_SET_MM_ARG_{START,END} fails with EPERM later on anyway. After all geteuid() is dead cheap to call, but - * mmap() is not. */ - if (geteuid() != 0) - log_debug("Skipping PR_SET_MM, as we don't have privileges."); - else if (mm_size < l+1) { - size_t nn_size; - char *nn; - - nn_size = PAGE_ALIGN(l+1); - nn = mmap(NULL, nn_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); - if (nn == MAP_FAILED) { - log_debug_errno(errno, "mmap() failed: %m"); - goto use_saved_argv; - } - - strncpy(nn, name, nn_size); - - /* Now, let's tell the kernel about this new memory */ - if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) { - log_debug_errno(errno, "PR_SET_MM_ARG_START failed, proceeding without: %m"); - (void) munmap(nn, nn_size); - goto use_saved_argv; - } - - /* And update the end pointer to the new end, too. If this fails, we don't really know what to do, it's - * pretty unlikely that we can rollback, hence we'll just accept the failure, and continue. */ - if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0) - log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m"); - - if (mm) - (void) munmap(mm, mm_size); - - mm = nn; - mm_size = nn_size; - } else { - strncpy(mm, name, mm_size); - - /* Update the end pointer, continuing regardless of any failure. */ - if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) mm + l + 1, 0, 0) < 0) - log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m"); - } - -use_saved_argv: - /* Fourth step: in all cases we'll also update the original argv[], so that our own code gets it right too if - * it still looks here */ - - if (saved_argc > 0) { - int i; - - if (saved_argv[0]) { - size_t k; - - k = strlen(saved_argv[0]); - strncpy(saved_argv[0], name, k); - if (l > k) - truncated = true; - } - - for (i = 1; i < saved_argc; i++) { - if (!saved_argv[i]) - break; - - memzero(saved_argv[i], strlen(saved_argv[i])); - } - } - - return !truncated; -} - -int is_kernel_thread(pid_t pid) { - _cleanup_free_ char *line = NULL; - unsigned long long flags; - size_t l, i; - const char *p; - char *q; - int r; - - if (IN_SET(pid, 0, 1) || pid == getpid_cached()) /* pid 1, and we ourselves certainly aren't a kernel thread */ - return 0; - if (!pid_is_valid(pid)) - return -EINVAL; - - p = procfs_file_alloca(pid, "stat"); - r = read_one_line_file(p, &line); - if (r == -ENOENT) - return -ESRCH; - if (r < 0) - return r; - - /* Skip past the comm field */ - q = strrchr(line, ')'); - if (!q) - return -EINVAL; - q++; - - /* Skip 6 fields to reach the flags field */ - for (i = 0; i < 6; i++) { - l = strspn(q, WHITESPACE); - if (l < 1) - return -EINVAL; - q += l; - - l = strcspn(q, WHITESPACE); - if (l < 1) - return -EINVAL; - q += l; - } - - /* Skip preceeding whitespace */ - l = strspn(q, WHITESPACE); - if (l < 1) - return -EINVAL; - q += l; - - /* Truncate the rest */ - l = strcspn(q, WHITESPACE); - if (l < 1) - return -EINVAL; - q[l] = 0; - - r = safe_atollu(q, &flags); - if (r < 0) - return r; - - return !!(flags & PF_KTHREAD); -} - -int get_process_capeff(pid_t pid, char **capeff) { - const char *p; - int r; - - assert(capeff); - assert(pid >= 0); - - p = procfs_file_alloca(pid, "status"); - - r = get_proc_field(p, "CapEff", WHITESPACE, capeff); - if (r == -ENOENT) - return -ESRCH; - - return r; -} - -static int get_process_link_contents(const char *proc_file, char **name) { - int r; - - assert(proc_file); - assert(name); - - r = readlink_malloc(proc_file, name); - if (r == -ENOENT) - return -ESRCH; - if (r < 0) - return r; - - return 0; -} - -int get_process_exe(pid_t pid, char **name) { - const char *p; - char *d; - int r; - - assert(pid >= 0); - - p = procfs_file_alloca(pid, "exe"); - r = get_process_link_contents(p, name); - if (r < 0) - return r; - - d = endswith(*name, " (deleted)"); - if (d) - *d = '\0'; - - return 0; -} - -static int get_process_id(pid_t pid, const char *field, uid_t *uid) { - _cleanup_fclose_ FILE *f = NULL; - char line[LINE_MAX]; - const char *p; - - assert(field); - assert(uid); - - if (pid < 0) - return -EINVAL; - - p = procfs_file_alloca(pid, "status"); - f = fopen(p, "re"); - if (!f) { - if (errno == ENOENT) - return -ESRCH; - return -errno; - } - - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - - FOREACH_LINE(line, f, return -errno) { - char *l; - - l = strstrip(line); - - if (startswith(l, field)) { - l += strlen(field); - l += strspn(l, WHITESPACE); - - l[strcspn(l, WHITESPACE)] = 0; - - return parse_uid(l, uid); - } - } - - return -EIO; -} - -int get_process_uid(pid_t pid, uid_t *uid) { - - if (pid == 0 || pid == getpid_cached()) { - *uid = getuid(); - return 0; - } - - return get_process_id(pid, "Uid:", uid); -} - -int get_process_gid(pid_t pid, gid_t *gid) { - - if (pid == 0 || pid == getpid_cached()) { - *gid = getgid(); - return 0; - } - - assert_cc(sizeof(uid_t) == sizeof(gid_t)); - return get_process_id(pid, "Gid:", gid); -} - -int get_process_cwd(pid_t pid, char **cwd) { - const char *p; - - assert(pid >= 0); - - p = procfs_file_alloca(pid, "cwd"); - - return get_process_link_contents(p, cwd); -} - -int get_process_root(pid_t pid, char **root) { - const char *p; - - assert(pid >= 0); - - p = procfs_file_alloca(pid, "root"); - - return get_process_link_contents(p, root); -} - -int get_process_environ(pid_t pid, char **env) { - _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *outcome = NULL; - int c; - const char *p; - size_t allocated = 0, sz = 0; - - assert(pid >= 0); - assert(env); - - p = procfs_file_alloca(pid, "environ"); - - f = fopen(p, "re"); - if (!f) { - if (errno == ENOENT) - return -ESRCH; - return -errno; - } - - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - - while ((c = fgetc(f)) != EOF) { - if (!GREEDY_REALLOC(outcome, allocated, sz + 5)) - return -ENOMEM; - - if (c == '\0') - outcome[sz++] = '\n'; - else - sz += cescape_char(c, outcome + sz); - } - - if (!outcome) { - outcome = strdup(""); - if (!outcome) - return -ENOMEM; - } else - outcome[sz] = '\0'; - - *env = TAKE_PTR(outcome); - - return 0; -} - -int get_process_ppid(pid_t pid, pid_t *_ppid) { - int r; - _cleanup_free_ char *line = NULL; - long unsigned ppid; - const char *p; - - assert(pid >= 0); - assert(_ppid); - - if (pid == 0 || pid == getpid_cached()) { - *_ppid = getppid(); - return 0; - } - - p = procfs_file_alloca(pid, "stat"); - r = read_one_line_file(p, &line); - if (r == -ENOENT) - return -ESRCH; - if (r < 0) - return r; - - /* Let's skip the pid and comm fields. The latter is enclosed - * in () but does not escape any () in its value, so let's - * skip over it manually */ - - p = strrchr(line, ')'); - if (!p) - return -EIO; - - p++; - - if (sscanf(p, " " - "%*c " /* state */ - "%lu ", /* ppid */ - &ppid) != 1) - return -EIO; - - if ((long unsigned) (pid_t) ppid != ppid) - return -ERANGE; - - *_ppid = (pid_t) ppid; - - return 0; -} - -int wait_for_terminate(pid_t pid, siginfo_t *status) { - siginfo_t dummy; - - assert(pid >= 1); - - if (!status) - status = &dummy; - - for (;;) { - zero(*status); - - if (waitid(P_PID, pid, status, WEXITED) < 0) { - - if (errno == EINTR) - continue; - - return negative_errno(); - } - - return 0; - } -} - -/* - * Return values: - * < 0 : wait_for_terminate() failed to get the state of the - * process, the process was terminated by a signal, or - * failed for an unknown reason. - * >=0 : The process terminated normally, and its exit code is - * returned. - * - * That is, success is indicated by a return value of zero, and an - * error is indicated by a non-zero value. - * - * A warning is emitted if the process terminates abnormally, - * and also if it returns non-zero unless check_exit_code is true. - */ -int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags) { - _cleanup_free_ char *buffer = NULL; - siginfo_t status; - int r, prio; - - assert(pid > 1); - - if (!name) { - r = get_process_comm(pid, &buffer); - if (r < 0) - log_debug_errno(r, "Failed to acquire process name of " PID_FMT ", ignoring: %m", pid); - else - name = buffer; - } - - prio = flags & WAIT_LOG_ABNORMAL ? LOG_ERR : LOG_DEBUG; - - r = wait_for_terminate(pid, &status); - if (r < 0) - return log_full_errno(prio, r, "Failed to wait for %s: %m", strna(name)); - - if (status.si_code == CLD_EXITED) { - if (status.si_status != EXIT_SUCCESS) - log_full(flags & WAIT_LOG_NON_ZERO_EXIT_STATUS ? LOG_ERR : LOG_DEBUG, - "%s failed with exit status %i.", strna(name), status.si_status); - else - log_debug("%s succeeded.", name); - - return status.si_status; - - } else if (IN_SET(status.si_code, CLD_KILLED, CLD_DUMPED)) { - - log_full(prio, "%s terminated by signal %s.", strna(name), signal_to_string(status.si_status)); - return -EPROTO; - } - - log_full(prio, "%s failed due to unknown reason.", strna(name)); - return -EPROTO; -} - -/* - * Return values: - * - * < 0 : wait_for_terminate_with_timeout() failed to get the state of the process, the process timed out, the process - * was terminated by a signal, or failed for an unknown reason. - * - * >=0 : The process terminated normally with no failures. - * - * Success is indicated by a return value of zero, a timeout is indicated by ETIMEDOUT, and all other child failure - * states are indicated by error is indicated by a non-zero value. - * - * This call assumes SIGCHLD has been blocked already, in particular before the child to wait for has been forked off - * to remain entirely race-free. - */ -int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout) { - sigset_t mask; - int r; - usec_t until; - - assert_se(sigemptyset(&mask) == 0); - assert_se(sigaddset(&mask, SIGCHLD) == 0); - - /* Drop into a sigtimewait-based timeout. Waiting for the - * pid to exit. */ - until = now(CLOCK_MONOTONIC) + timeout; - for (;;) { - usec_t n; - siginfo_t status = {}; - struct timespec ts; - - n = now(CLOCK_MONOTONIC); - if (n >= until) - break; - - r = sigtimedwait(&mask, NULL, timespec_store(&ts, until - n)) < 0 ? -errno : 0; - /* Assuming we woke due to the child exiting. */ - if (waitid(P_PID, pid, &status, WEXITED|WNOHANG) == 0) { - if (status.si_pid == pid) { - /* This is the correct child.*/ - if (status.si_code == CLD_EXITED) - return (status.si_status == 0) ? 0 : -EPROTO; - else - return -EPROTO; - } - } - /* Not the child, check for errors and proceed appropriately */ - if (r < 0) { - switch (r) { - case -EAGAIN: - /* Timed out, child is likely hung. */ - return -ETIMEDOUT; - case -EINTR: - /* Received a different signal and should retry */ - continue; - default: - /* Return any unexpected errors */ - return r; - } - } - } - - return -EPROTO; -} - -void sigkill_wait(pid_t pid) { - assert(pid > 1); - - if (kill(pid, SIGKILL) > 0) - (void) wait_for_terminate(pid, NULL); -} - -void sigkill_waitp(pid_t *pid) { - PROTECT_ERRNO; - - if (!pid) - return; - if (*pid <= 1) - return; - - sigkill_wait(*pid); -} - -void sigterm_wait(pid_t pid) { - assert(pid > 1); - - if (kill_and_sigcont(pid, SIGTERM) > 0) - (void) wait_for_terminate(pid, NULL); -} - -int kill_and_sigcont(pid_t pid, int sig) { - int r; - - r = kill(pid, sig) < 0 ? -errno : 0; - - /* If this worked, also send SIGCONT, unless we already just sent a SIGCONT, or SIGKILL was sent which isn't - * affected by a process being suspended anyway. */ - if (r >= 0 && !IN_SET(sig, SIGCONT, SIGKILL)) - (void) kill(pid, SIGCONT); - - return r; -} - -int getenv_for_pid(pid_t pid, const char *field, char **ret) { - _cleanup_fclose_ FILE *f = NULL; - char *value = NULL; - bool done = false; - const char *path; - size_t l; - - assert(pid >= 0); - assert(field); - assert(ret); - - if (pid == 0 || pid == getpid_cached()) { - const char *e; - - e = getenv(field); - if (!e) { - *ret = NULL; - return 0; - } - - value = strdup(e); - if (!value) - return -ENOMEM; - - *ret = value; - return 1; - } - - path = procfs_file_alloca(pid, "environ"); - - f = fopen(path, "re"); - if (!f) { - if (errno == ENOENT) - return -ESRCH; - - return -errno; - } - - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - - l = strlen(field); - - do { - char line[LINE_MAX]; - size_t i; - - for (i = 0; i < sizeof(line)-1; i++) { - int c; - - c = getc(f); - if (_unlikely_(c == EOF)) { - done = true; - break; - } else if (c == 0) - break; - - line[i] = c; - } - line[i] = 0; - - if (strneq(line, field, l) && line[l] == '=') { - value = strdup(line + l + 1); - if (!value) - return -ENOMEM; - - *ret = value; - return 1; - } - - } while (!done); - - *ret = NULL; - return 0; -} - -bool pid_is_unwaited(pid_t pid) { - /* Checks whether a PID is still valid at all, including a zombie */ - - if (pid < 0) - return false; - - if (pid <= 1) /* If we or PID 1 would be dead and have been waited for, this code would not be running */ - return true; - - if (pid == getpid_cached()) - return true; - - if (kill(pid, 0) >= 0) - return true; - - return errno != ESRCH; -} - -bool pid_is_alive(pid_t pid) { - int r; - - /* Checks whether a PID is still valid and not a zombie */ - - if (pid < 0) - return false; - - if (pid <= 1) /* If we or PID 1 would be a zombie, this code would not be running */ - return true; - - if (pid == getpid_cached()) - return true; - - r = get_process_state(pid); - if (IN_SET(r, -ESRCH, 'Z')) - return false; - - return true; -} - -int pid_from_same_root_fs(pid_t pid) { - const char *root; - - if (pid < 0) - return false; - - if (pid == 0 || pid == getpid_cached()) - return true; - - root = procfs_file_alloca(pid, "root"); - - return files_same(root, "/proc/1/root", 0); -} -#endif /* NM_IGNORED */ - -bool is_main_thread(void) { - static thread_local int cached = 0; - - if (_unlikely_(cached == 0)) - cached = getpid_cached() == gettid() ? 1 : -1; - - return cached > 0; -} - -#if 0 /* NM_IGNORED */ -_noreturn_ void freeze(void) { - - log_close(); - - /* Make sure nobody waits for us on a socket anymore */ - close_all_fds(NULL, 0); - - sync(); - - /* Let's not freeze right away, but keep reaping zombies. */ - for (;;) { - int r; - siginfo_t si = {}; - - r = waitid(P_ALL, 0, &si, WEXITED); - if (r < 0 && errno != EINTR) - break; - } - - /* waitid() failed with an unexpected error, things are really borked. Freeze now! */ - for (;;) - pause(); -} - -bool oom_score_adjust_is_valid(int oa) { - return oa >= OOM_SCORE_ADJ_MIN && oa <= OOM_SCORE_ADJ_MAX; -} - -unsigned long personality_from_string(const char *p) { - int architecture; - - if (!p) - return PERSONALITY_INVALID; - - /* Parse a personality specifier. We use our own identifiers that indicate specific ABIs, rather than just - * hints regarding the register size, since we want to keep things open for multiple locally supported ABIs for - * the same register size. */ - - architecture = architecture_from_string(p); - if (architecture < 0) - return PERSONALITY_INVALID; - - if (architecture == native_architecture()) - return PER_LINUX; -#ifdef SECONDARY_ARCHITECTURE - if (architecture == SECONDARY_ARCHITECTURE) - return PER_LINUX32; -#endif - - return PERSONALITY_INVALID; -} - -const char* personality_to_string(unsigned long p) { - int architecture = _ARCHITECTURE_INVALID; - - if (p == PER_LINUX) - architecture = native_architecture(); -#ifdef SECONDARY_ARCHITECTURE - else if (p == PER_LINUX32) - architecture = SECONDARY_ARCHITECTURE; -#endif - - if (architecture < 0) - return NULL; - - return architecture_to_string(architecture); -} - -int safe_personality(unsigned long p) { - int ret; - - /* So here's the deal, personality() is weirdly defined by glibc. In some cases it returns a failure via errno, - * and in others as negative return value containing an errno-like value. Let's work around this: this is a - * wrapper that uses errno if it is set, and uses the return value otherwise. And then it sets both errno and - * the return value indicating the same issue, so that we are definitely on the safe side. - * - * See https://github.com/systemd/systemd/issues/6737 */ - - errno = 0; - ret = personality(p); - if (ret < 0) { - if (errno != 0) - return -errno; - - errno = -ret; - } - - return ret; -} - -int opinionated_personality(unsigned long *ret) { - int current; - - /* Returns the current personality, or PERSONALITY_INVALID if we can't determine it. This function is a bit - * opinionated though, and ignores all the finer-grained bits and exotic personalities, only distinguishing the - * two most relevant personalities: PER_LINUX and PER_LINUX32. */ - - current = safe_personality(PERSONALITY_INVALID); - if (current < 0) - return current; - - if (((unsigned long) current & 0xffff) == PER_LINUX32) - *ret = PER_LINUX32; - else - *ret = PER_LINUX; - - return 0; -} - -void valgrind_summary_hack(void) { -#if HAVE_VALGRIND_VALGRIND_H - if (getpid_cached() == 1 && RUNNING_ON_VALGRIND) { - pid_t pid; - pid = raw_clone(SIGCHLD); - if (pid < 0) - log_emergency_errno(errno, "Failed to fork off valgrind helper: %m"); - else if (pid == 0) - exit(EXIT_SUCCESS); - else { - log_info("Spawned valgrind helper as PID "PID_FMT".", pid); - (void) wait_for_terminate(pid, NULL); - } - } -#endif -} - -int pid_compare_func(const void *a, const void *b) { - const pid_t *p = a, *q = b; - - /* Suitable for usage in qsort() */ - return CMP(*p, *q); -} - -int ioprio_parse_priority(const char *s, int *ret) { - int i, r; - - assert(s); - assert(ret); - - r = safe_atoi(s, &i); - if (r < 0) - return r; - - if (!ioprio_priority_is_valid(i)) - return -EINVAL; - - *ret = i; - return 0; -} -#endif /* NM_IGNORED */ - -/* The cached PID, possible values: - * - * == UNSET [0] → cache not initialized yet - * == BUSY [-1] → some thread is initializing it at the moment - * any other → the cached PID - */ - -#define CACHED_PID_UNSET ((pid_t) 0) -#define CACHED_PID_BUSY ((pid_t) -1) - -static pid_t cached_pid = CACHED_PID_UNSET; - -void reset_cached_pid(void) { - /* Invoked in the child after a fork(), i.e. at the first moment the PID changed */ - cached_pid = CACHED_PID_UNSET; -} - -/* We use glibc __register_atfork() + __dso_handle directly here, as they are not included in the glibc - * headers. __register_atfork() is mostly equivalent to pthread_atfork(), but doesn't require us to link against - * libpthread, as it is part of glibc anyway. */ -extern int __register_atfork(void (*prepare) (void), void (*parent) (void), void (*child) (void), void *dso_handle); -extern void* __dso_handle __attribute__ ((__weak__)); - -pid_t getpid_cached(void) { - static bool installed = false; - pid_t current_value; - - /* getpid_cached() is much like getpid(), but caches the value in local memory, to avoid having to invoke a - * system call each time. This restores glibc behaviour from before 2.24, when getpid() was unconditionally - * cached. Starting with 2.24 getpid() started to become prohibitively expensive when used for detecting when - * objects were used across fork()s. With this caching the old behaviour is somewhat restored. - * - * https://bugzilla.redhat.com/show_bug.cgi?id=1443976 - * https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=c579f48edba88380635ab98cb612030e3ed8691e - */ - - current_value = __sync_val_compare_and_swap(&cached_pid, CACHED_PID_UNSET, CACHED_PID_BUSY); - - switch (current_value) { - - case CACHED_PID_UNSET: { /* Not initialized yet, then do so now */ - pid_t new_pid; - - new_pid = raw_getpid(); - - if (!installed) { - /* __register_atfork() either returns 0 or -ENOMEM, in its glibc implementation. Since it's - * only half-documented (glibc doesn't document it but LSB does — though only superficially) - * we'll check for errors only in the most generic fashion possible. */ - - if (__register_atfork(NULL, NULL, reset_cached_pid, __dso_handle) != 0) { - /* OOM? Let's try again later */ - cached_pid = CACHED_PID_UNSET; - return new_pid; - } - - installed = true; - } - - cached_pid = new_pid; - return new_pid; - } - - case CACHED_PID_BUSY: /* Somebody else is currently initializing */ - return raw_getpid(); - - default: /* Properly initialized */ - return current_value; - } -} - -#if 0 /* NM_IGNORED */ -int must_be_root(void) { - - if (geteuid() == 0) - return 0; - - log_error("Need to be root."); - return -EPERM; -} - -int safe_fork_full( - const char *name, - const int except_fds[], - size_t n_except_fds, - ForkFlags flags, - pid_t *ret_pid) { - - pid_t original_pid, pid; - sigset_t saved_ss, ss; - bool block_signals = false; - int prio, r; - - /* A wrapper around fork(), that does a couple of important initializations in addition to mere forking. Always - * returns the child's PID in *ret_pid. Returns == 0 in the child, and > 0 in the parent. */ - - prio = flags & FORK_LOG ? LOG_ERR : LOG_DEBUG; - - original_pid = getpid_cached(); - - if (flags & (FORK_RESET_SIGNALS|FORK_DEATHSIG)) { - - /* We temporarily block all signals, so that the new child has them blocked initially. This way, we can - * be sure that SIGTERMs are not lost we might send to the child. */ - - if (sigfillset(&ss) < 0) - return log_full_errno(prio, errno, "Failed to reset signal set: %m"); - - block_signals = true; - - } else if (flags & FORK_WAIT) { - - /* Let's block SIGCHLD at least, so that we can safely watch for the child process */ - - if (sigemptyset(&ss) < 0) - return log_full_errno(prio, errno, "Failed to clear signal set: %m"); - - if (sigaddset(&ss, SIGCHLD) < 0) - return log_full_errno(prio, errno, "Failed to add SIGCHLD to signal set: %m"); - - block_signals = true; - } - - if (block_signals) - if (sigprocmask(SIG_SETMASK, &ss, &saved_ss) < 0) - return log_full_errno(prio, errno, "Failed to set signal mask: %m"); - - if (flags & FORK_NEW_MOUNTNS) - pid = raw_clone(SIGCHLD|CLONE_NEWNS); - else - pid = fork(); - if (pid < 0) { - r = -errno; - - if (block_signals) /* undo what we did above */ - (void) sigprocmask(SIG_SETMASK, &saved_ss, NULL); - - return log_full_errno(prio, r, "Failed to fork: %m"); - } - if (pid > 0) { - /* We are in the parent process */ - - log_debug("Successfully forked off '%s' as PID " PID_FMT ".", strna(name), pid); - - if (flags & FORK_WAIT) { - r = wait_for_terminate_and_check(name, pid, (flags & FORK_LOG ? WAIT_LOG : 0)); - if (r < 0) - return r; - if (r != EXIT_SUCCESS) /* exit status > 0 should be treated as failure, too */ - return -EPROTO; - } - - if (block_signals) /* undo what we did above */ - (void) sigprocmask(SIG_SETMASK, &saved_ss, NULL); - - if (ret_pid) - *ret_pid = pid; - - return 1; - } - - /* We are in the child process */ - - if (flags & FORK_REOPEN_LOG) { - /* Close the logs if requested, before we log anything. And make sure we reopen it if needed. */ - log_close(); - log_set_open_when_needed(true); - } - - if (name) { - r = rename_process(name); - if (r < 0) - log_full_errno(flags & FORK_LOG ? LOG_WARNING : LOG_DEBUG, - r, "Failed to rename process, ignoring: %m"); - } - - if (flags & FORK_DEATHSIG) - if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) { - log_full_errno(prio, errno, "Failed to set death signal: %m"); - _exit(EXIT_FAILURE); - } - - if (flags & FORK_RESET_SIGNALS) { - r = reset_all_signal_handlers(); - if (r < 0) { - log_full_errno(prio, r, "Failed to reset signal handlers: %m"); - _exit(EXIT_FAILURE); - } - - /* This implicitly undoes the signal mask stuff we did before the fork()ing above */ - r = reset_signal_mask(); - if (r < 0) { - log_full_errno(prio, r, "Failed to reset signal mask: %m"); - _exit(EXIT_FAILURE); - } - } else if (block_signals) { /* undo what we did above */ - if (sigprocmask(SIG_SETMASK, &saved_ss, NULL) < 0) { - log_full_errno(prio, errno, "Failed to restore signal mask: %m"); - _exit(EXIT_FAILURE); - } - } - - if (flags & FORK_DEATHSIG) { - pid_t ppid; - /* Let's see if the parent PID is still the one we started from? If not, then the parent - * already died by the time we set PR_SET_PDEATHSIG, hence let's emulate the effect */ - - ppid = getppid(); - if (ppid == 0) - /* Parent is in a differn't PID namespace. */; - else if (ppid != original_pid) { - log_debug("Parent died early, raising SIGTERM."); - (void) raise(SIGTERM); - _exit(EXIT_FAILURE); - } - } - - if (FLAGS_SET(flags, FORK_NEW_MOUNTNS | FORK_MOUNTNS_SLAVE)) { - - /* Optionally, make sure we never propagate mounts to the host. */ - - if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) { - log_full_errno(prio, errno, "Failed to remount root directory as MS_SLAVE: %m"); - _exit(EXIT_FAILURE); - } - } - - if (flags & FORK_CLOSE_ALL_FDS) { - /* Close the logs here in case it got reopened above, as close_all_fds() would close them for us */ - log_close(); - - r = close_all_fds(except_fds, n_except_fds); - if (r < 0) { - log_full_errno(prio, r, "Failed to close all file descriptors: %m"); - _exit(EXIT_FAILURE); - } - } - - /* When we were asked to reopen the logs, do so again now */ - if (flags & FORK_REOPEN_LOG) { - log_open(); - log_set_open_when_needed(false); - } - - if (flags & FORK_NULL_STDIO) { - r = make_null_stdio(); - if (r < 0) { - log_full_errno(prio, r, "Failed to connect stdin/stdout to /dev/null: %m"); - _exit(EXIT_FAILURE); - } - } - - if (ret_pid) - *ret_pid = getpid_cached(); - - return 0; -} - -int fork_agent(const char *name, const int except[], size_t n_except, pid_t *ret_pid, const char *path, ...) { - bool stdout_is_tty, stderr_is_tty; - size_t n, i; - va_list ap; - char **l; - int r; - - assert(path); - - /* Spawns a temporary TTY agent, making sure it goes away when we go away */ - - r = safe_fork_full(name, except, n_except, FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS, ret_pid); - if (r < 0) - return r; - if (r > 0) - return 0; - - /* In the child: */ - - stdout_is_tty = isatty(STDOUT_FILENO); - stderr_is_tty = isatty(STDERR_FILENO); - - if (!stdout_is_tty || !stderr_is_tty) { - int fd; - - /* Detach from stdout/stderr. and reopen - * /dev/tty for them. This is important to - * ensure that when systemctl is started via - * popen() or a similar call that expects to - * read EOF we actually do generate EOF and - * not delay this indefinitely by because we - * keep an unused copy of stdin around. */ - fd = open("/dev/tty", O_WRONLY); - if (fd < 0) { - log_error_errno(errno, "Failed to open /dev/tty: %m"); - _exit(EXIT_FAILURE); - } - - if (!stdout_is_tty && dup2(fd, STDOUT_FILENO) < 0) { - log_error_errno(errno, "Failed to dup2 /dev/tty: %m"); - _exit(EXIT_FAILURE); - } - - if (!stderr_is_tty && dup2(fd, STDERR_FILENO) < 0) { - log_error_errno(errno, "Failed to dup2 /dev/tty: %m"); - _exit(EXIT_FAILURE); - } - - safe_close_above_stdio(fd); - } - - /* Count arguments */ - va_start(ap, path); - for (n = 0; va_arg(ap, char*); n++) - ; - va_end(ap); - - /* Allocate strv */ - l = newa(char*, n + 1); - - /* Fill in arguments */ - va_start(ap, path); - for (i = 0; i <= n; i++) - l[i] = va_arg(ap, char*); - va_end(ap); - - execv(path, l); - _exit(EXIT_FAILURE); -} - -int set_oom_score_adjust(int value) { - char t[DECIMAL_STR_MAX(int)]; - - sprintf(t, "%i", value); - - return write_string_file("/proc/self/oom_score_adj", t, - WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_DISABLE_BUFFER); -} - -static const char *const ioprio_class_table[] = { - [IOPRIO_CLASS_NONE] = "none", - [IOPRIO_CLASS_RT] = "realtime", - [IOPRIO_CLASS_BE] = "best-effort", - [IOPRIO_CLASS_IDLE] = "idle" -}; - -DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ioprio_class, int, IOPRIO_N_CLASSES); - -static const char *const sigchld_code_table[] = { - [CLD_EXITED] = "exited", - [CLD_KILLED] = "killed", - [CLD_DUMPED] = "dumped", - [CLD_TRAPPED] = "trapped", - [CLD_STOPPED] = "stopped", - [CLD_CONTINUED] = "continued", -}; - -DEFINE_STRING_TABLE_LOOKUP(sigchld_code, int); - -static const char* const sched_policy_table[] = { - [SCHED_OTHER] = "other", - [SCHED_BATCH] = "batch", - [SCHED_IDLE] = "idle", - [SCHED_FIFO] = "fifo", - [SCHED_RR] = "rr" -}; - -DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(sched_policy, int, INT_MAX); -#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/process-util.h b/src/systemd/src/basic/process-util.h deleted file mode 100644 index e11164bd..00000000 --- a/src/systemd/src/basic/process-util.h +++ /dev/null @@ -1,200 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <alloca.h> -#include <errno.h> -#include <sched.h> -#include <signal.h> -#include <stdbool.h> -#include <stddef.h> -#include <stdio.h> -#include <string.h> -#include <sys/resource.h> -#include <sys/types.h> - -#include "format-util.h" -#include "ioprio.h" -#include "macro.h" -#include "time-util.h" - -#define procfs_file_alloca(pid, field) \ - ({ \ - pid_t _pid_ = (pid); \ - const char *_r_; \ - if (_pid_ == 0) { \ - _r_ = ("/proc/self/" field); \ - } else { \ - _r_ = alloca(STRLEN("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + sizeof(field)); \ - sprintf((char*) _r_, "/proc/"PID_FMT"/" field, _pid_); \ - } \ - _r_; \ - }) - -int get_process_state(pid_t pid); -int get_process_comm(pid_t pid, char **name); -int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line); -int get_process_exe(pid_t pid, char **name); -int get_process_uid(pid_t pid, uid_t *uid); -int get_process_gid(pid_t pid, gid_t *gid); -int get_process_capeff(pid_t pid, char **capeff); -int get_process_cwd(pid_t pid, char **cwd); -int get_process_root(pid_t pid, char **root); -int get_process_environ(pid_t pid, char **environ); -int get_process_ppid(pid_t pid, pid_t *ppid); - -int wait_for_terminate(pid_t pid, siginfo_t *status); - -typedef enum WaitFlags { - WAIT_LOG_ABNORMAL = 1 << 0, - WAIT_LOG_NON_ZERO_EXIT_STATUS = 1 << 1, - - /* A shortcut for requesting the most complete logging */ - WAIT_LOG = WAIT_LOG_ABNORMAL|WAIT_LOG_NON_ZERO_EXIT_STATUS, -} WaitFlags; - -int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags); -int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout); - -void sigkill_wait(pid_t pid); -void sigkill_waitp(pid_t *pid); -void sigterm_wait(pid_t pid); - -int kill_and_sigcont(pid_t pid, int sig); - -int rename_process(const char name[]); -int is_kernel_thread(pid_t pid); - -int getenv_for_pid(pid_t pid, const char *field, char **_value); - -bool pid_is_alive(pid_t pid); -bool pid_is_unwaited(pid_t pid); -int pid_from_same_root_fs(pid_t pid); - -bool is_main_thread(void); - -_noreturn_ void freeze(void); - -bool oom_score_adjust_is_valid(int oa); - -#ifndef PERSONALITY_INVALID -/* personality(7) documents that 0xffffffffUL is used for querying the - * current personality, hence let's use that here as error - * indicator. */ -#define PERSONALITY_INVALID 0xffffffffLU -#endif - -unsigned long personality_from_string(const char *p); -const char *personality_to_string(unsigned long); - -int safe_personality(unsigned long p); -int opinionated_personality(unsigned long *ret); - -int ioprio_class_to_string_alloc(int i, char **s); -int ioprio_class_from_string(const char *s); - -const char *sigchld_code_to_string(int i) _const_; -int sigchld_code_from_string(const char *s) _pure_; - -int sched_policy_to_string_alloc(int i, char **s); -int sched_policy_from_string(const char *s); - -static inline pid_t PTR_TO_PID(const void *p) { - return (pid_t) ((uintptr_t) p); -} - -static inline void* PID_TO_PTR(pid_t pid) { - return (void*) ((uintptr_t) pid); -} - -void valgrind_summary_hack(void); - -int pid_compare_func(const void *a, const void *b); - -#if 0 /* NM_IGNORED */ -static inline bool nice_is_valid(int n) { - return n >= PRIO_MIN && n < PRIO_MAX; -} - -static inline bool sched_policy_is_valid(int i) { - return IN_SET(i, SCHED_OTHER, SCHED_BATCH, SCHED_IDLE, SCHED_FIFO, SCHED_RR); -} - -static inline bool sched_priority_is_valid(int i) { - return i >= 0 && i <= sched_get_priority_max(SCHED_RR); -} - -static inline bool ioprio_class_is_valid(int i) { - return IN_SET(i, IOPRIO_CLASS_NONE, IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE); -} - -static inline bool ioprio_priority_is_valid(int i) { - return i >= 0 && i < IOPRIO_BE_NR; -} - -static inline bool pid_is_valid(pid_t p) { - return p > 0; -} - -static inline int sched_policy_to_string_alloc_with_check(int n, char **s) { - if (!sched_policy_is_valid(n)) - return -EINVAL; - - return sched_policy_to_string_alloc(n, s); -} -#endif /* NM_IGNORED */ - -int ioprio_parse_priority(const char *s, int *ret); - -pid_t getpid_cached(void); -void reset_cached_pid(void); - -int must_be_root(void); - -typedef enum ForkFlags { - FORK_RESET_SIGNALS = 1 << 0, - FORK_CLOSE_ALL_FDS = 1 << 1, - FORK_DEATHSIG = 1 << 2, - FORK_NULL_STDIO = 1 << 3, - FORK_REOPEN_LOG = 1 << 4, - FORK_LOG = 1 << 5, - FORK_WAIT = 1 << 6, - FORK_NEW_MOUNTNS = 1 << 7, - FORK_MOUNTNS_SLAVE = 1 << 8, -} ForkFlags; - -int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid); - -static inline int safe_fork(const char *name, ForkFlags flags, pid_t *ret_pid) { - return safe_fork_full(name, NULL, 0, flags, ret_pid); -} - -int fork_agent(const char *name, const int except[], size_t n_except, pid_t *pid, const char *path, ...); - -int set_oom_score_adjust(int value); - -#if SIZEOF_PID_T == 4 -/* The highest possibly (theoretic) pid_t value on this architecture. */ -#define PID_T_MAX ((pid_t) INT32_MAX) -/* The maximum number of concurrent processes Linux allows on this architecture, as well as the highest valid PID value - * the kernel will potentially assign. This reflects a value compiled into the kernel (PID_MAX_LIMIT), and sets the - * upper boundary on what may be written to the /proc/sys/kernel/pid_max sysctl (but do note that the sysctl is off by - * 1, since PID 0 can never exist and there can hence only be one process less than the limit would suggest). Since - * these values are documented in proc(5) we feel quite confident that they are stable enough for the near future at - * least to define them here too. */ -#define TASKS_MAX 4194303U -#elif SIZEOF_PID_T == 2 -#define PID_T_MAX ((pid_t) INT16_MAX) -#define TASKS_MAX 32767U -#else -#error "Unknown pid_t size" -#endif - -assert_cc(TASKS_MAX <= (unsigned long) PID_T_MAX) - -/* Like TAKE_PTR() but for child PIDs, resetting them to 0 */ -#define TAKE_PID(pid) \ - ({ \ - pid_t _pid_ = (pid); \ - (pid) = 0; \ - _pid_; \ - }) diff --git a/src/systemd/src/basic/random-util.c b/src/systemd/src/basic/random-util.c deleted file mode 100644 index 5c07e067..00000000 --- a/src/systemd/src/basic/random-util.c +++ /dev/null @@ -1,220 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#ifdef __x86_64__ -#include <cpuid.h> -#endif - -#include <elf.h> -#include <errno.h> -#include <fcntl.h> -#include <linux/random.h> -#include <stdbool.h> -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <sys/time.h> - -#if HAVE_SYS_AUXV_H -# include <sys/auxv.h> -#endif - -#if USE_SYS_RANDOM_H -# include <sys/random.h> -#else -# include <linux/random.h> -#endif - -#include "fd-util.h" -#include "io-util.h" -#include "missing.h" -#include "random-util.h" -#include "time-util.h" - - -int rdrand64(uint64_t *ret) { - -#ifdef __x86_64__ - static int have_rdrand = -1; - unsigned char err; - - if (have_rdrand < 0) { - uint32_t eax, ebx, ecx, edx; - - /* Check if RDRAND is supported by the CPU */ - if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) == 0) { - have_rdrand = false; - return -EOPNOTSUPP; - } - - have_rdrand = !!(ecx & (1U << 30)); - } - - if (have_rdrand == 0) - return -EOPNOTSUPP; - - asm volatile("rdrand %0;" - "setc %1" - : "=r" (*ret), - "=qm" (err)); - if (!err) - return -EAGAIN; - - return 0; -#else - return -EOPNOTSUPP; -#endif -} - -int acquire_random_bytes(void *p, size_t n, bool high_quality_required) { - static int have_syscall = -1; - - _cleanup_close_ int fd = -1; - size_t already_done = 0; - int r; - - /* Gathers some randomness from the kernel. This call will never block. If - * high_quality_required, it will always return some data from the kernel, - * regardless of whether the random pool is fully initialized or not. - * Otherwise, it will return success if at least some random bytes were - * successfully acquired, and an error if the kernel has no entropy whatsover - * for us. */ - - /* Use the getrandom() syscall unless we know we don't have it. */ - if (have_syscall != 0 && !HAS_FEATURE_MEMORY_SANITIZER) { -#if !HAVE_GETRANDOM - /* NetworkManager Note: systemd calls the syscall directly in this case. Don't add that workaround. - * If you don't compile against a libc that provides getrandom(), you don't get it. */ - r = -1; - errno = ENOSYS; -#else - r = getrandom(p, n, GRND_NONBLOCK); -#endif - if (r > 0) { - have_syscall = true; - if ((size_t) r == n) - return 0; - if (!high_quality_required) { - /* Fill in the remaining bytes using pseudorandom values */ - pseudorandom_bytes((uint8_t*) p + r, n - r); - return 0; - } - - already_done = r; - } else if (errno == ENOSYS) - /* We lack the syscall, continue with reading from /dev/urandom. */ - have_syscall = false; - else if (errno == EAGAIN) { - /* The kernel has no entropy whatsoever. Let's remember to - * use the syscall the next time again though. - * - * If high_quality_required is false, return an error so that - * random_bytes() can produce some pseudorandom - * bytes. Otherwise, fall back to /dev/urandom, which we know - * is empty, but the kernel will produce some bytes for us on - * a best-effort basis. */ - have_syscall = true; - - if (!high_quality_required) { - uint64_t u; - size_t k; - - /* Try x86-64' RDRAND intrinsic if we have it. We only use it if high quality - * randomness is not required, as we don't trust it (who does?). Note that we only do a - * single iteration of RDRAND here, even though the Intel docs suggest calling this in - * a tight loop of 10 invocatins or so. That's because we don't really care about the - * quality here. */ - - if (rdrand64(&u) < 0) - return -ENODATA; - - k = MIN(n, sizeof(u)); - memcpy(p, &u, k); - - /* We only get 64bit out of RDRAND, the rest let's fill up with pseudo-random crap. */ - pseudorandom_bytes((uint8_t*) p + k, n - k); - return 0; - } - } else - return -errno; - } - - fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) - return errno == ENOENT ? -ENOSYS : -errno; - - return loop_read_exact(fd, (uint8_t*) p + already_done, n - already_done, true); -} - -void initialize_srand(void) { - static bool srand_called = false; - unsigned x; -#if HAVE_SYS_AUXV_H - void *auxv; -#endif - - if (srand_called) - return; - -#if HAVE_SYS_AUXV_H - /* The kernel provides us with 16 bytes of entropy in auxv, so let's - * try to make use of that to seed the pseudo-random generator. It's - * better than nothing... */ - - auxv = (void*) getauxval(AT_RANDOM); - if (auxv) { - assert_cc(sizeof(x) <= 16); - memcpy(&x, auxv, sizeof(x)); - } else -#endif - x = 0; - - x ^= (unsigned) now(CLOCK_REALTIME); - x ^= (unsigned) gettid(); - - srand(x); - srand_called = true; -} - -/* INT_MAX gives us only 31 bits, so use 24 out of that. */ -#if RAND_MAX >= INT_MAX -# define RAND_STEP 3 -#else -/* SHORT_INT_MAX or lower gives at most 15 bits, we just just 8 out of that. */ -# define RAND_STEP 1 -#endif - -void pseudorandom_bytes(void *p, size_t n) { - uint8_t *q; - - initialize_srand(); - - for (q = p; q < (uint8_t*) p + n; q += RAND_STEP) { - unsigned rr; - - rr = (unsigned) rand(); - -#if RAND_STEP >= 3 - if ((size_t) (q - (uint8_t*) p + 2) < n) - q[2] = rr >> 16; -#endif -#if RAND_STEP >= 2 - if ((size_t) (q - (uint8_t*) p + 1) < n) - q[1] = rr >> 8; -#endif - q[0] = rr; - } -} - -void random_bytes(void *p, size_t n) { - int r; - - r = acquire_random_bytes(p, n, false); - if (r >= 0) - return; - - /* If some idiot made /dev/urandom unavailable to us, or the - * kernel has no entropy, use a PRNG instead. */ - return pseudorandom_bytes(p, n); -} diff --git a/src/systemd/src/basic/random-util.h b/src/systemd/src/basic/random-util.h deleted file mode 100644 index affcc9ac..00000000 --- a/src/systemd/src/basic/random-util.h +++ /dev/null @@ -1,25 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> - -int acquire_random_bytes(void *p, size_t n, bool high_quality_required); -void pseudorandom_bytes(void *p, size_t n); -void random_bytes(void *p, size_t n); -void initialize_srand(void); - -static inline uint64_t random_u64(void) { - uint64_t u; - random_bytes(&u, sizeof(u)); - return u; -} - -static inline uint32_t random_u32(void) { - uint32_t u; - random_bytes(&u, sizeof(u)); - return u; -} - -int rdrand64(uint64_t *ret); diff --git a/src/systemd/src/basic/refcnt.h b/src/systemd/src/basic/refcnt.h deleted file mode 100644 index d2be6086..00000000 --- a/src/systemd/src/basic/refcnt.h +++ /dev/null @@ -1,16 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -/* A type-safe atomic refcounter. - * - * DO NOT USE THIS UNLESS YOU ACTUALLY CARE ABOUT THREAD SAFETY! */ - -typedef struct { - volatile unsigned _value; -} RefCount; - -#define REFCNT_GET(r) ((r)._value) -#define REFCNT_INC(r) (__sync_add_and_fetch(&(r)._value, 1)) -#define REFCNT_DEC(r) (__sync_sub_and_fetch(&(r)._value, 1)) - -#define REFCNT_INIT ((RefCount) { ._value = 1 }) diff --git a/src/systemd/src/basic/set.h b/src/systemd/src/basic/set.h deleted file mode 100644 index 66471381..00000000 --- a/src/systemd/src/basic/set.h +++ /dev/null @@ -1,132 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include "extract-word.h" -#include "hashmap.h" -#include "macro.h" - -Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS) - -static inline Set *set_free(Set *s) { - internal_hashmap_free(HASHMAP_BASE(s)); - return NULL; -} - -static inline Set *set_free_free(Set *s) { - internal_hashmap_free_free(HASHMAP_BASE(s)); - return NULL; -} - -/* no set_free_free_free */ - -static inline Set *set_copy(Set *s) { - return (Set*) internal_hashmap_copy(HASHMAP_BASE(s)); -} - -int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define set_ensure_allocated(h, ops) internal_set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) - -int set_put(Set *s, const void *key); -/* no set_update */ -/* no set_replace */ -static inline void *set_get(Set *s, void *key) { - return internal_hashmap_get(HASHMAP_BASE(s), key); -} -/* no set_get2 */ - -static inline bool set_contains(Set *s, const void *key) { - return internal_hashmap_contains(HASHMAP_BASE(s), key); -} - -static inline void *set_remove(Set *s, const void *key) { - return internal_hashmap_remove(HASHMAP_BASE(s), key); -} - -/* no set_remove2 */ -/* no set_remove_value */ -int set_remove_and_put(Set *s, const void *old_key, const void *new_key); -/* no set_remove_and_replace */ -int set_merge(Set *s, Set *other); - -static inline int set_reserve(Set *h, unsigned entries_add) { - return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add); -} - -static inline int set_move(Set *s, Set *other) { - return internal_hashmap_move(HASHMAP_BASE(s), HASHMAP_BASE(other)); -} - -static inline int set_move_one(Set *s, Set *other, const void *key) { - return internal_hashmap_move_one(HASHMAP_BASE(s), HASHMAP_BASE(other), key); -} - -static inline unsigned set_size(Set *s) { - return internal_hashmap_size(HASHMAP_BASE(s)); -} - -static inline bool set_isempty(Set *s) { - return set_size(s) == 0; -} - -static inline unsigned set_buckets(Set *s) { - return internal_hashmap_buckets(HASHMAP_BASE(s)); -} - -bool set_iterate(Set *s, Iterator *i, void **value); - -static inline void set_clear(Set *s) { - internal_hashmap_clear(HASHMAP_BASE(s)); -} - -static inline void set_clear_free(Set *s) { - internal_hashmap_clear_free(HASHMAP_BASE(s)); -} - -/* no set_clear_free_free */ - -static inline void *set_steal_first(Set *s) { - return internal_hashmap_steal_first(HASHMAP_BASE(s)); -} - -#define set_clear_with_destructor(_s, _f) \ - ({ \ - void *_item; \ - while ((_item = set_steal_first(_s))) \ - _f(_item); \ - }) -#define set_free_with_destructor(_s, _f) \ - ({ \ - set_clear_with_destructor(_s, _f); \ - set_free(_s); \ - }) - -/* no set_steal_first_key */ -/* no set_first_key */ - -static inline void *set_first(Set *s) { - return internal_hashmap_first(HASHMAP_BASE(s)); -} - -/* no set_next */ - -static inline char **set_get_strv(Set *s) { - return internal_hashmap_get_strv(HASHMAP_BASE(s)); -} - -int set_consume(Set *s, void *value); -int set_put_strdup(Set *s, const char *p); -int set_put_strdupv(Set *s, char **l); -int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags flags); - -#define SET_FOREACH(e, s, i) \ - for ((i) = ITERATOR_FIRST; set_iterate((s), &(i), (void**)&(e)); ) - -#define SET_FOREACH_MOVE(e, d, s) \ - for (; ({ e = set_first(s); assert_se(!e || set_move_one(d, s, e) >= 0); e; }); ) - -DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free); -DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free_free); - -#define _cleanup_set_free_ _cleanup_(set_freep) -#define _cleanup_set_free_free_ _cleanup_(set_free_freep) diff --git a/src/systemd/src/basic/signal-util.h b/src/systemd/src/basic/signal-util.h deleted file mode 100644 index 92f2804c..00000000 --- a/src/systemd/src/basic/signal-util.h +++ /dev/null @@ -1,43 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <signal.h> - -#include "macro.h" - -int reset_all_signal_handlers(void); -int reset_signal_mask(void); - -int ignore_signals(int sig, ...); -int default_signals(int sig, ...); -int sigaction_many(const struct sigaction *sa, ...); - -int sigset_add_many(sigset_t *ss, ...); -int sigprocmask_many(int how, sigset_t *old, ...); - -const char *signal_to_string(int i) _const_; -int signal_from_string(const char *s) _pure_; - -void nop_signal_handler(int sig); - -static inline void block_signals_reset(sigset_t *ss) { - assert_se(sigprocmask(SIG_SETMASK, ss, NULL) >= 0); -} - -#define BLOCK_SIGNALS(...) \ - _cleanup_(block_signals_reset) _unused_ sigset_t _saved_sigset = ({ \ - sigset_t _t; \ - assert_se(sigprocmask_many(SIG_BLOCK, &_t, __VA_ARGS__, -1) >= 0); \ - _t; \ - }) - -static inline bool SIGNAL_VALID(int signo) { - return signo > 0 && signo < _NSIG; -} - -static inline const char* signal_to_string_with_check(int n) { - if (!SIGNAL_VALID(n)) - return NULL; - - return signal_to_string(n); -} diff --git a/src/systemd/src/basic/siphash24.h b/src/systemd/src/basic/siphash24.h deleted file mode 100644 index 77bb9a8c..00000000 --- a/src/systemd/src/basic/siphash24.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include <inttypes.h> -#include <stddef.h> -#include <stdint.h> -#include <sys/types.h> - -#if 0 /* NM_IGNORED */ -struct siphash { - uint64_t v0; - uint64_t v1; - uint64_t v2; - uint64_t v3; - uint64_t padding; - size_t inlen; -}; -#else /* NM_IGNORED */ -struct siphash { - CSipHash _csiphash; -}; - -static inline void -siphash24_init (struct siphash *state, const uint8_t k[16]) -{ - c_siphash_init ((CSipHash *) state, k); -} - -static inline void -siphash24_compress (const void *in, size_t inlen, struct siphash *state) -{ - c_siphash_append ((CSipHash *) state, in, inlen); -} - -static inline uint64_t -siphash24_finalize (struct siphash *state) -{ - return c_siphash_finalize ((CSipHash *) state); -} - -static inline uint64_t -siphash24 (const void *in, size_t inlen, const uint8_t k[16]) -{ - return c_siphash_hash (k, in, inlen); -} -#endif /* NM_IGNORED */ - -void siphash24_init(struct siphash *state, const uint8_t k[16]); -void siphash24_compress(const void *in, size_t inlen, struct siphash *state); -#define siphash24_compress_byte(byte, state) siphash24_compress((const uint8_t[]) { (byte) }, 1, (state)) - -uint64_t siphash24_finalize(struct siphash *state); - -uint64_t siphash24(const void *in, size_t inlen, const uint8_t k[16]); diff --git a/src/systemd/src/basic/socket-util.c b/src/systemd/src/basic/socket-util.c deleted file mode 100644 index acc22901..00000000 --- a/src/systemd/src/basic/socket-util.c +++ /dev/null @@ -1,1254 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <arpa/inet.h> -#include <errno.h> -#include <limits.h> -#include <net/if.h> -#include <netdb.h> -#include <netinet/ip.h> -#include <poll.h> -#include <stddef.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "format-util.h" -#include "log.h" -#include "macro.h" -#include "missing.h" -#include "parse-util.h" -#include "path-util.h" -#include "process-util.h" -#include "socket-util.h" -#include "string-table.h" -#include "string-util.h" -#include "strv.h" -#include "user-util.h" -#include "utf8.h" -#include "util.h" - -#if 0 /* NM_IGNORED */ -#if ENABLE_IDN -# define IDN_FLAGS NI_IDN -#else -# define IDN_FLAGS 0 -#endif - -static const char* const socket_address_type_table[] = { - [SOCK_STREAM] = "Stream", - [SOCK_DGRAM] = "Datagram", - [SOCK_RAW] = "Raw", - [SOCK_RDM] = "ReliableDatagram", - [SOCK_SEQPACKET] = "SequentialPacket", - [SOCK_DCCP] = "DatagramCongestionControl", -}; - -DEFINE_STRING_TABLE_LOOKUP(socket_address_type, int); - -int socket_address_parse(SocketAddress *a, const char *s) { - _cleanup_free_ char *n = NULL; - char *e; - int r; - - assert(a); - assert(s); - - zero(*a); - a->type = SOCK_STREAM; - - if (*s == '[') { - uint16_t port; - - /* IPv6 in [x:.....:z]:p notation */ - - e = strchr(s+1, ']'); - if (!e) - return -EINVAL; - - n = strndup(s+1, e-s-1); - if (!n) - return -ENOMEM; - - errno = 0; - if (inet_pton(AF_INET6, n, &a->sockaddr.in6.sin6_addr) <= 0) - return errno > 0 ? -errno : -EINVAL; - - e++; - if (*e != ':') - return -EINVAL; - - e++; - r = parse_ip_port(e, &port); - if (r < 0) - return r; - - a->sockaddr.in6.sin6_family = AF_INET6; - a->sockaddr.in6.sin6_port = htobe16(port); - a->size = sizeof(struct sockaddr_in6); - - } else if (*s == '/') { - /* AF_UNIX socket */ - - size_t l; - - l = strlen(s); - if (l >= sizeof(a->sockaddr.un.sun_path)) - return -EINVAL; - - a->sockaddr.un.sun_family = AF_UNIX; - memcpy(a->sockaddr.un.sun_path, s, l); - a->size = offsetof(struct sockaddr_un, sun_path) + l + 1; - - } else if (*s == '@') { - /* Abstract AF_UNIX socket */ - size_t l; - - l = strlen(s+1); - if (l >= sizeof(a->sockaddr.un.sun_path) - 1) - return -EINVAL; - - a->sockaddr.un.sun_family = AF_UNIX; - memcpy(a->sockaddr.un.sun_path+1, s+1, l); - a->size = offsetof(struct sockaddr_un, sun_path) + 1 + l; - - } else if (startswith(s, "vsock:")) { - /* AF_VSOCK socket in vsock:cid:port notation */ - const char *cid_start = s + STRLEN("vsock:"); - unsigned port; - - e = strchr(cid_start, ':'); - if (!e) - return -EINVAL; - - r = safe_atou(e+1, &port); - if (r < 0) - return r; - - n = strndup(cid_start, e - cid_start); - if (!n) - return -ENOMEM; - - if (!isempty(n)) { - r = safe_atou(n, &a->sockaddr.vm.svm_cid); - if (r < 0) - return r; - } else - a->sockaddr.vm.svm_cid = VMADDR_CID_ANY; - - a->sockaddr.vm.svm_family = AF_VSOCK; - a->sockaddr.vm.svm_port = port; - a->size = sizeof(struct sockaddr_vm); - - } else { - uint16_t port; - - e = strchr(s, ':'); - if (e) { - r = parse_ip_port(e + 1, &port); - if (r < 0) - return r; - - n = strndup(s, e-s); - if (!n) - return -ENOMEM; - - /* IPv4 in w.x.y.z:p notation? */ - r = inet_pton(AF_INET, n, &a->sockaddr.in.sin_addr); - if (r < 0) - return -errno; - - if (r > 0) { - /* Gotcha, it's a traditional IPv4 address */ - a->sockaddr.in.sin_family = AF_INET; - a->sockaddr.in.sin_port = htobe16(port); - a->size = sizeof(struct sockaddr_in); - } else { - unsigned idx; - - if (strlen(n) > IF_NAMESIZE-1) - return -EINVAL; - - /* Uh, our last resort, an interface name */ - idx = if_nametoindex(n); - if (idx == 0) - return -EINVAL; - - a->sockaddr.in6.sin6_family = AF_INET6; - a->sockaddr.in6.sin6_port = htobe16(port); - a->sockaddr.in6.sin6_scope_id = idx; - a->sockaddr.in6.sin6_addr = in6addr_any; - a->size = sizeof(struct sockaddr_in6); - } - } else { - - /* Just a port */ - r = parse_ip_port(s, &port); - if (r < 0) - return r; - - if (socket_ipv6_is_supported()) { - a->sockaddr.in6.sin6_family = AF_INET6; - a->sockaddr.in6.sin6_port = htobe16(port); - a->sockaddr.in6.sin6_addr = in6addr_any; - a->size = sizeof(struct sockaddr_in6); - } else { - a->sockaddr.in.sin_family = AF_INET; - a->sockaddr.in.sin_port = htobe16(port); - a->sockaddr.in.sin_addr.s_addr = INADDR_ANY; - a->size = sizeof(struct sockaddr_in); - } - } - } - - return 0; -} - -int socket_address_parse_and_warn(SocketAddress *a, const char *s) { - SocketAddress b; - int r; - - /* Similar to socket_address_parse() but warns for IPv6 sockets when we don't support them. */ - - r = socket_address_parse(&b, s); - if (r < 0) - return r; - - if (!socket_ipv6_is_supported() && b.sockaddr.sa.sa_family == AF_INET6) { - log_warning("Binding to IPv6 address not available since kernel does not support IPv6."); - return -EAFNOSUPPORT; - } - - *a = b; - return 0; -} - -int socket_address_parse_netlink(SocketAddress *a, const char *s) { - int family; - unsigned group = 0; - _cleanup_free_ char *sfamily = NULL; - assert(a); - assert(s); - - zero(*a); - a->type = SOCK_RAW; - - errno = 0; - if (sscanf(s, "%ms %u", &sfamily, &group) < 1) - return errno > 0 ? -errno : -EINVAL; - - family = netlink_family_from_string(sfamily); - if (family < 0) - return -EINVAL; - - a->sockaddr.nl.nl_family = AF_NETLINK; - a->sockaddr.nl.nl_groups = group; - - a->type = SOCK_RAW; - a->size = sizeof(struct sockaddr_nl); - a->protocol = family; - - return 0; -} - -int socket_address_verify(const SocketAddress *a) { - assert(a); - - switch (socket_address_family(a)) { - - case AF_INET: - if (a->size != sizeof(struct sockaddr_in)) - return -EINVAL; - - if (a->sockaddr.in.sin_port == 0) - return -EINVAL; - - if (!IN_SET(a->type, SOCK_STREAM, SOCK_DGRAM)) - return -EINVAL; - - return 0; - - case AF_INET6: - if (a->size != sizeof(struct sockaddr_in6)) - return -EINVAL; - - if (a->sockaddr.in6.sin6_port == 0) - return -EINVAL; - - if (!IN_SET(a->type, SOCK_STREAM, SOCK_DGRAM)) - return -EINVAL; - - return 0; - - case AF_UNIX: - if (a->size < offsetof(struct sockaddr_un, sun_path)) - return -EINVAL; - - if (a->size > offsetof(struct sockaddr_un, sun_path)) { - - if (a->sockaddr.un.sun_path[0] != 0) { - char *e; - - /* path */ - e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path)); - if (!e) - return -EINVAL; - - if (a->size != offsetof(struct sockaddr_un, sun_path) + (e - a->sockaddr.un.sun_path) + 1) - return -EINVAL; - } - } - - if (!IN_SET(a->type, SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET)) - return -EINVAL; - - return 0; - - case AF_NETLINK: - - if (a->size != sizeof(struct sockaddr_nl)) - return -EINVAL; - - if (!IN_SET(a->type, SOCK_RAW, SOCK_DGRAM)) - return -EINVAL; - - return 0; - - case AF_VSOCK: - if (a->size != sizeof(struct sockaddr_vm)) - return -EINVAL; - - if (!IN_SET(a->type, SOCK_STREAM, SOCK_DGRAM)) - return -EINVAL; - - return 0; - - default: - return -EAFNOSUPPORT; - } -} - -int socket_address_print(const SocketAddress *a, char **ret) { - int r; - - assert(a); - assert(ret); - - r = socket_address_verify(a); - if (r < 0) - return r; - - if (socket_address_family(a) == AF_NETLINK) { - _cleanup_free_ char *sfamily = NULL; - - r = netlink_family_to_string_alloc(a->protocol, &sfamily); - if (r < 0) - return r; - - r = asprintf(ret, "%s %u", sfamily, a->sockaddr.nl.nl_groups); - if (r < 0) - return -ENOMEM; - - return 0; - } - - return sockaddr_pretty(&a->sockaddr.sa, a->size, false, true, ret); -} - -bool socket_address_can_accept(const SocketAddress *a) { - assert(a); - - return - IN_SET(a->type, SOCK_STREAM, SOCK_SEQPACKET); -} - -bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) { - assert(a); - assert(b); - - /* Invalid addresses are unequal to all */ - if (socket_address_verify(a) < 0 || - socket_address_verify(b) < 0) - return false; - - if (a->type != b->type) - return false; - - if (socket_address_family(a) != socket_address_family(b)) - return false; - - switch (socket_address_family(a)) { - - case AF_INET: - if (a->sockaddr.in.sin_addr.s_addr != b->sockaddr.in.sin_addr.s_addr) - return false; - - if (a->sockaddr.in.sin_port != b->sockaddr.in.sin_port) - return false; - - break; - - case AF_INET6: - if (memcmp(&a->sockaddr.in6.sin6_addr, &b->sockaddr.in6.sin6_addr, sizeof(a->sockaddr.in6.sin6_addr)) != 0) - return false; - - if (a->sockaddr.in6.sin6_port != b->sockaddr.in6.sin6_port) - return false; - - break; - - case AF_UNIX: - if (a->size <= offsetof(struct sockaddr_un, sun_path) || - b->size <= offsetof(struct sockaddr_un, sun_path)) - return false; - - if ((a->sockaddr.un.sun_path[0] == 0) != (b->sockaddr.un.sun_path[0] == 0)) - return false; - - if (a->sockaddr.un.sun_path[0]) { - if (!path_equal_or_files_same(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, 0)) - return false; - } else { - if (a->size != b->size) - return false; - - if (memcmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, a->size) != 0) - return false; - } - - break; - - case AF_NETLINK: - if (a->protocol != b->protocol) - return false; - - if (a->sockaddr.nl.nl_groups != b->sockaddr.nl.nl_groups) - return false; - - break; - - case AF_VSOCK: - if (a->sockaddr.vm.svm_cid != b->sockaddr.vm.svm_cid) - return false; - - if (a->sockaddr.vm.svm_port != b->sockaddr.vm.svm_port) - return false; - - break; - - default: - /* Cannot compare, so we assume the addresses are different */ - return false; - } - - return true; -} - -bool socket_address_is(const SocketAddress *a, const char *s, int type) { - struct SocketAddress b; - - assert(a); - assert(s); - - if (socket_address_parse(&b, s) < 0) - return false; - - b.type = type; - - return socket_address_equal(a, &b); -} - -bool socket_address_is_netlink(const SocketAddress *a, const char *s) { - struct SocketAddress b; - - assert(a); - assert(s); - - if (socket_address_parse_netlink(&b, s) < 0) - return false; - - return socket_address_equal(a, &b); -} - -const char* socket_address_get_path(const SocketAddress *a) { - assert(a); - - if (socket_address_family(a) != AF_UNIX) - return NULL; - - if (a->sockaddr.un.sun_path[0] == 0) - return NULL; - - return a->sockaddr.un.sun_path; -} - -bool socket_ipv6_is_supported(void) { - if (access("/proc/net/if_inet6", F_OK) != 0) - return false; - - return true; -} - -bool socket_address_matches_fd(const SocketAddress *a, int fd) { - SocketAddress b; - socklen_t solen; - - assert(a); - assert(fd >= 0); - - b.size = sizeof(b.sockaddr); - if (getsockname(fd, &b.sockaddr.sa, &b.size) < 0) - return false; - - if (b.sockaddr.sa.sa_family != a->sockaddr.sa.sa_family) - return false; - - solen = sizeof(b.type); - if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &b.type, &solen) < 0) - return false; - - if (b.type != a->type) - return false; - - if (a->protocol != 0) { - solen = sizeof(b.protocol); - if (getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &b.protocol, &solen) < 0) - return false; - - if (b.protocol != a->protocol) - return false; - } - - return socket_address_equal(a, &b); -} - -int sockaddr_port(const struct sockaddr *_sa, unsigned *ret_port) { - union sockaddr_union *sa = (union sockaddr_union*) _sa; - - /* Note, this returns the port as 'unsigned' rather than 'uint16_t', as AF_VSOCK knows larger ports */ - - assert(sa); - - switch (sa->sa.sa_family) { - - case AF_INET: - *ret_port = be16toh(sa->in.sin_port); - return 0; - - case AF_INET6: - *ret_port = be16toh(sa->in6.sin6_port); - return 0; - - case AF_VSOCK: - *ret_port = sa->vm.svm_port; - return 0; - - default: - return -EAFNOSUPPORT; - } -} - -int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret) { - union sockaddr_union *sa = (union sockaddr_union*) _sa; - char *p; - int r; - - assert(sa); - assert(salen >= sizeof(sa->sa.sa_family)); - - switch (sa->sa.sa_family) { - - case AF_INET: { - uint32_t a; - - a = be32toh(sa->in.sin_addr.s_addr); - - if (include_port) - r = asprintf(&p, - "%u.%u.%u.%u:%u", - a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF, - be16toh(sa->in.sin_port)); - else - r = asprintf(&p, - "%u.%u.%u.%u", - a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF); - if (r < 0) - return -ENOMEM; - break; - } - - case AF_INET6: { - static const unsigned char ipv4_prefix[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF - }; - - if (translate_ipv6 && - memcmp(&sa->in6.sin6_addr, ipv4_prefix, sizeof(ipv4_prefix)) == 0) { - const uint8_t *a = sa->in6.sin6_addr.s6_addr+12; - if (include_port) - r = asprintf(&p, - "%u.%u.%u.%u:%u", - a[0], a[1], a[2], a[3], - be16toh(sa->in6.sin6_port)); - else - r = asprintf(&p, - "%u.%u.%u.%u", - a[0], a[1], a[2], a[3]); - if (r < 0) - return -ENOMEM; - } else { - char a[INET6_ADDRSTRLEN]; - - inet_ntop(AF_INET6, &sa->in6.sin6_addr, a, sizeof(a)); - - if (include_port) { - r = asprintf(&p, - "[%s]:%u", - a, - be16toh(sa->in6.sin6_port)); - if (r < 0) - return -ENOMEM; - } else { - p = strdup(a); - if (!p) - return -ENOMEM; - } - } - - break; - } - - case AF_UNIX: - if (salen <= offsetof(struct sockaddr_un, sun_path)) { - p = strdup("<unnamed>"); - if (!p) - return -ENOMEM; - - } else if (sa->un.sun_path[0] == 0) { - /* abstract */ - - /* FIXME: We assume we can print the - * socket path here and that it hasn't - * more than one NUL byte. That is - * actually an invalid assumption */ - - p = new(char, sizeof(sa->un.sun_path)+1); - if (!p) - return -ENOMEM; - - p[0] = '@'; - memcpy(p+1, sa->un.sun_path+1, sizeof(sa->un.sun_path)-1); - p[sizeof(sa->un.sun_path)] = 0; - - } else { - p = strndup(sa->un.sun_path, sizeof(sa->un.sun_path)); - if (!p) - return -ENOMEM; - } - - break; - - case AF_VSOCK: - if (include_port) - r = asprintf(&p, - "vsock:%u:%u", - sa->vm.svm_cid, - sa->vm.svm_port); - else - r = asprintf(&p, "vsock:%u", sa->vm.svm_cid); - if (r < 0) - return -ENOMEM; - break; - - default: - return -EOPNOTSUPP; - } - - *ret = p; - return 0; -} - -int getpeername_pretty(int fd, bool include_port, char **ret) { - union sockaddr_union sa; - socklen_t salen = sizeof(sa); - int r; - - assert(fd >= 0); - assert(ret); - - if (getpeername(fd, &sa.sa, &salen) < 0) - return -errno; - - if (sa.sa.sa_family == AF_UNIX) { - struct ucred ucred = {}; - - /* UNIX connection sockets are anonymous, so let's use - * PID/UID as pretty credentials instead */ - - r = getpeercred(fd, &ucred); - if (r < 0) - return r; - - if (asprintf(ret, "PID "PID_FMT"/UID "UID_FMT, ucred.pid, ucred.uid) < 0) - return -ENOMEM; - - return 0; - } - - /* For remote sockets we translate IPv6 addresses back to IPv4 - * if applicable, since that's nicer. */ - - return sockaddr_pretty(&sa.sa, salen, true, include_port, ret); -} - -int getsockname_pretty(int fd, char **ret) { - union sockaddr_union sa; - socklen_t salen = sizeof(sa); - - assert(fd >= 0); - assert(ret); - - if (getsockname(fd, &sa.sa, &salen) < 0) - return -errno; - - /* For local sockets we do not translate IPv6 addresses back - * to IPv6 if applicable, since this is usually used for - * listening sockets where the difference between IPv4 and - * IPv6 matters. */ - - return sockaddr_pretty(&sa.sa, salen, false, true, ret); -} - -int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret) { - int r; - char host[NI_MAXHOST], *ret; - - assert(_ret); - - r = getnameinfo(&sa->sa, salen, host, sizeof(host), NULL, 0, IDN_FLAGS); - if (r != 0) { - int saved_errno = errno; - - r = sockaddr_pretty(&sa->sa, salen, true, true, &ret); - if (r < 0) - return r; - - log_debug_errno(saved_errno, "getnameinfo(%s) failed: %m", ret); - } else { - ret = strdup(host); - if (!ret) - return -ENOMEM; - } - - *_ret = ret; - return 0; -} - -int socket_address_unlink(SocketAddress *a) { - assert(a); - - if (socket_address_family(a) != AF_UNIX) - return 0; - - if (a->sockaddr.un.sun_path[0] == 0) - return 0; - - if (unlink(a->sockaddr.un.sun_path) < 0) - return -errno; - - return 1; -} - -static const char* const netlink_family_table[] = { - [NETLINK_ROUTE] = "route", - [NETLINK_FIREWALL] = "firewall", - [NETLINK_INET_DIAG] = "inet-diag", - [NETLINK_NFLOG] = "nflog", - [NETLINK_XFRM] = "xfrm", - [NETLINK_SELINUX] = "selinux", - [NETLINK_ISCSI] = "iscsi", - [NETLINK_AUDIT] = "audit", - [NETLINK_FIB_LOOKUP] = "fib-lookup", - [NETLINK_CONNECTOR] = "connector", - [NETLINK_NETFILTER] = "netfilter", - [NETLINK_IP6_FW] = "ip6-fw", - [NETLINK_DNRTMSG] = "dnrtmsg", - [NETLINK_KOBJECT_UEVENT] = "kobject-uevent", - [NETLINK_GENERIC] = "generic", - [NETLINK_SCSITRANSPORT] = "scsitransport", - [NETLINK_ECRYPTFS] = "ecryptfs", - [NETLINK_RDMA] = "rdma", -}; - -DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(netlink_family, int, INT_MAX); - -static const char* const socket_address_bind_ipv6_only_table[_SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX] = { - [SOCKET_ADDRESS_DEFAULT] = "default", - [SOCKET_ADDRESS_BOTH] = "both", - [SOCKET_ADDRESS_IPV6_ONLY] = "ipv6-only" -}; - -DEFINE_STRING_TABLE_LOOKUP(socket_address_bind_ipv6_only, SocketAddressBindIPv6Only); - -SocketAddressBindIPv6Only socket_address_bind_ipv6_only_or_bool_from_string(const char *n) { - int r; - - r = parse_boolean(n); - if (r > 0) - return SOCKET_ADDRESS_IPV6_ONLY; - if (r == 0) - return SOCKET_ADDRESS_BOTH; - - return socket_address_bind_ipv6_only_from_string(n); -} - -bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b) { - assert(a); - assert(b); - - if (a->sa.sa_family != b->sa.sa_family) - return false; - - if (a->sa.sa_family == AF_INET) - return a->in.sin_addr.s_addr == b->in.sin_addr.s_addr; - - if (a->sa.sa_family == AF_INET6) - return memcmp(&a->in6.sin6_addr, &b->in6.sin6_addr, sizeof(a->in6.sin6_addr)) == 0; - - if (a->sa.sa_family == AF_VSOCK) - return a->vm.svm_cid == b->vm.svm_cid; - - return false; -} - -int fd_inc_sndbuf(int fd, size_t n) { - int r, value; - socklen_t l = sizeof(value); - - r = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, &l); - if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2) - return 0; - - /* If we have the privileges we will ignore the kernel limit. */ - - value = (int) n; - if (setsockopt(fd, SOL_SOCKET, SO_SNDBUFFORCE, &value, sizeof(value)) < 0) - if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)) < 0) - return -errno; - - return 1; -} - -int fd_inc_rcvbuf(int fd, size_t n) { - int r, value; - socklen_t l = sizeof(value); - - r = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, &l); - if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2) - return 0; - - /* If we have the privileges we will ignore the kernel limit. */ - - value = (int) n; - if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &value, sizeof(value)) < 0) - if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)) < 0) - return -errno; - return 1; -} - -static const char* const ip_tos_table[] = { - [IPTOS_LOWDELAY] = "low-delay", - [IPTOS_THROUGHPUT] = "throughput", - [IPTOS_RELIABILITY] = "reliability", - [IPTOS_LOWCOST] = "low-cost", -}; - -DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ip_tos, int, 0xff); - -bool ifname_valid(const char *p) { - bool numeric = true; - - /* Checks whether a network interface name is valid. This is inspired by dev_valid_name() in the kernel sources - * but slightly stricter, as we only allow non-control, non-space ASCII characters in the interface name. We - * also don't permit names that only container numbers, to avoid confusion with numeric interface indexes. */ - - if (isempty(p)) - return false; - - if (strlen(p) >= IFNAMSIZ) - return false; - - if (dot_or_dot_dot(p)) - return false; - - while (*p) { - if ((unsigned char) *p >= 127U) - return false; - - if ((unsigned char) *p <= 32U) - return false; - - if (IN_SET(*p, ':', '/')) - return false; - - numeric = numeric && (*p >= '0' && *p <= '9'); - p++; - } - - if (numeric) - return false; - - return true; -} - -bool address_label_valid(const char *p) { - - if (isempty(p)) - return false; - - if (strlen(p) >= IFNAMSIZ) - return false; - - while (*p) { - if ((uint8_t) *p >= 127U) - return false; - - if ((uint8_t) *p <= 31U) - return false; - p++; - } - - return true; -} - -int getpeercred(int fd, struct ucred *ucred) { - socklen_t n = sizeof(struct ucred); - struct ucred u; - int r; - - assert(fd >= 0); - assert(ucred); - - r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &u, &n); - if (r < 0) - return -errno; - - if (n != sizeof(struct ucred)) - return -EIO; - - /* Check if the data is actually useful and not suppressed due to namespacing issues */ - if (!pid_is_valid(u.pid)) - return -ENODATA; - - /* Note that we don't check UID/GID here, as namespace translation works differently there: instead of - * receiving in "invalid" user/group we get the overflow UID/GID. */ - - *ucred = u; - return 0; -} - -int getpeersec(int fd, char **ret) { - _cleanup_free_ char *s = NULL; - socklen_t n = 64; - - assert(fd >= 0); - assert(ret); - - for (;;) { - s = new0(char, n+1); - if (!s) - return -ENOMEM; - - if (getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n) >= 0) - break; - - if (errno != ERANGE) - return -errno; - - s = mfree(s); - } - - if (isempty(s)) - return -EOPNOTSUPP; - - *ret = TAKE_PTR(s); - - return 0; -} - -int getpeergroups(int fd, gid_t **ret) { - socklen_t n = sizeof(gid_t) * 64; - _cleanup_free_ gid_t *d = NULL; - - assert(fd >= 0); - assert(ret); - - for (;;) { - d = malloc(n); - if (!d) - return -ENOMEM; - - if (getsockopt(fd, SOL_SOCKET, SO_PEERGROUPS, d, &n) >= 0) - break; - - if (errno != ERANGE) - return -errno; - - d = mfree(d); - } - - assert_se(n % sizeof(gid_t) == 0); - n /= sizeof(gid_t); - - if ((socklen_t) (int) n != n) - return -E2BIG; - - *ret = TAKE_PTR(d); - - return (int) n; -} - -ssize_t send_one_fd_iov_sa( - int transport_fd, - int fd, - struct iovec *iov, size_t iovlen, - const struct sockaddr *sa, socklen_t len, - int flags) { - - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(int))]; - } control = {}; - struct msghdr mh = { - .msg_name = (struct sockaddr*) sa, - .msg_namelen = len, - .msg_iov = iov, - .msg_iovlen = iovlen, - }; - ssize_t k; - - assert(transport_fd >= 0); - - /* - * We need either an FD or data to send. - * If there's nothing, return an error. - */ - if (fd < 0 && !iov) - return -EINVAL; - - if (fd >= 0) { - struct cmsghdr *cmsg; - - mh.msg_control = &control; - mh.msg_controllen = sizeof(control); - - cmsg = CMSG_FIRSTHDR(&mh); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - cmsg->cmsg_len = CMSG_LEN(sizeof(int)); - memcpy(CMSG_DATA(cmsg), &fd, sizeof(int)); - - mh.msg_controllen = CMSG_SPACE(sizeof(int)); - } - k = sendmsg(transport_fd, &mh, MSG_NOSIGNAL | flags); - if (k < 0) - return (ssize_t) -errno; - - return k; -} - -int send_one_fd_sa( - int transport_fd, - int fd, - const struct sockaddr *sa, socklen_t len, - int flags) { - - assert(fd >= 0); - - return (int) send_one_fd_iov_sa(transport_fd, fd, NULL, 0, sa, len, flags); -} - -ssize_t receive_one_fd_iov( - int transport_fd, - struct iovec *iov, size_t iovlen, - int flags, - int *ret_fd) { - - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(int))]; - } control = {}; - struct msghdr mh = { - .msg_control = &control, - .msg_controllen = sizeof(control), - .msg_iov = iov, - .msg_iovlen = iovlen, - }; - struct cmsghdr *cmsg, *found = NULL; - ssize_t k; - - assert(transport_fd >= 0); - assert(ret_fd); - - /* - * Receive a single FD via @transport_fd. We don't care for - * the transport-type. We retrieve a single FD at most, so for - * packet-based transports, the caller must ensure to send - * only a single FD per packet. This is best used in - * combination with send_one_fd(). - */ - - k = recvmsg(transport_fd, &mh, MSG_CMSG_CLOEXEC | flags); - if (k < 0) - return (ssize_t) -errno; - - CMSG_FOREACH(cmsg, &mh) { - if (cmsg->cmsg_level == SOL_SOCKET && - cmsg->cmsg_type == SCM_RIGHTS && - cmsg->cmsg_len == CMSG_LEN(sizeof(int))) { - assert(!found); - found = cmsg; - break; - } - } - - if (!found) - cmsg_close_all(&mh); - - /* If didn't receive an FD or any data, return an error. */ - if (k == 0 && !found) - return -EIO; - - if (found) - *ret_fd = *(int*) CMSG_DATA(found); - else - *ret_fd = -1; - - return k; -} - -int receive_one_fd(int transport_fd, int flags) { - int fd; - ssize_t k; - - k = receive_one_fd_iov(transport_fd, NULL, 0, flags, &fd); - if (k == 0) - return fd; - - /* k must be negative, since receive_one_fd_iov() only returns - * a positive value if data was received through the iov. */ - assert(k < 0); - return (int) k; -} -#endif /* NM_IGNORED */ - -ssize_t next_datagram_size_fd(int fd) { - ssize_t l; - int k; - - /* This is a bit like FIONREAD/SIOCINQ, however a bit more powerful. The difference being: recv(MSG_PEEK) will - * actually cause the next datagram in the queue to be validated regarding checksums, which FIONREAD doesn't - * do. This difference is actually of major importance as we need to be sure that the size returned here - * actually matches what we will read with recvmsg() next, as otherwise we might end up allocating a buffer of - * the wrong size. */ - - l = recv(fd, NULL, 0, MSG_PEEK|MSG_TRUNC); - if (l < 0) { - if (IN_SET(errno, EOPNOTSUPP, EFAULT)) - goto fallback; - - return -errno; - } - if (l == 0) - goto fallback; - - return l; - -fallback: - k = 0; - - /* Some sockets (AF_PACKET) do not support null-sized recv() with MSG_TRUNC set, let's fall back to FIONREAD - * for them. Checksums don't matter for raw sockets anyway, hence this should be fine. */ - - if (ioctl(fd, FIONREAD, &k) < 0) - return -errno; - - return (ssize_t) k; -} - -#if 0 /* NM_IGNORED */ -int flush_accept(int fd) { - - struct pollfd pollfd = { - .fd = fd, - .events = POLLIN, - }; - int r; - - /* Similar to flush_fd() but flushes all incoming connection by accepting them and immediately closing them. */ - - for (;;) { - int cfd; - - r = poll(&pollfd, 1, 0); - if (r < 0) { - if (errno == EINTR) - continue; - - return -errno; - - } else if (r == 0) - return 0; - - cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); - if (cfd < 0) { - if (errno == EINTR) - continue; - - if (errno == EAGAIN) - return 0; - - return -errno; - } - - close(cfd); - } -} - -struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t length) { - struct cmsghdr *cmsg; - - assert(mh); - - CMSG_FOREACH(cmsg, mh) - if (cmsg->cmsg_level == level && - cmsg->cmsg_type == type && - (length == (socklen_t) -1 || length == cmsg->cmsg_len)) - return cmsg; - - return NULL; -} - -int socket_ioctl_fd(void) { - int fd; - - /* Create a socket to invoke the various network interface ioctl()s on. Traditionally only AF_INET was good for - * that. Since kernel 4.6 AF_NETLINK works for this too. We first try to use AF_INET hence, but if that's not - * available (for example, because it is made unavailable via SECCOMP or such), we'll fall back to the more - * generic AF_NETLINK. */ - - fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, 0); - if (fd < 0) - fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_GENERIC); - if (fd < 0) - return -errno; - - return fd; -} -#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/socket-util.h b/src/systemd/src/basic/socket-util.h deleted file mode 100644 index d7b814ae..00000000 --- a/src/systemd/src/basic/socket-util.h +++ /dev/null @@ -1,187 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <netinet/ether.h> -#include <netinet/in.h> -#include <stdbool.h> -#include <stddef.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/un.h> -#include <linux/netlink.h> -#include <linux/if_infiniband.h> -#include <linux/if_packet.h> - -#include "macro.h" -#include "missing.h" -#include "util.h" - -union sockaddr_union { - /* The minimal, abstract version */ - struct sockaddr sa; - - /* The libc provided version that allocates "enough room" for every protocol */ - struct sockaddr_storage storage; - - /* Protoctol-specific implementations */ - struct sockaddr_in in; - struct sockaddr_in6 in6; - struct sockaddr_un un; - struct sockaddr_nl nl; - struct sockaddr_ll ll; -#if 0 /* NM_IGNORED */ - struct sockaddr_vm vm; -#endif /* NM_IGNORED */ - - /* Ensure there is enough space to store Infiniband addresses */ - uint8_t ll_buffer[offsetof(struct sockaddr_ll, sll_addr) + CONST_MAX(ETH_ALEN, INFINIBAND_ALEN)]; - - /* Ensure there is enough space after the AF_UNIX sun_path for one more NUL byte, just to be sure that the path - * component is always followed by at least one NUL byte. */ - uint8_t un_buffer[sizeof(struct sockaddr_un) + 1]; -}; - -typedef struct SocketAddress { - union sockaddr_union sockaddr; - - /* We store the size here explicitly due to the weird - * sockaddr_un semantics for abstract sockets */ - socklen_t size; - - /* Socket type, i.e. SOCK_STREAM, SOCK_DGRAM, ... */ - int type; - - /* Socket protocol, IPPROTO_xxx, usually 0, except for netlink */ - int protocol; -} SocketAddress; - -typedef enum SocketAddressBindIPv6Only { - SOCKET_ADDRESS_DEFAULT, - SOCKET_ADDRESS_BOTH, - SOCKET_ADDRESS_IPV6_ONLY, - _SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX, - _SOCKET_ADDRESS_BIND_IPV6_ONLY_INVALID = -1 -} SocketAddressBindIPv6Only; - -#define socket_address_family(a) ((a)->sockaddr.sa.sa_family) - -const char* socket_address_type_to_string(int t) _const_; -int socket_address_type_from_string(const char *s) _pure_; - -int socket_address_parse(SocketAddress *a, const char *s); -int socket_address_parse_and_warn(SocketAddress *a, const char *s); -int socket_address_parse_netlink(SocketAddress *a, const char *s); -int socket_address_print(const SocketAddress *a, char **p); -int socket_address_verify(const SocketAddress *a) _pure_; -int socket_address_unlink(SocketAddress *a); - -bool socket_address_can_accept(const SocketAddress *a) _pure_; - -int socket_address_listen( - const SocketAddress *a, - int flags, - int backlog, - SocketAddressBindIPv6Only only, - const char *bind_to_device, - bool reuse_port, - bool free_bind, - bool transparent, - mode_t directory_mode, - mode_t socket_mode, - const char *label); -int make_socket_fd(int log_level, const char* address, int type, int flags); - -bool socket_address_is(const SocketAddress *a, const char *s, int type); -bool socket_address_is_netlink(const SocketAddress *a, const char *s); - -bool socket_address_matches_fd(const SocketAddress *a, int fd); - -bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) _pure_; - -const char* socket_address_get_path(const SocketAddress *a); - -bool socket_ipv6_is_supported(void); - -int sockaddr_port(const struct sockaddr *_sa, unsigned *port); - -int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret); -int getpeername_pretty(int fd, bool include_port, char **ret); -int getsockname_pretty(int fd, char **ret); - -int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret); - -const char* socket_address_bind_ipv6_only_to_string(SocketAddressBindIPv6Only b) _const_; -SocketAddressBindIPv6Only socket_address_bind_ipv6_only_from_string(const char *s) _pure_; -SocketAddressBindIPv6Only socket_address_bind_ipv6_only_or_bool_from_string(const char *s); - -int netlink_family_to_string_alloc(int b, char **s); -int netlink_family_from_string(const char *s) _pure_; - -bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b); - -int fd_inc_sndbuf(int fd, size_t n); -int fd_inc_rcvbuf(int fd, size_t n); - -int ip_tos_to_string_alloc(int i, char **s); -int ip_tos_from_string(const char *s); - -bool ifname_valid(const char *p); -bool address_label_valid(const char *p); - -int getpeercred(int fd, struct ucred *ucred); -int getpeersec(int fd, char **ret); -int getpeergroups(int fd, gid_t **ret); - -ssize_t send_one_fd_iov_sa( - int transport_fd, - int fd, - struct iovec *iov, size_t iovlen, - const struct sockaddr *sa, socklen_t len, - int flags); -int send_one_fd_sa(int transport_fd, - int fd, - const struct sockaddr *sa, socklen_t len, - int flags); -#define send_one_fd_iov(transport_fd, fd, iov, iovlen, flags) send_one_fd_iov_sa(transport_fd, fd, iov, iovlen, NULL, 0, flags) -#define send_one_fd(transport_fd, fd, flags) send_one_fd_iov_sa(transport_fd, fd, NULL, 0, NULL, 0, flags) -ssize_t receive_one_fd_iov(int transport_fd, struct iovec *iov, size_t iovlen, int flags, int *ret_fd); -int receive_one_fd(int transport_fd, int flags); - -ssize_t next_datagram_size_fd(int fd); - -int flush_accept(int fd); - -#define CMSG_FOREACH(cmsg, mh) \ - for ((cmsg) = CMSG_FIRSTHDR(mh); (cmsg); (cmsg) = CMSG_NXTHDR((mh), (cmsg))) - -struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t length); - -/* - * Certain hardware address types (e.g Infiniband) do not fit into sll_addr - * (8 bytes) and run over the structure. This macro returns the correct size that - * must be passed to kernel. - */ -#define SOCKADDR_LL_LEN(sa) \ - ({ \ - const struct sockaddr_ll *_sa = &(sa); \ - size_t _mac_len = sizeof(_sa->sll_addr); \ - assert(_sa->sll_family == AF_PACKET); \ - if (be16toh(_sa->sll_hatype) == ARPHRD_ETHER) \ - _mac_len = MAX(_mac_len, (size_t) ETH_ALEN); \ - if (be16toh(_sa->sll_hatype) == ARPHRD_INFINIBAND) \ - _mac_len = MAX(_mac_len, (size_t) INFINIBAND_ALEN); \ - offsetof(struct sockaddr_ll, sll_addr) + _mac_len; \ - }) - -/* Covers only file system and abstract AF_UNIX socket addresses, but not unnamed socket addresses. */ -#define SOCKADDR_UN_LEN(sa) \ - ({ \ - const struct sockaddr_un *_sa = &(sa); \ - assert(_sa->sun_family == AF_UNIX); \ - offsetof(struct sockaddr_un, sun_path) + \ - (_sa->sun_path[0] == 0 ? \ - 1 + strnlen(_sa->sun_path+1, sizeof(_sa->sun_path)-1) : \ - strnlen(_sa->sun_path, sizeof(_sa->sun_path))); \ - }) - -int socket_ioctl_fd(void); diff --git a/src/systemd/src/basic/sparse-endian.h b/src/systemd/src/basic/sparse-endian.h deleted file mode 100644 index 5e59de54..00000000 --- a/src/systemd/src/basic/sparse-endian.h +++ /dev/null @@ -1,93 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (c) 2012 Josh Triplett <josh@joshtriplett.org> - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ -#ifndef SPARSE_ENDIAN_H -#define SPARSE_ENDIAN_H - -#include <byteswap.h> -#include <endian.h> -#include <stdint.h> - -#ifdef __CHECKER__ -#define __sd_bitwise __attribute__((bitwise)) -#define __sd_force __attribute__((force)) -#else -#define __sd_bitwise -#define __sd_force -#endif - -typedef uint16_t __sd_bitwise le16_t; -typedef uint16_t __sd_bitwise be16_t; -typedef uint32_t __sd_bitwise le32_t; -typedef uint32_t __sd_bitwise be32_t; -typedef uint64_t __sd_bitwise le64_t; -typedef uint64_t __sd_bitwise be64_t; - -#undef htobe16 -#undef htole16 -#undef be16toh -#undef le16toh -#undef htobe32 -#undef htole32 -#undef be32toh -#undef le32toh -#undef htobe64 -#undef htole64 -#undef be64toh -#undef le64toh - -#if __BYTE_ORDER == __LITTLE_ENDIAN -#define bswap_16_on_le(x) __bswap_16(x) -#define bswap_32_on_le(x) __bswap_32(x) -#define bswap_64_on_le(x) __bswap_64(x) -#define bswap_16_on_be(x) (x) -#define bswap_32_on_be(x) (x) -#define bswap_64_on_be(x) (x) -#elif __BYTE_ORDER == __BIG_ENDIAN -#define bswap_16_on_le(x) (x) -#define bswap_32_on_le(x) (x) -#define bswap_64_on_le(x) (x) -#define bswap_16_on_be(x) __bswap_16(x) -#define bswap_32_on_be(x) __bswap_32(x) -#define bswap_64_on_be(x) __bswap_64(x) -#endif - -static inline le16_t htole16(uint16_t value) { return (le16_t __sd_force) bswap_16_on_be(value); } -static inline le32_t htole32(uint32_t value) { return (le32_t __sd_force) bswap_32_on_be(value); } -static inline le64_t htole64(uint64_t value) { return (le64_t __sd_force) bswap_64_on_be(value); } - -static inline be16_t htobe16(uint16_t value) { return (be16_t __sd_force) bswap_16_on_le(value); } -static inline be32_t htobe32(uint32_t value) { return (be32_t __sd_force) bswap_32_on_le(value); } -static inline be64_t htobe64(uint64_t value) { return (be64_t __sd_force) bswap_64_on_le(value); } - -static inline uint16_t le16toh(le16_t value) { return bswap_16_on_be((uint16_t __sd_force)value); } -static inline uint32_t le32toh(le32_t value) { return bswap_32_on_be((uint32_t __sd_force)value); } -static inline uint64_t le64toh(le64_t value) { return bswap_64_on_be((uint64_t __sd_force)value); } - -static inline uint16_t be16toh(be16_t value) { return bswap_16_on_le((uint16_t __sd_force)value); } -static inline uint32_t be32toh(be32_t value) { return bswap_32_on_le((uint32_t __sd_force)value); } -static inline uint64_t be64toh(be64_t value) { return bswap_64_on_le((uint64_t __sd_force)value); } - -#undef __sd_bitwise -#undef __sd_force - -#endif /* SPARSE_ENDIAN_H */ diff --git a/src/systemd/src/basic/stat-util.c b/src/systemd/src/basic/stat-util.c deleted file mode 100644 index d645ce30..00000000 --- a/src/systemd/src/basic/stat-util.c +++ /dev/null @@ -1,272 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <dirent.h> -#include <errno.h> -#include <fcntl.h> -#include <linux/magic.h> -#include <sched.h> -#include <sys/stat.h> -#include <sys/statvfs.h> -#include <sys/types.h> -#include <unistd.h> - -#include "dirent-util.h" -#include "fd-util.h" -#include "fs-util.h" -#include "macro.h" -#include "missing.h" -#include "stat-util.h" -#include "string-util.h" - -#if 0 /* NM_IGNORED */ -int is_symlink(const char *path) { - struct stat info; - - assert(path); - - if (lstat(path, &info) < 0) - return -errno; - - return !!S_ISLNK(info.st_mode); -} - -int is_dir(const char* path, bool follow) { - struct stat st; - int r; - - assert(path); - - if (follow) - r = stat(path, &st); - else - r = lstat(path, &st); - if (r < 0) - return -errno; - - return !!S_ISDIR(st.st_mode); -} - -int is_dir_fd(int fd) { - struct stat st; - int r; - - r = fstat(fd, &st); - if (r < 0) - return -errno; - - return !!S_ISDIR(st.st_mode); -} - -int is_device_node(const char *path) { - struct stat info; - - assert(path); - - if (lstat(path, &info) < 0) - return -errno; - - return !!(S_ISBLK(info.st_mode) || S_ISCHR(info.st_mode)); -} - -int dir_is_empty(const char *path) { - _cleanup_closedir_ DIR *d; - struct dirent *de; - - d = opendir(path); - if (!d) - return -errno; - - FOREACH_DIRENT(de, d, return -errno) - return 0; - - return 1; -} - -bool null_or_empty(struct stat *st) { - assert(st); - - if (S_ISREG(st->st_mode) && st->st_size <= 0) - return true; - - /* We don't want to hardcode the major/minor of /dev/null, - * hence we do a simpler "is this a device node?" check. */ - - if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) - return true; - - return false; -} - -int null_or_empty_path(const char *fn) { - struct stat st; - - assert(fn); - - if (stat(fn, &st) < 0) - return -errno; - - return null_or_empty(&st); -} - -int null_or_empty_fd(int fd) { - struct stat st; - - assert(fd >= 0); - - if (fstat(fd, &st) < 0) - return -errno; - - return null_or_empty(&st); -} - -int path_is_read_only_fs(const char *path) { - struct statvfs st; - - assert(path); - - if (statvfs(path, &st) < 0) - return -errno; - - if (st.f_flag & ST_RDONLY) - return true; - - /* On NFS, statvfs() might not reflect whether we can actually - * write to the remote share. Let's try again with - * access(W_OK) which is more reliable, at least sometimes. */ - if (access(path, W_OK) < 0 && errno == EROFS) - return true; - - return false; -} - -int files_same(const char *filea, const char *fileb, int flags) { - struct stat a, b; - - assert(filea); - assert(fileb); - - if (fstatat(AT_FDCWD, filea, &a, flags) < 0) - return -errno; - - if (fstatat(AT_FDCWD, fileb, &b, flags) < 0) - return -errno; - - return a.st_dev == b.st_dev && - a.st_ino == b.st_ino; -} - -bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) { - assert(s); - assert_cc(sizeof(statfs_f_type_t) >= sizeof(s->f_type)); - - return F_TYPE_EQUAL(s->f_type, magic_value); -} - -int fd_is_fs_type(int fd, statfs_f_type_t magic_value) { - struct statfs s; - - if (fstatfs(fd, &s) < 0) - return -errno; - - return is_fs_type(&s, magic_value); -} - -int path_is_fs_type(const char *path, statfs_f_type_t magic_value) { - _cleanup_close_ int fd = -1; - - fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH); - if (fd < 0) - return -errno; - - return fd_is_fs_type(fd, magic_value); -} - -bool is_temporary_fs(const struct statfs *s) { - return is_fs_type(s, TMPFS_MAGIC) || - is_fs_type(s, RAMFS_MAGIC); -} - -bool is_network_fs(const struct statfs *s) { - return is_fs_type(s, CIFS_MAGIC_NUMBER) || - is_fs_type(s, CODA_SUPER_MAGIC) || - is_fs_type(s, NCP_SUPER_MAGIC) || - is_fs_type(s, NFS_SUPER_MAGIC) || - is_fs_type(s, SMB_SUPER_MAGIC) || - is_fs_type(s, V9FS_MAGIC) || - is_fs_type(s, AFS_SUPER_MAGIC) || - is_fs_type(s, OCFS2_SUPER_MAGIC); -} - -int fd_is_temporary_fs(int fd) { - struct statfs s; - - if (fstatfs(fd, &s) < 0) - return -errno; - - return is_temporary_fs(&s); -} - -int fd_is_network_fs(int fd) { - struct statfs s; - - if (fstatfs(fd, &s) < 0) - return -errno; - - return is_network_fs(&s); -} - -int fd_is_network_ns(int fd) { - int r; - - r = fd_is_fs_type(fd, NSFS_MAGIC); - if (r <= 0) - return r; - - r = ioctl(fd, NS_GET_NSTYPE); - if (r < 0) - return -errno; - - return r == CLONE_NEWNET; -} - -int path_is_temporary_fs(const char *path) { - _cleanup_close_ int fd = -1; - - fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH); - if (fd < 0) - return -errno; - - return fd_is_temporary_fs(fd); -} -#endif /* NM_IGNORED */ - -int stat_verify_regular(const struct stat *st) { - assert(st); - - /* Checks whether the specified stat() structure refers to a regular file. If not returns an appropriate error - * code. */ - - if (S_ISDIR(st->st_mode)) - return -EISDIR; - - if (S_ISLNK(st->st_mode)) - return -ELOOP; - - if (!S_ISREG(st->st_mode)) - return -EBADFD; - - return 0; -} - -int fd_verify_regular(int fd) { - struct stat st; - - assert(fd >= 0); - - if (fstat(fd, &st) < 0) - return -errno; - - return stat_verify_regular(&st); -} diff --git a/src/systemd/src/basic/stat-util.h b/src/systemd/src/basic/stat-util.h deleted file mode 100644 index 1a725f1d..00000000 --- a/src/systemd/src/basic/stat-util.h +++ /dev/null @@ -1,61 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <stdbool.h> -#include <stddef.h> -#include <sys/stat.h> -#include <sys/statfs.h> -#include <sys/types.h> -#include <sys/vfs.h> - -#include "macro.h" - -int is_symlink(const char *path); -int is_dir(const char *path, bool follow); -int is_dir_fd(int fd); -int is_device_node(const char *path); - -int dir_is_empty(const char *path); - -static inline int dir_is_populated(const char *path) { - int r; - r = dir_is_empty(path); - if (r < 0) - return r; - return !r; -} - -bool null_or_empty(struct stat *st) _pure_; -int null_or_empty_path(const char *fn); -int null_or_empty_fd(int fd); - -int path_is_read_only_fs(const char *path); - -int files_same(const char *filea, const char *fileb, int flags); - -/* The .f_type field of struct statfs is really weird defined on - * different archs. Let's give its type a name. */ -typedef typeof(((struct statfs*)NULL)->f_type) statfs_f_type_t; - -bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) _pure_; -int fd_is_fs_type(int fd, statfs_f_type_t magic_value); -int path_is_fs_type(const char *path, statfs_f_type_t magic_value); - -bool is_temporary_fs(const struct statfs *s) _pure_; -bool is_network_fs(const struct statfs *s) _pure_; - -int fd_is_temporary_fs(int fd); -int fd_is_network_fs(int fd); - -int fd_is_network_ns(int fd); - -int path_is_temporary_fs(const char *path); - -/* Because statfs.t_type can be int on some architectures, we have to cast - * the const magic to the type, otherwise the compiler warns about - * signed/unsigned comparison, because the magic can be 32 bit unsigned. - */ -#define F_TYPE_EQUAL(a, b) (a == (typeof(a)) b) - -int stat_verify_regular(const struct stat *st); -int fd_verify_regular(int fd); diff --git a/src/systemd/src/basic/stdio-util.h b/src/systemd/src/basic/stdio-util.h deleted file mode 100644 index 73c03274..00000000 --- a/src/systemd/src/basic/stdio-util.h +++ /dev/null @@ -1,60 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <printf.h> -#include <stdarg.h> -#include <stdio.h> -#include <sys/types.h> - -#include "macro.h" - -#define snprintf_ok(buf, len, fmt, ...) \ - ((size_t) snprintf(buf, len, fmt, __VA_ARGS__) < (len)) - -#define xsprintf(buf, fmt, ...) \ - assert_message_se(snprintf_ok(buf, ELEMENTSOF(buf), fmt, __VA_ARGS__), "xsprintf: " #buf "[] must be big enough") - -#define VA_FORMAT_ADVANCE(format, ap) \ -do { \ - int _argtypes[128]; \ - size_t _i, _k; \ - _k = parse_printf_format((format), ELEMENTSOF(_argtypes), _argtypes); \ - assert(_k < ELEMENTSOF(_argtypes)); \ - for (_i = 0; _i < _k; _i++) { \ - if (_argtypes[_i] & PA_FLAG_PTR) { \ - (void) va_arg(ap, void*); \ - continue; \ - } \ - \ - switch (_argtypes[_i]) { \ - case PA_INT: \ - case PA_INT|PA_FLAG_SHORT: \ - case PA_CHAR: \ - (void) va_arg(ap, int); \ - break; \ - case PA_INT|PA_FLAG_LONG: \ - (void) va_arg(ap, long int); \ - break; \ - case PA_INT|PA_FLAG_LONG_LONG: \ - (void) va_arg(ap, long long int); \ - break; \ - case PA_WCHAR: \ - (void) va_arg(ap, wchar_t); \ - break; \ - case PA_WSTRING: \ - case PA_STRING: \ - case PA_POINTER: \ - (void) va_arg(ap, void*); \ - break; \ - case PA_FLOAT: \ - case PA_DOUBLE: \ - (void) va_arg(ap, double); \ - break; \ - case PA_DOUBLE|PA_FLAG_LONG_DOUBLE: \ - (void) va_arg(ap, long double); \ - break; \ - default: \ - assert_not_reached("Unknown format string argument."); \ - } \ - } \ -} while (false) diff --git a/src/systemd/src/basic/string-table.c b/src/systemd/src/basic/string-table.c deleted file mode 100644 index 94412ed2..00000000 --- a/src/systemd/src/basic/string-table.c +++ /dev/null @@ -1,19 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include "string-table.h" -#include "string-util.h" - -ssize_t string_table_lookup(const char * const *table, size_t len, const char *key) { - size_t i; - - if (!key) - return -1; - - for (i = 0; i < len; ++i) - if (streq_ptr(table[i], key)) - return (ssize_t) i; - - return -1; -} diff --git a/src/systemd/src/basic/string-table.h b/src/systemd/src/basic/string-table.h deleted file mode 100644 index 9bd78793..00000000 --- a/src/systemd/src/basic/string-table.h +++ /dev/null @@ -1,112 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#pragma once - -#include <errno.h> -#include <stddef.h> -#include <stdio.h> -#include <string.h> -#include <sys/types.h> - -#include "macro.h" -#include "parse-util.h" -#include "string-util.h" - -ssize_t string_table_lookup(const char * const *table, size_t len, const char *key); - -/* For basic lookup tables with strictly enumerated entries */ -#define _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \ - scope const char *name##_to_string(type i) { \ - if (i < 0 || i >= (type) ELEMENTSOF(name##_table)) \ - return NULL; \ - return name##_table[i]; \ - } - -#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope) \ - scope type name##_from_string(const char *s) { \ - return (type) string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \ - } - -#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,scope) \ - scope type name##_from_string(const char *s) { \ - int b; \ - if (!s) \ - return -1; \ - b = parse_boolean(s); \ - if (b == 0) \ - return (type) 0; \ - else if (b > 0) \ - return yes; \ - return (type) string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \ - } - -#define _DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,scope) \ - scope int name##_to_string_alloc(type i, char **str) { \ - char *s; \ - if (i < 0 || i > max) \ - return -ERANGE; \ - if (i < (type) ELEMENTSOF(name##_table)) { \ - s = strdup(name##_table[i]); \ - if (!s) \ - return -ENOMEM; \ - } else { \ - if (asprintf(&s, "%i", i) < 0) \ - return -ENOMEM; \ - } \ - *str = s; \ - return 0; \ - } - -#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,scope) \ - type name##_from_string(const char *s) { \ - type i; \ - unsigned u = 0; \ - if (!s) \ - return (type) -1; \ - for (i = 0; i < (type) ELEMENTSOF(name##_table); i++) \ - if (streq_ptr(name##_table[i], s)) \ - return i; \ - if (safe_atou(s, &u) >= 0 && u <= max) \ - return (type) u; \ - return (type) -1; \ - } \ - -#define _DEFINE_STRING_TABLE_LOOKUP(name,type,scope) \ - _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \ - _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope) - -#define _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,scope) \ - _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \ - _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,scope) - -#define DEFINE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,) -#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,static) -#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,static) -#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,static) - -#define DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes) _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,) - -/* For string conversions where numbers are also acceptable */ -#define DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(name,type,max) \ - _DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,) \ - _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,) - -#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max) \ - _DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,static) -#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max) \ - _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,static) - -#define DUMP_STRING_TABLE(name,type,max) \ - do { \ - type _k; \ - flockfile(stdout); \ - for (_k = 0; _k < (max); _k++) { \ - const char *_t; \ - _t = name##_to_string(_k); \ - if (!_t) \ - continue; \ - fputs_unlocked(_t, stdout); \ - fputc_unlocked('\n', stdout); \ - } \ - funlockfile(stdout); \ - } while(false) diff --git a/src/systemd/src/basic/string-util.c b/src/systemd/src/basic/string-util.c deleted file mode 100644 index 1747f35b..00000000 --- a/src/systemd/src/basic/string-util.c +++ /dev/null @@ -1,1079 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <errno.h> -#include <stdarg.h> -#include <stdint.h> -#include <stdio.h> -#include <stdio_ext.h> -#include <stdlib.h> -#include <string.h> - -#include "alloc-util.h" -#include "escape.h" -#include "gunicode.h" -#include "locale-util.h" -#include "macro.h" -#include "string-util.h" -#include "terminal-util.h" -#include "utf8.h" -#include "util.h" -#include "fileio.h" - -int strcmp_ptr(const char *a, const char *b) { - - /* Like strcmp(), but tries to make sense of NULL pointers */ - if (a && b) - return strcmp(a, b); - - if (!a && b) - return -1; - - if (a && !b) - return 1; - - return 0; -} - -char* endswith(const char *s, const char *postfix) { - size_t sl, pl; - - assert(s); - assert(postfix); - - sl = strlen(s); - pl = strlen(postfix); - - if (pl == 0) - return (char*) s + sl; - - if (sl < pl) - return NULL; - - if (memcmp(s + sl - pl, postfix, pl) != 0) - return NULL; - - return (char*) s + sl - pl; -} - -char* endswith_no_case(const char *s, const char *postfix) { - size_t sl, pl; - - assert(s); - assert(postfix); - - sl = strlen(s); - pl = strlen(postfix); - - if (pl == 0) - return (char*) s + sl; - - if (sl < pl) - return NULL; - - if (strcasecmp(s + sl - pl, postfix) != 0) - return NULL; - - return (char*) s + sl - pl; -} - -char* first_word(const char *s, const char *word) { - size_t sl, wl; - const char *p; - - assert(s); - assert(word); - - /* Checks if the string starts with the specified word, either - * followed by NUL or by whitespace. Returns a pointer to the - * NUL or the first character after the whitespace. */ - - sl = strlen(s); - wl = strlen(word); - - if (sl < wl) - return NULL; - - if (wl == 0) - return (char*) s; - - if (memcmp(s, word, wl) != 0) - return NULL; - - p = s + wl; - if (*p == 0) - return (char*) p; - - if (!strchr(WHITESPACE, *p)) - return NULL; - - p += strspn(p, WHITESPACE); - return (char*) p; -} - -static size_t strcspn_escaped(const char *s, const char *reject) { - bool escaped = false; - int n; - - for (n=0; s[n]; n++) { - if (escaped) - escaped = false; - else if (s[n] == '\\') - escaped = true; - else if (strchr(reject, s[n])) - break; - } - - /* if s ends in \, return index of previous char */ - return n - escaped; -} - -/* Split a string into words. */ -const char* split(const char **state, size_t *l, const char *separator, bool quoted) { - const char *current; - - current = *state; - - if (!*current) { - assert(**state == '\0'); - return NULL; - } - - current += strspn(current, separator); - if (!*current) { - *state = current; - return NULL; - } - - if (quoted && strchr("\'\"", *current)) { - char quotechars[2] = {*current, '\0'}; - - *l = strcspn_escaped(current + 1, quotechars); - if (current[*l + 1] == '\0' || current[*l + 1] != quotechars[0] || - (current[*l + 2] && !strchr(separator, current[*l + 2]))) { - /* right quote missing or garbage at the end */ - *state = current; - return NULL; - } - *state = current++ + *l + 2; - } else if (quoted) { - *l = strcspn_escaped(current, separator); - if (current[*l] && !strchr(separator, current[*l])) { - /* unfinished escape */ - *state = current; - return NULL; - } - *state = current + *l; - } else { - *l = strcspn(current, separator); - *state = current + *l; - } - - return current; -} - -char *strnappend(const char *s, const char *suffix, size_t b) { - size_t a; - char *r; - - if (!s && !suffix) - return strdup(""); - - if (!s) - return strndup(suffix, b); - - if (!suffix) - return strdup(s); - - assert(s); - assert(suffix); - - a = strlen(s); - if (b > ((size_t) -1) - a) - return NULL; - - r = new(char, a+b+1); - if (!r) - return NULL; - - memcpy(r, s, a); - memcpy(r+a, suffix, b); - r[a+b] = 0; - - return r; -} - -char *strappend(const char *s, const char *suffix) { - return strnappend(s, suffix, strlen_ptr(suffix)); -} - -#if 0 /* NM_IGNORED */ -char *strjoin_real(const char *x, ...) { - va_list ap; - size_t l; - char *r, *p; - - va_start(ap, x); - - if (x) { - l = strlen(x); - - for (;;) { - const char *t; - size_t n; - - t = va_arg(ap, const char *); - if (!t) - break; - - n = strlen(t); - if (n > ((size_t) -1) - l) { - va_end(ap); - return NULL; - } - - l += n; - } - } else - l = 0; - - va_end(ap); - - r = new(char, l+1); - if (!r) - return NULL; - - if (x) { - p = stpcpy(r, x); - - va_start(ap, x); - - for (;;) { - const char *t; - - t = va_arg(ap, const char *); - if (!t) - break; - - p = stpcpy(p, t); - } - - va_end(ap); - } else - r[0] = 0; - - return r; -} - -char *strstrip(char *s) { - if (!s) - return NULL; - - /* Drops trailing whitespace. Modifies the string in place. Returns pointer to first non-space character */ - - return delete_trailing_chars(skip_leading_chars(s, WHITESPACE), WHITESPACE); -} - -char *delete_chars(char *s, const char *bad) { - char *f, *t; - - /* Drops all specified bad characters, regardless where in the string */ - - if (!s) - return NULL; - - if (!bad) - bad = WHITESPACE; - - for (f = s, t = s; *f; f++) { - if (strchr(bad, *f)) - continue; - - *(t++) = *f; - } - - *t = 0; - - return s; -} - -char *delete_trailing_chars(char *s, const char *bad) { - char *p, *c = s; - - /* Drops all specified bad characters, at the end of the string */ - - if (!s) - return NULL; - - if (!bad) - bad = WHITESPACE; - - for (p = s; *p; p++) - if (!strchr(bad, *p)) - c = p + 1; - - *c = 0; - - return s; -} -#endif /* NM_IGNORED */ - -char *truncate_nl(char *s) { - assert(s); - - s[strcspn(s, NEWLINE)] = 0; - return s; -} - -char ascii_tolower(char x) { - - if (x >= 'A' && x <= 'Z') - return x - 'A' + 'a'; - - return x; -} - -char ascii_toupper(char x) { - - if (x >= 'a' && x <= 'z') - return x - 'a' + 'A'; - - return x; -} - -char *ascii_strlower(char *t) { - char *p; - - assert(t); - - for (p = t; *p; p++) - *p = ascii_tolower(*p); - - return t; -} - -char *ascii_strupper(char *t) { - char *p; - - assert(t); - - for (p = t; *p; p++) - *p = ascii_toupper(*p); - - return t; -} - -char *ascii_strlower_n(char *t, size_t n) { - size_t i; - - if (n <= 0) - return t; - - for (i = 0; i < n; i++) - t[i] = ascii_tolower(t[i]); - - return t; -} - -int ascii_strcasecmp_n(const char *a, const char *b, size_t n) { - - for (; n > 0; a++, b++, n--) { - int x, y; - - x = (int) (uint8_t) ascii_tolower(*a); - y = (int) (uint8_t) ascii_tolower(*b); - - if (x != y) - return x - y; - } - - return 0; -} - -int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m) { - int r; - - r = ascii_strcasecmp_n(a, b, MIN(n, m)); - if (r != 0) - return r; - - if (n < m) - return -1; - else if (n > m) - return 1; - else - return 0; -} - -bool chars_intersect(const char *a, const char *b) { - const char *p; - - /* Returns true if any of the chars in a are in b. */ - for (p = a; *p; p++) - if (strchr(b, *p)) - return true; - - return false; -} - -bool string_has_cc(const char *p, const char *ok) { - const char *t; - - assert(p); - - /* - * Check if a string contains control characters. If 'ok' is - * non-NULL it may be a string containing additional CCs to be - * considered OK. - */ - - for (t = p; *t; t++) { - if (ok && strchr(ok, *t)) - continue; - - if (*t > 0 && *t < ' ') - return true; - - if (*t == 127) - return true; - } - - return false; -} - -#if 0 /* NM_IGNORED */ -static int write_ellipsis(char *buf, bool unicode) { - if (unicode || is_locale_utf8()) { - buf[0] = 0xe2; /* tri-dot ellipsis: … */ - buf[1] = 0x80; - buf[2] = 0xa6; - } else { - buf[0] = '.'; - buf[1] = '.'; - buf[2] = '.'; - } - - return 3; -} - -static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) { - size_t x, need_space, suffix_len; - char *t; - - assert(s); - assert(percent <= 100); - assert(new_length != (size_t) -1); - - if (old_length <= new_length) - return strndup(s, old_length); - - /* Special case short ellipsations */ - switch (new_length) { - - case 0: - return strdup(""); - - case 1: - if (is_locale_utf8()) - return strdup("…"); - else - return strdup("."); - - case 2: - if (!is_locale_utf8()) - return strdup(".."); - - break; - - default: - break; - } - - /* Calculate how much space the ellipsis will take up. If we are in UTF-8 mode we only need space for one - * character ("…"), otherwise for three characters ("..."). Note that in both cases we need 3 bytes of storage, - * either for the UTF-8 encoded character or for three ASCII characters. */ - need_space = is_locale_utf8() ? 1 : 3; - - t = new(char, new_length+3); - if (!t) - return NULL; - - assert(new_length >= need_space); - - x = ((new_length - need_space) * percent + 50) / 100; - assert(x <= new_length - need_space); - - memcpy(t, s, x); - write_ellipsis(t + x, false); - suffix_len = new_length - x - need_space; - memcpy(t + x + 3, s + old_length - suffix_len, suffix_len); - *(t + x + 3 + suffix_len) = '\0'; - - return t; -} - -char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) { - size_t x, k, len, len2; - const char *i, *j; - char *e; - int r; - - /* Note that 'old_length' refers to bytes in the string, while 'new_length' refers to character cells taken up - * on screen. This distinction doesn't matter for ASCII strings, but it does matter for non-ASCII UTF-8 - * strings. - * - * Ellipsation is done in a locale-dependent way: - * 1. If the string passed in is fully ASCII and the current locale is not UTF-8, three dots are used ("...") - * 2. Otherwise, a unicode ellipsis is used ("…") - * - * In other words: you'll get a unicode ellipsis as soon as either the string contains non-ASCII characters or - * the current locale is UTF-8. - */ - - assert(s); - assert(percent <= 100); - - if (new_length == (size_t) -1) - return strndup(s, old_length); - - if (new_length == 0) - return strdup(""); - - /* If no multibyte characters use ascii_ellipsize_mem for speed */ - if (ascii_is_valid_n(s, old_length)) - return ascii_ellipsize_mem(s, old_length, new_length, percent); - - x = ((new_length - 1) * percent) / 100; - assert(x <= new_length - 1); - - k = 0; - for (i = s; i < s + old_length; i = utf8_next_char(i)) { - char32_t c; - int w; - - r = utf8_encoded_to_unichar(i, &c); - if (r < 0) - return NULL; - - w = unichar_iswide(c) ? 2 : 1; - if (k + w <= x) - k += w; - else - break; - } - - for (j = s + old_length; j > i; ) { - char32_t c; - int w; - const char *jj; - - jj = utf8_prev_char(j); - r = utf8_encoded_to_unichar(jj, &c); - if (r < 0) - return NULL; - - w = unichar_iswide(c) ? 2 : 1; - if (k + w <= new_length) { - k += w; - j = jj; - } else - break; - } - assert(i <= j); - - /* we don't actually need to ellipsize */ - if (i == j) - return memdup_suffix0(s, old_length); - - /* make space for ellipsis, if possible */ - if (j < s + old_length) - j = utf8_next_char(j); - else if (i > s) - i = utf8_prev_char(i); - - len = i - s; - len2 = s + old_length - j; - e = new(char, len + 3 + len2 + 1); - if (!e) - return NULL; - - /* - printf("old_length=%zu new_length=%zu x=%zu len=%u len2=%u k=%u\n", - old_length, new_length, x, len, len2, k); - */ - - memcpy(e, s, len); - write_ellipsis(e + len, true); - memcpy(e + len + 3, j, len2); - *(e + len + 3 + len2) = '\0'; - - return e; -} - -char *cellescape(char *buf, size_t len, const char *s) { - /* Escape and ellipsize s into buffer buf of size len. Only non-control ASCII - * characters are copied as they are, everything else is escaped. The result - * is different then if escaping and ellipsization was performed in two - * separate steps, because each sequence is either stored in full or skipped. - * - * This function should be used for logging about strings which expected to - * be plain ASCII in a safe way. - * - * An ellipsis will be used if s is too long. It was always placed at the - * very end. - */ - - size_t i = 0, last_char_width[4] = {}, k = 0, j; - - assert(len > 0); /* at least a terminating NUL */ - - for (;;) { - char four[4]; - int w; - - if (*s == 0) /* terminating NUL detected? then we are done! */ - goto done; - - w = cescape_char(*s, four); - if (i + w + 1 > len) /* This character doesn't fit into the buffer anymore? In that case let's - * ellipsize at the previous location */ - break; - - /* OK, there was space, let's add this escaped character to the buffer */ - memcpy(buf + i, four, w); - i += w; - - /* And remember its width in the ring buffer */ - last_char_width[k] = w; - k = (k + 1) % 4; - - s++; - } - - /* Ellipsation is necessary. This means we might need to truncate the string again to make space for 4 - * characters ideally, but the buffer is shorter than that in the first place take what we can get */ - for (j = 0; j < ELEMENTSOF(last_char_width); j++) { - - if (i + 4 <= len) /* nice, we reached our space goal */ - break; - - k = k == 0 ? 3 : k - 1; - if (last_char_width[k] == 0) /* bummer, we reached the beginning of the strings */ - break; - - assert(i >= last_char_width[k]); - i -= last_char_width[k]; - } - - if (i + 4 <= len) /* yay, enough space */ - i += write_ellipsis(buf + i, false); - else if (i + 3 <= len) { /* only space for ".." */ - buf[i++] = '.'; - buf[i++] = '.'; - } else if (i + 2 <= len) /* only space for a single "." */ - buf[i++] = '.'; - else - assert(i + 1 <= len); - - done: - buf[i] = '\0'; - return buf; -} -#endif /* NM_IGNORED */ - -bool nulstr_contains(const char *nulstr, const char *needle) { - const char *i; - - if (!nulstr) - return false; - - NULSTR_FOREACH(i, nulstr) - if (streq(i, needle)) - return true; - - return false; -} - -char* strshorten(char *s, size_t l) { - assert(s); - - if (strnlen(s, l+1) > l) - s[l] = 0; - - return s; -} - -char *strreplace(const char *text, const char *old_string, const char *new_string) { - size_t l, old_len, new_len, allocated = 0; - char *t, *ret = NULL; - const char *f; - - assert(old_string); - assert(new_string); - - if (!text) - return NULL; - - old_len = strlen(old_string); - new_len = strlen(new_string); - - l = strlen(text); - if (!GREEDY_REALLOC(ret, allocated, l+1)) - return NULL; - - f = text; - t = ret; - while (*f) { - size_t d, nl; - - if (!startswith(f, old_string)) { - *(t++) = *(f++); - continue; - } - - d = t - ret; - nl = l - old_len + new_len; - - if (!GREEDY_REALLOC(ret, allocated, nl + 1)) - return mfree(ret); - - l = nl; - t = ret + d; - - t = stpcpy(t, new_string); - f += old_len; - } - - *t = 0; - return ret; -} - -static void advance_offsets(ssize_t diff, size_t offsets[2], size_t shift[2], size_t size) { - if (!offsets) - return; - - if ((size_t) diff < offsets[0]) - shift[0] += size; - if ((size_t) diff < offsets[1]) - shift[1] += size; -} - -char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) { - const char *i, *begin = NULL; - enum { - STATE_OTHER, - STATE_ESCAPE, - STATE_CSI, - STATE_CSO, - } state = STATE_OTHER; - char *obuf = NULL; - size_t osz = 0, isz, shift[2] = {}; - FILE *f; - - assert(ibuf); - assert(*ibuf); - - /* This does three things: - * - * 1. Replaces TABs by 8 spaces - * 2. Strips ANSI color sequences (a subset of CSI), i.e. ESC '[' … 'm' sequences - * 3. Strips ANSI operating system sequences (CSO), i.e. ESC ']' … BEL sequences - * - * Everything else will be left as it is. In particular other ANSI sequences are left as they are, as are any - * other special characters. Truncated ANSI sequences are left-as is too. This call is supposed to suppress the - * most basic formatting noise, but nothing else. - * - * Why care for CSO sequences? Well, to undo what terminal_urlify() and friends generate. */ - - isz = _isz ? *_isz : strlen(*ibuf); - - f = open_memstream(&obuf, &osz); - if (!f) - return NULL; - - /* Note we turn off internal locking on f for performance reasons. It's safe to do so since we created f here - * and it doesn't leave our scope. */ - - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - - for (i = *ibuf; i < *ibuf + isz + 1; i++) { - - switch (state) { - - case STATE_OTHER: - if (i >= *ibuf + isz) /* EOT */ - break; - else if (*i == '\x1B') - state = STATE_ESCAPE; - else if (*i == '\t') { - fputs(" ", f); - advance_offsets(i - *ibuf, highlight, shift, 7); - } else - fputc(*i, f); - - break; - - case STATE_ESCAPE: - if (i >= *ibuf + isz) { /* EOT */ - fputc('\x1B', f); - advance_offsets(i - *ibuf, highlight, shift, 1); - break; - } else if (*i == '[') { /* ANSI CSI */ - state = STATE_CSI; - begin = i + 1; - } else if (*i == ']') { /* ANSI CSO */ - state = STATE_CSO; - begin = i + 1; - } else { - fputc('\x1B', f); - fputc(*i, f); - advance_offsets(i - *ibuf, highlight, shift, 1); - state = STATE_OTHER; - } - - break; - - case STATE_CSI: - - if (i >= *ibuf + isz || /* EOT … */ - !strchr("01234567890;m", *i)) { /* … or invalid chars in sequence */ - fputc('\x1B', f); - fputc('[', f); - advance_offsets(i - *ibuf, highlight, shift, 2); - state = STATE_OTHER; - i = begin-1; - } else if (*i == 'm') - state = STATE_OTHER; - - break; - - case STATE_CSO: - - if (i >= *ibuf + isz || /* EOT … */ - (*i != '\a' && (uint8_t) *i < 32U) || (uint8_t) *i > 126U) { /* … or invalid chars in sequence */ - fputc('\x1B', f); - fputc(']', f); - advance_offsets(i - *ibuf, highlight, shift, 2); - state = STATE_OTHER; - i = begin-1; - } else if (*i == '\a') - state = STATE_OTHER; - - break; - } - } - - if (fflush_and_check(f) < 0) { - fclose(f); - return mfree(obuf); - } - - fclose(f); - - free(*ibuf); - *ibuf = obuf; - - if (_isz) - *_isz = osz; - - if (highlight) { - highlight[0] += shift[0]; - highlight[1] += shift[1]; - } - - return obuf; -} - -#if 0 /* NM_IGNORED */ -char *strextend_with_separator(char **x, const char *separator, ...) { - bool need_separator; - size_t f, l, l_separator; - char *r, *p; - va_list ap; - - assert(x); - - l = f = strlen_ptr(*x); - - need_separator = !isempty(*x); - l_separator = strlen_ptr(separator); - - va_start(ap, separator); - for (;;) { - const char *t; - size_t n; - - t = va_arg(ap, const char *); - if (!t) - break; - - n = strlen(t); - - if (need_separator) - n += l_separator; - - if (n > ((size_t) -1) - l) { - va_end(ap); - return NULL; - } - - l += n; - need_separator = true; - } - va_end(ap); - - need_separator = !isempty(*x); - - r = realloc(*x, l+1); - if (!r) - return NULL; - - p = r + f; - - va_start(ap, separator); - for (;;) { - const char *t; - - t = va_arg(ap, const char *); - if (!t) - break; - - if (need_separator && separator) - p = stpcpy(p, separator); - - p = stpcpy(p, t); - - need_separator = true; - } - va_end(ap); - - assert(p == r + l); - - *p = 0; - *x = r; - - return r + l; -} -#endif /* NM_IGNORED */ - -char *strrep(const char *s, unsigned n) { - size_t l; - char *r, *p; - unsigned i; - - assert(s); - - l = strlen(s); - p = r = malloc(l * n + 1); - if (!r) - return NULL; - - for (i = 0; i < n; i++) - p = stpcpy(p, s); - - *p = 0; - return r; -} - -int split_pair(const char *s, const char *sep, char **l, char **r) { - char *x, *a, *b; - - assert(s); - assert(sep); - assert(l); - assert(r); - - if (isempty(sep)) - return -EINVAL; - - x = strstr(s, sep); - if (!x) - return -EINVAL; - - a = strndup(s, x - s); - if (!a) - return -ENOMEM; - - b = strdup(x + strlen(sep)); - if (!b) { - free(a); - return -ENOMEM; - } - - *l = a; - *r = b; - - return 0; -} - -int free_and_strdup(char **p, const char *s) { - char *t; - - assert(p); - - /* Replaces a string pointer with an strdup()ed new string, - * possibly freeing the old one. */ - - if (streq_ptr(*p, s)) - return 0; - - if (s) { - t = strdup(s); - if (!t) - return -ENOMEM; - } else - t = NULL; - - free(*p); - *p = t; - - return 1; -} - -#if !HAVE_EXPLICIT_BZERO -/* - * Pointer to memset is volatile so that compiler must de-reference - * the pointer and can't assume that it points to any function in - * particular (such as memset, which it then might further "optimize") - * This approach is inspired by openssl's crypto/mem_clr.c. - */ -typedef void *(*memset_t)(void *,int,size_t); - -static volatile memset_t memset_func = memset; - -void explicit_bzero(void *p, size_t l) { - memset_func(p, '\0', l); -} -#endif - -char* string_erase(char *x) { - if (!x) - return NULL; - - /* A delicious drop of snake-oil! To be called on memory where - * we stored passphrases or so, after we used them. */ - explicit_bzero(x, strlen(x)); - return x; -} - -char *string_free_erase(char *s) { - return mfree(string_erase(s)); -} - -bool string_is_safe(const char *p) { - const char *t; - - if (!p) - return false; - - for (t = p; *t; t++) { - if (*t > 0 && *t < ' ') /* no control characters */ - return false; - - if (strchr(QUOTES "\\\x7f", *t)) - return false; - } - - return true; -} diff --git a/src/systemd/src/basic/string-util.h b/src/systemd/src/basic/string-util.h deleted file mode 100644 index c0cc4e78..00000000 --- a/src/systemd/src/basic/string-util.h +++ /dev/null @@ -1,230 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <alloca.h> -#include <stdbool.h> -#include <stddef.h> -#include <string.h> - -#include "macro.h" - -/* What is interpreted as whitespace? */ -#define WHITESPACE " \t\n\r" -#define NEWLINE "\n\r" -#define QUOTES "\"\'" -#define COMMENTS "#;" -#define GLOB_CHARS "*?[" -#define DIGITS "0123456789" -#define LOWERCASE_LETTERS "abcdefghijklmnopqrstuvwxyz" -#define UPPERCASE_LETTERS "ABCDEFGHIJKLMNOPQRSTUVWXYZ" -#define LETTERS LOWERCASE_LETTERS UPPERCASE_LETTERS -#define ALPHANUMERICAL LETTERS DIGITS -#define HEXDIGITS DIGITS "abcdefABCDEF" - -#define streq(a,b) (strcmp((a),(b)) == 0) -#define strneq(a, b, n) (strncmp((a), (b), (n)) == 0) -#define strcaseeq(a,b) (strcasecmp((a),(b)) == 0) -#define strncaseeq(a, b, n) (strncasecmp((a), (b), (n)) == 0) - -int strcmp_ptr(const char *a, const char *b) _pure_; - -static inline bool streq_ptr(const char *a, const char *b) { - return strcmp_ptr(a, b) == 0; -} - -static inline const char* strempty(const char *s) { - return s ?: ""; -} - -static inline const char* strnull(const char *s) { - return s ?: "(null)"; -} - -static inline const char *strna(const char *s) { - return s ?: "n/a"; -} - -static inline bool isempty(const char *p) { - return !p || !p[0]; -} - -static inline const char *empty_to_null(const char *p) { - return isempty(p) ? NULL : p; -} - -static inline const char *empty_to_dash(const char *str) { - return isempty(str) ? "-" : str; -} - -static inline char *startswith(const char *s, const char *prefix) { - size_t l; - - l = strlen(prefix); - if (strncmp(s, prefix, l) == 0) - return (char*) s + l; - - return NULL; -} - -static inline char *startswith_no_case(const char *s, const char *prefix) { - size_t l; - - l = strlen(prefix); - if (strncasecmp(s, prefix, l) == 0) - return (char*) s + l; - - return NULL; -} - -char *endswith(const char *s, const char *postfix) _pure_; -char *endswith_no_case(const char *s, const char *postfix) _pure_; - -char *first_word(const char *s, const char *word) _pure_; - -const char* split(const char **state, size_t *l, const char *separator, bool quoted); - -#define FOREACH_WORD(word, length, s, state) \ - _FOREACH_WORD(word, length, s, WHITESPACE, false, state) - -#define FOREACH_WORD_SEPARATOR(word, length, s, separator, state) \ - _FOREACH_WORD(word, length, s, separator, false, state) - -#define _FOREACH_WORD(word, length, s, separator, quoted, state) \ - for ((state) = (s), (word) = split(&(state), &(length), (separator), (quoted)); (word); (word) = split(&(state), &(length), (separator), (quoted))) - -char *strappend(const char *s, const char *suffix); -char *strnappend(const char *s, const char *suffix, size_t length); - -char *strjoin_real(const char *x, ...) _sentinel_; -#define strjoin(a, ...) strjoin_real((a), __VA_ARGS__, NULL) - -#define strjoina(a, ...) \ - ({ \ - const char *_appendees_[] = { a, __VA_ARGS__ }; \ - char *_d_, *_p_; \ - size_t _len_ = 0; \ - size_t _i_; \ - for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_) && _appendees_[_i_]; _i_++) \ - _len_ += strlen(_appendees_[_i_]); \ - _p_ = _d_ = alloca(_len_ + 1); \ - for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_) && _appendees_[_i_]; _i_++) \ - _p_ = stpcpy(_p_, _appendees_[_i_]); \ - *_p_ = 0; \ - _d_; \ - }) - -char *strstrip(char *s); -char *delete_chars(char *s, const char *bad); -char *delete_trailing_chars(char *s, const char *bad); -char *truncate_nl(char *s); - -static inline char *skip_leading_chars(const char *s, const char *bad) { - - if (!s) - return NULL; - - if (!bad) - bad = WHITESPACE; - - return (char*) s + strspn(s, bad); -} - -char ascii_tolower(char x); -char *ascii_strlower(char *s); -char *ascii_strlower_n(char *s, size_t n); - -char ascii_toupper(char x); -char *ascii_strupper(char *s); - -int ascii_strcasecmp_n(const char *a, const char *b, size_t n); -int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m); - -bool chars_intersect(const char *a, const char *b) _pure_; - -static inline bool _pure_ in_charset(const char *s, const char* charset) { - assert(s); - assert(charset); - return s[strspn(s, charset)] == '\0'; -} - -bool string_has_cc(const char *p, const char *ok) _pure_; - -char *ellipsize_mem(const char *s, size_t old_length_bytes, size_t new_length_columns, unsigned percent); -static inline char *ellipsize(const char *s, size_t length, unsigned percent) { - return ellipsize_mem(s, strlen(s), length, percent); -} - -char *cellescape(char *buf, size_t len, const char *s); - -/* This limit is arbitrary, enough to give some idea what the string contains */ -#define CELLESCAPE_DEFAULT_LENGTH 64 - -bool nulstr_contains(const char *nulstr, const char *needle); - -char* strshorten(char *s, size_t l); - -char *strreplace(const char *text, const char *old_string, const char *new_string); - -char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]); - -char *strextend_with_separator(char **x, const char *separator, ...) _sentinel_; - -#define strextend(x, ...) strextend_with_separator(x, NULL, __VA_ARGS__) - -char *strrep(const char *s, unsigned n); - -int split_pair(const char *s, const char *sep, char **l, char **r); - -int free_and_strdup(char **p, const char *s); - -/* Normal memmem() requires haystack to be nonnull, which is annoying for zero-length buffers */ -static inline void *memmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) { - - if (needlelen <= 0) - return (void*) haystack; - - if (haystacklen < needlelen) - return NULL; - - assert(haystack); - assert(needle); - - return memmem(haystack, haystacklen, needle, needlelen); -} - -#if !HAVE_EXPLICIT_BZERO -void explicit_bzero(void *p, size_t l); -#endif - -char *string_erase(char *x); - -char *string_free_erase(char *s); -DEFINE_TRIVIAL_CLEANUP_FUNC(char *, string_free_erase); -#define _cleanup_string_free_erase_ _cleanup_(string_free_erasep) - -bool string_is_safe(const char *p) _pure_; - -static inline size_t strlen_ptr(const char *s) { - if (!s) - return 0; - - return strlen(s); -} - -/* Like startswith(), but operates on arbitrary memory blocks */ -static inline void *memory_startswith(const void *p, size_t sz, const char *token) { - size_t n; - - assert(token); - - n = strlen(token); - if (sz < n) - return NULL; - - assert(p); - - if (memcmp(p, token, n) != 0) - return NULL; - - return (uint8_t*) p + n; -} diff --git a/src/systemd/src/basic/strv.c b/src/systemd/src/basic/strv.c deleted file mode 100644 index 6f80b317..00000000 --- a/src/systemd/src/basic/strv.c +++ /dev/null @@ -1,888 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <errno.h> -#include <fnmatch.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "alloc-util.h" -#include "escape.h" -#include "extract-word.h" -#include "fileio.h" -#include "string-util.h" -#include "strv.h" -#include "util.h" - -char *strv_find(char **l, const char *name) { - char **i; - - assert(name); - - STRV_FOREACH(i, l) - if (streq(*i, name)) - return *i; - - return NULL; -} - -char *strv_find_prefix(char **l, const char *name) { - char **i; - - assert(name); - - STRV_FOREACH(i, l) - if (startswith(*i, name)) - return *i; - - return NULL; -} - -char *strv_find_startswith(char **l, const char *name) { - char **i, *e; - - assert(name); - - /* Like strv_find_prefix, but actually returns only the - * suffix, not the whole item */ - - STRV_FOREACH(i, l) { - e = startswith(*i, name); - if (e) - return e; - } - - return NULL; -} - -void strv_clear(char **l) { - char **k; - - if (!l) - return; - - for (k = l; *k; k++) - free(*k); - - *l = NULL; -} - -char **strv_free(char **l) { - strv_clear(l); - return mfree(l); -} - -char **strv_free_erase(char **l) { - char **i; - - STRV_FOREACH(i, l) - string_erase(*i); - - return strv_free(l); -} - -char **strv_copy(char * const *l) { - char **r, **k; - - k = r = new(char*, strv_length(l) + 1); - if (!r) - return NULL; - - if (l) - for (; *l; k++, l++) { - *k = strdup(*l); - if (!*k) { - strv_free(r); - return NULL; - } - } - - *k = NULL; - return r; -} - -size_t strv_length(char * const *l) { - size_t n = 0; - - if (!l) - return 0; - - for (; *l; l++) - n++; - - return n; -} - -char **strv_new_ap(const char *x, va_list ap) { - const char *s; - _cleanup_strv_free_ char **a = NULL; - size_t n = 0, i = 0; - va_list aq; - - /* As a special trick we ignore all listed strings that equal - * STRV_IGNORE. This is supposed to be used with the - * STRV_IFNOTNULL() macro to include possibly NULL strings in - * the string list. */ - - if (x) { - n = x == STRV_IGNORE ? 0 : 1; - - va_copy(aq, ap); - while ((s = va_arg(aq, const char*))) { - if (s == STRV_IGNORE) - continue; - - n++; - } - - va_end(aq); - } - - a = new(char*, n+1); - if (!a) - return NULL; - - if (x) { - if (x != STRV_IGNORE) { - a[i] = strdup(x); - if (!a[i]) - return NULL; - i++; - } - - while ((s = va_arg(ap, const char*))) { - - if (s == STRV_IGNORE) - continue; - - a[i] = strdup(s); - if (!a[i]) - return NULL; - - i++; - } - } - - a[i] = NULL; - - return TAKE_PTR(a); -} - -char **strv_new(const char *x, ...) { - char **r; - va_list ap; - - va_start(ap, x); - r = strv_new_ap(x, ap); - va_end(ap); - - return r; -} - -int strv_extend_strv(char ***a, char **b, bool filter_duplicates) { - char **s, **t; - size_t p, q, i = 0, j; - - assert(a); - - if (strv_isempty(b)) - return 0; - - p = strv_length(*a); - q = strv_length(b); - - t = reallocarray(*a, p + q + 1, sizeof(char *)); - if (!t) - return -ENOMEM; - - t[p] = NULL; - *a = t; - - STRV_FOREACH(s, b) { - - if (filter_duplicates && strv_contains(t, *s)) - continue; - - t[p+i] = strdup(*s); - if (!t[p+i]) - goto rollback; - - i++; - t[p+i] = NULL; - } - - assert(i <= q); - - return (int) i; - -rollback: - for (j = 0; j < i; j++) - free(t[p + j]); - - t[p] = NULL; - return -ENOMEM; -} - -int strv_extend_strv_concat(char ***a, char **b, const char *suffix) { - int r; - char **s; - - STRV_FOREACH(s, b) { - char *v; - - v = strappend(*s, suffix); - if (!v) - return -ENOMEM; - - r = strv_push(a, v); - if (r < 0) { - free(v); - return r; - } - } - - return 0; -} - -char **strv_split(const char *s, const char *separator) { - const char *word, *state; - size_t l; - size_t n, i; - char **r; - - assert(s); - - s += strspn(s, separator); - if (isempty(s)) - return new0(char*, 1); - - n = 0; - FOREACH_WORD_SEPARATOR(word, l, s, separator, state) - n++; - - r = new(char*, n+1); - if (!r) - return NULL; - - i = 0; - FOREACH_WORD_SEPARATOR(word, l, s, separator, state) { - r[i] = strndup(word, l); - if (!r[i]) { - strv_free(r); - return NULL; - } - - i++; - } - - r[i] = NULL; - return r; -} - -char **strv_split_newlines(const char *s) { - char **l; - size_t n; - - assert(s); - - /* Special version of strv_split() that splits on newlines and - * suppresses an empty string at the end */ - - l = strv_split(s, NEWLINE); - if (!l) - return NULL; - - n = strv_length(l); - if (n <= 0) - return l; - - if (isempty(l[n - 1])) - l[n - 1] = mfree(l[n - 1]); - - return l; -} - -#if 0 /* NM_IGNORED */ -int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags) { - _cleanup_strv_free_ char **l = NULL; - size_t n = 0, allocated = 0; - int r; - - assert(t); - assert(s); - - for (;;) { - _cleanup_free_ char *word = NULL; - - r = extract_first_word(&s, &word, separators, flags); - if (r < 0) - return r; - if (r == 0) - break; - - if (!GREEDY_REALLOC(l, allocated, n + 2)) - return -ENOMEM; - - l[n++] = TAKE_PTR(word); - - l[n] = NULL; - } - - if (!l) { - l = new0(char*, 1); - if (!l) - return -ENOMEM; - } - - *t = TAKE_PTR(l); - - return (int) n; -} -#endif /* NM_IGNORED */ - -char *strv_join(char **l, const char *separator) { - char *r, *e; - char **s; - size_t n, k; - - if (!separator) - separator = " "; - - k = strlen(separator); - - n = 0; - STRV_FOREACH(s, l) { - if (s != l) - n += k; - n += strlen(*s); - } - - r = new(char, n+1); - if (!r) - return NULL; - - e = r; - STRV_FOREACH(s, l) { - if (s != l) - e = stpcpy(e, separator); - - e = stpcpy(e, *s); - } - - *e = 0; - - return r; -} - -int strv_push(char ***l, char *value) { - char **c; - size_t n, m; - - if (!value) - return 0; - - n = strv_length(*l); - - /* Increase and check for overflow */ - m = n + 2; - if (m < n) - return -ENOMEM; - - c = reallocarray(*l, m, sizeof(char*)); - if (!c) - return -ENOMEM; - - c[n] = value; - c[n+1] = NULL; - - *l = c; - return 0; -} - -int strv_push_pair(char ***l, char *a, char *b) { - char **c; - size_t n, m; - - if (!a && !b) - return 0; - - n = strv_length(*l); - - /* increase and check for overflow */ - m = n + !!a + !!b + 1; - if (m < n) - return -ENOMEM; - - c = reallocarray(*l, m, sizeof(char*)); - if (!c) - return -ENOMEM; - - if (a) - c[n++] = a; - if (b) - c[n++] = b; - c[n] = NULL; - - *l = c; - return 0; -} - -int strv_insert(char ***l, size_t position, char *value) { - char **c; - size_t n, m, i; - - if (!value) - return 0; - - n = strv_length(*l); - position = MIN(position, n); - - /* increase and check for overflow */ - m = n + 2; - if (m < n) - return -ENOMEM; - - c = new(char*, m); - if (!c) - return -ENOMEM; - - for (i = 0; i < position; i++) - c[i] = (*l)[i]; - c[position] = value; - for (i = position; i < n; i++) - c[i+1] = (*l)[i]; - - c[n+1] = NULL; - - free(*l); - *l = c; - - return 0; -} - -int strv_consume(char ***l, char *value) { - int r; - - r = strv_push(l, value); - if (r < 0) - free(value); - - return r; -} - -int strv_consume_pair(char ***l, char *a, char *b) { - int r; - - r = strv_push_pair(l, a, b); - if (r < 0) { - free(a); - free(b); - } - - return r; -} - -int strv_consume_prepend(char ***l, char *value) { - int r; - - r = strv_push_prepend(l, value); - if (r < 0) - free(value); - - return r; -} - -int strv_extend(char ***l, const char *value) { - char *v; - - if (!value) - return 0; - - v = strdup(value); - if (!v) - return -ENOMEM; - - return strv_consume(l, v); -} - -int strv_extend_front(char ***l, const char *value) { - size_t n, m; - char *v, **c; - - assert(l); - - /* Like strv_extend(), but prepends rather than appends the new entry */ - - if (!value) - return 0; - - n = strv_length(*l); - - /* Increase and overflow check. */ - m = n + 2; - if (m < n) - return -ENOMEM; - - v = strdup(value); - if (!v) - return -ENOMEM; - - c = reallocarray(*l, m, sizeof(char*)); - if (!c) { - free(v); - return -ENOMEM; - } - - memmove(c+1, c, n * sizeof(char*)); - c[0] = v; - c[n+1] = NULL; - - *l = c; - return 0; -} - -char **strv_uniq(char **l) { - char **i; - - /* Drops duplicate entries. The first identical string will be - * kept, the others dropped */ - - STRV_FOREACH(i, l) - strv_remove(i+1, *i); - - return l; -} - -bool strv_is_uniq(char **l) { - char **i; - - STRV_FOREACH(i, l) - if (strv_find(i+1, *i)) - return false; - - return true; -} - -char **strv_remove(char **l, const char *s) { - char **f, **t; - - if (!l) - return NULL; - - assert(s); - - /* Drops every occurrence of s in the string list, edits - * in-place. */ - - for (f = t = l; *f; f++) - if (streq(*f, s)) - free(*f); - else - *(t++) = *f; - - *t = NULL; - return l; -} - -char **strv_parse_nulstr(const char *s, size_t l) { - /* l is the length of the input data, which will be split at NULs into - * elements of the resulting strv. Hence, the number of items in the resulting strv - * will be equal to one plus the number of NUL bytes in the l bytes starting at s, - * unless s[l-1] is NUL, in which case the final empty string is not stored in - * the resulting strv, and length is equal to the number of NUL bytes. - * - * Note that contrary to a normal nulstr which cannot contain empty strings, because - * the input data is terminated by any two consequent NUL bytes, this parser accepts - * empty strings in s. - */ - - const char *p; - size_t c = 0, i = 0; - char **v; - - assert(s || l <= 0); - - if (l <= 0) - return new0(char*, 1); - - for (p = s; p < s + l; p++) - if (*p == 0) - c++; - - if (s[l-1] != 0) - c++; - - v = new0(char*, c+1); - if (!v) - return NULL; - - p = s; - while (p < s + l) { - const char *e; - - e = memchr(p, 0, s + l - p); - - v[i] = strndup(p, e ? e - p : s + l - p); - if (!v[i]) { - strv_free(v); - return NULL; - } - - i++; - - if (!e) - break; - - p = e + 1; - } - - assert(i == c); - - return v; -} - -char **strv_split_nulstr(const char *s) { - const char *i; - char **r = NULL; - - NULSTR_FOREACH(i, s) - if (strv_extend(&r, i) < 0) { - strv_free(r); - return NULL; - } - - if (!r) - return strv_new(NULL, NULL); - - return r; -} - -int strv_make_nulstr(char **l, char **p, size_t *q) { - /* A valid nulstr with two NULs at the end will be created, but - * q will be the length without the two trailing NULs. Thus the output - * string is a valid nulstr and can be iterated over using NULSTR_FOREACH, - * and can also be parsed by strv_parse_nulstr as long as the length - * is provided separately. - */ - - size_t n_allocated = 0, n = 0; - _cleanup_free_ char *m = NULL; - char **i; - - assert(p); - assert(q); - - STRV_FOREACH(i, l) { - size_t z; - - z = strlen(*i); - - if (!GREEDY_REALLOC(m, n_allocated, n + z + 2)) - return -ENOMEM; - - memcpy(m + n, *i, z + 1); - n += z + 1; - } - - if (!m) { - m = new0(char, 1); - if (!m) - return -ENOMEM; - n = 1; - } else - /* make sure there is a second extra NUL at the end of resulting nulstr */ - m[n] = '\0'; - - assert(n > 0); - *p = m; - *q = n - 1; - - m = NULL; - - return 0; -} - -bool strv_overlap(char **a, char **b) { - char **i; - - STRV_FOREACH(i, a) - if (strv_contains(b, *i)) - return true; - - return false; -} - -static int str_compare(const void *_a, const void *_b) { - const char **a = (const char**) _a, **b = (const char**) _b; - - return strcmp(*a, *b); -} - -char **strv_sort(char **l) { - qsort_safe(l, strv_length(l), sizeof(char*), str_compare); - return l; -} - -bool strv_equal(char **a, char **b) { - - if (strv_isempty(a)) - return strv_isempty(b); - - if (strv_isempty(b)) - return false; - - for ( ; *a || *b; ++a, ++b) - if (!streq_ptr(*a, *b)) - return false; - - return true; -} - -void strv_print(char **l) { - char **s; - - STRV_FOREACH(s, l) - puts(*s); -} - -int strv_extendf(char ***l, const char *format, ...) { - va_list ap; - char *x; - int r; - - va_start(ap, format); - r = vasprintf(&x, format, ap); - va_end(ap); - - if (r < 0) - return -ENOMEM; - - return strv_consume(l, x); -} - -char **strv_reverse(char **l) { - size_t n, i; - - n = strv_length(l); - if (n <= 1) - return l; - - for (i = 0; i < n / 2; i++) - SWAP_TWO(l[i], l[n-1-i]); - - return l; -} - -char **strv_shell_escape(char **l, const char *bad) { - char **s; - - /* Escapes every character in every string in l that is in bad, - * edits in-place, does not roll-back on error. */ - - STRV_FOREACH(s, l) { - char *v; - - v = shell_escape(*s, bad); - if (!v) - return NULL; - - free(*s); - *s = v; - } - - return l; -} - -bool strv_fnmatch(char* const* patterns, const char *s, int flags) { - char* const* p; - - STRV_FOREACH(p, patterns) - if (fnmatch(*p, s, flags) == 0) - return true; - - return false; -} - -char ***strv_free_free(char ***l) { - char ***i; - - if (!l) - return NULL; - - for (i = l; *i; i++) - strv_free(*i); - - return mfree(l); -} - -char **strv_skip(char **l, size_t n) { - - while (n > 0) { - if (strv_isempty(l)) - return l; - - l++, n--; - } - - return l; -} - -int strv_extend_n(char ***l, const char *value, size_t n) { - size_t i, j, k; - char **nl; - - assert(l); - - if (!value) - return 0; - if (n == 0) - return 0; - - /* Adds the value n times to l */ - - k = strv_length(*l); - - nl = reallocarray(*l, k + n + 1, sizeof(char *)); - if (!nl) - return -ENOMEM; - - *l = nl; - - for (i = k; i < k + n; i++) { - nl[i] = strdup(value); - if (!nl[i]) - goto rollback; - } - - nl[i] = NULL; - return 0; - -rollback: - for (j = k; j < i; j++) - free(nl[j]); - - nl[k] = NULL; - return -ENOMEM; -} - -int fputstrv(FILE *f, char **l, const char *separator, bool *space) { - bool b = false; - char **s; - int r; - - /* Like fputs(), but for strv, and with a less stupid argument order */ - - if (!space) - space = &b; - - STRV_FOREACH(s, l) { - r = fputs_with_space(f, *s, separator, space); - if (r < 0) - return r; - } - - return 0; -} diff --git a/src/systemd/src/basic/strv.h b/src/systemd/src/basic/strv.h deleted file mode 100644 index 51d03db9..00000000 --- a/src/systemd/src/basic/strv.h +++ /dev/null @@ -1,177 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <fnmatch.h> -#include <stdarg.h> -#include <stdbool.h> -#include <stddef.h> - -#include "alloc-util.h" -#include "extract-word.h" -#include "macro.h" -#include "util.h" - -char *strv_find(char **l, const char *name) _pure_; -char *strv_find_prefix(char **l, const char *name) _pure_; -char *strv_find_startswith(char **l, const char *name) _pure_; - -char **strv_free(char **l); -DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free); -#define _cleanup_strv_free_ _cleanup_(strv_freep) - -char **strv_free_erase(char **l); -DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free_erase); -#define _cleanup_strv_free_erase_ _cleanup_(strv_free_erasep) - -void strv_clear(char **l); - -char **strv_copy(char * const *l); -size_t strv_length(char * const *l) _pure_; - -int strv_extend_strv(char ***a, char **b, bool filter_duplicates); -int strv_extend_strv_concat(char ***a, char **b, const char *suffix); -int strv_extend(char ***l, const char *value); -int strv_extendf(char ***l, const char *format, ...) _printf_(2,0); -int strv_extend_front(char ***l, const char *value); -int strv_push(char ***l, char *value); -int strv_push_pair(char ***l, char *a, char *b); -int strv_insert(char ***l, size_t position, char *value); - -static inline int strv_push_prepend(char ***l, char *value) { - return strv_insert(l, 0, value); -} - -int strv_consume(char ***l, char *value); -int strv_consume_pair(char ***l, char *a, char *b); -int strv_consume_prepend(char ***l, char *value); - -char **strv_remove(char **l, const char *s); -char **strv_uniq(char **l); -bool strv_is_uniq(char **l); - -bool strv_equal(char **a, char **b); - -#define strv_contains(l, s) (!!strv_find((l), (s))) - -char **strv_new(const char *x, ...) _sentinel_; -char **strv_new_ap(const char *x, va_list ap); - -#define STRV_IGNORE ((const char *) -1) - -static inline const char* STRV_IFNOTNULL(const char *x) { - return x ? x : STRV_IGNORE; -} - -static inline bool strv_isempty(char * const *l) { - return !l || !*l; -} - -char **strv_split(const char *s, const char *separator); -char **strv_split_newlines(const char *s); - -int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags); - -char *strv_join(char **l, const char *separator); - -char **strv_parse_nulstr(const char *s, size_t l); -char **strv_split_nulstr(const char *s); -int strv_make_nulstr(char **l, char **p, size_t *n); - -bool strv_overlap(char **a, char **b) _pure_; - -#define STRV_FOREACH(s, l) \ - for ((s) = (l); (s) && *(s); (s)++) - -#define STRV_FOREACH_BACKWARDS(s, l) \ - for (s = ({ \ - char **_l = l; \ - _l ? _l + strv_length(_l) - 1U : NULL; \ - }); \ - (l) && ((s) >= (l)); \ - (s)--) - -#define STRV_FOREACH_PAIR(x, y, l) \ - for ((x) = (l), (y) = (x+1); (x) && *(x) && *(y); (x) += 2, (y) = (x + 1)) - -char **strv_sort(char **l); -void strv_print(char **l); - -#define STRV_MAKE(...) ((char**) ((const char*[]) { __VA_ARGS__, NULL })) - -#define STRV_MAKE_EMPTY ((char*[1]) { NULL }) - -#define strv_from_stdarg_alloca(first) \ - ({ \ - char **_l; \ - \ - if (!first) \ - _l = (char**) &first; \ - else { \ - size_t _n; \ - va_list _ap; \ - \ - _n = 1; \ - va_start(_ap, first); \ - while (va_arg(_ap, char*)) \ - _n++; \ - va_end(_ap); \ - \ - _l = newa(char*, _n+1); \ - _l[_n = 0] = (char*) first; \ - va_start(_ap, first); \ - for (;;) { \ - _l[++_n] = va_arg(_ap, char*); \ - if (!_l[_n]) \ - break; \ - } \ - va_end(_ap); \ - } \ - _l; \ - }) - -#define STR_IN_SET(x, ...) strv_contains(STRV_MAKE(__VA_ARGS__), x) -#define STRPTR_IN_SET(x, ...) \ - ({ \ - const char* _x = (x); \ - _x && strv_contains(STRV_MAKE(__VA_ARGS__), _x); \ - }) - -#define FOREACH_STRING(x, ...) \ - for (char **_l = ({ \ - char **_ll = STRV_MAKE(__VA_ARGS__); \ - x = _ll ? _ll[0] : NULL; \ - _ll; \ - }); \ - _l && *_l; \ - x = ({ \ - _l ++; \ - _l[0]; \ - })) - -char **strv_reverse(char **l); -char **strv_shell_escape(char **l, const char *bad); - -bool strv_fnmatch(char* const* patterns, const char *s, int flags); - -static inline bool strv_fnmatch_or_empty(char* const* patterns, const char *s, int flags) { - assert(s); - return strv_isempty(patterns) || - strv_fnmatch(patterns, s, flags); -} - -char ***strv_free_free(char ***l); -DEFINE_TRIVIAL_CLEANUP_FUNC(char***, strv_free_free); - -char **strv_skip(char **l, size_t n); - -int strv_extend_n(char ***l, const char *value, size_t n); - -int fputstrv(FILE *f, char **l, const char *separator, bool *space); - -#define strv_free_and_replace(a, b) \ - ({ \ - strv_free(a); \ - (a) = (b); \ - (b) = NULL; \ - 0; \ - }) diff --git a/src/systemd/src/basic/time-util.c b/src/systemd/src/basic/time-util.c deleted file mode 100644 index c0f53a47..00000000 --- a/src/systemd/src/basic/time-util.c +++ /dev/null @@ -1,1503 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <ctype.h> -#include <errno.h> -#include <limits.h> -#include <stdlib.h> -#include <string.h> -#include <sys/mman.h> -#include <sys/stat.h> -#include <sys/time.h> -#include <sys/timerfd.h> -#include <sys/timex.h> -#include <sys/types.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "fs-util.h" -#include "io-util.h" -#include "log.h" -#include "macro.h" -#include "parse-util.h" -#include "path-util.h" -#include "process-util.h" -#include "stat-util.h" -#include "string-util.h" -#include "strv.h" -#include "time-util.h" - -static clockid_t map_clock_id(clockid_t c) { - - /* Some more exotic archs (s390, ppc, …) lack the "ALARM" flavour of the clocks. Thus, clock_gettime() will - * fail for them. Since they are essentially the same as their non-ALARM pendants (their only difference is - * when timers are set on them), let's just map them accordingly. This way, we can get the correct time even on - * those archs. */ - - switch (c) { - - case CLOCK_BOOTTIME_ALARM: - return CLOCK_BOOTTIME; - - case CLOCK_REALTIME_ALARM: - return CLOCK_REALTIME; - - default: - return c; - } -} - -usec_t now(clockid_t clock_id) { - struct timespec ts; - - assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0); - - return timespec_load(&ts); -} - -nsec_t now_nsec(clockid_t clock_id) { - struct timespec ts; - - assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0); - - return timespec_load_nsec(&ts); -} - -dual_timestamp* dual_timestamp_get(dual_timestamp *ts) { - assert(ts); - - ts->realtime = now(CLOCK_REALTIME); - ts->monotonic = now(CLOCK_MONOTONIC); - - return ts; -} - -triple_timestamp* triple_timestamp_get(triple_timestamp *ts) { - assert(ts); - - ts->realtime = now(CLOCK_REALTIME); - ts->monotonic = now(CLOCK_MONOTONIC); - ts->boottime = clock_boottime_supported() ? now(CLOCK_BOOTTIME) : USEC_INFINITY; - - return ts; -} - -dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) { - int64_t delta; - assert(ts); - - if (u == USEC_INFINITY || u <= 0) { - ts->realtime = ts->monotonic = u; - return ts; - } - - ts->realtime = u; - - delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u; - ts->monotonic = usec_sub_signed(now(CLOCK_MONOTONIC), delta); - - return ts; -} - -triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u) { - int64_t delta; - - assert(ts); - - if (u == USEC_INFINITY || u <= 0) { - ts->realtime = ts->monotonic = ts->boottime = u; - return ts; - } - - ts->realtime = u; - delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u; - ts->monotonic = usec_sub_signed(now(CLOCK_MONOTONIC), delta); - ts->boottime = clock_boottime_supported() ? usec_sub_signed(now(CLOCK_BOOTTIME), delta) : USEC_INFINITY; - - return ts; -} - -dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) { - int64_t delta; - assert(ts); - - if (u == USEC_INFINITY) { - ts->realtime = ts->monotonic = USEC_INFINITY; - return ts; - } - - ts->monotonic = u; - delta = (int64_t) now(CLOCK_MONOTONIC) - (int64_t) u; - ts->realtime = usec_sub_signed(now(CLOCK_REALTIME), delta); - - return ts; -} - -dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u) { - int64_t delta; - - if (u == USEC_INFINITY) { - ts->realtime = ts->monotonic = USEC_INFINITY; - return ts; - } - - dual_timestamp_get(ts); - delta = (int64_t) now(clock_boottime_or_monotonic()) - (int64_t) u; - ts->realtime = usec_sub_signed(ts->realtime, delta); - ts->monotonic = usec_sub_signed(ts->monotonic, delta); - - return ts; -} - -usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock) { - - switch (clock) { - - case CLOCK_REALTIME: - case CLOCK_REALTIME_ALARM: - return ts->realtime; - - case CLOCK_MONOTONIC: - return ts->monotonic; - - case CLOCK_BOOTTIME: - case CLOCK_BOOTTIME_ALARM: - return ts->boottime; - - default: - return USEC_INFINITY; - } -} - -usec_t timespec_load(const struct timespec *ts) { - assert(ts); - - if (ts->tv_sec < 0 || ts->tv_nsec < 0) - return USEC_INFINITY; - - if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC) - return USEC_INFINITY; - - return - (usec_t) ts->tv_sec * USEC_PER_SEC + - (usec_t) ts->tv_nsec / NSEC_PER_USEC; -} - -nsec_t timespec_load_nsec(const struct timespec *ts) { - assert(ts); - - if (ts->tv_sec < 0 || ts->tv_nsec < 0) - return NSEC_INFINITY; - - if ((nsec_t) ts->tv_sec >= (UINT64_MAX - ts->tv_nsec) / NSEC_PER_SEC) - return NSEC_INFINITY; - - return (nsec_t) ts->tv_sec * NSEC_PER_SEC + (nsec_t) ts->tv_nsec; -} - -struct timespec *timespec_store(struct timespec *ts, usec_t u) { - assert(ts); - - if (u == USEC_INFINITY || - u / USEC_PER_SEC >= TIME_T_MAX) { - ts->tv_sec = (time_t) -1; - ts->tv_nsec = (long) -1; - return ts; - } - - ts->tv_sec = (time_t) (u / USEC_PER_SEC); - ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC); - - return ts; -} - -#if 0 /* NM_IGNORED */ -usec_t timeval_load(const struct timeval *tv) { - assert(tv); - - if (tv->tv_sec < 0 || tv->tv_usec < 0) - return USEC_INFINITY; - - if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC) - return USEC_INFINITY; - - return - (usec_t) tv->tv_sec * USEC_PER_SEC + - (usec_t) tv->tv_usec; -} - -struct timeval *timeval_store(struct timeval *tv, usec_t u) { - assert(tv); - - if (u == USEC_INFINITY || - u / USEC_PER_SEC > TIME_T_MAX) { - tv->tv_sec = (time_t) -1; - tv->tv_usec = (suseconds_t) -1; - } else { - tv->tv_sec = (time_t) (u / USEC_PER_SEC); - tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC); - } - - return tv; -} - -static char *format_timestamp_internal( - char *buf, - size_t l, - usec_t t, - bool utc, - bool us) { - - /* The weekdays in non-localized (English) form. We use this instead of the localized form, so that our - * generated timestamps may be parsed with parse_timestamp(), and always read the same. */ - static const char * const weekdays[] = { - [0] = "Sun", - [1] = "Mon", - [2] = "Tue", - [3] = "Wed", - [4] = "Thu", - [5] = "Fri", - [6] = "Sat", - }; - - struct tm tm; - time_t sec; - size_t n; - - assert(buf); - - if (l < - 3 + /* week day */ - 1 + 10 + /* space and date */ - 1 + 8 + /* space and time */ - (us ? 1 + 6 : 0) + /* "." and microsecond part */ - 1 + 1 + /* space and shortest possible zone */ - 1) - return NULL; /* Not enough space even for the shortest form. */ - if (t <= 0 || t == USEC_INFINITY) - return NULL; /* Timestamp is unset */ - - /* Let's not format times with years > 9999 */ - if (t > USEC_TIMESTAMP_FORMATTABLE_MAX) { - assert(l >= strlen("--- XXXX-XX-XX XX:XX:XX") + 1); - strcpy(buf, "--- XXXX-XX-XX XX:XX:XX"); - return buf; - } - - sec = (time_t) (t / USEC_PER_SEC); /* Round down */ - - if (!localtime_or_gmtime_r(&sec, &tm, utc)) - return NULL; - - /* Start with the week day */ - assert((size_t) tm.tm_wday < ELEMENTSOF(weekdays)); - memcpy(buf, weekdays[tm.tm_wday], 4); - - /* Add the main components */ - if (strftime(buf + 3, l - 3, " %Y-%m-%d %H:%M:%S", &tm) <= 0) - return NULL; /* Doesn't fit */ - - /* Append the microseconds part, if that's requested */ - if (us) { - n = strlen(buf); - if (n + 8 > l) - return NULL; /* Microseconds part doesn't fit. */ - - sprintf(buf + n, ".%06"PRI_USEC, t % USEC_PER_SEC); - } - - /* Append the timezone */ - n = strlen(buf); - if (utc) { - /* If this is UTC then let's explicitly use the "UTC" string here, because gmtime_r() normally uses the - * obsolete "GMT" instead. */ - if (n + 5 > l) - return NULL; /* "UTC" doesn't fit. */ - - strcpy(buf + n, " UTC"); - - } else if (!isempty(tm.tm_zone)) { - size_t tn; - - /* An explicit timezone is specified, let's use it, if it fits */ - tn = strlen(tm.tm_zone); - if (n + 1 + tn + 1 > l) { - /* The full time zone does not fit in. Yuck. */ - - if (n + 1 + _POSIX_TZNAME_MAX + 1 > l) - return NULL; /* Not even enough space for the POSIX minimum (of 6)? In that case, complain that it doesn't fit */ - - /* So the time zone doesn't fit in fully, but the caller passed enough space for the POSIX - * minimum time zone length. In this case suppress the timezone entirely, in order not to dump - * an overly long, hard to read string on the user. This should be safe, because the user will - * assume the local timezone anyway if none is shown. And so does parse_timestamp(). */ - } else { - buf[n++] = ' '; - strcpy(buf + n, tm.tm_zone); - } - } - - return buf; -} - -char *format_timestamp(char *buf, size_t l, usec_t t) { - return format_timestamp_internal(buf, l, t, false, false); -} - -char *format_timestamp_utc(char *buf, size_t l, usec_t t) { - return format_timestamp_internal(buf, l, t, true, false); -} - -char *format_timestamp_us(char *buf, size_t l, usec_t t) { - return format_timestamp_internal(buf, l, t, false, true); -} - -char *format_timestamp_us_utc(char *buf, size_t l, usec_t t) { - return format_timestamp_internal(buf, l, t, true, true); -} - -char *format_timestamp_relative(char *buf, size_t l, usec_t t) { - const char *s; - usec_t n, d; - - if (t <= 0 || t == USEC_INFINITY) - return NULL; - - n = now(CLOCK_REALTIME); - if (n > t) { - d = n - t; - s = "ago"; - } else { - d = t - n; - s = "left"; - } - - if (d >= USEC_PER_YEAR) - snprintf(buf, l, USEC_FMT " years " USEC_FMT " months %s", - d / USEC_PER_YEAR, - (d % USEC_PER_YEAR) / USEC_PER_MONTH, s); - else if (d >= USEC_PER_MONTH) - snprintf(buf, l, USEC_FMT " months " USEC_FMT " days %s", - d / USEC_PER_MONTH, - (d % USEC_PER_MONTH) / USEC_PER_DAY, s); - else if (d >= USEC_PER_WEEK) - snprintf(buf, l, USEC_FMT " weeks " USEC_FMT " days %s", - d / USEC_PER_WEEK, - (d % USEC_PER_WEEK) / USEC_PER_DAY, s); - else if (d >= 2*USEC_PER_DAY) - snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s); - else if (d >= 25*USEC_PER_HOUR) - snprintf(buf, l, "1 day " USEC_FMT "h %s", - (d - USEC_PER_DAY) / USEC_PER_HOUR, s); - else if (d >= 6*USEC_PER_HOUR) - snprintf(buf, l, USEC_FMT "h %s", - d / USEC_PER_HOUR, s); - else if (d >= USEC_PER_HOUR) - snprintf(buf, l, USEC_FMT "h " USEC_FMT "min %s", - d / USEC_PER_HOUR, - (d % USEC_PER_HOUR) / USEC_PER_MINUTE, s); - else if (d >= 5*USEC_PER_MINUTE) - snprintf(buf, l, USEC_FMT "min %s", - d / USEC_PER_MINUTE, s); - else if (d >= USEC_PER_MINUTE) - snprintf(buf, l, USEC_FMT "min " USEC_FMT "s %s", - d / USEC_PER_MINUTE, - (d % USEC_PER_MINUTE) / USEC_PER_SEC, s); - else if (d >= USEC_PER_SEC) - snprintf(buf, l, USEC_FMT "s %s", - d / USEC_PER_SEC, s); - else if (d >= USEC_PER_MSEC) - snprintf(buf, l, USEC_FMT "ms %s", - d / USEC_PER_MSEC, s); - else if (d > 0) - snprintf(buf, l, USEC_FMT"us %s", - d, s); - else - snprintf(buf, l, "now"); - - buf[l-1] = 0; - return buf; -} -#endif /* NM_IGNORED */ - -char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) { - static const struct { - const char *suffix; - usec_t usec; - } table[] = { - { "y", USEC_PER_YEAR }, - { "month", USEC_PER_MONTH }, - { "w", USEC_PER_WEEK }, - { "d", USEC_PER_DAY }, - { "h", USEC_PER_HOUR }, - { "min", USEC_PER_MINUTE }, - { "s", USEC_PER_SEC }, - { "ms", USEC_PER_MSEC }, - { "us", 1 }, - }; - - size_t i; - char *p = buf; - bool something = false; - - assert(buf); - assert(l > 0); - - if (t == USEC_INFINITY) { - strncpy(p, "infinity", l-1); - p[l-1] = 0; - return p; - } - - if (t <= 0) { - strncpy(p, "0", l-1); - p[l-1] = 0; - return p; - } - - /* The result of this function can be parsed with parse_sec */ - - for (i = 0; i < ELEMENTSOF(table); i++) { - int k = 0; - size_t n; - bool done = false; - usec_t a, b; - - if (t <= 0) - break; - - if (t < accuracy && something) - break; - - if (t < table[i].usec) - continue; - - if (l <= 1) - break; - - a = t / table[i].usec; - b = t % table[i].usec; - - /* Let's see if we should shows this in dot notation */ - if (t < USEC_PER_MINUTE && b > 0) { - usec_t cc; - signed char j; - - j = 0; - for (cc = table[i].usec; cc > 1; cc /= 10) - j++; - - for (cc = accuracy; cc > 1; cc /= 10) { - b /= 10; - j--; - } - - if (j > 0) { - k = snprintf(p, l, - "%s"USEC_FMT".%0*"PRI_USEC"%s", - p > buf ? " " : "", - a, - j, - b, - table[i].suffix); - - t = 0; - done = true; - } - } - - /* No? Then let's show it normally */ - if (!done) { - k = snprintf(p, l, - "%s"USEC_FMT"%s", - p > buf ? " " : "", - a, - table[i].suffix); - - t = b; - } - - n = MIN((size_t) k, l); - - l -= n; - p += n; - - something = true; - } - - *p = 0; - - return buf; -} - -#if 0 /* NM_IGNORED */ -void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) { - - assert(f); - assert(name); - assert(t); - - if (!dual_timestamp_is_set(t)) - return; - - fprintf(f, "%s="USEC_FMT" "USEC_FMT"\n", - name, - t->realtime, - t->monotonic); -} - -int dual_timestamp_deserialize(const char *value, dual_timestamp *t) { - uint64_t a, b; - int r, pos; - - assert(value); - assert(t); - - pos = strspn(value, WHITESPACE); - if (value[pos] == '-') - return -EINVAL; - pos += strspn(value + pos, DIGITS); - pos += strspn(value + pos, WHITESPACE); - if (value[pos] == '-') - return -EINVAL; - - r = sscanf(value, "%" PRIu64 "%" PRIu64 "%n", &a, &b, &pos); - if (r != 2) { - log_debug("Failed to parse dual timestamp value \"%s\".", value); - return -EINVAL; - } - - if (value[pos] != '\0') - /* trailing garbage */ - return -EINVAL; - - t->realtime = a; - t->monotonic = b; - - return 0; -} - -int timestamp_deserialize(const char *value, usec_t *timestamp) { - int r; - - assert(value); - - r = safe_atou64(value, timestamp); - if (r < 0) - return log_debug_errno(r, "Failed to parse timestamp value \"%s\": %m", value); - - return r; -} - -static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) { - static const struct { - const char *name; - const int nr; - } day_nr[] = { - { "Sunday", 0 }, - { "Sun", 0 }, - { "Monday", 1 }, - { "Mon", 1 }, - { "Tuesday", 2 }, - { "Tue", 2 }, - { "Wednesday", 3 }, - { "Wed", 3 }, - { "Thursday", 4 }, - { "Thu", 4 }, - { "Friday", 5 }, - { "Fri", 5 }, - { "Saturday", 6 }, - { "Sat", 6 }, - }; - - const char *k, *utc = NULL, *tzn = NULL; - struct tm tm, copy; - time_t x; - usec_t x_usec, plus = 0, minus = 0, ret; - int r, weekday = -1, dst = -1; - size_t i; - - /* Allowed syntaxes: - * - * 2012-09-22 16:34:22 - * 2012-09-22 16:34 (seconds will be set to 0) - * 2012-09-22 (time will be set to 00:00:00) - * 16:34:22 (date will be set to today) - * 16:34 (date will be set to today, seconds to 0) - * now - * yesterday (time is set to 00:00:00) - * today (time is set to 00:00:00) - * tomorrow (time is set to 00:00:00) - * +5min - * -5days - * @2147483647 (seconds since epoch) - */ - - assert(t); - assert(usec); - - if (t[0] == '@' && !with_tz) - return parse_sec(t + 1, usec); - - ret = now(CLOCK_REALTIME); - - if (!with_tz) { - if (streq(t, "now")) - goto finish; - - else if (t[0] == '+') { - r = parse_sec(t+1, &plus); - if (r < 0) - return r; - - goto finish; - - } else if (t[0] == '-') { - r = parse_sec(t+1, &minus); - if (r < 0) - return r; - - goto finish; - - } else if ((k = endswith(t, " ago"))) { - t = strndupa(t, k - t); - - r = parse_sec(t, &minus); - if (r < 0) - return r; - - goto finish; - - } else if ((k = endswith(t, " left"))) { - t = strndupa(t, k - t); - - r = parse_sec(t, &plus); - if (r < 0) - return r; - - goto finish; - } - - /* See if the timestamp is suffixed with UTC */ - utc = endswith_no_case(t, " UTC"); - if (utc) - t = strndupa(t, utc - t); - else { - const char *e = NULL; - int j; - - tzset(); - - /* See if the timestamp is suffixed by either the DST or non-DST local timezone. Note that we only - * support the local timezones here, nothing else. Not because we wouldn't want to, but simply because - * there are no nice APIs available to cover this. By accepting the local time zone strings, we make - * sure that all timestamps written by format_timestamp() can be parsed correctly, even though we don't - * support arbitrary timezone specifications. */ - - for (j = 0; j <= 1; j++) { - - if (isempty(tzname[j])) - continue; - - e = endswith_no_case(t, tzname[j]); - if (!e) - continue; - if (e == t) - continue; - if (e[-1] != ' ') - continue; - - break; - } - - if (IN_SET(j, 0, 1)) { - /* Found one of the two timezones specified. */ - t = strndupa(t, e - t - 1); - dst = j; - tzn = tzname[j]; - } - } - } - - x = (time_t) (ret / USEC_PER_SEC); - x_usec = 0; - - if (!localtime_or_gmtime_r(&x, &tm, utc)) - return -EINVAL; - - tm.tm_isdst = dst; - if (!with_tz && tzn) - tm.tm_zone = tzn; - - if (streq(t, "today")) { - tm.tm_sec = tm.tm_min = tm.tm_hour = 0; - goto from_tm; - - } else if (streq(t, "yesterday")) { - tm.tm_mday--; - tm.tm_sec = tm.tm_min = tm.tm_hour = 0; - goto from_tm; - - } else if (streq(t, "tomorrow")) { - tm.tm_mday++; - tm.tm_sec = tm.tm_min = tm.tm_hour = 0; - goto from_tm; - } - - for (i = 0; i < ELEMENTSOF(day_nr); i++) { - size_t skip; - - if (!startswith_no_case(t, day_nr[i].name)) - continue; - - skip = strlen(day_nr[i].name); - if (t[skip] != ' ') - continue; - - weekday = day_nr[i].nr; - t += skip + 1; - break; - } - - copy = tm; - k = strptime(t, "%y-%m-%d %H:%M:%S", &tm); - if (k) { - if (*k == '.') - goto parse_usec; - else if (*k == 0) - goto from_tm; - } - - tm = copy; - k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm); - if (k) { - if (*k == '.') - goto parse_usec; - else if (*k == 0) - goto from_tm; - } - - tm = copy; - k = strptime(t, "%y-%m-%d %H:%M", &tm); - if (k && *k == 0) { - tm.tm_sec = 0; - goto from_tm; - } - - tm = copy; - k = strptime(t, "%Y-%m-%d %H:%M", &tm); - if (k && *k == 0) { - tm.tm_sec = 0; - goto from_tm; - } - - tm = copy; - k = strptime(t, "%y-%m-%d", &tm); - if (k && *k == 0) { - tm.tm_sec = tm.tm_min = tm.tm_hour = 0; - goto from_tm; - } - - tm = copy; - k = strptime(t, "%Y-%m-%d", &tm); - if (k && *k == 0) { - tm.tm_sec = tm.tm_min = tm.tm_hour = 0; - goto from_tm; - } - - tm = copy; - k = strptime(t, "%H:%M:%S", &tm); - if (k) { - if (*k == '.') - goto parse_usec; - else if (*k == 0) - goto from_tm; - } - - tm = copy; - k = strptime(t, "%H:%M", &tm); - if (k && *k == 0) { - tm.tm_sec = 0; - goto from_tm; - } - - return -EINVAL; - -parse_usec: - { - unsigned add; - - k++; - r = parse_fractional_part_u(&k, 6, &add); - if (r < 0) - return -EINVAL; - - if (*k) - return -EINVAL; - - x_usec = add; - } - -from_tm: - if (weekday >= 0 && tm.tm_wday != weekday) - return -EINVAL; - - x = mktime_or_timegm(&tm, utc); - if (x < 0) - return -EINVAL; - - ret = (usec_t) x * USEC_PER_SEC + x_usec; - if (ret > USEC_TIMESTAMP_FORMATTABLE_MAX) - return -EINVAL; - -finish: - if (ret + plus < ret) /* overflow? */ - return -EINVAL; - ret += plus; - if (ret > USEC_TIMESTAMP_FORMATTABLE_MAX) - return -EINVAL; - - if (ret >= minus) - ret -= minus; - else - return -EINVAL; - - *usec = ret; - - return 0; -} - -typedef struct ParseTimestampResult { - usec_t usec; - int return_value; -} ParseTimestampResult; - -int parse_timestamp(const char *t, usec_t *usec) { - char *last_space, *tz = NULL; - ParseTimestampResult *shared, tmp; - int r; - - last_space = strrchr(t, ' '); - if (last_space != NULL && timezone_is_valid(last_space + 1, LOG_DEBUG)) - tz = last_space + 1; - - if (!tz || endswith_no_case(t, " UTC")) - return parse_timestamp_impl(t, usec, false); - - shared = mmap(NULL, sizeof *shared, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); - if (shared == MAP_FAILED) - return negative_errno(); - - r = safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT, NULL); - if (r < 0) { - (void) munmap(shared, sizeof *shared); - return r; - } - if (r == 0) { - bool with_tz = true; - - if (setenv("TZ", tz, 1) != 0) { - shared->return_value = negative_errno(); - _exit(EXIT_FAILURE); - } - - tzset(); - - /* If there is a timezone that matches the tzname fields, leave the parsing to the implementation. - * Otherwise just cut it off. */ - with_tz = !STR_IN_SET(tz, tzname[0], tzname[1]); - - /* Cut off the timezone if we dont need it. */ - if (with_tz) - t = strndupa(t, last_space - t); - - shared->return_value = parse_timestamp_impl(t, &shared->usec, with_tz); - - _exit(EXIT_SUCCESS); - } - - tmp = *shared; - if (munmap(shared, sizeof *shared) != 0) - return negative_errno(); - - if (tmp.return_value == 0) - *usec = tmp.usec; - - return tmp.return_value; -} - -static char* extract_multiplier(char *p, usec_t *multiplier) { - static const struct { - const char *suffix; - usec_t usec; - } table[] = { - { "seconds", USEC_PER_SEC }, - { "second", USEC_PER_SEC }, - { "sec", USEC_PER_SEC }, - { "s", USEC_PER_SEC }, - { "minutes", USEC_PER_MINUTE }, - { "minute", USEC_PER_MINUTE }, - { "min", USEC_PER_MINUTE }, - { "months", USEC_PER_MONTH }, - { "month", USEC_PER_MONTH }, - { "M", USEC_PER_MONTH }, - { "msec", USEC_PER_MSEC }, - { "ms", USEC_PER_MSEC }, - { "m", USEC_PER_MINUTE }, - { "hours", USEC_PER_HOUR }, - { "hour", USEC_PER_HOUR }, - { "hr", USEC_PER_HOUR }, - { "h", USEC_PER_HOUR }, - { "days", USEC_PER_DAY }, - { "day", USEC_PER_DAY }, - { "d", USEC_PER_DAY }, - { "weeks", USEC_PER_WEEK }, - { "week", USEC_PER_WEEK }, - { "w", USEC_PER_WEEK }, - { "years", USEC_PER_YEAR }, - { "year", USEC_PER_YEAR }, - { "y", USEC_PER_YEAR }, - { "usec", 1ULL }, - { "us", 1ULL }, - { "µs", 1ULL }, - }; - size_t i; - - for (i = 0; i < ELEMENTSOF(table); i++) { - char *e; - - e = startswith(p, table[i].suffix); - if (e) { - *multiplier = table[i].usec; - return e; - } - } - - return p; -} - -int parse_time(const char *t, usec_t *usec, usec_t default_unit) { - const char *p, *s; - usec_t r = 0; - bool something = false; - - assert(t); - assert(usec); - assert(default_unit > 0); - - p = t; - - p += strspn(p, WHITESPACE); - s = startswith(p, "infinity"); - if (s) { - s += strspn(s, WHITESPACE); - if (*s != 0) - return -EINVAL; - - *usec = USEC_INFINITY; - return 0; - } - - for (;;) { - usec_t multiplier = default_unit, k; - long long l, z = 0; - unsigned n = 0; - char *e; - - p += strspn(p, WHITESPACE); - - if (*p == 0) { - if (!something) - return -EINVAL; - - break; - } - - if (*p == '-') /* Don't allow "-0" */ - return -ERANGE; - - errno = 0; - l = strtoll(p, &e, 10); - if (errno > 0) - return -errno; - if (l < 0) - return -ERANGE; - - if (*e == '.') { - char *b = e + 1; - - /* Don't allow "0.-0", "3.+1" or "3. 1" */ - if (*b == '-' || *b == '+' || isspace(*b)) - return -EINVAL; - - errno = 0; - z = strtoll(b, &e, 10); - if (errno > 0) - return -errno; - if (z < 0) - return -ERANGE; - if (e == b) - return -EINVAL; - - n = e - b; - - } else if (e == p) - return -EINVAL; - - e += strspn(e, WHITESPACE); - p = extract_multiplier(e, &multiplier); - - something = true; - - k = (usec_t) z * multiplier; - - for (; n > 0; n--) - k /= 10; - - r += (usec_t) l * multiplier + k; - } - - *usec = r; - - return 0; -} - -int parse_sec(const char *t, usec_t *usec) { - return parse_time(t, usec, USEC_PER_SEC); -} - -int parse_sec_fix_0(const char *t, usec_t *usec) { - assert(t); - assert(usec); - - t += strspn(t, WHITESPACE); - - if (streq(t, "0")) { - *usec = USEC_INFINITY; - return 0; - } - - return parse_sec(t, usec); -} - -int parse_nsec(const char *t, nsec_t *nsec) { - static const struct { - const char *suffix; - nsec_t nsec; - } table[] = { - { "seconds", NSEC_PER_SEC }, - { "second", NSEC_PER_SEC }, - { "sec", NSEC_PER_SEC }, - { "s", NSEC_PER_SEC }, - { "minutes", NSEC_PER_MINUTE }, - { "minute", NSEC_PER_MINUTE }, - { "min", NSEC_PER_MINUTE }, - { "months", NSEC_PER_MONTH }, - { "month", NSEC_PER_MONTH }, - { "msec", NSEC_PER_MSEC }, - { "ms", NSEC_PER_MSEC }, - { "m", NSEC_PER_MINUTE }, - { "hours", NSEC_PER_HOUR }, - { "hour", NSEC_PER_HOUR }, - { "hr", NSEC_PER_HOUR }, - { "h", NSEC_PER_HOUR }, - { "days", NSEC_PER_DAY }, - { "day", NSEC_PER_DAY }, - { "d", NSEC_PER_DAY }, - { "weeks", NSEC_PER_WEEK }, - { "week", NSEC_PER_WEEK }, - { "w", NSEC_PER_WEEK }, - { "years", NSEC_PER_YEAR }, - { "year", NSEC_PER_YEAR }, - { "y", NSEC_PER_YEAR }, - { "usec", NSEC_PER_USEC }, - { "us", NSEC_PER_USEC }, - { "µs", NSEC_PER_USEC }, - { "nsec", 1ULL }, - { "ns", 1ULL }, - { "", 1ULL }, /* default is nsec */ - }; - - const char *p, *s; - nsec_t r = 0; - bool something = false; - - assert(t); - assert(nsec); - - p = t; - - p += strspn(p, WHITESPACE); - s = startswith(p, "infinity"); - if (s) { - s += strspn(s, WHITESPACE); - if (*s != 0) - return -EINVAL; - - *nsec = NSEC_INFINITY; - return 0; - } - - for (;;) { - long long l, z = 0; - size_t n = 0, i; - char *e; - - p += strspn(p, WHITESPACE); - - if (*p == 0) { - if (!something) - return -EINVAL; - - break; - } - - if (*p == '-') - return -ERANGE; - - errno = 0; - l = strtoll(p, &e, 10); - if (errno > 0) - return -errno; - if (l < 0) - return -ERANGE; - - if (*e == '.') { - char *b = e + 1; - - if (*b == '-' || *b == '+' || isspace(*b)) - return -EINVAL; - - errno = 0; - z = strtoll(b, &e, 10); - if (errno > 0) - return -errno; - if (z < 0) - return -ERANGE; - if (e == b) - return -EINVAL; - - n = e - b; - - } else if (e == p) - return -EINVAL; - - e += strspn(e, WHITESPACE); - - for (i = 0; i < ELEMENTSOF(table); i++) - if (startswith(e, table[i].suffix)) { - nsec_t k = (nsec_t) z * table[i].nsec; - - for (; n > 0; n--) - k /= 10; - - r += (nsec_t) l * table[i].nsec + k; - p = e + strlen(table[i].suffix); - - something = true; - break; - } - - if (i >= ELEMENTSOF(table)) - return -EINVAL; - - } - - *nsec = r; - - return 0; -} - -bool ntp_synced(void) { - struct timex txc = {}; - - if (adjtimex(&txc) < 0) - return false; - - if (txc.status & STA_UNSYNC) - return false; - - return true; -} - -int get_timezones(char ***ret) { - _cleanup_fclose_ FILE *f = NULL; - _cleanup_strv_free_ char **zones = NULL; - size_t n_zones = 0, n_allocated = 0; - - assert(ret); - - zones = strv_new("UTC", NULL); - if (!zones) - return -ENOMEM; - - n_allocated = 2; - n_zones = 1; - - f = fopen("/usr/share/zoneinfo/zone.tab", "re"); - if (f) { - char l[LINE_MAX]; - - FOREACH_LINE(l, f, return -errno) { - char *p, *w; - size_t k; - - p = strstrip(l); - - if (isempty(p) || *p == '#') - continue; - - /* Skip over country code */ - p += strcspn(p, WHITESPACE); - p += strspn(p, WHITESPACE); - - /* Skip over coordinates */ - p += strcspn(p, WHITESPACE); - p += strspn(p, WHITESPACE); - - /* Found timezone name */ - k = strcspn(p, WHITESPACE); - if (k <= 0) - continue; - - w = strndup(p, k); - if (!w) - return -ENOMEM; - - if (!GREEDY_REALLOC(zones, n_allocated, n_zones + 2)) { - free(w); - return -ENOMEM; - } - - zones[n_zones++] = w; - zones[n_zones] = NULL; - } - - strv_sort(zones); - - } else if (errno != ENOENT) - return -errno; - - *ret = TAKE_PTR(zones); - - return 0; -} -#endif /* NM_IGNORED */ - -bool timezone_is_valid(const char *name, int log_level) { - bool slash = false; - const char *p, *t; - _cleanup_close_ int fd = -1; - char buf[4]; - int r; - - if (isempty(name)) - return false; - - if (name[0] == '/') - return false; - - for (p = name; *p; p++) { - if (!(*p >= '0' && *p <= '9') && - !(*p >= 'a' && *p <= 'z') && - !(*p >= 'A' && *p <= 'Z') && - !IN_SET(*p, '-', '_', '+', '/')) - return false; - - if (*p == '/') { - - if (slash) - return false; - - slash = true; - } else - slash = false; - } - - if (slash) - return false; - - if (p - name >= PATH_MAX) - return false; - - t = strjoina("/usr/share/zoneinfo/", name); - - fd = open(t, O_RDONLY|O_CLOEXEC); - if (fd < 0) { - log_full_errno(log_level, errno, "Failed to open timezone file '%s': %m", t); - return false; - } - - r = fd_verify_regular(fd); - if (r < 0) { - log_full_errno(log_level, r, "Timezone file '%s' is not a regular file: %m", t); - return false; - } - - r = loop_read_exact(fd, buf, 4, false); - if (r < 0) { - log_full_errno(log_level, r, "Failed to read from timezone file '%s': %m", t); - return false; - } - - /* Magic from tzfile(5) */ - if (memcmp(buf, "TZif", 4) != 0) { - log_full(log_level, "Timezone file '%s' has wrong magic bytes", t); - return false; - } - - return true; -} - -bool clock_boottime_supported(void) { - static int supported = -1; - - /* Note that this checks whether CLOCK_BOOTTIME is available in general as well as available for timerfds()! */ - - if (supported < 0) { - int fd; - - fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC); - if (fd < 0) - supported = false; - else { - safe_close(fd); - supported = true; - } - } - - return supported; -} - -clockid_t clock_boottime_or_monotonic(void) { - if (clock_boottime_supported()) - return CLOCK_BOOTTIME; - else - return CLOCK_MONOTONIC; -} - -bool clock_supported(clockid_t clock) { - struct timespec ts; - - switch (clock) { - - case CLOCK_MONOTONIC: - case CLOCK_REALTIME: - return true; - - case CLOCK_BOOTTIME: - return clock_boottime_supported(); - - case CLOCK_BOOTTIME_ALARM: - if (!clock_boottime_supported()) - return false; - - _fallthrough_; - default: - /* For everything else, check properly */ - return clock_gettime(clock, &ts) >= 0; - } -} - -#if 0 /* NM_IGNORED */ -int get_timezone(char **tz) { - _cleanup_free_ char *t = NULL; - const char *e; - char *z; - int r; - - r = readlink_malloc("/etc/localtime", &t); - if (r < 0) - return r; /* returns EINVAL if not a symlink */ - - e = path_startswith(t, "/usr/share/zoneinfo/"); - if (!e) - e = path_startswith(t, "../usr/share/zoneinfo/"); - if (!e) - return -EINVAL; - - if (!timezone_is_valid(e, LOG_DEBUG)) - return -EINVAL; - - z = strdup(e); - if (!z) - return -ENOMEM; - - *tz = z; - return 0; -} - -time_t mktime_or_timegm(struct tm *tm, bool utc) { - return utc ? timegm(tm) : mktime(tm); -} - -struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc) { - return utc ? gmtime_r(t, tm) : localtime_r(t, tm); -} - -unsigned long usec_to_jiffies(usec_t u) { - static thread_local unsigned long hz = 0; - long r; - - if (hz == 0) { - r = sysconf(_SC_CLK_TCK); - - assert(r > 0); - hz = r; - } - - return DIV_ROUND_UP(u , USEC_PER_SEC / hz); -} - -usec_t usec_shift_clock(usec_t x, clockid_t from, clockid_t to) { - usec_t a, b; - - if (x == USEC_INFINITY) - return USEC_INFINITY; - if (map_clock_id(from) == map_clock_id(to)) - return x; - - a = now(from); - b = now(to); - - if (x > a) - /* x lies in the future */ - return usec_add(b, usec_sub_unsigned(x, a)); - else - /* x lies in the past */ - return usec_sub_unsigned(b, usec_sub_unsigned(a, x)); -} - -bool in_utc_timezone(void) { - tzset(); - - return timezone == 0 && daylight == 0; -} - -int time_change_fd(void) { - - /* We only care for the cancellation event, hence we set the timeout to the latest possible value. */ - static const struct itimerspec its = { - .it_value.tv_sec = TIME_T_MAX, - }; - - _cleanup_close_ int fd; - - assert_cc(sizeof(time_t) == sizeof(TIME_T_MAX)); - - /* Uses TFD_TIMER_CANCEL_ON_SET to get notifications whenever CLOCK_REALTIME makes a jump relative to - * CLOCK_MONOTONIC. */ - - fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC); - if (fd < 0) - return -errno; - - if (timerfd_settime(fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its, NULL) < 0) - return -errno; - - return TAKE_FD(fd); -} -#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/time-util.h b/src/systemd/src/basic/time-util.h deleted file mode 100644 index 344f2dc5..00000000 --- a/src/systemd/src/basic/time-util.h +++ /dev/null @@ -1,183 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <inttypes.h> -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> -#include <stdio.h> -#include <time.h> - -typedef uint64_t usec_t; -typedef uint64_t nsec_t; - -#define PRI_NSEC PRIu64 -#define PRI_USEC PRIu64 -#define NSEC_FMT "%" PRI_NSEC -#define USEC_FMT "%" PRI_USEC - -#include "macro.h" - -typedef struct dual_timestamp { - usec_t realtime; - usec_t monotonic; -} dual_timestamp; - -typedef struct triple_timestamp { - usec_t realtime; - usec_t monotonic; - usec_t boottime; -} triple_timestamp; - -#define USEC_INFINITY ((usec_t) -1) -#define NSEC_INFINITY ((nsec_t) -1) - -#define MSEC_PER_SEC 1000ULL -#define USEC_PER_SEC ((usec_t) 1000000ULL) -#define USEC_PER_MSEC ((usec_t) 1000ULL) -#define NSEC_PER_SEC ((nsec_t) 1000000000ULL) -#define NSEC_PER_MSEC ((nsec_t) 1000000ULL) -#define NSEC_PER_USEC ((nsec_t) 1000ULL) - -#define USEC_PER_MINUTE ((usec_t) (60ULL*USEC_PER_SEC)) -#define NSEC_PER_MINUTE ((nsec_t) (60ULL*NSEC_PER_SEC)) -#define USEC_PER_HOUR ((usec_t) (60ULL*USEC_PER_MINUTE)) -#define NSEC_PER_HOUR ((nsec_t) (60ULL*NSEC_PER_MINUTE)) -#define USEC_PER_DAY ((usec_t) (24ULL*USEC_PER_HOUR)) -#define NSEC_PER_DAY ((nsec_t) (24ULL*NSEC_PER_HOUR)) -#define USEC_PER_WEEK ((usec_t) (7ULL*USEC_PER_DAY)) -#define NSEC_PER_WEEK ((nsec_t) (7ULL*NSEC_PER_DAY)) -#define USEC_PER_MONTH ((usec_t) (2629800ULL*USEC_PER_SEC)) -#define NSEC_PER_MONTH ((nsec_t) (2629800ULL*NSEC_PER_SEC)) -#define USEC_PER_YEAR ((usec_t) (31557600ULL*USEC_PER_SEC)) -#define NSEC_PER_YEAR ((nsec_t) (31557600ULL*NSEC_PER_SEC)) - -/* We assume a maximum timezone length of 6. TZNAME_MAX is not defined on Linux, but glibc internally initializes this - * to 6. Let's rely on that. */ -#define FORMAT_TIMESTAMP_MAX (3+1+10+1+8+1+6+1+6+1) -#define FORMAT_TIMESTAMP_WIDTH 28 /* when outputting, assume this width */ -#define FORMAT_TIMESTAMP_RELATIVE_MAX 256 -#define FORMAT_TIMESPAN_MAX 64 - -#define TIME_T_MAX (time_t)((UINTMAX_C(1) << ((sizeof(time_t) << 3) - 1)) - 1) - -#define DUAL_TIMESTAMP_NULL ((struct dual_timestamp) {}) -#define TRIPLE_TIMESTAMP_NULL ((struct triple_timestamp) {}) - -usec_t now(clockid_t clock); -nsec_t now_nsec(clockid_t clock); - -dual_timestamp* dual_timestamp_get(dual_timestamp *ts); -dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u); -dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u); -dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u); - -triple_timestamp* triple_timestamp_get(triple_timestamp *ts); -triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u); - -#define DUAL_TIMESTAMP_HAS_CLOCK(clock) \ - IN_SET(clock, CLOCK_REALTIME, CLOCK_REALTIME_ALARM, CLOCK_MONOTONIC) - -#define TRIPLE_TIMESTAMP_HAS_CLOCK(clock) \ - IN_SET(clock, CLOCK_REALTIME, CLOCK_REALTIME_ALARM, CLOCK_MONOTONIC, CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM) - -static inline bool dual_timestamp_is_set(const dual_timestamp *ts) { - return ((ts->realtime > 0 && ts->realtime != USEC_INFINITY) || - (ts->monotonic > 0 && ts->monotonic != USEC_INFINITY)); -} - -static inline bool triple_timestamp_is_set(const triple_timestamp *ts) { - return ((ts->realtime > 0 && ts->realtime != USEC_INFINITY) || - (ts->monotonic > 0 && ts->monotonic != USEC_INFINITY) || - (ts->boottime > 0 && ts->boottime != USEC_INFINITY)); -} - -usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock); - -usec_t timespec_load(const struct timespec *ts) _pure_; -nsec_t timespec_load_nsec(const struct timespec *ts) _pure_; -struct timespec *timespec_store(struct timespec *ts, usec_t u); - -usec_t timeval_load(const struct timeval *tv) _pure_; -struct timeval *timeval_store(struct timeval *tv, usec_t u); - -char *format_timestamp(char *buf, size_t l, usec_t t); -char *format_timestamp_utc(char *buf, size_t l, usec_t t); -char *format_timestamp_us(char *buf, size_t l, usec_t t); -char *format_timestamp_us_utc(char *buf, size_t l, usec_t t); -char *format_timestamp_relative(char *buf, size_t l, usec_t t); -char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy); - -void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t); -int dual_timestamp_deserialize(const char *value, dual_timestamp *t); -int timestamp_deserialize(const char *value, usec_t *timestamp); - -int parse_timestamp(const char *t, usec_t *usec); - -int parse_sec(const char *t, usec_t *usec); -int parse_sec_fix_0(const char *t, usec_t *usec); -int parse_time(const char *t, usec_t *usec, usec_t default_unit); -int parse_nsec(const char *t, nsec_t *nsec); - -bool ntp_synced(void); - -int get_timezones(char ***l); -bool timezone_is_valid(const char *name, int log_level); - -bool clock_boottime_supported(void); -bool clock_supported(clockid_t clock); -clockid_t clock_boottime_or_monotonic(void); - -usec_t usec_shift_clock(usec_t, clockid_t from, clockid_t to); - -int get_timezone(char **timezone); - -time_t mktime_or_timegm(struct tm *tm, bool utc); -struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc); - -unsigned long usec_to_jiffies(usec_t usec); - -bool in_utc_timezone(void); - -static inline usec_t usec_add(usec_t a, usec_t b) { - usec_t c; - - /* Adds two time values, and makes sure USEC_INFINITY as input results as USEC_INFINITY in output, and doesn't - * overflow. */ - - c = a + b; - if (c < a || c < b) /* overflow check */ - return USEC_INFINITY; - - return c; -} - -static inline usec_t usec_sub_unsigned(usec_t timestamp, usec_t delta) { - - if (timestamp == USEC_INFINITY) /* Make sure infinity doesn't degrade */ - return USEC_INFINITY; - if (timestamp < delta) - return 0; - - return timestamp - delta; -} - -static inline usec_t usec_sub_signed(usec_t timestamp, int64_t delta) { - if (delta < 0) - return usec_add(timestamp, (usec_t) (-delta)); - else - return usec_sub_unsigned(timestamp, (usec_t) delta); -} - -#if SIZEOF_TIME_T == 8 -/* The last second we can format is 31. Dec 9999, 1s before midnight, because otherwise we'd enter 5 digit year - * territory. However, since we want to stay away from this in all timezones we take one day off. */ -#define USEC_TIMESTAMP_FORMATTABLE_MAX ((usec_t) 253402214399000000) -#elif SIZEOF_TIME_T == 4 -/* With a 32bit time_t we can't go beyond 2038... */ -#define USEC_TIMESTAMP_FORMATTABLE_MAX ((usec_t) 2147483647000000) -#else -#error "Yuck, time_t is neither 4 nor 8 bytes wide?" -#endif - -int time_change_fd(void); diff --git a/src/systemd/src/basic/umask-util.h b/src/systemd/src/basic/umask-util.h deleted file mode 100644 index e964292e..00000000 --- a/src/systemd/src/basic/umask-util.h +++ /dev/null @@ -1,28 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <stdbool.h> -#include <sys/stat.h> -#include <sys/types.h> - -#include "macro.h" - -static inline void umaskp(mode_t *u) { - umask(*u); -} - -#define _cleanup_umask_ _cleanup_(umaskp) - -struct _umask_struct_ { - mode_t mask; - bool quit; -}; - -static inline void _reset_umask_(struct _umask_struct_ *s) { - umask(s->mask); -}; - -#define RUN_WITH_UMASK(mask) \ - for (_cleanup_(_reset_umask_) struct _umask_struct_ _saved_umask_ = { umask(mask), false }; \ - !_saved_umask_.quit ; \ - _saved_umask_.quit = true) diff --git a/src/systemd/src/basic/utf8.c b/src/systemd/src/basic/utf8.c deleted file mode 100644 index 7238bca7..00000000 --- a/src/systemd/src/basic/utf8.c +++ /dev/null @@ -1,453 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -/* Parts of this file are based on the GLIB utf8 validation functions. The - * original license text follows. */ - -/* gutf8.c - Operations on UTF-8 strings. - * - * Copyright (C) 1999 Tom Tromey - * Copyright (C) 2000 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "nm-sd-adapt.h" - -#include <errno.h> -#include <stdbool.h> -#include <stdlib.h> -#include <string.h> - -#include "alloc-util.h" -#include "gunicode.h" -#include "hexdecoct.h" -#include "macro.h" -#include "utf8.h" - -bool unichar_is_valid(char32_t ch) { - - if (ch >= 0x110000) /* End of unicode space */ - return false; - if ((ch & 0xFFFFF800) == 0xD800) /* Reserved area for UTF-16 */ - return false; - if ((ch >= 0xFDD0) && (ch <= 0xFDEF)) /* Reserved */ - return false; - if ((ch & 0xFFFE) == 0xFFFE) /* BOM (Byte Order Mark) */ - return false; - - return true; -} - -static bool unichar_is_control(char32_t ch) { - - /* - 0 to ' '-1 is the C0 range. - DEL=0x7F, and DEL+1 to 0x9F is C1 range. - '\t' is in C0 range, but more or less harmless and commonly used. - */ - - return (ch < ' ' && !IN_SET(ch, '\t', '\n')) || - (0x7F <= ch && ch <= 0x9F); -} - -/* count of characters used to encode one unicode char */ -static int utf8_encoded_expected_len(const char *str) { - unsigned char c; - - assert(str); - - c = (unsigned char) str[0]; - if (c < 0x80) - return 1; - if ((c & 0xe0) == 0xc0) - return 2; - if ((c & 0xf0) == 0xe0) - return 3; - if ((c & 0xf8) == 0xf0) - return 4; - if ((c & 0xfc) == 0xf8) - return 5; - if ((c & 0xfe) == 0xfc) - return 6; - - return 0; -} - -/* decode one unicode char */ -int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) { - char32_t unichar; - int len, i; - - assert(str); - - len = utf8_encoded_expected_len(str); - - switch (len) { - case 1: - *ret_unichar = (char32_t)str[0]; - return 0; - case 2: - unichar = str[0] & 0x1f; - break; - case 3: - unichar = (char32_t)str[0] & 0x0f; - break; - case 4: - unichar = (char32_t)str[0] & 0x07; - break; - case 5: - unichar = (char32_t)str[0] & 0x03; - break; - case 6: - unichar = (char32_t)str[0] & 0x01; - break; - default: - return -EINVAL; - } - - for (i = 1; i < len; i++) { - if (((char32_t)str[i] & 0xc0) != 0x80) - return -EINVAL; - unichar <<= 6; - unichar |= (char32_t)str[i] & 0x3f; - } - - *ret_unichar = unichar; - - return 0; -} - -bool utf8_is_printable_newline(const char* str, size_t length, bool newline) { - const char *p; - - assert(str); - - for (p = str; length;) { - int encoded_len, r; - char32_t val; - - encoded_len = utf8_encoded_valid_unichar(p); - if (encoded_len < 0 || - (size_t) encoded_len > length) - return false; - - r = utf8_encoded_to_unichar(p, &val); - if (r < 0 || - unichar_is_control(val) || - (!newline && val == '\n')) - return false; - - length -= encoded_len; - p += encoded_len; - } - - return true; -} - -const char *utf8_is_valid(const char *str) { - const uint8_t *p; - - assert(str); - - for (p = (const uint8_t*) str; *p; ) { - int len; - - len = utf8_encoded_valid_unichar((const char *)p); - if (len < 0) - return NULL; - - p += len; - } - - return str; -} - -char *utf8_escape_invalid(const char *str) { - char *p, *s; - - assert(str); - - p = s = malloc(strlen(str) * 4 + 1); - if (!p) - return NULL; - - while (*str) { - int len; - - len = utf8_encoded_valid_unichar(str); - if (len > 0) { - s = mempcpy(s, str, len); - str += len; - } else { - s = stpcpy(s, UTF8_REPLACEMENT_CHARACTER); - str += 1; - } - } - - *s = '\0'; - - return p; -} - -char *utf8_escape_non_printable(const char *str) { - char *p, *s; - - assert(str); - - p = s = malloc(strlen(str) * 4 + 1); - if (!p) - return NULL; - - while (*str) { - int len; - - len = utf8_encoded_valid_unichar(str); - if (len > 0) { - if (utf8_is_printable(str, len)) { - s = mempcpy(s, str, len); - str += len; - } else { - while (len > 0) { - *(s++) = '\\'; - *(s++) = 'x'; - *(s++) = hexchar((int) *str >> 4); - *(s++) = hexchar((int) *str); - - str += 1; - len--; - } - } - } else { - s = stpcpy(s, UTF8_REPLACEMENT_CHARACTER); - str += 1; - } - } - - *s = '\0'; - - return p; -} - -char *ascii_is_valid(const char *str) { - const char *p; - - /* Check whether the string consists of valid ASCII bytes, - * i.e values between 0 and 127, inclusive. */ - - assert(str); - - for (p = str; *p; p++) - if ((unsigned char) *p >= 128) - return NULL; - - return (char*) str; -} - -char *ascii_is_valid_n(const char *str, size_t len) { - size_t i; - - /* Very similar to ascii_is_valid(), but checks exactly len - * bytes and rejects any NULs in that range. */ - - assert(str); - - for (i = 0; i < len; i++) - if ((unsigned char) str[i] >= 128 || str[i] == 0) - return NULL; - - return (char*) str; -} - -/** - * utf8_encode_unichar() - Encode single UCS-4 character as UTF-8 - * @out_utf8: output buffer of at least 4 bytes or NULL - * @g: UCS-4 character to encode - * - * This encodes a single UCS-4 character as UTF-8 and writes it into @out_utf8. - * The length of the character is returned. It is not zero-terminated! If the - * output buffer is NULL, only the length is returned. - * - * Returns: The length in bytes that the UTF-8 representation does or would - * occupy. - */ -size_t utf8_encode_unichar(char *out_utf8, char32_t g) { - - if (g < (1 << 7)) { - if (out_utf8) - out_utf8[0] = g & 0x7f; - return 1; - } else if (g < (1 << 11)) { - if (out_utf8) { - out_utf8[0] = 0xc0 | ((g >> 6) & 0x1f); - out_utf8[1] = 0x80 | (g & 0x3f); - } - return 2; - } else if (g < (1 << 16)) { - if (out_utf8) { - out_utf8[0] = 0xe0 | ((g >> 12) & 0x0f); - out_utf8[1] = 0x80 | ((g >> 6) & 0x3f); - out_utf8[2] = 0x80 | (g & 0x3f); - } - return 3; - } else if (g < (1 << 21)) { - if (out_utf8) { - out_utf8[0] = 0xf0 | ((g >> 18) & 0x07); - out_utf8[1] = 0x80 | ((g >> 12) & 0x3f); - out_utf8[2] = 0x80 | ((g >> 6) & 0x3f); - out_utf8[3] = 0x80 | (g & 0x3f); - } - return 4; - } - - return 0; -} - -char *utf16_to_utf8(const void *s, size_t length) { - const uint8_t *f; - char *r, *t; - - r = new(char, (length * 4 + 1) / 2 + 1); - if (!r) - return NULL; - - f = s; - t = r; - - while (f < (const uint8_t*) s + length) { - char16_t w1, w2; - - /* see RFC 2781 section 2.2 */ - - w1 = f[1] << 8 | f[0]; - f += 2; - - if (!utf16_is_surrogate(w1)) { - t += utf8_encode_unichar(t, w1); - - continue; - } - - if (utf16_is_trailing_surrogate(w1)) - continue; - else if (f >= (const uint8_t*) s + length) - break; - - w2 = f[1] << 8 | f[0]; - f += 2; - - if (!utf16_is_trailing_surrogate(w2)) { - f -= 2; - continue; - } - - t += utf8_encode_unichar(t, utf16_surrogate_pair_to_unichar(w1, w2)); - } - - *t = 0; - return r; -} - -/* expected size used to encode one unicode char */ -static int utf8_unichar_to_encoded_len(char32_t unichar) { - - if (unichar < 0x80) - return 1; - if (unichar < 0x800) - return 2; - if (unichar < 0x10000) - return 3; - if (unichar < 0x200000) - return 4; - if (unichar < 0x4000000) - return 5; - - return 6; -} - -/* validate one encoded unicode char and return its length */ -int utf8_encoded_valid_unichar(const char *str) { - int len, i, r; - char32_t unichar; - - assert(str); - - len = utf8_encoded_expected_len(str); - if (len == 0) - return -EINVAL; - - /* ascii is valid */ - if (len == 1) - return 1; - - /* check if expected encoded chars are available */ - for (i = 0; i < len; i++) - if ((str[i] & 0x80) != 0x80) - return -EINVAL; - - r = utf8_encoded_to_unichar(str, &unichar); - if (r < 0) - return r; - - /* check if encoded length matches encoded value */ - if (utf8_unichar_to_encoded_len(unichar) != len) - return -EINVAL; - - /* check if value has valid range */ - if (!unichar_is_valid(unichar)) - return -EINVAL; - - return len; -} - -size_t utf8_n_codepoints(const char *str) { - size_t n = 0; - - /* Returns the number of UTF-8 codepoints in this string, or (size_t) -1 if the string is not valid UTF-8. */ - - while (*str != 0) { - int k; - - k = utf8_encoded_valid_unichar(str); - if (k < 0) - return (size_t) -1; - - str += k; - n++; - } - - return n; -} - -#if 0 /* NM_IGNORED */ -size_t utf8_console_width(const char *str) { - size_t n = 0; - - /* Returns the approximate width a string will take on screen when printed on a character cell - * terminal/console. */ - - while (*str != 0) { - char32_t c; - - if (utf8_encoded_to_unichar(str, &c) < 0) - return (size_t) -1; - - str = utf8_next_char(str); - - n += unichar_iswide(c) ? 2 : 1; - } - - return n; -} -#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/utf8.h b/src/systemd/src/basic/utf8.h deleted file mode 100644 index f37f87ff..00000000 --- a/src/systemd/src/basic/utf8.h +++ /dev/null @@ -1,48 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> -#if 0 /* NM_IGNORED */ -#include <uchar.h> -#endif /* NM_IGNORED */ - -#include "macro.h" -#include "missing.h" - -#define UTF8_REPLACEMENT_CHARACTER "\xef\xbf\xbd" -#define UTF8_BYTE_ORDER_MARK "\xef\xbb\xbf" - -bool unichar_is_valid(char32_t c); - -const char *utf8_is_valid(const char *s) _pure_; -char *ascii_is_valid(const char *s) _pure_; -char *ascii_is_valid_n(const char *str, size_t len); - -bool utf8_is_printable_newline(const char* str, size_t length, bool newline) _pure_; -#define utf8_is_printable(str, length) utf8_is_printable_newline(str, length, true) - -char *utf8_escape_invalid(const char *s); -char *utf8_escape_non_printable(const char *str); - -size_t utf8_encode_unichar(char *out_utf8, char32_t g); -char *utf16_to_utf8(const void *s, size_t length); - -int utf8_encoded_valid_unichar(const char *str); -int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar); - -static inline bool utf16_is_surrogate(char16_t c) { - return (0xd800 <= c && c <= 0xdfff); -} - -static inline bool utf16_is_trailing_surrogate(char16_t c) { - return (0xdc00 <= c && c <= 0xdfff); -} - -static inline char32_t utf16_surrogate_pair_to_unichar(char16_t lead, char16_t trail) { - return ((lead - 0xd800) << 10) + (trail - 0xdc00) + 0x10000; -} - -size_t utf8_n_codepoints(const char *str); -size_t utf8_console_width(const char *str); diff --git a/src/systemd/src/basic/util.c b/src/systemd/src/basic/util.c deleted file mode 100644 index 2c6101ea..00000000 --- a/src/systemd/src/basic/util.c +++ /dev/null @@ -1,609 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt.h" - -#include <alloca.h> -#include <errno.h> -#include <fcntl.h> -#include <sched.h> -#include <signal.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/mman.h> -#include <sys/prctl.h> -#include <sys/statfs.h> -#include <sys/sysmacros.h> -#include <sys/types.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "btrfs-util.h" -#include "build.h" -#include "cgroup-util.h" -#include "def.h" -#include "device-nodes.h" -#include "dirent-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "format-util.h" -#include "hashmap.h" -#include "hostname-util.h" -#include "log.h" -#include "macro.h" -#include "missing.h" -#include "parse-util.h" -#include "path-util.h" -#include "process-util.h" -#include "procfs-util.h" -#include "set.h" -#include "signal-util.h" -#include "stat-util.h" -#include "string-util.h" -#include "strv.h" -#include "time-util.h" -#include "umask-util.h" -#include "user-util.h" -#include "util.h" -#include "virt.h" - -#if 0 /* NM_IGNORED */ -int saved_argc = 0; -char **saved_argv = NULL; -static int saved_in_initrd = -1; -#endif /* NM_IGNORED */ - -size_t page_size(void) { - static thread_local size_t pgsz = 0; - long r; - - if (_likely_(pgsz > 0)) - return pgsz; - - r = sysconf(_SC_PAGESIZE); - assert(r > 0); - - pgsz = (size_t) r; - return pgsz; -} - -#if 0 /* NM_IGNORED */ -bool plymouth_running(void) { - return access("/run/plymouth/pid", F_OK) >= 0; -} - -bool display_is_local(const char *display) { - assert(display); - - return - display[0] == ':' && - display[1] >= '0' && - display[1] <= '9'; -} - -bool kexec_loaded(void) { - _cleanup_free_ char *s = NULL; - - if (read_one_line_file("/sys/kernel/kexec_loaded", &s) < 0) - return false; - - return s[0] == '1'; -} - -int prot_from_flags(int flags) { - - switch (flags & O_ACCMODE) { - - case O_RDONLY: - return PROT_READ; - - case O_WRONLY: - return PROT_WRITE; - - case O_RDWR: - return PROT_READ|PROT_WRITE; - - default: - return -EINVAL; - } -} - -bool in_initrd(void) { - struct statfs s; - - if (saved_in_initrd >= 0) - return saved_in_initrd; - - /* We make two checks here: - * - * 1. the flag file /etc/initrd-release must exist - * 2. the root file system must be a memory file system - * - * The second check is extra paranoia, since misdetecting an - * initrd can have bad consequences due the initrd - * emptying when transititioning to the main systemd. - */ - - saved_in_initrd = access("/etc/initrd-release", F_OK) >= 0 && - statfs("/", &s) >= 0 && - is_temporary_fs(&s); - - return saved_in_initrd; -} - -void in_initrd_force(bool value) { - saved_in_initrd = value; -} - -/* hey glibc, APIs with callbacks without a user pointer are so useless */ -void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size, - int (*compar) (const void *, const void *, void *), void *arg) { - size_t l, u, idx; - const void *p; - int comparison; - - assert(!size_multiply_overflow(nmemb, size)); - - l = 0; - u = nmemb; - while (l < u) { - idx = (l + u) / 2; - p = (const uint8_t*) base + idx * size; - comparison = compar(key, p, arg); - if (comparison < 0) - u = idx; - else if (comparison > 0) - l = idx + 1; - else - return (void *)p; - } - return NULL; -} - -int on_ac_power(void) { - bool found_offline = false, found_online = false; - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; - - d = opendir("/sys/class/power_supply"); - if (!d) - return errno == ENOENT ? true : -errno; - - FOREACH_DIRENT(de, d, return -errno) { - _cleanup_close_ int fd = -1, device = -1; - char contents[6]; - ssize_t n; - - device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (device < 0) { - if (IN_SET(errno, ENOENT, ENOTDIR)) - continue; - - return -errno; - } - - fd = openat(device, "type", O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) { - if (errno == ENOENT) - continue; - - return -errno; - } - - n = read(fd, contents, sizeof(contents)); - if (n < 0) - return -errno; - - if (n != 6 || memcmp(contents, "Mains\n", 6)) - continue; - - safe_close(fd); - fd = openat(device, "online", O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) { - if (errno == ENOENT) - continue; - - return -errno; - } - - n = read(fd, contents, sizeof(contents)); - if (n < 0) - return -errno; - - if (n != 2 || contents[1] != '\n') - return -EIO; - - if (contents[0] == '1') { - found_online = true; - break; - } else if (contents[0] == '0') - found_offline = true; - else - return -EIO; - } - - return found_online || !found_offline; -} - -int container_get_leader(const char *machine, pid_t *pid) { - _cleanup_free_ char *s = NULL, *class = NULL; - const char *p; - pid_t leader; - int r; - - assert(machine); - assert(pid); - - if (streq(machine, ".host")) { - *pid = 1; - return 0; - } - - if (!machine_name_is_valid(machine)) - return -EINVAL; - - p = strjoina("/run/systemd/machines/", machine); - r = parse_env_file(NULL, p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL); - if (r == -ENOENT) - return -EHOSTDOWN; - if (r < 0) - return r; - if (!s) - return -EIO; - - if (!streq_ptr(class, "container")) - return -EIO; - - r = parse_pid(s, &leader); - if (r < 0) - return r; - if (leader <= 1) - return -EIO; - - *pid = leader; - return 0; -} - -int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd) { - _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, netnsfd = -1, usernsfd = -1; - int rfd = -1; - - assert(pid >= 0); - - if (mntns_fd) { - const char *mntns; - - mntns = procfs_file_alloca(pid, "ns/mnt"); - mntnsfd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (mntnsfd < 0) - return -errno; - } - - if (pidns_fd) { - const char *pidns; - - pidns = procfs_file_alloca(pid, "ns/pid"); - pidnsfd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (pidnsfd < 0) - return -errno; - } - - if (netns_fd) { - const char *netns; - - netns = procfs_file_alloca(pid, "ns/net"); - netnsfd = open(netns, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (netnsfd < 0) - return -errno; - } - - if (userns_fd) { - const char *userns; - - userns = procfs_file_alloca(pid, "ns/user"); - usernsfd = open(userns, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (usernsfd < 0 && errno != ENOENT) - return -errno; - } - - if (root_fd) { - const char *root; - - root = procfs_file_alloca(pid, "root"); - rfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); - if (rfd < 0) - return -errno; - } - - if (pidns_fd) - *pidns_fd = pidnsfd; - - if (mntns_fd) - *mntns_fd = mntnsfd; - - if (netns_fd) - *netns_fd = netnsfd; - - if (userns_fd) - *userns_fd = usernsfd; - - if (root_fd) - *root_fd = rfd; - - pidnsfd = mntnsfd = netnsfd = usernsfd = -1; - - return 0; -} - -int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd) { - if (userns_fd >= 0) { - /* Can't setns to your own userns, since then you could - * escalate from non-root to root in your own namespace, so - * check if namespaces equal before attempting to enter. */ - _cleanup_free_ char *userns_fd_path = NULL; - int r; - if (asprintf(&userns_fd_path, "/proc/self/fd/%d", userns_fd) < 0) - return -ENOMEM; - - r = files_same(userns_fd_path, "/proc/self/ns/user", 0); - if (r < 0) - return r; - if (r) - userns_fd = -1; - } - - if (pidns_fd >= 0) - if (setns(pidns_fd, CLONE_NEWPID) < 0) - return -errno; - - if (mntns_fd >= 0) - if (setns(mntns_fd, CLONE_NEWNS) < 0) - return -errno; - - if (netns_fd >= 0) - if (setns(netns_fd, CLONE_NEWNET) < 0) - return -errno; - - if (userns_fd >= 0) - if (setns(userns_fd, CLONE_NEWUSER) < 0) - return -errno; - - if (root_fd >= 0) { - if (fchdir(root_fd) < 0) - return -errno; - - if (chroot(".") < 0) - return -errno; - } - - return reset_uid_gid(); -} - -uint64_t physical_memory(void) { - _cleanup_free_ char *root = NULL, *value = NULL; - uint64_t mem, lim; - size_t ps; - long sc; - int r; - - /* We return this as uint64_t in case we are running as 32bit process on a 64bit kernel with huge amounts of - * memory. - * - * In order to support containers nicely that have a configured memory limit we'll take the minimum of the - * physically reported amount of memory and the limit configured for the root cgroup, if there is any. */ - - sc = sysconf(_SC_PHYS_PAGES); - assert(sc > 0); - - ps = page_size(); - mem = (uint64_t) sc * (uint64_t) ps; - - r = cg_get_root_path(&root); - if (r < 0) { - log_debug_errno(r, "Failed to determine root cgroup, ignoring cgroup memory limit: %m"); - return mem; - } - - r = cg_all_unified(); - if (r < 0) { - log_debug_errno(r, "Failed to determine root unified mode, ignoring cgroup memory limit: %m"); - return mem; - } - if (r > 0) { - r = cg_get_attribute("memory", root, "memory.max", &value); - if (r < 0) { - log_debug_errno(r, "Failed to read memory.max cgroup attribute, ignoring cgroup memory limit: %m"); - return mem; - } - - if (streq(value, "max")) - return mem; - } else { - r = cg_get_attribute("memory", root, "memory.limit_in_bytes", &value); - if (r < 0) { - log_debug_errno(r, "Failed to read memory.limit_in_bytes cgroup attribute, ignoring cgroup memory limit: %m"); - return mem; - } - } - - r = safe_atou64(value, &lim); - if (r < 0) { - log_debug_errno(r, "Failed to parse cgroup memory limit '%s', ignoring: %m", value); - return mem; - } - if (lim == UINT64_MAX) - return mem; - - /* Make sure the limit is a multiple of our own page size */ - lim /= ps; - lim *= ps; - - return MIN(mem, lim); -} - -uint64_t physical_memory_scale(uint64_t v, uint64_t max) { - uint64_t p, m, ps, r; - - assert(max > 0); - - /* Returns the physical memory size, multiplied by v divided by max. Returns UINT64_MAX on overflow. On success - * the result is a multiple of the page size (rounds down). */ - - ps = page_size(); - assert(ps > 0); - - p = physical_memory() / ps; - assert(p > 0); - - m = p * v; - if (m / p != v) - return UINT64_MAX; - - m /= max; - - r = m * ps; - if (r / ps != m) - return UINT64_MAX; - - return r; -} - -uint64_t system_tasks_max(void) { - - uint64_t a = TASKS_MAX, b = TASKS_MAX; - _cleanup_free_ char *root = NULL; - int r; - - /* Determine the maximum number of tasks that may run on this system. We check three sources to determine this - * limit: - * - * a) the maximum tasks value the kernel allows on this architecture - * b) the cgroups pids_max attribute for the system - * c) the kernel's configured maximum PID value - * - * And then pick the smallest of the three */ - - r = procfs_tasks_get_limit(&a); - if (r < 0) - log_debug_errno(r, "Failed to read maximum number of tasks from /proc, ignoring: %m"); - - r = cg_get_root_path(&root); - if (r < 0) - log_debug_errno(r, "Failed to determine cgroup root path, ignoring: %m"); - else { - _cleanup_free_ char *value = NULL; - - r = cg_get_attribute("pids", root, "pids.max", &value); - if (r < 0) - log_debug_errno(r, "Failed to read pids.max attribute of cgroup root, ignoring: %m"); - else if (!streq(value, "max")) { - r = safe_atou64(value, &b); - if (r < 0) - log_debug_errno(r, "Failed to parse pids.max attribute of cgroup root, ignoring: %m"); - } - } - - return MIN3(TASKS_MAX, - a <= 0 ? TASKS_MAX : a, - b <= 0 ? TASKS_MAX : b); -} - -uint64_t system_tasks_max_scale(uint64_t v, uint64_t max) { - uint64_t t, m; - - assert(max > 0); - - /* Multiply the system's task value by the fraction v/max. Hence, if max==100 this calculates percentages - * relative to the system's maximum number of tasks. Returns UINT64_MAX on overflow. */ - - t = system_tasks_max(); - assert(t > 0); - - m = t * v; - if (m / t != v) /* overflow? */ - return UINT64_MAX; - - return m / max; -} - -int version(void) { - puts(PACKAGE_STRING "\n" - SYSTEMD_FEATURES); - return 0; -} - -/* This is a direct translation of str_verscmp from boot.c */ -static bool is_digit(int c) { - return c >= '0' && c <= '9'; -} - -static int c_order(int c) { - if (c == 0 || is_digit(c)) - return 0; - - if ((c >= 'a') && (c <= 'z')) - return c; - - return c + 0x10000; -} - -int str_verscmp(const char *s1, const char *s2) { - const char *os1, *os2; - - assert(s1); - assert(s2); - - os1 = s1; - os2 = s2; - - while (*s1 || *s2) { - int first; - - while ((*s1 && !is_digit(*s1)) || (*s2 && !is_digit(*s2))) { - int order; - - order = c_order(*s1) - c_order(*s2); - if (order != 0) - return order; - s1++; - s2++; - } - - while (*s1 == '0') - s1++; - while (*s2 == '0') - s2++; - - first = 0; - while (is_digit(*s1) && is_digit(*s2)) { - if (first == 0) - first = *s1 - *s2; - s1++; - s2++; - } - - if (is_digit(*s1)) - return 1; - if (is_digit(*s2)) - return -1; - - if (first != 0) - return first; - } - - return strcmp(os1, os2); -} - -/* Turn off core dumps but only if we're running outside of a container. */ -void disable_coredumps(void) { - int r; - - if (detect_container() > 0) - return; - - r = write_string_file("/proc/sys/kernel/core_pattern", "|/bin/false", 0); - if (r < 0) - log_debug_errno(r, "Failed to turn off coredumps, ignoring: %m"); -} -#endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/util.h b/src/systemd/src/basic/util.h deleted file mode 100644 index 308933ac..00000000 --- a/src/systemd/src/basic/util.h +++ /dev/null @@ -1,209 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <alloca.h> -#include <errno.h> -#include <fcntl.h> -#include <inttypes.h> -#include <limits.h> -#include <locale.h> -#include <stdarg.h> -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/inotify.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/statfs.h> -#include <sys/sysmacros.h> -#include <sys/types.h> -#include <time.h> -#include <unistd.h> - -#include "format-util.h" -#include "macro.h" -#include "missing.h" -#include "time-util.h" - -size_t page_size(void) _pure_; -#define PAGE_ALIGN(l) ALIGN_TO((l), page_size()) - -static inline const char* yes_no(bool b) { - return b ? "yes" : "no"; -} - -static inline const char* true_false(bool b) { - return b ? "true" : "false"; -} - -static inline const char* one_zero(bool b) { - return b ? "1" : "0"; -} - -static inline const char* enable_disable(bool b) { - return b ? "enable" : "disable"; -} - -bool plymouth_running(void); - -bool display_is_local(const char *display) _pure_; - -#define NULSTR_FOREACH(i, l) \ - for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1) - -#define NULSTR_FOREACH_PAIR(i, j, l) \ - for ((i) = (l), (j) = strchr((i), 0)+1; (i) && *(i); (i) = strchr((j), 0)+1, (j) = *(i) ? strchr((i), 0)+1 : (i)) - -extern int saved_argc; -extern char **saved_argv; - -bool kexec_loaded(void); - -int prot_from_flags(int flags) _const_; - -bool in_initrd(void); -void in_initrd_force(bool value); - -void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size, - int (*compar) (const void *, const void *, void *), - void *arg); - -/** - * Normal bsearch requires base to be nonnull. Here were require - * that only if nmemb > 0. - */ -static inline void* bsearch_safe(const void *key, const void *base, - size_t nmemb, size_t size, comparison_fn_t compar) { - if (nmemb <= 0) - return NULL; - - assert(base); - return bsearch(key, base, nmemb, size, compar); -} - -/** - * Normal qsort requires base to be nonnull. Here were require - * that only if nmemb > 0. - */ -static inline void qsort_safe(void *base, size_t nmemb, size_t size, comparison_fn_t compar) { - if (nmemb <= 1) - return; - - assert(base); - qsort(base, nmemb, size, compar); -} - -/* A wrapper around the above, but that adds typesafety: the element size is automatically derived from the type and so - * is the prototype for the comparison function */ -#define typesafe_qsort(p, n, func) \ - ({ \ - int (*_func_)(const typeof(p[0])*, const typeof(p[0])*) = func; \ - qsort_safe((p), (n), sizeof((p)[0]), (__compar_fn_t) _func_); \ - }) - -static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, int (*compar)(const void*, const void*, void*), void *userdata) { - if (nmemb <= 1) - return; - - assert(base); - qsort_r(base, nmemb, size, compar, userdata); -} - -/* Normal memcpy requires src to be nonnull. We do nothing if n is 0. */ -static inline void memcpy_safe(void *dst, const void *src, size_t n) { - if (n == 0) - return; - assert(src); - memcpy(dst, src, n); -} - -/* Normal memcmp requires s1 and s2 to be nonnull. We do nothing if n is 0. */ -static inline int memcmp_safe(const void *s1, const void *s2, size_t n) { - if (n == 0) - return 0; - assert(s1); - assert(s2); - return memcmp(s1, s2, n); -} - -int on_ac_power(void); - -#define memzero(x,l) (memset((x), 0, (l))) -#define zero(x) (memzero(&(x), sizeof(x))) - -static inline void *mempset(void *s, int c, size_t n) { - memset(s, c, n); - return (uint8_t*)s + n; -} - -static inline void _reset_errno_(int *saved_errno) { - errno = *saved_errno; -} - -#define PROTECT_ERRNO _cleanup_(_reset_errno_) __attribute__((unused)) int _saved_errno_ = errno - -static inline int negative_errno(void) { - /* This helper should be used to shut up gcc if you know 'errno' is - * negative. Instead of "return -errno;", use "return negative_errno();" - * It will suppress bogus gcc warnings in case it assumes 'errno' might - * be 0 and thus the caller's error-handling might not be triggered. */ - assert_return(errno > 0, -EINVAL); - return -errno; -} - -static inline unsigned u64log2(uint64_t n) { -#if __SIZEOF_LONG_LONG__ == 8 - return (n > 1) ? (unsigned) __builtin_clzll(n) ^ 63U : 0; -#else -#error "Wut?" -#endif -} - -static inline unsigned u32ctz(uint32_t n) { -#if __SIZEOF_INT__ == 4 - return __builtin_ctz(n); -#else -#error "Wut?" -#endif -} - -static inline unsigned log2i(int x) { - assert(x > 0); - - return __SIZEOF_INT__ * 8 - __builtin_clz(x) - 1; -} - -static inline unsigned log2u(unsigned x) { - assert(x > 0); - - return sizeof(unsigned) * 8 - __builtin_clz(x) - 1; -} - -static inline unsigned log2u_round_up(unsigned x) { - assert(x > 0); - - if (x == 1) - return 0; - - return log2u(x - 1) + 1; -} - -int container_get_leader(const char *machine, pid_t *pid); - -int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd); -int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd); - -uint64_t physical_memory(void); -uint64_t physical_memory_scale(uint64_t v, uint64_t max); - -uint64_t system_tasks_max(void); -uint64_t system_tasks_max_scale(uint64_t v, uint64_t max); - -int version(void); - -int str_verscmp(const char *s1, const char *s2); - -void disable_coredumps(void); diff --git a/src/systemd/src/libsystemd-network/arp-util.c b/src/systemd/src/libsystemd-network/arp-util.c index 4fbecb54..154ba6b7 100644 --- a/src/systemd/src/libsystemd-network/arp-util.c +++ b/src/systemd/src/libsystemd-network/arp-util.c @@ -3,7 +3,7 @@ Copyright © 2014 Axis Communications AB. All rights reserved. ***/ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" #include <linux/filter.h> #include <arpa/inet.h> diff --git a/src/systemd/src/libsystemd-network/dhcp-identifier.c b/src/systemd/src/libsystemd-network/dhcp-identifier.c index f18713e8..91f5e1b0 100644 --- a/src/systemd/src/libsystemd-network/dhcp-identifier.c +++ b/src/systemd/src/libsystemd-network/dhcp-identifier.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" #include <linux/if_infiniband.h> #include <net/if_arp.h> @@ -15,23 +15,24 @@ #include "sparse-endian.h" #include "virt.h" -#if 0 /* NM_IGNORED */ -#else /* NM_IGNORED */ -#include <net/if.h> -#endif /* NM_IGNORED */ - #define SYSTEMD_PEN 43793 #define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09) #define APPLICATION_ID SD_ID128_MAKE(a5,0a,d1,12,bf,60,45,77,a2,fb,74,1a,b1,95,5b,03) #define USEC_2000 ((usec_t) 946684800000000) /* 2000-01-01 00:00:00 UTC */ -int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len) { +int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len, bool strict) { struct duid d; assert_cc(sizeof(d.raw) >= MAX_DUID_LEN); if (duid_len > MAX_DUID_LEN) return -EINVAL; + if (!strict) { + /* Strict validation is not requested. We only ensure that the + * DUID is not too long. */ + return 0; + } + switch (duid_type) { case DUID_TYPE_LLT: if (duid_len <= sizeof(d.llt)) @@ -117,8 +118,13 @@ int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) { assert(len); r = sd_id128_get_machine(&machine_id); - if (r < 0) + if (r < 0) { +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + machine_id = SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10); +#else return r; +#endif + } unaligned_write_be16(&duid->type, DUID_TYPE_EN); unaligned_write_be32(&duid->en.pen, SYSTEMD_PEN); @@ -154,38 +160,37 @@ int dhcp_identifier_set_duid_uuid(struct duid *duid, size_t *len) { } #endif -int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, void *_id) { +int dhcp_identifier_set_iaid( + int ifindex, + const uint8_t *mac, + size_t mac_len, + bool legacy_unstable_byteorder, + void *_id) { #if 0 /* NM_IGNORED */ /* name is a pointer to memory in the sd_device struct, so must * have the same scope */ _cleanup_(sd_device_unrefp) sd_device *device = NULL; -#else /* NM_IGNORED */ - char name_buf[IF_NAMESIZE]; -#endif /* NM_IGNORED */ const char *name = NULL; uint64_t id; + uint32_t id32; -#if 0 /* NM_IGNORED */ if (detect_container() <= 0) { /* not in a container, udev will be around */ char ifindex_str[2 + DECIMAL_STR_MAX(int)]; - int initialized, r; + int r; sprintf(ifindex_str, "n%d", ifindex); if (sd_device_new_from_device_id(&device, ifindex_str) >= 0) { - r = sd_device_get_is_initialized(device, &initialized); + r = sd_device_get_is_initialized(device); if (r < 0) return r; - if (!initialized) + if (r == 0) /* not yet ready */ return -EBUSY; name = net_get_name(device); } } -#else /* NM_IGNORED */ - name = if_indextoname(ifindex, name_buf); -#endif /* NM_IGNORED */ if (name) id = siphash24(name, strlen(name), HASH_KEY.bytes); @@ -193,10 +198,23 @@ int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, void *_i /* fall back to MAC address if no predictable name available */ id = siphash24(mac, mac_len, HASH_KEY.bytes); - id = htole64(id); + id32 = (id & 0xffffffff) ^ (id >> 32); - /* fold into 32 bits */ - unaligned_write_be32(_id, (id & 0xffffffff) ^ (id >> 32)); + if (legacy_unstable_byteorder) + /* for historical reasons (a bug), the bits were swapped and thus + * the result was endianness dependant. Preserve that behavior. */ + id32 = __bswap_32(id32); + else + /* the fixed behavior returns a stable byte order. Since LE is expected + * to be more common, swap the bytes on LE to give the same as legacy + * behavior. */ + id32 = be32toh(id32); + unaligned_write_ne32(_id, id32); return 0; +#else /* NM_IGNORED */ + /* for NetworkManager, we don't use this function and we should never call here. + * This got replaced by nm_utils_create_dhcp_iaid(). */ + g_return_val_if_reached (-EINVAL); +#endif /* NM_IGNORED */ } diff --git a/src/systemd/src/libsystemd-network/dhcp-identifier.h b/src/systemd/src/libsystemd-network/dhcp-identifier.h index 64315d3a..b3115125 100644 --- a/src/systemd/src/libsystemd-network/dhcp-identifier.h +++ b/src/systemd/src/libsystemd-network/dhcp-identifier.h @@ -52,9 +52,9 @@ struct duid { }; } _packed_; -int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len); +int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len, bool strict); int dhcp_identifier_set_duid_llt(struct duid *duid, usec_t t, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len); int dhcp_identifier_set_duid_ll(struct duid *duid, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len); int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len); int dhcp_identifier_set_duid_uuid(struct duid *duid, size_t *len); -int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, void *_id); +int dhcp_identifier_set_iaid(int ifindex, const uint8_t *mac, size_t mac_len, bool legacy_unstable_byteorder, void *_id); diff --git a/src/systemd/src/libsystemd-network/dhcp-network.c b/src/systemd/src/libsystemd-network/dhcp-network.c index 80e9577c..ba596908 100644 --- a/src/systemd/src/libsystemd-network/dhcp-network.c +++ b/src/systemd/src/libsystemd-network/dhcp-network.c @@ -3,7 +3,7 @@ Copyright © 2013 Intel Corporation. All rights reserved. ***/ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" #include <errno.h> #include <net/ethernet.h> @@ -80,7 +80,7 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link, .filter = filter }; _cleanup_close_ int s = -1; - int r, on = 1; + int r; assert(ifindex > 0); assert(link); @@ -89,9 +89,9 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link, if (s < 0) return -errno; - r = setsockopt(s, SOL_PACKET, PACKET_AUXDATA, &on, sizeof(on)); + r = setsockopt_int(s, SOL_PACKET, PACKET_AUXDATA, true); if (r < 0) - return -errno; + return r; r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)); if (r < 0) @@ -151,19 +151,19 @@ int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port) { }; _cleanup_close_ int s = -1; char ifname[IF_NAMESIZE] = ""; - int r, on = 1, tos = IPTOS_CLASS_CS6; + int r; s = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); if (s < 0) return -errno; - r = setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); + r = setsockopt_int(s, IPPROTO_IP, IP_TOS, IPTOS_CLASS_CS6); if (r < 0) - return -errno; + return r; - r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true); if (r < 0) - return -errno; + return r; if (ifindex > 0) { if (if_indextoname(ifindex, ifname) == 0) @@ -175,18 +175,18 @@ int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port) { } if (address == INADDR_ANY) { - r = setsockopt(s, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on)); + r = setsockopt_int(s, IPPROTO_IP, IP_PKTINFO, true); if (r < 0) - return -errno; + return r; - r = setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)); + r = setsockopt_int(s, SOL_SOCKET, SO_BROADCAST, true); if (r < 0) - return -errno; + return r; } else { - r = setsockopt(s, IPPROTO_IP, IP_FREEBIND, &on, sizeof(on)); + r = setsockopt_int(s, IPPROTO_IP, IP_FREEBIND, true); if (r < 0) - return -errno; + return r; } r = bind(s, &src.sa, sizeof(src.in)); diff --git a/src/systemd/src/libsystemd-network/dhcp-option.c b/src/systemd/src/libsystemd-network/dhcp-option.c index 4a4f7de0..b065ae49 100644 --- a/src/systemd/src/libsystemd-network/dhcp-option.c +++ b/src/systemd/src/libsystemd-network/dhcp-option.c @@ -3,7 +3,7 @@ Copyright © 2013 Intel Corporation. All rights reserved. ***/ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" #include <errno.h> #include <stdint.h> diff --git a/src/systemd/src/libsystemd-network/dhcp-packet.c b/src/systemd/src/libsystemd-network/dhcp-packet.c index 8f8acb0d..9e565e28 100644 --- a/src/systemd/src/libsystemd-network/dhcp-packet.c +++ b/src/systemd/src/libsystemd-network/dhcp-packet.c @@ -3,7 +3,7 @@ Copyright © 2013 Intel Corporation. All rights reserved. ***/ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" #include <errno.h> #include <net/ethernet.h> @@ -108,70 +108,62 @@ int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum, ui /* IP */ - if (packet->ip.version != IPVERSION) { - log_debug("ignoring packet: not IPv4"); - return -EINVAL; - } + if (packet->ip.version != IPVERSION) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "ignoring packet: not IPv4"); - if (packet->ip.ihl < 5) { - log_debug("ignoring packet: IPv4 IHL (%u words) invalid", - packet->ip.ihl); - return -EINVAL; - } + if (packet->ip.ihl < 5) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "ignoring packet: IPv4 IHL (%u words) invalid", + packet->ip.ihl); hdrlen = packet->ip.ihl * 4; - if (hdrlen < 20) { - log_debug("ignoring packet: IPv4 IHL (%zu bytes) " - "smaller than minimum (20 bytes)", hdrlen); - return -EINVAL; - } - - if (len < hdrlen) { - log_debug("ignoring packet: packet (%zu bytes) " - "smaller than expected (%zu) by IP header", len, - hdrlen); - return -EINVAL; - } + if (hdrlen < 20) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "ignoring packet: IPv4 IHL (%zu bytes) " + "smaller than minimum (20 bytes)", + hdrlen); + + if (len < hdrlen) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "ignoring packet: packet (%zu bytes) " + "smaller than expected (%zu) by IP header", + len, hdrlen); /* UDP */ - if (packet->ip.protocol != IPPROTO_UDP) { - log_debug("ignoring packet: not UDP"); - return -EINVAL; - } + if (packet->ip.protocol != IPPROTO_UDP) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "ignoring packet: not UDP"); - if (len < hdrlen + be16toh(packet->udp.len)) { - log_debug("ignoring packet: packet (%zu bytes) " - "smaller than expected (%zu) by UDP header", len, - hdrlen + be16toh(packet->udp.len)); - return -EINVAL; - } + if (len < hdrlen + be16toh(packet->udp.len)) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "ignoring packet: packet (%zu bytes) " + "smaller than expected (%zu) by UDP header", + len, hdrlen + be16toh(packet->udp.len)); - if (be16toh(packet->udp.dest) != port) { - log_debug("ignoring packet: to port %u, which " - "is not the DHCP client port (%u)", - be16toh(packet->udp.dest), port); - return -EINVAL; - } + if (be16toh(packet->udp.dest) != port) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "ignoring packet: to port %u, which " + "is not the DHCP client port (%u)", + be16toh(packet->udp.dest), port); /* checksums - computing these is relatively expensive, so only do it if all the other checks have passed */ - if (dhcp_packet_checksum((uint8_t*)&packet->ip, hdrlen)) { - log_debug("ignoring packet: invalid IP checksum"); - return -EINVAL; - } + if (dhcp_packet_checksum((uint8_t*)&packet->ip, hdrlen)) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "ignoring packet: invalid IP checksum"); if (checksum && packet->udp.check) { packet->ip.check = packet->udp.len; packet->ip.ttl = 0; if (dhcp_packet_checksum((uint8_t*)&packet->ip.ttl, - be16toh(packet->udp.len) + 12)) { - log_debug("ignoring packet: invalid UDP checksum"); - return -EINVAL; - } + be16toh(packet->udp.len) + 12)) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "ignoring packet: invalid UDP checksum"); } return 0; diff --git a/src/systemd/src/libsystemd-network/dhcp6-internal.h b/src/systemd/src/libsystemd-network/dhcp6-internal.h index 06e2e532..157fc0aa 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-internal.h +++ b/src/systemd/src/libsystemd-network/dhcp6-internal.h @@ -73,8 +73,6 @@ struct DHCP6IA { struct ia_pd ia_pd; struct ia_ta ia_ta; }; - sd_event_source *timeout_t1; - sd_event_source *timeout_t2; LIST_HEAD(DHCP6Address, addresses); }; @@ -86,8 +84,8 @@ typedef struct DHCP6IA DHCP6IA; int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, size_t optlen, const void *optval); -int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia); -int dhcp6_option_append_pd(uint8_t *buf, size_t len, DHCP6IA *pd); +int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia); +int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd); int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn); int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen, uint8_t **optvalue); diff --git a/src/systemd/src/libsystemd-network/dhcp6-lease-internal.h b/src/systemd/src/libsystemd-network/dhcp6-lease-internal.h index ff0b0f00..e004f48b 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/systemd/src/libsystemd-network/dhcp6-lease-internal.h @@ -37,7 +37,6 @@ struct sd_dhcp6_lease { size_t ntp_fqdn_count; }; -int dhcp6_lease_clear_timers(DHCP6IA *ia); int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire); DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia); @@ -50,6 +49,7 @@ int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease *lease); int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit); int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid); +int dhcp6_lease_get_pd_iaid(sd_dhcp6_lease *lease, be32_t *iaid); int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen); int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval, diff --git a/src/systemd/src/libsystemd-network/dhcp6-network.c b/src/systemd/src/libsystemd-network/dhcp6-network.c index 98aa6261..73c195a7 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-network.c +++ b/src/systemd/src/libsystemd-network/dhcp6-network.c @@ -3,7 +3,7 @@ Copyright © 2014 Intel Corporation. All rights reserved. ***/ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" #include <errno.h> #include <netinet/in.h> @@ -27,7 +27,7 @@ int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) { .in6.sin6_scope_id = index, }; _cleanup_close_ int s = -1; - int r, off = 0, on = 1; + int r; assert(index > 0); assert(local_address); @@ -38,17 +38,17 @@ int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) { if (s < 0) return -errno; - r = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); + r = setsockopt_int(s, IPPROTO_IPV6, IPV6_V6ONLY, true); if (r < 0) - return -errno; + return r; - r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, sizeof(off)); + r = setsockopt_int(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, false); if (r < 0) - return -errno; + return r; - r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true); if (r < 0) - return -errno; + return r; r = bind(s, &src.sa, sizeof(src.in6)); if (r < 0) diff --git a/src/systemd/src/libsystemd-network/dhcp6-option.c b/src/systemd/src/libsystemd-network/dhcp6-option.c index d63d4b26..5a83aaaa 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-option.c +++ b/src/systemd/src/libsystemd-network/dhcp6-option.c @@ -3,7 +3,7 @@ Copyright © 2014-2015 Intel Corporation. All rights reserved. ***/ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" #include <errno.h> #include <netinet/in.h> @@ -39,9 +39,9 @@ typedef struct DHCP6PDPrefixOption { uint8_t options[]; } _packed_ DHCP6PDPrefixOption; -#define DHCP6_OPTION_IA_NA_LEN (sizeof(struct ia_na)) -#define DHCP6_OPTION_IA_PD_LEN (sizeof(struct ia_pd)) -#define DHCP6_OPTION_IA_TA_LEN (sizeof(struct ia_ta)) +#define DHCP6_OPTION_IA_NA_LEN (sizeof(struct ia_na)) +#define DHCP6_OPTION_IA_PD_LEN (sizeof(struct ia_pd)) +#define DHCP6_OPTION_IA_TA_LEN (sizeof(struct ia_ta)) static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode, size_t optlen) { @@ -51,14 +51,14 @@ static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode, assert_return(*buf, -EINVAL); assert_return(buflen, -EINVAL); - if (optlen > 0xffff || *buflen < optlen + sizeof(DHCP6Option)) + if (optlen > 0xffff || *buflen < optlen + offsetof(DHCP6Option, data)) return -ENOBUFS; option->code = htobe16(optcode); option->len = htobe16(optlen); - *buf += sizeof(DHCP6Option); - *buflen -= sizeof(DHCP6Option); + *buf += offsetof(DHCP6Option, data); + *buflen -= offsetof(DHCP6Option, data); return 0; } @@ -81,14 +81,17 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, return 0; } -int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) { +int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) { uint16_t len; uint8_t *ia_hdr; size_t iaid_offset, ia_buflen, ia_addrlen = 0; DHCP6Address *addr; int r; - assert_return(buf && *buf && buflen && ia, -EINVAL); + assert_return(buf, -EINVAL); + assert_return(*buf, -EINVAL); + assert_return(buflen, -EINVAL); + assert_return(ia, -EINVAL); switch (ia->type) { case SD_DHCP6_OPTION_IA_NA: @@ -111,8 +114,8 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) { ia_hdr = *buf; ia_buflen = *buflen; - *buf += sizeof(DHCP6Option); - *buflen -= sizeof(DHCP6Option); + *buf += offsetof(DHCP6Option, data); + *buflen -= offsetof(DHCP6Option, data); memcpy(*buf, (char*) ia + iaid_offset, len); @@ -130,7 +133,7 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) { *buf += sizeof(addr->iaaddr); *buflen -= sizeof(addr->iaaddr); - ia_addrlen += sizeof(DHCP6Option) + sizeof(addr->iaaddr); + ia_addrlen += offsetof(DHCP6Option, data) + sizeof(addr->iaaddr); } r = option_append_hdr(&ia_hdr, &ia_buflen, ia->type, len + ia_addrlen); @@ -167,7 +170,7 @@ int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) { return r; } -int dhcp6_option_append_pd(uint8_t *buf, size_t len, DHCP6IA *pd) { +int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd) { DHCP6Option *option = (DHCP6Option *)buf; size_t i = sizeof(*option) + sizeof(pd->ia_pd); DHCP6Address *prefix; @@ -212,7 +215,7 @@ static int option_parse_hdr(uint8_t **buf, size_t *buflen, uint16_t *optcode, si assert_return(optcode, -EINVAL); assert_return(optlen, -EINVAL); - if (*buflen < sizeof(DHCP6Option)) + if (*buflen < offsetof(DHCP6Option, data)) return -ENOMSG; len = be16toh(option->len); @@ -253,7 +256,7 @@ int dhcp6_option_parse_status(DHCP6Option *option, size_t len) { DHCP6StatusOption *statusopt = (DHCP6StatusOption *)option; if (len < sizeof(DHCP6StatusOption) || - be16toh(option->len) + sizeof(DHCP6Option) < sizeof(DHCP6StatusOption)) + be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(DHCP6StatusOption)) return -ENOBUFS; return be16toh(statusopt->status); @@ -266,7 +269,7 @@ static int dhcp6_option_parse_address(DHCP6Option *option, DHCP6IA *ia, uint32_t lt_valid, lt_pref; int r; - if (be16toh(option->len) + sizeof(DHCP6Option) < sizeof(*addr_option)) + if (be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(*addr_option)) return -ENOBUFS; lt_valid = be32toh(addr_option->iaaddr.lifetime_valid); @@ -279,8 +282,8 @@ static int dhcp6_option_parse_address(DHCP6Option *option, DHCP6IA *ia, return 0; } - if (be16toh(option->len) + sizeof(DHCP6Option) > sizeof(*addr_option)) { - r = dhcp6_option_parse_status((DHCP6Option *)addr_option->options, be16toh(option->len) + sizeof(DHCP6Option) - sizeof(*addr_option)); + if (be16toh(option->len) + offsetof(DHCP6Option, data) > sizeof(*addr_option)) { + r = dhcp6_option_parse_status((DHCP6Option *)addr_option->options, be16toh(option->len) + offsetof(DHCP6Option, data) - sizeof(*addr_option)); if (r != 0) return r < 0 ? r: 0; } @@ -306,7 +309,7 @@ static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia, uint32_t lt_valid, lt_pref; int r; - if (be16toh(option->len) + sizeof(DHCP6Option) < sizeof(*pdprefix_option)) + if (be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(*pdprefix_option)) return -ENOBUFS; lt_valid = be32toh(pdprefix_option->iapdprefix.lifetime_valid); @@ -319,8 +322,8 @@ static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia, return 0; } - if (be16toh(option->len) + sizeof(DHCP6Option) > sizeof(*pdprefix_option)) { - r = dhcp6_option_parse_status((DHCP6Option *)pdprefix_option->options, be16toh(option->len) + sizeof(DHCP6Option) - sizeof(*pdprefix_option)); + if (be16toh(option->len) + offsetof(DHCP6Option, data) > sizeof(*pdprefix_option)) { + r = dhcp6_option_parse_status((DHCP6Option *)pdprefix_option->options, be16toh(option->len) + offsetof(DHCP6Option, data) - sizeof(*pdprefix_option)); if (r != 0) return r < 0 ? r: 0; } @@ -356,10 +359,8 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { switch (iatype) { case SD_DHCP6_OPTION_IA_NA: - if (len < DHCP6_OPTION_IA_NA_LEN) { - r = -ENOBUFS; - goto error; - } + if (len < DHCP6_OPTION_IA_NA_LEN) + return -ENOBUFS; iaaddr_offset = DHCP6_OPTION_IA_NA_LEN; memcpy(&ia->ia_na, iaoption->data, sizeof(ia->ia_na)); @@ -370,18 +371,15 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { if (lt_t1 && lt_t2 && lt_t1 > lt_t2) { log_dhcp6_client(client, "IA NA T1 %ds > T2 %ds", lt_t1, lt_t2); - r = -EINVAL; - goto error; + return -EINVAL; } break; case SD_DHCP6_OPTION_IA_PD: - if (len < sizeof(ia->ia_pd)) { - r = -ENOBUFS; - goto error; - } + if (len < sizeof(ia->ia_pd)) + return -ENOBUFS; iaaddr_offset = sizeof(ia->ia_pd); memcpy(&ia->ia_pd, iaoption->data, sizeof(ia->ia_pd)); @@ -392,17 +390,14 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { if (lt_t1 && lt_t2 && lt_t1 > lt_t2) { log_dhcp6_client(client, "IA PD T1 %ds > T2 %ds", lt_t1, lt_t2); - r = -EINVAL; - goto error; + return -EINVAL; } break; case SD_DHCP6_OPTION_IA_TA: - if (len < DHCP6_OPTION_IA_TA_LEN) { - r = -ENOBUFS; - goto error; - } + if (len < DHCP6_OPTION_IA_TA_LEN) + return -ENOBUFS; iaaddr_offset = DHCP6_OPTION_IA_TA_LEN; memcpy(&ia->ia_ta.id, iaoption->data, sizeof(ia->ia_ta)); @@ -410,8 +405,7 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { break; default: - r = -ENOMSG; - goto error; + return -ENOMSG; } ia->type = iatype; @@ -420,10 +414,8 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { while (i < len) { DHCP6Option *option = (DHCP6Option *)&iaoption->data[i]; - if (len < i + sizeof(*option) || len < i + sizeof(*option) + be16toh(option->len)) { - r = -ENOBUFS; - goto error; - } + if (len < i + sizeof(*option) || len < i + sizeof(*option) + be16toh(option->len)) + return -ENOBUFS; opt = be16toh(option->code); optlen = be16toh(option->len); @@ -433,13 +425,12 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA)) { log_dhcp6_client(client, "IA Address option not in IA NA or TA option"); - r = -EINVAL; - goto error; + return -EINVAL; } r = dhcp6_option_parse_address(option, ia, <_valid); if (r < 0) - goto error; + return r; if (lt_valid < lt_min) lt_min = lt_valid; @@ -450,13 +441,12 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_PD)) { log_dhcp6_client(client, "IA PD Prefix option not in IA PD option"); - r = -EINVAL; - goto error; + return -EINVAL; } r = dhcp6_option_parse_pdprefix(option, ia, <_valid); if (r < 0) - goto error; + return r; if (lt_valid < lt_min) lt_min = lt_valid; @@ -465,13 +455,14 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { case SD_DHCP6_OPTION_STATUS_CODE: - status = dhcp6_option_parse_status(option, optlen); - if (status) { + status = dhcp6_option_parse_status(option, optlen + offsetof(DHCP6Option, data)); + if (status < 0) + return status; + if (status > 0) { log_dhcp6_client(client, "IA status %d", status); - r = -EINVAL; - goto error; + return -EINVAL; } break; @@ -515,8 +506,7 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { break; } -error: - return r; + return 0; } int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen, @@ -559,36 +549,22 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char * if (c == 0) /* End of name */ break; - else if (c <= 63) { - const char *label; - - /* Literal label */ - label = (const char *)&optval[pos]; - pos += c; - if (pos >= optlen) - return -EMSGSIZE; - - if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) { - r = -ENOMEM; - goto fail; - } - - if (first) - first = false; - else - ret[n++] = '.'; - - r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX); - if (r < 0) - goto fail; - - n += r; - continue; - } else { - r = -EBADMSG; - goto fail; - } - } + if (c > 63) + return -EBADMSG; + + /* Literal label */ + label = (const char *)&optval[pos]; + pos += c; + if (pos >= optlen) + return -EMSGSIZE; + + if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) + return -ENOMEM; + + if (first) + first = false; + else + ret[n++] = '.'; r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX); if (r < 0) diff --git a/src/systemd/src/libsystemd-network/lldp-internal.h b/src/systemd/src/libsystemd-network/lldp-internal.h index 328d51f8..88b54933 100644 --- a/src/systemd/src/libsystemd-network/lldp-internal.h +++ b/src/systemd/src/libsystemd-network/lldp-internal.h @@ -34,3 +34,6 @@ struct sd_lldp { #define log_lldp_errno(error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "LLDP: " fmt, ##__VA_ARGS__) #define log_lldp(fmt, ...) log_lldp_errno(0, fmt, ##__VA_ARGS__) + +const char* lldp_event_to_string(sd_lldp_event e) _const_; +sd_lldp_event lldp_event_from_string(const char *s) _pure_; diff --git a/src/systemd/src/libsystemd-network/lldp-neighbor.c b/src/systemd/src/libsystemd-network/lldp-neighbor.c index cd9358a4..bbe7bf9d 100644 --- a/src/systemd/src/libsystemd-network/lldp-neighbor.c +++ b/src/systemd/src/libsystemd-network/lldp-neighbor.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" #include "alloc-util.h" #include "escape.h" @@ -9,40 +9,24 @@ #include "in-addr-util.h" #include "lldp-internal.h" #include "lldp-neighbor.h" +#include "missing.h" #include "unaligned.h" +#include "util.h" -static void lldp_neighbor_id_hash_func(const void *p, struct siphash *state) { - const LLDPNeighborID *id = p; - +static void lldp_neighbor_id_hash_func(const LLDPNeighborID *id, struct siphash *state) { siphash24_compress(id->chassis_id, id->chassis_id_size, state); siphash24_compress(&id->chassis_id_size, sizeof(id->chassis_id_size), state); siphash24_compress(id->port_id, id->port_id_size, state); siphash24_compress(&id->port_id_size, sizeof(id->port_id_size), state); } -static int lldp_neighbor_id_compare_func(const void *a, const void *b) { - const LLDPNeighborID *x = a, *y = b; - int r; - - r = memcmp(x->chassis_id, y->chassis_id, MIN(x->chassis_id_size, y->chassis_id_size)); - if (r != 0) - return r; - - r = CMP(x->chassis_id_size, y->chassis_id_size); - if (r != 0) - return r; - - r = memcmp(x->port_id, y->port_id, MIN(x->port_id_size, y->port_id_size)); - if (r != 0) - return r; - - return CMP(x->port_id_size, y->port_id_size); +int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y) { + return memcmp_nn(x->chassis_id, x->chassis_id_size, y->chassis_id, y->chassis_id_size) + ?: memcmp_nn(x->port_id, x->port_id_size, y->port_id, y->port_id_size); } -const struct hash_ops lldp_neighbor_id_hash_ops = { - .hash = lldp_neighbor_id_hash_func, - .compare = lldp_neighbor_id_compare_func -}; +DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(lldp_neighbor_hash_ops, LLDPNeighborID, lldp_neighbor_id_hash_func, lldp_neighbor_id_compare_func, + sd_lldp_neighbor, lldp_neighbor_unlink); int lldp_neighbor_prioq_compare_func(const void *a, const void *b) { const sd_lldp_neighbor *x = a, *y = b; @@ -100,7 +84,12 @@ sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n) { if (!n->lldp) return NULL; - assert_se(hashmap_remove(n->lldp->neighbor_by_id, &n->id) == n); + /* Only remove the neighbor object from the hash table if it's in there, don't complain if it isn't. This is + * because we are used as destructor call for hashmap_clear() and thus sometimes are called to de-register + * ourselves from the hashtable and sometimes are called after we already are de-registered. */ + + (void) hashmap_remove_value(n->lldp->neighbor_by_id, &n->id, n); + assert_se(prioq_remove(n->lldp->neighbor_by_expiry, n, &n->prioq_idx) >= 0); n->lldp = NULL; @@ -704,7 +693,7 @@ _public_ int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type) { return type == k; } -_public_ int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[3], uint8_t *subtype) { +_public_ int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t *subtype) { const uint8_t *d; size_t length; int r; @@ -733,7 +722,7 @@ _public_ int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[3], u return 0; } -_public_ int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[3], uint8_t subtype) { +_public_ int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t subtype) { uint8_t k[3], st; int r; diff --git a/src/systemd/src/libsystemd-network/lldp-neighbor.h b/src/systemd/src/libsystemd-network/lldp-neighbor.h index 494bc517..62dbff42 100644 --- a/src/systemd/src/libsystemd-network/lldp-neighbor.h +++ b/src/systemd/src/libsystemd-network/lldp-neighbor.h @@ -80,7 +80,8 @@ static inline void* LLDP_NEIGHBOR_TLV_DATA(const sd_lldp_neighbor *n) { return ((uint8_t*) LLDP_NEIGHBOR_RAW(n)) + n->rindex + 2; } -extern const struct hash_ops lldp_neighbor_id_hash_ops; +extern const struct hash_ops lldp_neighbor_hash_ops; +int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y); int lldp_neighbor_prioq_compare_func(const void *a, const void *b); sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n); diff --git a/src/systemd/src/libsystemd-network/lldp-network.c b/src/systemd/src/libsystemd-network/lldp-network.c index db779aaa..5ba9f081 100644 --- a/src/systemd/src/libsystemd-network/lldp-network.c +++ b/src/systemd/src/libsystemd-network/lldp-network.c @@ -1,12 +1,13 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" #include <linux/filter.h> #include <netinet/if_ether.h> #include "fd-util.h" #include "lldp-network.h" +#include "missing.h" #include "socket-util.h" int lldp_network_bind_raw_socket(int ifindex) { diff --git a/src/systemd/src/libsystemd-network/network-internal.c b/src/systemd/src/libsystemd-network/network-internal.c index 57a59030..d33c4feb 100644 --- a/src/systemd/src/libsystemd-network/network-internal.c +++ b/src/systemd/src/libsystemd-network/network-internal.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" #include <arpa/inet.h> #include <linux/if.h> @@ -258,11 +258,10 @@ int config_parse_ifalias(const char *unit, return 0; } - free(*s); - if (*n) - *s = TAKE_PTR(n); + if (isempty(n)) + *s = mfree(*s); else - *s = NULL; + free_and_replace(*s, n); return 0; } @@ -297,7 +296,7 @@ int config_parse_hwaddr(const char *unit, return 0; } - *hwaddr = TAKE_PTR(n); + free_and_replace(*hwaddr, n); return 0; } @@ -375,36 +374,6 @@ int config_parse_hwaddrs(const char *unit, return 0; } -int config_parse_iaid(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - uint32_t iaid; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - r = safe_atou32(rvalue, &iaid); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, - "Unable to read IAID, ignoring assignment: %s", rvalue); - return 0; - } - - *((uint32_t *)data) = iaid; - - return 0; -} - int config_parse_bridge_port_priority( const char *unit, const char *filename, diff --git a/src/systemd/src/libsystemd-network/network-internal.h b/src/systemd/src/libsystemd-network/network-internal.h index 06d61184..dfe4c424 100644 --- a/src/systemd/src/libsystemd-network/network-internal.h +++ b/src/systemd/src/libsystemd-network/network-internal.h @@ -36,7 +36,6 @@ CONFIG_PARSER_PROTOTYPE(config_parse_hwaddr); CONFIG_PARSER_PROTOTYPE(config_parse_hwaddrs); CONFIG_PARSER_PROTOTYPE(config_parse_ifnames); CONFIG_PARSER_PROTOTYPE(config_parse_ifalias); -CONFIG_PARSER_PROTOTYPE(config_parse_iaid); CONFIG_PARSER_PROTOTYPE(config_parse_bridge_port_priority); int net_get_unique_predictable_data(sd_device *device, uint64_t *result); diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-client.c b/src/systemd/src/libsystemd-network/sd-dhcp-client.c index 91589453..2f9244d8 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp-client.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp-client.c @@ -3,7 +3,7 @@ Copyright © 2013 Intel Corporation. All rights reserved. ***/ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" #include <errno.h> #include <net/ethernet.h> @@ -23,11 +23,13 @@ #include "dhcp-lease-internal.h" #include "dhcp-protocol.h" #include "dns-domain.h" +#include "event-util.h" #include "hostname-util.h" +#include "io-util.h" #include "random-util.h" #include "string-util.h" -#include "util.h" #include "strv.h" +#include "util.h" #define MAX_CLIENT_ID_LEN (sizeof(uint32_t) + MAX_DUID_LEN) /* Arbitrary limit */ #define MAX_MAC_ADDR_LEN CONST_MAX(INFINIBAND_ALEN, ETH_ALEN) @@ -88,7 +90,7 @@ struct sd_dhcp_client { uint32_t mtu; uint32_t xid; usec_t start_time; - unsigned int attempt; + unsigned attempt; usec_t request_sent; sd_event_source *timeout_t1; sd_event_source *timeout_t2; @@ -118,19 +120,19 @@ static const uint8_t default_req_opts[] = { */ /* NOTE: using PRL options that Windows 10 RFC7844 implementation uses */ static const uint8_t default_req_opts_anonymize[] = { - SD_DHCP_OPTION_SUBNET_MASK, /* 1 */ - SD_DHCP_OPTION_ROUTER, /* 3 */ - SD_DHCP_OPTION_DOMAIN_NAME_SERVER, /* 6 */ - SD_DHCP_OPTION_DOMAIN_NAME, /* 15 */ - SD_DHCP_OPTION_ROUTER_DISCOVER, /* 31 */ - SD_DHCP_OPTION_STATIC_ROUTE, /* 33 */ - SD_DHCP_OPTION_VENDOR_SPECIFIC, /* 43 */ - SD_DHCP_OPTION_NETBIOS_NAMESERVER, /* 44 */ - SD_DHCP_OPTION_NETBIOS_NODETYPE, /* 46 */ - SD_DHCP_OPTION_NETBIOS_SCOPE, /* 47 */ - SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, /* 121 */ - SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE, /* 249 */ - SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY, /* 252 */ + SD_DHCP_OPTION_SUBNET_MASK, /* 1 */ + SD_DHCP_OPTION_ROUTER, /* 3 */ + SD_DHCP_OPTION_DOMAIN_NAME_SERVER, /* 6 */ + SD_DHCP_OPTION_DOMAIN_NAME, /* 15 */ + SD_DHCP_OPTION_ROUTER_DISCOVER, /* 31 */ + SD_DHCP_OPTION_STATIC_ROUTE, /* 33 */ + SD_DHCP_OPTION_VENDOR_SPECIFIC, /* 43 */ + SD_DHCP_OPTION_NETBIOS_NAMESERVER, /* 44 */ + SD_DHCP_OPTION_NETBIOS_NODETYPE, /* 46 */ + SD_DHCP_OPTION_NETBIOS_SCOPE, /* 47 */ + SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, /* 121 */ + SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE, /* 249 */ + SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY, /* 252 */ }; static int client_receive_message_raw( @@ -301,27 +303,22 @@ int sd_dhcp_client_set_client_id( assert_return(data_len > 0 && data_len <= MAX_CLIENT_ID_LEN, -EINVAL); G_STATIC_ASSERT_EXPR (_NM_SD_MAX_CLIENT_ID_LEN == MAX_CLIENT_ID_LEN); - switch (type) { - - case ARPHRD_ETHER: - if (data_len != ETH_ALEN) - return -EINVAL; - break; - - case ARPHRD_INFINIBAND: - if (data_len != INFINIBAND_ALEN) - return -EINVAL; - break; - - default: - break; - } - if (client->client_id_len == data_len + sizeof(client->client_id.type) && client->client_id.type == type && memcmp(&client->client_id.raw.data, data, data_len) == 0) return 0; + /* For hardware types, log debug message about unexpected data length. + * + * Note that infiniband's INFINIBAND_ALEN is 20 bytes long, but only + * last last 8 bytes of the address are stable and suitable to put into + * the client-id. The caller is advised to account for that. */ + if ((type == ARPHRD_ETHER && data_len != ETH_ALEN) || + (type == ARPHRD_INFINIBAND && data_len != 8)) + log_dhcp_client(client, "Changing client ID to hardware type %u with " + "unexpected address length %zu", + type, data_len); + if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) { log_dhcp_client(client, "Changing client ID on running DHCP " "client, restarting"); @@ -347,8 +344,9 @@ int sd_dhcp_client_set_client_id( */ static int dhcp_client_set_iaid_duid_internal( sd_dhcp_client *client, + bool iaid_append, + bool iaid_set, uint32_t iaid, - bool append_iaid, uint16_t duid_type, const void *duid, size_t duid_len, @@ -359,10 +357,10 @@ static int dhcp_client_set_iaid_duid_internal( size_t len; assert_return(client, -EINVAL); - assert_return(duid_len == 0 || duid != NULL, -EINVAL); + assert_return(duid_len == 0 || duid, -EINVAL); - if (duid != NULL) { - r = dhcp_validate_duid_len(duid_type, duid_len); + if (duid) { + r = dhcp_validate_duid_len(duid_type, duid_len, true); if (r < 0) return r; } @@ -370,26 +368,27 @@ static int dhcp_client_set_iaid_duid_internal( zero(client->client_id); client->client_id.type = 255; - if (append_iaid) { - /* If IAID is not configured, generate it. */ - if (iaid == 0) { + if (iaid_append) { + if (iaid_set) + client->client_id.ns.iaid = htobe32(iaid); + else { r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, + true, &client->client_id.ns.iaid); if (r < 0) return r; - } else - client->client_id.ns.iaid = htobe32(iaid); + } } - if (duid != NULL) { + if (duid) { client->client_id.ns.duid.type = htobe16(duid_type); memcpy(&client->client_id.ns.duid.raw.data, duid, duid_len); len = sizeof(client->client_id.ns.duid.type) + duid_len; } else switch (duid_type) { case DUID_TYPE_LLT: - if (!client->mac_addr || client->mac_addr_len == 0) + if (client->mac_addr_len == 0) return -EOPNOTSUPP; r = dhcp_identifier_set_duid_llt(&client->client_id.ns.duid, llt_time, client->mac_addr, client->mac_addr_len, client->arp_type, &len); @@ -402,7 +401,7 @@ static int dhcp_client_set_iaid_duid_internal( return r; break; case DUID_TYPE_LL: - if (!client->mac_addr || client->mac_addr_len == 0) + if (client->mac_addr_len == 0) return -EOPNOTSUPP; r = dhcp_identifier_set_duid_ll(&client->client_id.ns.duid, client->mac_addr, client->mac_addr_len, client->arp_type, &len); @@ -419,10 +418,10 @@ static int dhcp_client_set_iaid_duid_internal( } client->client_id_len = sizeof(client->client_id.type) + len + - (append_iaid ? sizeof(client->client_id.ns.iaid) : 0); + (iaid_append ? sizeof(client->client_id.ns.iaid) : 0); if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) { - log_dhcp_client(client, "Configured %sDUID, restarting.", append_iaid ? "IAID+" : ""); + log_dhcp_client(client, "Configured %sDUID, restarting.", iaid_append ? "IAID+" : ""); client_stop(client, SD_DHCP_CLIENT_EVENT_STOP); sd_dhcp_client_start(client); } @@ -432,18 +431,20 @@ static int dhcp_client_set_iaid_duid_internal( int sd_dhcp_client_set_iaid_duid( sd_dhcp_client *client, + bool iaid_set, uint32_t iaid, uint16_t duid_type, const void *duid, size_t duid_len) { - return dhcp_client_set_iaid_duid_internal(client, iaid, true, duid_type, duid, duid_len, 0); + return dhcp_client_set_iaid_duid_internal(client, true, iaid_set, iaid, duid_type, duid, duid_len, 0); } int sd_dhcp_client_set_iaid_duid_llt( sd_dhcp_client *client, + bool iaid_set, uint32_t iaid, usec_t llt_time) { - return dhcp_client_set_iaid_duid_internal(client, iaid, true, DUID_TYPE_LLT, NULL, 0, llt_time); + return dhcp_client_set_iaid_duid_internal(client, true, iaid_set, iaid, DUID_TYPE_LLT, NULL, 0, llt_time); } int sd_dhcp_client_set_duid( @@ -451,13 +452,13 @@ int sd_dhcp_client_set_duid( uint16_t duid_type, const void *duid, size_t duid_len) { - return dhcp_client_set_iaid_duid_internal(client, 0, false, duid_type, duid, duid_len, 0); + return dhcp_client_set_iaid_duid_internal(client, false, false, 0, duid_type, duid, duid_len, 0); } int sd_dhcp_client_set_duid_llt( sd_dhcp_client *client, usec_t llt_time) { - return dhcp_client_set_iaid_duid_internal(client, 0, false, DUID_TYPE_LLT, NULL, 0, llt_time); + return dhcp_client_set_iaid_duid_internal(client, false, false, 0, DUID_TYPE_LLT, NULL, 0, llt_time); } #endif /* NM_IGNORED */ @@ -550,11 +551,10 @@ static int client_initialize(sd_dhcp_client *client) { client->fd = asynchronous_close(client->fd); - client->timeout_resend = sd_event_source_unref(client->timeout_resend); - - client->timeout_t1 = sd_event_source_unref(client->timeout_t1); - client->timeout_t2 = sd_event_source_unref(client->timeout_t2); - client->timeout_expire = sd_event_source_unref(client->timeout_expire); + (void) event_source_disable(client->timeout_resend); + (void) event_source_disable(client->timeout_t1); + (void) event_source_disable(client->timeout_t2); + (void) event_source_disable(client->timeout_expire); client->attempt = 1; @@ -655,7 +655,8 @@ static int client_message_init( client->client_id.type = 255; - r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, &client->client_id.ns.iaid); + r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, + true, &client->client_id.ns.iaid); if (r < 0) return r; @@ -703,7 +704,7 @@ static int client_message_init( let the server know how large the server may make its DHCP messages. Note (from ConnMan): Some DHCP servers will send bigger DHCP packets - than the defined default size unless the Maximum Messge Size option + than the defined default size unless the Maximum Message Size option is explicitly set RFC3442 "Requirements to Avoid Sizing Constraints": @@ -1068,22 +1069,11 @@ static int client_timeout_resend( next_timeout += (random_u32() & 0x1fffff); - client->timeout_resend = sd_event_source_unref(client->timeout_resend); - - r = sd_event_add_time(client->event, - &client->timeout_resend, - clock_boottime_or_monotonic(), - next_timeout, 10 * USEC_PER_MSEC, - client_timeout_resend, client); - if (r < 0) - goto error; - - r = sd_event_source_set_priority(client->timeout_resend, - client->event_priority); - if (r < 0) - goto error; - - r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer"); + r = event_reset_time(client->event, &client->timeout_resend, + clock_boottime_or_monotonic(), + next_timeout, 10 * USEC_PER_MSEC, + client_timeout_resend, client, + client->event_priority, "dhcp4-resend-timer", true); if (r < 0) goto error; @@ -1180,31 +1170,16 @@ static int client_initialize_time_events(sd_dhcp_client *client) { assert(client); assert(client->event); - client->timeout_resend = sd_event_source_unref(client->timeout_resend); - if (client->start_delay) { assert_se(sd_event_now(client->event, clock_boottime_or_monotonic(), &usec) >= 0); usec += client->start_delay; } - r = sd_event_add_time(client->event, - &client->timeout_resend, - clock_boottime_or_monotonic(), - usec, 0, - client_timeout_resend, client); - if (r < 0) - goto error; - - r = sd_event_source_set_priority(client->timeout_resend, - client->event_priority); - if (r < 0) - goto error; - - r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer"); - if (r < 0) - goto error; - -error: + r = event_reset_time(client->event, &client->timeout_resend, + clock_boottime_or_monotonic(), + usec, 0, + client_timeout_resend, client, + client->event_priority, "dhcp4-resend-timer", true); if (r < 0) client_stop(client, r); @@ -1462,13 +1437,14 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { assert(client->lease); assert(client->lease->lifetime); - client->timeout_t1 = sd_event_source_unref(client->timeout_t1); - client->timeout_t2 = sd_event_source_unref(client->timeout_t2); - client->timeout_expire = sd_event_source_unref(client->timeout_expire); - /* don't set timers for infinite leases */ - if (client->lease->lifetime == 0xffffffff) + if (client->lease->lifetime == 0xffffffff) { + (void) event_source_disable(client->timeout_t1); + (void) event_source_disable(client->timeout_t2); + (void) event_source_disable(client->timeout_expire); + return 0; + } r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); if (r < 0) @@ -1520,19 +1496,11 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { } /* arm lifetime timeout */ - r = sd_event_add_time(client->event, &client->timeout_expire, - clock_boottime_or_monotonic(), - lifetime_timeout, 10 * USEC_PER_MSEC, - client_timeout_expire, client); - if (r < 0) - return r; - - r = sd_event_source_set_priority(client->timeout_expire, - client->event_priority); - if (r < 0) - return r; - - r = sd_event_source_set_description(client->timeout_expire, "dhcp4-lifetime"); + r = event_reset_time(client->event, &client->timeout_expire, + clock_boottime_or_monotonic(), + lifetime_timeout, 10 * USEC_PER_MSEC, + client_timeout_expire, client, + client->event_priority, "dhcp4-lifetime", true); if (r < 0) return r; @@ -1544,21 +1512,11 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { return 0; /* arm T2 timeout */ - r = sd_event_add_time(client->event, - &client->timeout_t2, - clock_boottime_or_monotonic(), - t2_timeout, - 10 * USEC_PER_MSEC, - client_timeout_t2, client); - if (r < 0) - return r; - - r = sd_event_source_set_priority(client->timeout_t2, - client->event_priority); - if (r < 0) - return r; - - r = sd_event_source_set_description(client->timeout_t2, "dhcp4-t2-timeout"); + r = event_reset_time(client->event, &client->timeout_t2, + clock_boottime_or_monotonic(), + t2_timeout, 10 * USEC_PER_MSEC, + client_timeout_t2, client, + client->event_priority, "dhcp4-t2-timeout", true); if (r < 0) return r; @@ -1570,20 +1528,11 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { return 0; /* arm T1 timeout */ - r = sd_event_add_time(client->event, - &client->timeout_t1, - clock_boottime_or_monotonic(), - t1_timeout, 10 * USEC_PER_MSEC, - client_timeout_t1, client); - if (r < 0) - return r; - - r = sd_event_source_set_priority(client->timeout_t1, - client->event_priority); - if (r < 0) - return r; - - r = sd_event_source_set_description(client->timeout_t1, "dhcp4-t1-timer"); + r = event_reset_time(client->event, &client->timeout_t1, + clock_boottime_or_monotonic(), + t1_timeout, 10 * USEC_PER_MSEC, + client_timeout_t1, client, + client->event_priority, "dhcp4-t1-timer", true); if (r < 0) return r; @@ -1608,26 +1557,14 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i r = client_handle_offer(client, message, len); if (r >= 0) { - client->timeout_resend = - sd_event_source_unref(client->timeout_resend); - client->state = DHCP_STATE_REQUESTING; client->attempt = 1; - r = sd_event_add_time(client->event, - &client->timeout_resend, - clock_boottime_or_monotonic(), - 0, 0, - client_timeout_resend, client); - if (r < 0) - goto error; - - r = sd_event_source_set_priority(client->timeout_resend, - client->event_priority); - if (r < 0) - goto error; - - r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer"); + r = event_reset_time(client->event, &client->timeout_resend, + clock_boottime_or_monotonic(), + 0, 0, + client_timeout_resend, client, + client->event_priority, "dhcp4-resend-timer", true); if (r < 0) goto error; } else if (r == -ENOMSG) @@ -1644,8 +1581,7 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i r = client_handle_ack(client, message, len); if (r >= 0) { client->start_delay = 0; - client->timeout_resend = - sd_event_source_unref(client->timeout_resend); + (void) event_source_disable(client->timeout_resend); client->receive_message = sd_event_source_unref(client->receive_message); client->fd = asynchronous_close(client->fd); @@ -1685,9 +1621,6 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i } else if (r == -EADDRNOTAVAIL) { /* got a NAK, let's restart the client */ - client->timeout_resend = - sd_event_source_unref(client->timeout_resend); - client_notify(client, SD_DHCP_CLIENT_EVENT_EXPIRED); r = client_initialize(client); @@ -1855,8 +1788,7 @@ static int client_receive_message_raw( if (!packet) return -ENOMEM; - iov.iov_base = packet; - iov.iov_len = buflen; + iov = IOVEC_MAKE(packet, buflen); len = recvmsg(fd, &msg, 0); if (len < 0) { @@ -1868,7 +1800,7 @@ static int client_receive_message_raw( } else if ((size_t)len < sizeof(DHCPPacket)) return 0; - CMSG_FOREACH(cmsg, &msg) { + CMSG_FOREACH(cmsg, &msg) if (cmsg->cmsg_level == SOL_PACKET && cmsg->cmsg_type == PACKET_AUXDATA && cmsg->cmsg_len == CMSG_LEN(sizeof(struct tpacket_auxdata))) { @@ -1877,7 +1809,6 @@ static int client_receive_message_raw( checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY); break; } - } r = dhcp_packet_verify_headers(packet, len, checksum, client->port); if (r < 0) @@ -1958,33 +1889,17 @@ sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client) { return client->event; } -sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client) { - - if (!client) - return NULL; - - assert(client->n_ref >= 1); - client->n_ref++; - - return client; -} - -sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) { - - if (!client) - return NULL; - - assert(client->n_ref >= 1); - client->n_ref--; - - if (client->n_ref > 0) - return NULL; +static sd_dhcp_client *dhcp_client_free(sd_dhcp_client *client) { + assert(client); log_dhcp_client(client, "FREE"); - client_initialize(client); + client->timeout_resend = sd_event_source_unref(client->timeout_resend); + client->timeout_t1 = sd_event_source_unref(client->timeout_t1); + client->timeout_t2 = sd_event_source_unref(client->timeout_t2); + client->timeout_expire = sd_event_source_unref(client->timeout_expire); - client->receive_message = sd_event_source_unref(client->receive_message); + client_initialize(client); sd_dhcp_client_detach_event(client); @@ -1997,24 +1912,27 @@ sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) { return mfree(client); } +DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp_client, sd_dhcp_client, dhcp_client_free); + int sd_dhcp_client_new(sd_dhcp_client **ret, int anonymize) { _cleanup_(sd_dhcp_client_unrefp) sd_dhcp_client *client = NULL; assert_return(ret, -EINVAL); - client = new0(sd_dhcp_client, 1); + client = new(sd_dhcp_client, 1); if (!client) return -ENOMEM; - client->n_ref = 1; - client->state = DHCP_STATE_INIT; - client->ifindex = -1; - client->fd = -1; - client->attempt = 1; - client->mtu = DHCP_DEFAULT_MIN_SIZE; - client->port = DHCP_PORT_CLIENT; - - client->anonymize = !!anonymize; + *client = (sd_dhcp_client) { + .n_ref = 1, + .state = DHCP_STATE_INIT, + .ifindex = -1, + .fd = -1, + .attempt = 1, + .mtu = DHCP_DEFAULT_MIN_SIZE, + .port = DHCP_PORT_CLIENT, + .anonymize = !!anonymize, + }; /* NOTE: this could be moved to a function. */ if (anonymize) { client->req_opts_size = ELEMENTSOF(default_req_opts_anonymize); diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c index cac07d3e..fc5077c2 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c @@ -3,7 +3,7 @@ Copyright © 2013 Intel Corporation. All rights reserved. ***/ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" #include <arpa/inet.h> #include <errno.h> @@ -18,6 +18,7 @@ #include "dhcp-lease-internal.h" #include "dhcp-protocol.h" #include "dns-domain.h" +#include "env-file.h" #include "fd-util.h" #include "fileio.h" #include "hexdecoct.h" @@ -28,6 +29,7 @@ #include "stdio-util.h" #include "string-util.h" #include "strv.h" +#include "tmpfile-util.h" #include "unaligned.h" int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr) { @@ -248,27 +250,8 @@ int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, s return 0; } -sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease) { - - if (!lease) - return NULL; - - assert(lease->n_ref >= 1); - lease->n_ref++; - - return lease; -} - -sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) { - - if (!lease) - return NULL; - - assert(lease->n_ref >= 1); - lease->n_ref--; - - if (lease->n_ref > 0) - return NULL; +static sd_dhcp_lease *dhcp_lease_free(sd_dhcp_lease *lease) { + assert(lease); while (lease->private_options) { struct sd_dhcp_raw_option *option = lease->private_options; @@ -292,6 +275,8 @@ sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) { return mfree(lease); } +DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp_lease, sd_dhcp_lease, dhcp_lease_free); + static int lease_parse_u32(const uint8_t *option, size_t len, uint32_t *ret, uint32_t min) { assert(option); assert(ret); @@ -351,8 +336,7 @@ static int lease_parse_string(const uint8_t *option, size_t len, char **ret) { if (!string) return -ENOMEM; - free(*ret); - *ret = string; + free_and_replace(*ret, string); } return 0; @@ -373,7 +357,7 @@ static int lease_parse_domain(const uint8_t *option, size_t len, char **ret) { return 0; } - r = dns_name_normalize(name, &normalized); + r = dns_name_normalize(name, 0, &normalized); if (r < 0) return r; @@ -1036,7 +1020,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { if (r < 0) return r; - r = parse_env_file(NULL, lease_file, NEWLINE, + r = parse_env_file(NULL, lease_file, "ADDRESS", &address, "ROUTER", &router, "NETMASK", &netmask, @@ -1087,8 +1071,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { "OPTION_251", &options[27], "OPTION_252", &options[28], "OPTION_253", &options[29], - "OPTION_254", &options[30], - NULL); + "OPTION_254", &options[30]); if (r < 0) return r; @@ -1320,3 +1303,9 @@ int sd_dhcp_route_get_gateway(sd_dhcp_route *route, struct in_addr *gateway) { *gateway = route->gw_addr; return 0; } + +int sd_dhcp_route_get_option(sd_dhcp_route *route) { + assert_return(route, -EINVAL); + + return route->option; +} diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c index 989b7261..6b55083e 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c @@ -3,7 +3,7 @@ Copyright © 2014-2015 Intel Corporation. All rights reserved. ***/ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" #include <errno.h> #include <string.h> @@ -18,6 +18,7 @@ #include "dhcp6-lease-internal.h" #include "dhcp6-protocol.h" #include "dns-domain.h" +#include "event-util.h" #include "fd-util.h" #include "hostname-util.h" #include "in-addr-util.h" @@ -29,6 +30,13 @@ #define MAX_MAC_ADDR_LEN INFINIBAND_ALEN +/* what to request from the server, addresses (IA_NA) and/or prefixes (IA_PD) */ +enum { + DHCP6_REQUEST_IA_NA = 1, + DHCP6_REQUEST_IA_TA = 2, /* currently not used */ + DHCP6_REQUEST_IA_PD = 4, +}; + struct sd_dhcp6_client { unsigned n_ref; @@ -42,12 +50,15 @@ struct sd_dhcp6_client { uint16_t arp_type; DHCP6IA ia_na; DHCP6IA ia_pd; - bool prefix_delegation; + sd_event_source *timeout_t1; + sd_event_source *timeout_t2; + unsigned request; be32_t transaction_id; usec_t transaction_start; struct sd_dhcp6_lease *lease; int fd; bool information_request; + bool iaid_set; be16_t *req_opts; size_t req_opts_allocated; size_t req_opts_len; @@ -197,9 +208,13 @@ static int dhcp6_client_set_duid_internal( assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); if (duid != NULL) { - r = dhcp_validate_duid_len(duid_type, duid_len); - if (r < 0) - return r; + r = dhcp_validate_duid_len(duid_type, duid_len, true); + if (r < 0) { + r = dhcp_validate_duid_len(duid_type, duid_len, false); + if (r < 0) + return r; + log_dhcp6_client(client, "Setting DUID of type %u with unexpected content", duid_type); + } client->duid.type = htobe16(duid_type); memcpy(&client->duid.raw.data, duid, duid_len); @@ -208,10 +223,10 @@ static int dhcp6_client_set_duid_internal( #if 0 /* NM_IGNORED */ switch (duid_type) { case DUID_TYPE_LLT: - if (!client->mac_addr || client->mac_addr_len == 0) + if (client->mac_addr_len == 0) return -EOPNOTSUPP; - r = dhcp_identifier_set_duid_llt(&client->duid, 0, client->mac_addr, client->mac_addr_len, client->arp_type, &client->duid_len); + r = dhcp_identifier_set_duid_llt(&client->duid, llt_time, client->mac_addr, client->mac_addr_len, client->arp_type, &client->duid_len); if (r < 0) return r; break; @@ -221,7 +236,7 @@ static int dhcp6_client_set_duid_internal( return r; break; case DUID_TYPE_LL: - if (!client->mac_addr || client->mac_addr_len == 0) + if (client->mac_addr_len == 0) return -EOPNOTSUPP; r = dhcp_identifier_set_duid_ll(&client->duid, client->mac_addr, client->mac_addr_len, client->arp_type, &client->duid_len); @@ -251,7 +266,6 @@ int sd_dhcp6_client_set_duid( return dhcp6_client_set_duid_internal(client, duid_type, duid, duid_len, 0); } -#if 0 /* NM_IGNORED */ int sd_dhcp6_client_set_duid_llt( sd_dhcp6_client *client, usec_t llt_time) { @@ -264,10 +278,10 @@ int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) { client->ia_na.ia_na.id = htobe32(iaid); client->ia_pd.ia_pd.id = htobe32(iaid); + client->iaid_set = true; return 0; } -#endif /* NM_IGNORED */ int sd_dhcp6_client_set_fqdn( sd_dhcp6_client *client, @@ -333,10 +347,44 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) return 0; } -int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, bool delegation) { +int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegation) { assert_return(client, -EINVAL); + assert_return(delegation, -EINVAL); - client->prefix_delegation = delegation; + *delegation = FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD); + + return 0; +} + +int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, int delegation) { + assert_return(client, -EINVAL); + + SET_FLAG(client->request, DHCP6_REQUEST_IA_PD, delegation); + + return 0; +} + +int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client, int *request) { + assert_return(client, -EINVAL); + assert_return(request, -EINVAL); + + *request = FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA); + + return 0; +} + +int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, int request) { + assert_return(client, -EINVAL); + + SET_FLAG(client->request, DHCP6_REQUEST_IA_NA, request); + + return 0; +} + +int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id) { + assert_return(client, -EINVAL); + + client->transaction_id = transaction_id; return 0; } @@ -360,21 +408,10 @@ static void client_notify(sd_dhcp6_client *client, int event) { client->callback(client, event, client->userdata); } -static void client_set_lease(sd_dhcp6_client *client, sd_dhcp6_lease *lease) { - assert(client); - - if (client->lease) { - dhcp6_lease_clear_timers(&client->lease->ia); - sd_dhcp6_lease_unref(client->lease); - } - - client->lease = lease; -} - static int client_reset(sd_dhcp6_client *client) { assert(client); - client_set_lease(client, NULL); + client->lease = sd_dhcp6_lease_unref(client->lease); client->receive_message = sd_event_source_unref(client->receive_message); @@ -382,16 +419,13 @@ static int client_reset(sd_dhcp6_client *client) { client->transaction_id = 0; client->transaction_start = 0; - client->ia_na.timeout_t1 = - sd_event_source_unref(client->ia_na.timeout_t1); - client->ia_na.timeout_t2 = - sd_event_source_unref(client->ia_na.timeout_t2); - client->retransmit_time = 0; client->retransmit_count = 0; - client->timeout_resend = sd_event_source_unref(client->timeout_resend); - client->timeout_resend_expire = - sd_event_source_unref(client->timeout_resend_expire); + + (void) event_source_disable(client->timeout_resend); + (void) event_source_disable(client->timeout_resend_expire); + (void) event_source_disable(client->timeout_t1); + (void) event_source_disable(client->timeout_t2); client->state = DHCP6_STATE_STOPPED; @@ -444,9 +478,12 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { if (r < 0) return r; - r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na); - if (r < 0) - return r; + if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA)) { + r = dhcp6_option_append_ia(&opt, &optlen, + &client->ia_na); + if (r < 0) + return r; + } if (client->fqdn) { r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn); @@ -454,7 +491,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { return r; } - if (client->prefix_delegation) { + if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) { r = dhcp6_option_append_pd(opt, optlen, &client->ia_pd); if (r < 0) return r; @@ -479,9 +516,12 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { if (r < 0) return r; - r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia); - if (r < 0) - return r; + if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA)) { + r = dhcp6_option_append_ia(&opt, &optlen, + &client->lease->ia); + if (r < 0) + return r; + } if (client->fqdn) { r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn); @@ -489,7 +529,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { return r; } - if (client->prefix_delegation) { + if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) { r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd); if (r < 0) return r; @@ -503,9 +543,11 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { case DHCP6_STATE_REBIND: message->type = DHCP6_REBIND; - r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia); - if (r < 0) - return r; + if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA)) { + r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia); + if (r < 0) + return r; + } if (client->fqdn) { r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn); @@ -513,7 +555,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { return r; } - if (client->prefix_delegation) { + if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) { r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd); if (r < 0) return r; @@ -570,8 +612,7 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) assert(client); assert(client->lease); - client->lease->ia.timeout_t2 = - sd_event_source_unref(client->lease->ia.timeout_t2); + (void) event_source_disable(client->timeout_t2); log_dhcp6_client(client, "Timeout T2"); @@ -587,8 +628,7 @@ static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) assert(client); assert(client->lease); - client->lease->ia.timeout_t1 = - sd_event_source_unref(client->lease->ia.timeout_t1); + (void) event_source_disable(client->timeout_t1); log_dhcp6_client(client, "Timeout T1"); @@ -636,7 +676,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda assert(client); assert(client->event); - client->timeout_resend = sd_event_source_unref(client->timeout_resend); + (void) event_source_disable(client->timeout_resend); switch (client->state) { case DHCP6_STATE_INFORMATION_REQUEST: @@ -678,7 +718,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda init_retransmit_time = DHCP6_REB_TIMEOUT; max_retransmit_time = DHCP6_REB_MAX_RT; - if (!client->timeout_resend_expire) { + if (event_source_is_enabled(client->timeout_resend_expire) <= 0) { r = dhcp6_lease_ia_rebind_expire(&client->lease->ia, &expire); if (r < 0) { @@ -727,43 +767,24 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda log_dhcp6_client(client, "Next retransmission in %s", format_timespan(time_string, FORMAT_TIMESPAN_MAX, client->retransmit_time, USEC_PER_SEC)); - r = sd_event_add_time(client->event, &client->timeout_resend, - clock_boottime_or_monotonic(), - time_now + client->retransmit_time, - 10 * USEC_PER_MSEC, client_timeout_resend, - client); + r = event_reset_time(client->event, &client->timeout_resend, + clock_boottime_or_monotonic(), + time_now + client->retransmit_time, 10 * USEC_PER_MSEC, + client_timeout_resend, client, + client->event_priority, "dhcp6-resend-timer", true); if (r < 0) goto error; - r = sd_event_source_set_priority(client->timeout_resend, - client->event_priority); - if (r < 0) - goto error; - - r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timer"); - if (r < 0) - goto error; - - if (max_retransmit_duration && !client->timeout_resend_expire) { + if (max_retransmit_duration && event_source_is_enabled(client->timeout_resend_expire) <= 0) { log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs", max_retransmit_duration / USEC_PER_SEC); - r = sd_event_add_time(client->event, - &client->timeout_resend_expire, - clock_boottime_or_monotonic(), - time_now + max_retransmit_duration, - USEC_PER_SEC, - client_timeout_resend_expire, client); - if (r < 0) - goto error; - - r = sd_event_source_set_priority(client->timeout_resend_expire, - client->event_priority); - if (r < 0) - goto error; - - r = sd_event_source_set_description(client->timeout_resend_expire, "dhcp6-resend-expire-timer"); + r = event_reset_time(client->event, &client->timeout_resend_expire, + clock_boottime_or_monotonic(), + time_now + max_retransmit_duration, USEC_PER_SEC, + client_timeout_resend_expire, client, + client->event_priority, "dhcp6-resend-expire-timer", true); if (r < 0) goto error; } @@ -777,19 +798,20 @@ error: static int client_ensure_iaid(sd_dhcp6_client *client) { int r; - be32_t iaid; + uint32_t iaid; assert(client); - if (client->ia_na.ia_na.id) + if (client->iaid_set) return 0; - r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, &iaid); + r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, true, &iaid); if (r < 0) return r; client->ia_na.ia_na.id = iaid; client->ia_pd.ia_pd.id = iaid; + client->iaid_set = true; return 0; } @@ -799,10 +821,11 @@ static int client_parse_message( DHCP6Message *message, size_t len, sd_dhcp6_lease *lease) { + + uint32_t lt_t1 = ~0, lt_t2 = ~0; + bool clientid = false; size_t pos = 0; int r; - bool clientid = false; - uint32_t lt_t1 = ~0, lt_t2 = ~0; assert(client); assert(message); @@ -812,20 +835,22 @@ static int client_parse_message( len -= sizeof(DHCP6Message); while (pos < len) { - DHCP6Option *option = (DHCP6Option *)&message->options[pos]; + DHCP6Option *option = (DHCP6Option *) &message->options[pos]; uint16_t optcode, optlen; - int status; - uint8_t *optval; be32_t iaid_lease; + uint8_t *optval; + int status; - if (len < pos + offsetof(DHCP6Option, data) || - len < pos + offsetof(DHCP6Option, data) + be16toh(option->len)) + if (len < pos + offsetof(DHCP6Option, data)) return -ENOBUFS; optcode = be16toh(option->code); optlen = be16toh(option->len); optval = option->data; + if (len < pos + offsetof(DHCP6Option, data) + optlen) + return -ENOBUFS; + switch (optcode) { case SD_DHCP6_OPTION_CLIENTID: if (clientid) { @@ -870,8 +895,11 @@ static int client_parse_message( break; case SD_DHCP6_OPTION_STATUS_CODE: - status = dhcp6_option_parse_status(option, optlen); - if (status) { + status = dhcp6_option_parse_status(option, optlen + sizeof(DHCP6Option)); + if (status < 0) + return status; + + if (status > 0) { log_dhcp6_client(client, "%s Status %s", dhcp6_message_type_to_string(message->type), dhcp6_message_status_to_string(status)); @@ -920,7 +948,7 @@ static int client_parse_message( if (r < 0 && r != -ENOMSG) return r; - r = dhcp6_lease_get_iaid(lease, &iaid_lease); + r = dhcp6_lease_get_pd_iaid(lease, &iaid_lease); if (r < 0) return r; @@ -973,7 +1001,7 @@ static int client_parse_message( break; } - pos += sizeof(*option) + optlen; + pos += offsetof(DHCP6Option, data) + optlen; } if (!clientid) { @@ -1033,8 +1061,8 @@ static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, si return 0; } - client_set_lease(client, lease); - lease = NULL; + sd_dhcp6_lease_unref(client->lease); + client->lease = TAKE_PTR(lease); return DHCP6_STATE_BOUND; } @@ -1062,8 +1090,8 @@ static int client_receive_advertise(sd_dhcp6_client *client, DHCP6Message *adver r = dhcp6_lease_get_preference(client->lease, &pref_lease); if (r < 0 || pref_advertise > pref_lease) { - client_set_lease(client, lease); - lease = NULL; + sd_dhcp6_lease_unref(client->lease); + client->lease = TAKE_PTR(lease); r = 0; } @@ -1194,19 +1222,41 @@ static int client_receive_message( return 0; } +static int client_get_lifetime(sd_dhcp6_client *client, uint32_t *lifetime_t1, + uint32_t *lifetime_t2) { + assert_return(client, -EINVAL); + assert_return(client->lease, -EINVAL); + + if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) { + *lifetime_t1 = be32toh(client->lease->ia.ia_na.lifetime_t1); + *lifetime_t2 = be32toh(client->lease->ia.ia_na.lifetime_t2); + + return 0; + } + + if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) { + *lifetime_t1 = be32toh(client->lease->pd.ia_pd.lifetime_t1); + *lifetime_t2 = be32toh(client->lease->pd.ia_pd.lifetime_t2); + + return 0; + } + + return -ENOMSG; +} + static int client_start(sd_dhcp6_client *client, enum DHCP6State state) { int r; usec_t timeout, time_now; char time_string[FORMAT_TIMESPAN_MAX]; + uint32_t lifetime_t1, lifetime_t2; assert_return(client, -EINVAL); assert_return(client->event, -EINVAL); assert_return(client->ifindex > 0, -EINVAL); assert_return(client->state != state, -EINVAL); - client->timeout_resend_expire = - sd_event_source_unref(client->timeout_resend_expire); - client->timeout_resend = sd_event_source_unref(client->timeout_resend); + (void) event_source_disable(client->timeout_resend_expire); + (void) event_source_disable(client->timeout_resend); client->retransmit_time = 0; client->retransmit_count = 0; @@ -1257,59 +1307,40 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) { case DHCP6_STATE_BOUND: - if (client->lease->ia.ia_na.lifetime_t1 == 0xffffffff || - client->lease->ia.ia_na.lifetime_t2 == 0xffffffff) { + r = client_get_lifetime(client, &lifetime_t1, &lifetime_t2); + if (r < 0) + goto error; + if (lifetime_t1 == 0xffffffff || lifetime_t2 == 0xffffffff) { log_dhcp6_client(client, "Infinite T1 0x%08x or T2 0x%08x", - be32toh(client->lease->ia.ia_na.lifetime_t1), - be32toh(client->lease->ia.ia_na.lifetime_t2)); + lifetime_t1, lifetime_t2); return 0; } - timeout = client_timeout_compute_random(be32toh(client->lease->ia.ia_na.lifetime_t1) * USEC_PER_SEC); + timeout = client_timeout_compute_random(lifetime_t1 * USEC_PER_SEC); log_dhcp6_client(client, "T1 expires in %s", format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC)); - client->lease->ia.timeout_t1 = sd_event_source_unref(client->lease->ia.timeout_t1); - r = sd_event_add_time(client->event, - &client->lease->ia.timeout_t1, - clock_boottime_or_monotonic(), time_now + timeout, - 10 * USEC_PER_SEC, client_timeout_t1, - client); + r = event_reset_time(client->event, &client->timeout_t1, + clock_boottime_or_monotonic(), + time_now + timeout, 10 * USEC_PER_SEC, + client_timeout_t1, client, + client->event_priority, "dhcp6-t1-timeout", true); if (r < 0) goto error; - r = sd_event_source_set_priority(client->lease->ia.timeout_t1, - client->event_priority); - if (r < 0) - goto error; - - r = sd_event_source_set_description(client->lease->ia.timeout_t1, "dhcp6-t1-timeout"); - if (r < 0) - goto error; - - timeout = client_timeout_compute_random(be32toh(client->lease->ia.ia_na.lifetime_t2) * USEC_PER_SEC); + timeout = client_timeout_compute_random(lifetime_t2 * USEC_PER_SEC); log_dhcp6_client(client, "T2 expires in %s", format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC)); - client->lease->ia.timeout_t2 = sd_event_source_unref(client->lease->ia.timeout_t2); - r = sd_event_add_time(client->event, - &client->lease->ia.timeout_t2, - clock_boottime_or_monotonic(), time_now + timeout, - 10 * USEC_PER_SEC, client_timeout_t2, - client); - if (r < 0) - goto error; - - r = sd_event_source_set_priority(client->lease->ia.timeout_t2, - client->event_priority); - if (r < 0) - goto error; - - r = sd_event_source_set_description(client->lease->ia.timeout_t2, "dhcp6-t2-timeout"); + r = event_reset_time(client->event, &client->timeout_t2, + clock_boottime_or_monotonic(), + time_now + timeout, 10 * USEC_PER_SEC, + client_timeout_t2, client, + client->event_priority, "dhcp6-t2-timeout", true); if (r < 0) goto error; @@ -1321,18 +1352,11 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) { client->transaction_id = random_u32() & htobe32(0x00ffffff); client->transaction_start = time_now; - r = sd_event_add_time(client->event, &client->timeout_resend, - clock_boottime_or_monotonic(), 0, 0, client_timeout_resend, - client); - if (r < 0) - goto error; - - r = sd_event_source_set_priority(client->timeout_resend, - client->event_priority); - if (r < 0) - goto error; - - r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timeout"); + r = event_reset_time(client->event, &client->timeout_resend, + clock_boottime_or_monotonic(), + 0, 0, + client_timeout_resend, client, + client->event_priority, "dhcp6-resend-timeout", true); if (r < 0) goto error; @@ -1371,6 +1395,9 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { if (!IN_SET(client->state, DHCP6_STATE_STOPPED)) return -EBUSY; + if (!client->information_request && !client->request) + return -EINVAL; + r = client_reset(client); if (r < 0) return r; @@ -1439,27 +1466,13 @@ sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) { return client->event; } -sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) { - - if (!client) - return NULL; - - assert(client->n_ref >= 1); - client->n_ref++; - - return client; -} - -sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) { - - if (!client) - return NULL; - - assert(client->n_ref >= 1); - client->n_ref--; +static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) { + assert(client); - if (client->n_ref > 0) - return NULL; + client->timeout_resend = sd_event_source_unref(client->timeout_resend); + client->timeout_resend_expire = sd_event_source_unref(client->timeout_resend_expire); + client->timeout_t1 = sd_event_source_unref(client->timeout_t1); + client->timeout_t2 = sd_event_source_unref(client->timeout_t2); client_reset(client); @@ -1472,29 +1485,36 @@ sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) { return mfree(client); } +DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_client, sd_dhcp6_client, dhcp6_client_free); + int sd_dhcp6_client_new(sd_dhcp6_client **ret) { _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL; + _cleanup_free_ be16_t *req_opts = NULL; size_t t; assert_return(ret, -EINVAL); - client = new0(sd_dhcp6_client, 1); - if (!client) + req_opts = new(be16_t, ELEMENTSOF(default_req_opts)); + if (!req_opts) return -ENOMEM; - client->n_ref = 1; - client->ia_na.type = SD_DHCP6_OPTION_IA_NA; - client->ia_pd.type = SD_DHCP6_OPTION_IA_PD; - client->ifindex = -1; - client->fd = -1; + for (t = 0; t < ELEMENTSOF(default_req_opts); t++) + req_opts[t] = htobe16(default_req_opts[t]); - client->req_opts_len = ELEMENTSOF(default_req_opts); - client->req_opts = new0(be16_t, client->req_opts_len); - if (!client->req_opts) + client = new(sd_dhcp6_client, 1); + if (!client) return -ENOMEM; - for (t = 0; t < client->req_opts_len; t++) - client->req_opts[t] = htobe16(default_req_opts[t]); + *client = (sd_dhcp6_client) { + .n_ref = 1, + .ia_na.type = SD_DHCP6_OPTION_IA_NA, + .ia_pd.type = SD_DHCP6_OPTION_IA_PD, + .ifindex = -1, + .request = DHCP6_REQUEST_IA_NA, + .fd = -1, + .req_opts_len = ELEMENTSOF(default_req_opts), + .req_opts = TAKE_PTR(req_opts), + }; *ret = TAKE_PTR(client); diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c index cca0b500..48188bf3 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c @@ -3,7 +3,7 @@ Copyright © 2014-2015 Intel Corporation. All rights reserved. ***/ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" #include <errno.h> @@ -13,15 +13,6 @@ #include "strv.h" #include "util.h" -int dhcp6_lease_clear_timers(DHCP6IA *ia) { - assert_return(ia, -EINVAL); - - ia->timeout_t1 = sd_event_source_unref(ia->timeout_t1); - ia->timeout_t2 = sd_event_source_unref(ia->timeout_t2); - - return 0; -} - int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire) { DHCP6Address *addr; uint32_t valid = 0, t; @@ -50,8 +41,6 @@ DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia) { if (!ia) return NULL; - dhcp6_lease_clear_timers(ia); - while (ia->addresses) { address = ia->addresses; @@ -65,15 +54,16 @@ DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia) { int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len) { + uint8_t *serverid; + assert_return(lease, -EINVAL); assert_return(id, -EINVAL); - free(lease->serverid); - - lease->serverid = memdup(id, len); - if (!lease->serverid) - return -EINVAL; + serverid = memdup(id, len); + if (!serverid) + return -ENOMEM; + free_and_replace(lease->serverid, serverid); lease->serverid_len = len; return 0; @@ -138,6 +128,15 @@ int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid) { return 0; } +int dhcp6_lease_get_pd_iaid(sd_dhcp6_lease *lease, be32_t *iaid) { + assert_return(lease, -EINVAL); + assert_return(iaid, -EINVAL); + + *iaid = lease->pd.ia_pd.id; + + return 0; +} + int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, struct in6_addr *addr, uint32_t *lifetime_preferred, uint32_t *lifetime_valid) { @@ -376,27 +375,8 @@ int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn) { return -ENOENT; } -sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease) { - - if (!lease) - return NULL; - - assert(lease->n_ref >= 1); - lease->n_ref++; - - return lease; -} - -sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease) { - - if (!lease) - return NULL; - - assert(lease->n_ref >= 1); - lease->n_ref--; - - if (lease->n_ref > 0) - return NULL; +static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) { + assert(lease); free(lease->serverid); dhcp6_lease_free_ia(&lease->ia); @@ -412,6 +392,8 @@ sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease) { return mfree(lease); } +DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_lease, sd_dhcp6_lease, dhcp6_lease_free); + int dhcp6_lease_new(sd_dhcp6_lease **ret) { sd_dhcp6_lease *lease; diff --git a/src/systemd/src/libsystemd-network/sd-ipv4acd.c b/src/systemd/src/libsystemd-network/sd-ipv4acd.c index a39a865a..b43da6b4 100644 --- a/src/systemd/src/libsystemd-network/sd-ipv4acd.c +++ b/src/systemd/src/libsystemd-network/sd-ipv4acd.c @@ -3,7 +3,7 @@ Copyright © 2014 Axis Communications AB. All rights reserved. ***/ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" #include <arpa/inet.h> #include <errno.h> @@ -16,6 +16,7 @@ #include "alloc-util.h" #include "arp-util.h" #include "ether-addr-util.h" +#include "event-util.h" #include "fd-util.h" #include "in-addr-util.h" #include "list.h" @@ -91,7 +92,7 @@ static void ipv4acd_set_state(sd_ipv4acd *acd, IPv4ACDState st, bool reset_count static void ipv4acd_reset(sd_ipv4acd *acd) { assert(acd); - acd->timer_event_source = sd_event_source_unref(acd->timer_event_source); + (void) event_source_disable(acd->timer_event_source); acd->receive_message_event_source = sd_event_source_unref(acd->receive_message_event_source); acd->fd = safe_close(acd->fd); @@ -99,25 +100,10 @@ static void ipv4acd_reset(sd_ipv4acd *acd) { ipv4acd_set_state(acd, IPV4ACD_STATE_INIT, true); } -sd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *acd) { - if (!acd) - return NULL; - - assert_se(acd->n_ref >= 1); - acd->n_ref++; - - return acd; -} - -sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *acd) { - if (!acd) - return NULL; - - assert_se(acd->n_ref >= 1); - acd->n_ref--; +static sd_ipv4acd *ipv4acd_free(sd_ipv4acd *acd) { + assert(acd); - if (acd->n_ref > 0) - return NULL; + acd->timer_event_source = sd_event_source_unref(acd->timer_event_source); ipv4acd_reset(acd); sd_ipv4acd_detach_event(acd); @@ -125,19 +111,23 @@ sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *acd) { return mfree(acd); } +DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_ipv4acd, sd_ipv4acd, ipv4acd_free); + int sd_ipv4acd_new(sd_ipv4acd **ret) { _cleanup_(sd_ipv4acd_unrefp) sd_ipv4acd *acd = NULL; assert_return(ret, -EINVAL); - acd = new0(sd_ipv4acd, 1); + acd = new(sd_ipv4acd, 1); if (!acd) return -ENOMEM; - acd->n_ref = 1; - acd->state = IPV4ACD_STATE_INIT; - acd->ifindex = -1; - acd->fd = -1; + *acd = (sd_ipv4acd) { + .n_ref = 1, + .state = IPV4ACD_STATE_INIT, + .ifindex = -1, + .fd = -1, + }; *ret = TAKE_PTR(acd); @@ -168,9 +158,7 @@ int sd_ipv4acd_stop(sd_ipv4acd *acd) { static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata); static int ipv4acd_set_next_wakeup(sd_ipv4acd *acd, usec_t usec, usec_t random_usec) { - _cleanup_(sd_event_source_unrefp) sd_event_source *timer = NULL; usec_t next_timeout, time_now; - int r; assert(acd); @@ -181,20 +169,11 @@ static int ipv4acd_set_next_wakeup(sd_ipv4acd *acd, usec_t usec, usec_t random_u assert_se(sd_event_now(acd->event, clock_boottime_or_monotonic(), &time_now) >= 0); - r = sd_event_add_time(acd->event, &timer, clock_boottime_or_monotonic(), time_now + next_timeout, 0, ipv4acd_on_timeout, acd); - if (r < 0) - return r; - - r = sd_event_source_set_priority(timer, acd->event_priority); - if (r < 0) - return r; - - (void) sd_event_source_set_description(timer, "ipv4acd-timer"); - - sd_event_source_unref(acd->timer_event_source); - acd->timer_event_source = TAKE_PTR(timer); - - return 0; + return event_reset_time(acd->event, &acd->timer_event_source, + clock_boottime_or_monotonic(), + time_now + next_timeout, 0, + ipv4acd_on_timeout, acd, + acd->event_priority, "ipv4acd-timer", true); } static bool ipv4acd_arp_conflict(sd_ipv4acd *acd, struct ether_arp *arp) { diff --git a/src/systemd/src/libsystemd-network/sd-ipv4ll.c b/src/systemd/src/libsystemd-network/sd-ipv4ll.c index 69fa60e2..f1b94829 100644 --- a/src/systemd/src/libsystemd-network/sd-ipv4ll.c +++ b/src/systemd/src/libsystemd-network/sd-ipv4ll.c @@ -3,7 +3,7 @@ Copyright © 2014 Axis Communications AB. All rights reserved. ***/ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" #include <arpa/inet.h> #include <errno.h> @@ -57,30 +57,15 @@ struct sd_ipv4ll { static void ipv4ll_on_acd(sd_ipv4acd *ll, int event, void *userdata); -sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll) { - if (!ll) - return NULL; - - assert(ll->n_ref >= 1); - ll->n_ref++; - - return ll; -} - -sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll) { - if (!ll) - return NULL; - - assert(ll->n_ref >= 1); - ll->n_ref--; - - if (ll->n_ref > 0) - return NULL; +static sd_ipv4ll *ipv4ll_free(sd_ipv4ll *ll) { + assert(ll); sd_ipv4acd_unref(ll->acd); return mfree(ll); } +DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_ipv4ll, sd_ipv4ll, ipv4ll_free); + int sd_ipv4ll_new(sd_ipv4ll **ret) { _cleanup_(sd_ipv4ll_unrefp) sd_ipv4ll *ll = NULL; int r; diff --git a/src/systemd/src/libsystemd-network/sd-lldp.c b/src/systemd/src/libsystemd-network/sd-lldp.c index 74ba8236..741128e1 100644 --- a/src/systemd/src/libsystemd-network/sd-lldp.c +++ b/src/systemd/src/libsystemd-network/sd-lldp.c @@ -1,39 +1,50 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" #include <arpa/inet.h> #include <linux/sockios.h> +#include <sys/ioctl.h> #include "sd-lldp.h" #include "alloc-util.h" +#include "ether-addr-util.h" +#include "event-util.h" #include "fd-util.h" #include "lldp-internal.h" #include "lldp-neighbor.h" #include "lldp-network.h" #include "socket-util.h" -#include "ether-addr-util.h" +#include "string-table.h" #define LLDP_DEFAULT_NEIGHBORS_MAX 128U -static void lldp_flush_neighbors(sd_lldp *lldp) { - sd_lldp_neighbor *n; +static const char * const lldp_event_table[_SD_LLDP_EVENT_MAX] = { + [SD_LLDP_EVENT_ADDED] = "added", + [SD_LLDP_EVENT_REMOVED] = "removed", + [SD_LLDP_EVENT_UPDATED] = "updated", + [SD_LLDP_EVENT_REFRESHED] = "refreshed", +}; + +DEFINE_STRING_TABLE_LOOKUP(lldp_event, sd_lldp_event); +static void lldp_flush_neighbors(sd_lldp *lldp) { assert(lldp); - while ((n = hashmap_first(lldp->neighbor_by_id))) - lldp_neighbor_unlink(n); + hashmap_clear(lldp->neighbor_by_id); } static void lldp_callback(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n) { assert(lldp); + assert(event >= 0 && event < _SD_LLDP_EVENT_MAX); - log_lldp("Invoking callback for '%c'.", event); - - if (!lldp->callback) + if (!lldp->callback) { + log_lldp("Received '%s' event.", lldp_event_to_string(event)); return; + } + log_lldp("Invoking callback for '%s' event.", lldp_event_to_string(event)); lldp->callback(lldp, event, n, lldp->userdata); } @@ -121,7 +132,7 @@ static int lldp_add_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) { } if (lldp_neighbor_equal(n, old)) { - /* Is this equal, then restart the TTL counter, but don't do anyting else. */ + /* Is this equal, then restart the TTL counter, but don't do anything else. */ old->timestamp = n->timestamp; lldp_start_timer(lldp, old); lldp_callback(lldp, SD_LLDP_EVENT_REFRESHED, old); @@ -225,7 +236,7 @@ static int lldp_receive_datagram(sd_event_source *s, int fd, uint32_t revents, v static void lldp_reset(sd_lldp *lldp) { assert(lldp); - lldp->timer_event_source = sd_event_source_unref(lldp->timer_event_source); + (void) event_source_disable(lldp->timer_event_source); lldp->io_event_source = sd_event_source_unref(lldp->io_event_source); lldp->fd = safe_close(lldp->fd); } @@ -331,27 +342,10 @@ _public_ int sd_lldp_set_ifindex(sd_lldp *lldp, int ifindex) { return 0; } -_public_ sd_lldp* sd_lldp_ref(sd_lldp *lldp) { - - if (!lldp) - return NULL; - - assert(lldp->n_ref > 0); - lldp->n_ref++; - - return lldp; -} - -_public_ sd_lldp* sd_lldp_unref(sd_lldp *lldp) { - - if (!lldp) - return NULL; - - assert(lldp->n_ref > 0); - lldp->n_ref --; +static sd_lldp* lldp_free(sd_lldp *lldp) { + assert(lldp); - if (lldp->n_ref > 0) - return NULL; + lldp->timer_event_source = sd_event_source_unref(lldp->timer_event_source); lldp_reset(lldp); sd_lldp_detach_event(lldp); @@ -362,22 +356,26 @@ _public_ sd_lldp* sd_lldp_unref(sd_lldp *lldp) { return mfree(lldp); } +DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_lldp, sd_lldp, lldp_free); + _public_ int sd_lldp_new(sd_lldp **ret) { _cleanup_(sd_lldp_unrefp) sd_lldp *lldp = NULL; int r; assert_return(ret, -EINVAL); - lldp = new0(sd_lldp, 1); + lldp = new(sd_lldp, 1); if (!lldp) return -ENOMEM; - lldp->n_ref = 1; - lldp->fd = -1; - lldp->neighbors_max = LLDP_DEFAULT_NEIGHBORS_MAX; - lldp->capability_mask = (uint16_t) -1; + *lldp = (sd_lldp) { + .n_ref = 1, + .fd = -1, + .neighbors_max = LLDP_DEFAULT_NEIGHBORS_MAX, + .capability_mask = (uint16_t) -1, + }; - lldp->neighbor_by_id = hashmap_new(&lldp_neighbor_id_hash_ops); + lldp->neighbor_by_id = hashmap_new(&lldp_neighbor_hash_ops); if (!lldp->neighbor_by_id) return -ENOMEM; @@ -390,10 +388,8 @@ _public_ int sd_lldp_new(sd_lldp **ret) { return 0; } -static int neighbor_compare_func(const void *a, const void *b) { - const sd_lldp_neighbor * const*x = a, * const *y = b; - - return lldp_neighbor_id_hash_ops.compare(&(*x)->id, &(*y)->id); +static int neighbor_compare_func(sd_lldp_neighbor * const *a, sd_lldp_neighbor * const *b) { + return lldp_neighbor_id_compare_func(&(*a)->id, &(*b)->id); } static int on_timer_event(sd_event_source *s, uint64_t usec, void *userdata) { @@ -413,7 +409,6 @@ static int on_timer_event(sd_event_source *s, uint64_t usec, void *userdata) { static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor) { sd_lldp_neighbor *n; - int r; assert(lldp); @@ -421,35 +416,17 @@ static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor) { lldp_neighbor_start_ttl(neighbor); n = prioq_peek(lldp->neighbor_by_expiry); - if (!n) { - - if (lldp->timer_event_source) - return sd_event_source_set_enabled(lldp->timer_event_source, SD_EVENT_OFF); - - return 0; - } - - if (lldp->timer_event_source) { - r = sd_event_source_set_time(lldp->timer_event_source, n->until); - if (r < 0) - return r; - - return sd_event_source_set_enabled(lldp->timer_event_source, SD_EVENT_ONESHOT); - } + if (!n) + return event_source_disable(lldp->timer_event_source); if (!lldp->event) return 0; - r = sd_event_add_time(lldp->event, &lldp->timer_event_source, clock_boottime_or_monotonic(), n->until, 0, on_timer_event, lldp); - if (r < 0) - return r; - - r = sd_event_source_set_priority(lldp->timer_event_source, lldp->event_priority); - if (r < 0) - return r; - - (void) sd_event_source_set_description(lldp->timer_event_source, "lldp-timer"); - return 0; + return event_reset_time(lldp->event, &lldp->timer_event_source, + clock_boottime_or_monotonic(), + n->until, 0, + on_timer_event, lldp, + lldp->event_priority, "lldp-timer", true); } _public_ int sd_lldp_get_neighbors(sd_lldp *lldp, sd_lldp_neighbor ***ret) { @@ -481,7 +458,7 @@ _public_ int sd_lldp_get_neighbors(sd_lldp *lldp, sd_lldp_neighbor ***ret) { assert((size_t) k == hashmap_size(lldp->neighbor_by_id)); /* Return things in a stable order */ - qsort(l, k, sizeof(sd_lldp_neighbor*), neighbor_compare_func); + typesafe_qsort(l, k, neighbor_compare_func); *ret = l; return k; diff --git a/src/systemd/src/libsystemd/sd-event/event-source.h b/src/systemd/src/libsystemd/sd-event/event-source.h new file mode 100644 index 00000000..99ab8fc1 --- /dev/null +++ b/src/systemd/src/libsystemd/sd-event/event-source.h @@ -0,0 +1,206 @@ +#pragma once +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include <sys/epoll.h> +#include <sys/timerfd.h> +#include <sys/wait.h> + +#include "sd-event.h" + +#include "fs-util.h" +#include "hashmap.h" +#include "list.h" +#include "prioq.h" + +typedef enum EventSourceType { + SOURCE_IO, + SOURCE_TIME_REALTIME, + SOURCE_TIME_BOOTTIME, + SOURCE_TIME_MONOTONIC, + SOURCE_TIME_REALTIME_ALARM, + SOURCE_TIME_BOOTTIME_ALARM, + SOURCE_SIGNAL, + SOURCE_CHILD, + SOURCE_DEFER, + SOURCE_POST, + SOURCE_EXIT, + SOURCE_WATCHDOG, + SOURCE_INOTIFY, + _SOURCE_EVENT_SOURCE_TYPE_MAX, + _SOURCE_EVENT_SOURCE_TYPE_INVALID = -1 +} EventSourceType; + +/* All objects we use in epoll events start with this value, so that + * we know how to dispatch it */ +typedef enum WakeupType { + WAKEUP_NONE, + WAKEUP_EVENT_SOURCE, + WAKEUP_CLOCK_DATA, + WAKEUP_SIGNAL_DATA, + WAKEUP_INOTIFY_DATA, + _WAKEUP_TYPE_MAX, + _WAKEUP_TYPE_INVALID = -1, +} WakeupType; + +struct inode_data; + +struct sd_event_source { + WakeupType wakeup; + + unsigned n_ref; + + sd_event *event; + void *userdata; + sd_event_handler_t prepare; + + char *description; + + EventSourceType type:5; + signed int enabled:3; + bool pending:1; + bool dispatching:1; + bool floating:1; + + int64_t priority; + unsigned pending_index; + unsigned prepare_index; + uint64_t pending_iteration; + uint64_t prepare_iteration; + + sd_event_destroy_t destroy_callback; + + LIST_FIELDS(sd_event_source, sources); + + union { + struct { + sd_event_io_handler_t callback; + int fd; + uint32_t events; + uint32_t revents; + bool registered:1; + bool owned:1; + } io; + struct { + sd_event_time_handler_t callback; + usec_t next, accuracy; + unsigned earliest_index; + unsigned latest_index; + } time; + struct { + sd_event_signal_handler_t callback; + struct signalfd_siginfo siginfo; + int sig; + } signal; + struct { + sd_event_child_handler_t callback; + siginfo_t siginfo; + pid_t pid; + int options; + } child; + struct { + sd_event_handler_t callback; + } defer; + struct { + sd_event_handler_t callback; + } post; + struct { + sd_event_handler_t callback; + unsigned prioq_index; + } exit; + struct { + sd_event_inotify_handler_t callback; + uint32_t mask; + struct inode_data *inode_data; + LIST_FIELDS(sd_event_source, by_inode_data); + } inotify; + }; +}; + +struct clock_data { + WakeupType wakeup; + int fd; + + /* For all clocks we maintain two priority queues each, one + * ordered for the earliest times the events may be + * dispatched, and one ordered by the latest times they must + * have been dispatched. The range between the top entries in + * the two prioqs is the time window we can freely schedule + * wakeups in */ + + Prioq *earliest; + Prioq *latest; + usec_t next; + + bool needs_rearm:1; +}; + +struct signal_data { + WakeupType wakeup; + + /* For each priority we maintain one signal fd, so that we + * only have to dequeue a single event per priority at a + * time. */ + + int fd; + int64_t priority; + sigset_t sigset; + sd_event_source *current; +}; + +/* A structure listing all event sources currently watching a specific inode */ +struct inode_data { + /* The identifier for the inode, the combination of the .st_dev + .st_ino fields of the file */ + ino_t ino; + dev_t dev; + + /* An fd of the inode to watch. The fd is kept open until the next iteration of the loop, so that we can + * rearrange the priority still until then, as we need the original inode to change the priority as we need to + * add a watch descriptor to the right inotify for the priority which we can only do if we have a handle to the + * original inode. We keep a list of all inode_data objects with an open fd in the to_close list (see below) of + * the sd-event object, so that it is efficient to close everything, before entering the next event loop + * iteration. */ + int fd; + + /* The inotify "watch descriptor" */ + int wd; + + /* The combination of the mask of all inotify watches on this inode we manage. This is also the mask that has + * most recently been set on the watch descriptor. */ + uint32_t combined_mask; + + /* All event sources subscribed to this inode */ + LIST_HEAD(sd_event_source, event_sources); + + /* The inotify object we watch this inode with */ + struct inotify_data *inotify_data; + + /* A linked list of all inode data objects with fds to close (see above) */ + LIST_FIELDS(struct inode_data, to_close); +}; + +/* A structure encapsulating an inotify fd */ +struct inotify_data { + WakeupType wakeup; + + /* For each priority we maintain one inotify fd, so that we only have to dequeue a single event per priority at + * a time */ + + int fd; + int64_t priority; + + Hashmap *inodes; /* The inode_data structures keyed by dev+ino */ + Hashmap *wd; /* The inode_data structures keyed by the watch descriptor for each */ + + /* The buffer we read inotify events into */ + union inotify_event_buffer buffer; + size_t buffer_filled; /* fill level of the buffer */ + + /* How many event sources are currently marked pending for this inotify. We won't read new events off the + * inotify fd as long as there are still pending events on the inotify (because we have no strategy of queuing + * the events locally if they can't be coalesced). */ + unsigned n_pending; + + /* A linked list of all inotify objects with data already read, that still need processing. We keep this list + * to make it efficient to figure out what inotify objects to process data on next. */ + LIST_FIELDS(struct inotify_data, buffered); +}; diff --git a/src/systemd/src/libsystemd/sd-event/event-util.c b/src/systemd/src/libsystemd/sd-event/event-util.c new file mode 100644 index 00000000..e8384cfd --- /dev/null +++ b/src/systemd/src/libsystemd/sd-event/event-util.c @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "nm-sd-adapt-core.h" + +#include <errno.h> + +#include "event-source.h" +#include "event-util.h" +#include "log.h" +#include "string-util.h" + +int event_reset_time( + sd_event *e, + sd_event_source **s, + clockid_t clock, + uint64_t usec, + uint64_t accuracy, + sd_event_time_handler_t callback, + void *userdata, + int64_t priority, + const char *description, + bool force_reset) { + + bool created = false; + int enabled, r; + clockid_t c; + + assert(e); + assert(s); + + if (*s) { + if (!force_reset) { + r = sd_event_source_get_enabled(*s, &enabled); + if (r < 0) + return log_debug_errno(r, "sd-event: Failed to query whether event source \"%s\" is enabled or not: %m", + strna((*s)->description ?: description)); + + if (enabled != SD_EVENT_OFF) + return 0; + } + + r = sd_event_source_get_time_clock(*s, &c); + if (r < 0) + return log_debug_errno(r, "sd-event: Failed to get clock id of event source \"%s\": %m", strna((*s)->description ?: description)); + + if (c != clock) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "sd-event: Current clock id %i of event source \"%s\" is different from specified one %i.", + (int)c, + strna((*s)->description ? : description), + (int)clock); + + r = sd_event_source_set_time(*s, usec); + if (r < 0) + return log_debug_errno(r, "sd-event: Failed to set time for event source \"%s\": %m", strna((*s)->description ?: description)); + + r = sd_event_source_set_time_accuracy(*s, accuracy); + if (r < 0) + return log_debug_errno(r, "sd-event: Failed to set accuracy for event source \"%s\": %m", strna((*s)->description ?: description)); + + /* callback function is not updated, as we do not have sd_event_source_set_time_callback(). */ + + (void) sd_event_source_set_userdata(*s, userdata); + + r = sd_event_source_set_enabled(*s, SD_EVENT_ONESHOT); + if (r < 0) + return log_debug_errno(r, "sd-event: Failed to enable event source \"%s\": %m", strna((*s)->description ?: description)); + } else { + r = sd_event_add_time(e, s, clock, usec, accuracy, callback, userdata); + if (r < 0) + return log_debug_errno(r, "sd-event: Failed to create timer event \"%s\": %m", strna(description)); + + created = true; + } + + r = sd_event_source_set_priority(*s, priority); + if (r < 0) + return log_debug_errno(r, "sd-event: Failed to set priority for event source \"%s\": %m", strna((*s)->description ?: description)); + + if (description) { + r = sd_event_source_set_description(*s, description); + if (r < 0) + return log_debug_errno(r, "sd-event: Failed to set description for event source \"%s\": %m", description); + } + + return created; +} + +int event_source_disable(sd_event_source *s) { + if (!s) + return 0; + + return sd_event_source_set_enabled(s, SD_EVENT_OFF); +} + +int event_source_is_enabled(sd_event_source *s) { + if (!s) + return false; + + return sd_event_source_get_enabled(s, NULL); +} diff --git a/src/systemd/src/libsystemd/sd-event/event-util.h b/src/systemd/src/libsystemd/sd-event/event-util.h new file mode 100644 index 00000000..00180955 --- /dev/null +++ b/src/systemd/src/libsystemd/sd-event/event-util.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include <stdbool.h> + +#include "sd-event.h" + +int event_reset_time(sd_event *e, sd_event_source **s, + clockid_t clock, uint64_t usec, uint64_t accuracy, + sd_event_time_handler_t callback, void *userdata, + int64_t priority, const char *description, bool force_reset); +int event_source_disable(sd_event_source *s); +int event_source_is_enabled(sd_event_source *s); diff --git a/src/systemd/src/libsystemd/sd-event/sd-event.c b/src/systemd/src/libsystemd/sd-event/sd-event.c index 2af3572d..e49cc9d8 100644 --- a/src/systemd/src/libsystemd/sd-event/sd-event.c +++ b/src/systemd/src/libsystemd/sd-event/sd-event.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" #include <sys/epoll.h> #include <sys/timerfd.h> @@ -11,6 +11,7 @@ #include "sd-id128.h" #include "alloc-util.h" +#include "event-source.h" #include "fd-util.h" #include "fs-util.h" #include "hashmap.h" @@ -28,24 +29,6 @@ #define DEFAULT_ACCURACY_USEC (250 * USEC_PER_MSEC) -typedef enum EventSourceType { - SOURCE_IO, - SOURCE_TIME_REALTIME, - SOURCE_TIME_BOOTTIME, - SOURCE_TIME_MONOTONIC, - SOURCE_TIME_REALTIME_ALARM, - SOURCE_TIME_BOOTTIME_ALARM, - SOURCE_SIGNAL, - SOURCE_CHILD, - SOURCE_DEFER, - SOURCE_POST, - SOURCE_EXIT, - SOURCE_WATCHDOG, - SOURCE_INOTIFY, - _SOURCE_EVENT_SOURCE_TYPE_MAX, - _SOURCE_EVENT_SOURCE_TYPE_INVALID = -1 -} EventSourceType; - static const char* const event_source_type_table[_SOURCE_EVENT_SOURCE_TYPE_MAX] = { [SOURCE_IO] = "io", [SOURCE_TIME_REALTIME] = "realtime", @@ -64,183 +47,8 @@ static const char* const event_source_type_table[_SOURCE_EVENT_SOURCE_TYPE_MAX] DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(event_source_type, int); -/* All objects we use in epoll events start with this value, so that - * we know how to dispatch it */ -typedef enum WakeupType { - WAKEUP_NONE, - WAKEUP_EVENT_SOURCE, - WAKEUP_CLOCK_DATA, - WAKEUP_SIGNAL_DATA, - WAKEUP_INOTIFY_DATA, - _WAKEUP_TYPE_MAX, - _WAKEUP_TYPE_INVALID = -1, -} WakeupType; - #define EVENT_SOURCE_IS_TIME(t) IN_SET((t), SOURCE_TIME_REALTIME, SOURCE_TIME_BOOTTIME, SOURCE_TIME_MONOTONIC, SOURCE_TIME_REALTIME_ALARM, SOURCE_TIME_BOOTTIME_ALARM) -struct inode_data; - -struct sd_event_source { - WakeupType wakeup; - - unsigned n_ref; - - sd_event *event; - void *userdata; - sd_event_handler_t prepare; - - char *description; - - EventSourceType type:5; - signed int enabled:3; - bool pending:1; - bool dispatching:1; - bool floating:1; - - int64_t priority; - unsigned pending_index; - unsigned prepare_index; - uint64_t pending_iteration; - uint64_t prepare_iteration; - - sd_event_destroy_t destroy_callback; - - LIST_FIELDS(sd_event_source, sources); - - union { - struct { - sd_event_io_handler_t callback; - int fd; - uint32_t events; - uint32_t revents; - bool registered:1; - bool owned:1; - } io; - struct { - sd_event_time_handler_t callback; - usec_t next, accuracy; - unsigned earliest_index; - unsigned latest_index; - } time; - struct { - sd_event_signal_handler_t callback; - struct signalfd_siginfo siginfo; - int sig; - } signal; - struct { - sd_event_child_handler_t callback; - siginfo_t siginfo; - pid_t pid; - int options; - } child; - struct { - sd_event_handler_t callback; - } defer; - struct { - sd_event_handler_t callback; - } post; - struct { - sd_event_handler_t callback; - unsigned prioq_index; - } exit; - struct { - sd_event_inotify_handler_t callback; - uint32_t mask; - struct inode_data *inode_data; - LIST_FIELDS(sd_event_source, by_inode_data); - } inotify; - }; -}; - -struct clock_data { - WakeupType wakeup; - int fd; - - /* For all clocks we maintain two priority queues each, one - * ordered for the earliest times the events may be - * dispatched, and one ordered by the latest times they must - * have been dispatched. The range between the top entries in - * the two prioqs is the time window we can freely schedule - * wakeups in */ - - Prioq *earliest; - Prioq *latest; - usec_t next; - - bool needs_rearm:1; -}; - -struct signal_data { - WakeupType wakeup; - - /* For each priority we maintain one signal fd, so that we - * only have to dequeue a single event per priority at a - * time. */ - - int fd; - int64_t priority; - sigset_t sigset; - sd_event_source *current; -}; - -/* A structure listing all event sources currently watching a specific inode */ -struct inode_data { - /* The identifier for the inode, the combination of the .st_dev + .st_ino fields of the file */ - ino_t ino; - dev_t dev; - - /* An fd of the inode to watch. The fd is kept open until the next iteration of the loop, so that we can - * rearrange the priority still until then, as we need the original inode to change the priority as we need to - * add a watch descriptor to the right inotify for the priority which we can only do if we have a handle to the - * original inode. We keep a list of all inode_data objects with an open fd in the to_close list (see below) of - * the sd-event object, so that it is efficient to close everything, before entering the next event loop - * iteration. */ - int fd; - - /* The inotify "watch descriptor" */ - int wd; - - /* The combination of the mask of all inotify watches on this inode we manage. This is also the mask that has - * most recently been set on the watch descriptor. */ - uint32_t combined_mask; - - /* All event sources subscribed to this inode */ - LIST_HEAD(sd_event_source, event_sources); - - /* The inotify object we watch this inode with */ - struct inotify_data *inotify_data; - - /* A linked list of all inode data objects with fds to close (see above) */ - LIST_FIELDS(struct inode_data, to_close); -}; - -/* A structure encapsulating an inotify fd */ -struct inotify_data { - WakeupType wakeup; - - /* For each priority we maintain one inotify fd, so that we only have to dequeue a single event per priority at - * a time */ - - int fd; - int64_t priority; - - Hashmap *inodes; /* The inode_data structures keyed by dev+ino */ - Hashmap *wd; /* The inode_data structures keyed by the watch descriptor for each */ - - /* The buffer we read inotify events into */ - union inotify_event_buffer buffer; - size_t buffer_filled; /* fill level of the buffer */ - - /* How many event sources are currently marked pending for this inotify. We won't read new events off the - * inotify fd as long as there are still pending events on the inotify (because we have no strategy of queuing - * the events locally if they can't be coalesced). */ - unsigned n_pending; - - /* A linked list of all inotify objects with data already read, that still need processing. We keep this list - * to make it efficient to figure out what inotify objects to process data on next. */ - LIST_FIELDS(struct inotify_data, buffered); -}; - struct sd_event { unsigned n_ref; @@ -316,6 +124,7 @@ static sd_event *event_resolve(sd_event *e) { static int pending_prioq_compare(const void *a, const void *b) { const sd_event_source *x = a, *y = b; + int r; assert(x->pending); assert(y->pending); @@ -327,22 +136,17 @@ static int pending_prioq_compare(const void *a, const void *b) { return 1; /* Lower priority values first */ - if (x->priority < y->priority) - return -1; - if (x->priority > y->priority) - return 1; + r = CMP(x->priority, y->priority); + if (r != 0) + return r; /* Older entries first */ - if (x->pending_iteration < y->pending_iteration) - return -1; - if (x->pending_iteration > y->pending_iteration) - return 1; - - return 0; + return CMP(x->pending_iteration, y->pending_iteration); } static int prepare_prioq_compare(const void *a, const void *b) { const sd_event_source *x = a, *y = b; + int r; assert(x->prepare); assert(y->prepare); @@ -356,18 +160,12 @@ static int prepare_prioq_compare(const void *a, const void *b) { /* Move most recently prepared ones last, so that we can stop * preparing as soon as we hit one that has already been * prepared in the current iteration */ - if (x->prepare_iteration < y->prepare_iteration) - return -1; - if (x->prepare_iteration > y->prepare_iteration) - return 1; + r = CMP(x->prepare_iteration, y->prepare_iteration); + if (r != 0) + return r; /* Lower priority values first */ - if (x->priority < y->priority) - return -1; - if (x->priority > y->priority) - return 1; - - return 0; + return CMP(x->priority, y->priority); } static int earliest_time_prioq_compare(const void *a, const void *b) { @@ -389,12 +187,7 @@ static int earliest_time_prioq_compare(const void *a, const void *b) { return 1; /* Order by time */ - if (x->time.next < y->time.next) - return -1; - if (x->time.next > y->time.next) - return 1; - - return 0; + return CMP(x->time.next, y->time.next); } static usec_t time_event_source_latest(const sd_event_source *s) { @@ -420,12 +213,7 @@ static int latest_time_prioq_compare(const void *a, const void *b) { return 1; /* Order by time */ - if (time_event_source_latest(x) < time_event_source_latest(y)) - return -1; - if (time_event_source_latest(x) > time_event_source_latest(y)) - return 1; - - return 0; + return CMP(time_event_source_latest(x), time_event_source_latest(y)); } static int exit_prioq_compare(const void *a, const void *b) { @@ -441,12 +229,7 @@ static int exit_prioq_compare(const void *a, const void *b) { return 1; /* Lower priority values first */ - if (x->priority < y->priority) - return -1; - if (x->priority > y->priority) - return 1; - - return 0; + return CMP(x->priority, y->priority); } static void free_clock_data(struct clock_data *d) { @@ -458,7 +241,7 @@ static void free_clock_data(struct clock_data *d) { prioq_free(d->latest); } -static void event_free(sd_event *e) { +static sd_event *event_free(sd_event *e) { sd_event_source *s; assert(e); @@ -494,7 +277,8 @@ static void event_free(sd_event *e) { hashmap_free(e->child_sources); set_free(e->post_sources); - free(e); + + return mfree(e); } _public_ int sd_event_new(sd_event** ret) { @@ -555,30 +339,7 @@ fail: return r; } -_public_ sd_event* sd_event_ref(sd_event *e) { - - if (!e) - return NULL; - - assert(e->n_ref >= 1); - e->n_ref++; - - return e; -} - -_public_ sd_event* sd_event_unref(sd_event *e) { - - if (!e) - return NULL; - - assert(e->n_ref >= 1); - e->n_ref--; - - if (e->n_ref <= 0) - event_free(e); - - return NULL; -} +DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_event, sd_event, event_free); static bool event_pid_changed(sd_event *e) { assert(e); @@ -974,7 +735,7 @@ static void source_disconnect(sd_event_source *s) { * continued to being watched. That's because inotify doesn't really have an API for that: we * can only change watch masks with access to the original inode either by fd or by path. But * paths aren't stable, and keeping an O_PATH fd open all the time would mean wasting an fd - * continously and keeping the mount busy which we can't really do. We could reconstruct the + * continuously and keeping the mount busy which we can't really do. We could reconstruct the * original inode from /proc/self/fdinfo/$INOTIFY_FD (as all watch descriptors are listed * there), but given the need for open_by_handle_at() which is privileged and not universally * available this would be quite an incomplete solution. Hence we go the other way, leave the @@ -1023,6 +784,7 @@ static void source_free(sd_event_source *s) { free(s->description); free(s); } +DEFINE_TRIVIAL_CLEANUP_FUNC(sd_event_source*, source_free); static int source_set_pending(sd_event_source *s, bool b) { int r; @@ -1116,7 +878,7 @@ _public_ int sd_event_add_io( sd_event_io_handler_t callback, void *userdata) { - sd_event_source *s; + _cleanup_(source_freep) sd_event_source *s = NULL; int r; assert_return(e, -EINVAL); @@ -1139,13 +901,12 @@ _public_ int sd_event_add_io( s->enabled = SD_EVENT_ON; r = source_io_register(s, s->enabled, events); - if (r < 0) { - source_free(s); + if (r < 0) return r; - } if (ret) *ret = s; + TAKE_PTR(s); return 0; } @@ -1154,7 +915,7 @@ static void initialize_perturb(sd_event *e) { sd_id128_t bootid = {}; /* When we sleep for longer, we try to realign the wakeup to - the same time wihtin each minute/second/250ms, so that + the same time within each minute/second/250ms, so that events all across the system can be coalesced into a single CPU wakeup. However, let's take some system-specific randomness for this value, so that in a network of systems @@ -1220,7 +981,7 @@ _public_ int sd_event_add_time( void *userdata) { EventSourceType type; - sd_event_source *s; + _cleanup_(source_freep) sd_event_source *s = NULL; struct clock_data *d; int r; @@ -1272,20 +1033,17 @@ _public_ int sd_event_add_time( r = prioq_put(d->earliest, s, &s->time.earliest_index); if (r < 0) - goto fail; + return r; r = prioq_put(d->latest, s, &s->time.latest_index); if (r < 0) - goto fail; + return r; if (ret) *ret = s; + TAKE_PTR(s); return 0; - -fail: - source_free(s); - return r; } #if 0 /* NM_IGNORED */ @@ -1302,7 +1060,7 @@ _public_ int sd_event_add_signal( sd_event_signal_handler_t callback, void *userdata) { - sd_event_source *s; + _cleanup_(source_freep) sd_event_source *s = NULL; struct signal_data *d; sigset_t ss; int r; @@ -1342,16 +1100,15 @@ _public_ int sd_event_add_signal( e->signal_sources[sig] = s; r = event_make_signal_data(e, sig, &d); - if (r < 0) { - source_free(s); + if (r < 0) return r; - } /* Use the signal name as description for the event source by default */ (void) sd_event_source_set_description(s, signal_to_string(sig)); if (ret) *ret = s; + TAKE_PTR(s); return 0; } @@ -1365,7 +1122,7 @@ _public_ int sd_event_add_child( sd_event_child_handler_t callback, void *userdata) { - sd_event_source *s; + _cleanup_(source_freep) sd_event_source *s = NULL; int r; assert_return(e, -EINVAL); @@ -1395,17 +1152,14 @@ _public_ int sd_event_add_child( s->enabled = SD_EVENT_ONESHOT; r = hashmap_put(e->child_sources, PID_TO_PTR(pid), s); - if (r < 0) { - source_free(s); + if (r < 0) return r; - } e->n_enabled_child_sources++; r = event_make_signal_data(e, SIGCHLD, NULL); if (r < 0) { e->n_enabled_child_sources--; - source_free(s); return r; } @@ -1413,6 +1167,7 @@ _public_ int sd_event_add_child( if (ret) *ret = s; + TAKE_PTR(s); return 0; } @@ -1423,7 +1178,7 @@ _public_ int sd_event_add_defer( sd_event_handler_t callback, void *userdata) { - sd_event_source *s; + _cleanup_(source_freep) sd_event_source *s = NULL; int r; assert_return(e, -EINVAL); @@ -1441,13 +1196,12 @@ _public_ int sd_event_add_defer( s->enabled = SD_EVENT_ONESHOT; r = source_set_pending(s, true); - if (r < 0) { - source_free(s); + if (r < 0) return r; - } if (ret) *ret = s; + TAKE_PTR(s); return 0; } @@ -1458,7 +1212,7 @@ _public_ int sd_event_add_post( sd_event_handler_t callback, void *userdata) { - sd_event_source *s; + _cleanup_(source_freep) sd_event_source *s = NULL; int r; assert_return(e, -EINVAL); @@ -1480,13 +1234,12 @@ _public_ int sd_event_add_post( s->enabled = SD_EVENT_ON; r = set_put(e->post_sources, s); - if (r < 0) { - source_free(s); + if (r < 0) return r; - } if (ret) *ret = s; + TAKE_PTR(s); return 0; } @@ -1497,7 +1250,7 @@ _public_ int sd_event_add_exit( sd_event_handler_t callback, void *userdata) { - sd_event_source *s; + _cleanup_(source_freep) sd_event_source *s = NULL; int r; assert_return(e, -EINVAL); @@ -1520,13 +1273,12 @@ _public_ int sd_event_add_exit( s->enabled = SD_EVENT_ONESHOT; r = prioq_put(s->event->exit, s, &s->exit.prioq_index); - if (r < 0) { - source_free(s); + if (r < 0) return r; - } if (ret) *ret = s; + TAKE_PTR(s); return 0; } @@ -1623,38 +1375,27 @@ static int event_make_inotify_data( return 1; } -static int inode_data_compare(const void *a, const void *b) { - const struct inode_data *x = a, *y = b; +static int inode_data_compare(const struct inode_data *x, const struct inode_data *y) { + int r; assert(x); assert(y); - if (x->dev < y->dev) - return -1; - if (x->dev > y->dev) - return 1; - - if (x->ino < y->ino) - return -1; - if (x->ino > y->ino) - return 1; + r = CMP(x->dev, y->dev); + if (r != 0) + return r; - return 0; + return CMP(x->ino, y->ino); } -static void inode_data_hash_func(const void *p, struct siphash *state) { - const struct inode_data *d = p; - - assert(p); +static void inode_data_hash_func(const struct inode_data *d, struct siphash *state) { + assert(d); siphash24_compress(&d->dev, sizeof(d->dev), state); siphash24_compress(&d->ino, sizeof(d->ino), state); } -const struct hash_ops inode_data_hash_ops = { - .hash = inode_data_hash_func, - .compare = inode_data_compare -}; +DEFINE_PRIVATE_HASH_OPS(inode_data_hash_ops, struct inode_data, inode_data_hash_func, inode_data_compare); static void event_free_inode_data( sd_event *e, @@ -1781,7 +1522,7 @@ static uint32_t inode_data_determine_mask(struct inode_data *d) { * * Note that we add all sources to the mask here, regardless whether enabled, disabled or oneshot. That's * because we cannot change the mask anymore after the event source was created once, since the kernel has no - * API for that. Hence we need to subscribe to the maximum mask we ever might be interested in, and supress + * API for that. Hence we need to subscribe to the maximum mask we ever might be interested in, and suppress * events we don't care for client-side. */ LIST_FOREACH(inotify.by_inode_data, s, d->event_sources) { @@ -1843,11 +1584,10 @@ _public_ int sd_event_add_inotify( sd_event_inotify_handler_t callback, void *userdata) { - bool rm_inotify = false, rm_inode = false; struct inotify_data *inotify_data = NULL; struct inode_data *inode_data = NULL; _cleanup_close_ int fd = -1; - sd_event_source *s; + _cleanup_(source_freep) sd_event_source *s = NULL; struct stat st; int r; @@ -1885,13 +1625,13 @@ _public_ int sd_event_add_inotify( /* Allocate an inotify object for this priority, and an inode object within it */ r = event_make_inotify_data(e, SD_EVENT_PRIORITY_NORMAL, &inotify_data); if (r < 0) - goto fail; - rm_inotify = r > 0; + return r; r = event_make_inode_data(e, inotify_data, st.st_dev, st.st_ino, &inode_data); - if (r < 0) - goto fail; - rm_inode = r > 0; + if (r < 0) { + event_free_inotify_data(e, inotify_data); + return r; + } /* Keep the O_PATH fd around until the first iteration of the loop, so that we can still change the priority of * the event source, until then, for which we need the original inode. */ @@ -1904,72 +1644,45 @@ _public_ int sd_event_add_inotify( LIST_PREPEND(inotify.by_inode_data, inode_data->event_sources, s); s->inotify.inode_data = inode_data; - rm_inode = rm_inotify = false; - /* Actually realize the watch now */ r = inode_data_realize_watch(e, inode_data); if (r < 0) - goto fail; + return r; (void) sd_event_source_set_description(s, path); if (ret) *ret = s; + TAKE_PTR(s); return 0; - -fail: - source_free(s); - - if (rm_inode) - event_free_inode_data(e, inode_data); - - if (rm_inotify) - event_free_inotify_data(e, inotify_data); - - return r; } -_public_ sd_event_source* sd_event_source_ref(sd_event_source *s) { - +static sd_event_source* event_source_free(sd_event_source *s) { if (!s) return NULL; - assert(s->n_ref >= 1); - s->n_ref++; + /* Here's a special hack: when we are called from a + * dispatch handler we won't free the event source + * immediately, but we will detach the fd from the + * epoll. This way it is safe for the caller to unref + * the event source and immediately close the fd, but + * we still retain a valid event source object after + * the callback. */ - return s; -} - -_public_ sd_event_source* sd_event_source_unref(sd_event_source *s) { - - if (!s) - return NULL; - - assert(s->n_ref >= 1); - s->n_ref--; - - if (s->n_ref <= 0) { - /* Here's a special hack: when we are called from a - * dispatch handler we won't free the event source - * immediately, but we will detach the fd from the - * epoll. This way it is safe for the caller to unref - * the event source and immediately close the fd, but - * we still retain a valid event source object after - * the callback. */ - - if (s->dispatching) { - if (s->type == SOURCE_IO) - source_io_unregister(s); + if (s->dispatching) { + if (s->type == SOURCE_IO) + source_io_unregister(s); - source_disconnect(s); - } else - source_free(s); - } + source_disconnect(s); + } else + source_free(s); return NULL; } +DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_event_source, sd_event_source, event_source_free); + _public_ int sd_event_source_set_description(sd_event_source *s, const char *description) { assert_return(s, -EINVAL); assert_return(!event_pid_changed(s->event), -ECHILD); @@ -1980,9 +1693,11 @@ _public_ int sd_event_source_set_description(sd_event_source *s, const char *des _public_ int sd_event_source_get_description(sd_event_source *s, const char **description) { assert_return(s, -EINVAL); assert_return(description, -EINVAL); - assert_return(s->description, -ENXIO); assert_return(!event_pid_changed(s->event), -ECHILD); + if (!s->description) + return -ENXIO; + *description = s->description; return 0; } @@ -2234,11 +1949,11 @@ fail: _public_ int sd_event_source_get_enabled(sd_event_source *s, int *m) { assert_return(s, -EINVAL); - assert_return(m, -EINVAL); assert_return(!event_pid_changed(s->event), -ECHILD); - *m = s->enabled; - return 0; + if (m) + *m = s->enabled; + return s->enabled != SD_EVENT_OFF; } _public_ int sd_event_source_set_enabled(sd_event_source *s, int m) { @@ -3796,4 +3511,32 @@ _public_ int sd_event_source_get_destroy_callback(sd_event_source *s, sd_event_d return !!s->destroy_callback; } + +_public_ int sd_event_source_get_floating(sd_event_source *s) { + assert_return(s, -EINVAL); + + return s->floating; +} + +_public_ int sd_event_source_set_floating(sd_event_source *s, int b) { + assert_return(s, -EINVAL); + + if (s->floating == !!b) + return 0; + + if (!s->event) /* Already disconnected */ + return -ESTALE; + + s->floating = b; + + if (b) { + sd_event_source_ref(s); + sd_event_unref(s->event); + } else { + sd_event_ref(s->event); + sd_event_source_unref(s); + } + + return 1; +} #endif /* NM_IGNORED */ diff --git a/src/systemd/src/libsystemd/sd-id128/id128-util.c b/src/systemd/src/libsystemd/sd-id128/id128-util.c index d4d668e8..f8f0883c 100644 --- a/src/systemd/src/libsystemd/sd-id128/id128-util.c +++ b/src/systemd/src/libsystemd/sd-id128/id128-util.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" #include <errno.h> #include <fcntl.h> @@ -187,16 +187,13 @@ int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync) { return id128_write_fd(fd, f, id, do_sync); } -void id128_hash_func(const void *p, struct siphash *state) { - siphash24_compress(p, 16, state); +void id128_hash_func(const sd_id128_t *p, struct siphash *state) { + siphash24_compress(p, sizeof(sd_id128_t), state); } -int id128_compare_func(const void *a, const void *b) { +int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) { return memcmp(a, b, 16); } -const struct hash_ops id128_hash_ops = { - .hash = id128_hash_func, - .compare = id128_compare_func, -}; +DEFINE_HASH_OPS(id128_hash_ops, sd_id128_t, id128_hash_func, id128_compare_func); #endif /* NM_IGNORED */ diff --git a/src/systemd/src/libsystemd/sd-id128/id128-util.h b/src/systemd/src/libsystemd/sd-id128/id128-util.h index 44f159c0..65f14ab2 100644 --- a/src/systemd/src/libsystemd/sd-id128/id128-util.h +++ b/src/systemd/src/libsystemd/sd-id128/id128-util.h @@ -28,6 +28,6 @@ int id128_read(const char *p, Id128Format f, sd_id128_t *ret); int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync); int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync); -void id128_hash_func(const void *p, struct siphash *state); -int id128_compare_func(const void *a, const void *b) _pure_; +void id128_hash_func(const sd_id128_t *p, struct siphash *state); +int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) _pure_; extern const struct hash_ops id128_hash_ops; diff --git a/src/systemd/src/libsystemd/sd-id128/sd-id128.c b/src/systemd/src/libsystemd/sd-id128/sd-id128.c index 483b2fe0..a476017b 100644 --- a/src/systemd/src/libsystemd/sd-id128/sd-id128.c +++ b/src/systemd/src/libsystemd/sd-id128/sd-id128.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" #include <errno.h> #include <fcntl.h> @@ -21,7 +21,7 @@ #include "util.h" #if 0 /* NM_IGNORED */ -_public_ char *sd_id128_to_string(sd_id128_t id, char s[SD_ID128_STRING_MAX]) { +_public_ char *sd_id128_to_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_STRING_MAX]) { unsigned n; assert_return(s, NULL); @@ -277,7 +277,9 @@ _public_ int sd_id128_randomize(sd_id128_t *ret) { assert_return(ret, -EINVAL); - r = acquire_random_bytes(&t, sizeof t, true); + /* We allow usage if x86-64 RDRAND here. It might not be trusted enough for keeping secrets, but it should be + * fine for UUIDS. */ + r = genuine_random_bytes(&t, sizeof t, RANDOM_ALLOW_RDRAND); if (r < 0) return r; @@ -289,19 +291,15 @@ _public_ int sd_id128_randomize(sd_id128_t *ret) { return 0; } -_public_ int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret) { +static int get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret) { _cleanup_(khash_unrefp) khash *h = NULL; - sd_id128_t m, result; + sd_id128_t result; const void *p; int r; - assert_return(ret, -EINVAL); - - r = sd_id128_get_machine(&m); - if (r < 0) - return r; + assert(ret); - r = khash_new_with_key(&h, "hmac(sha256)", &m, sizeof(m)); + r = khash_new_with_key(&h, "hmac(sha256)", &base, sizeof(base)); if (r < 0) return r; @@ -319,4 +317,30 @@ _public_ int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *re *ret = make_v4_uuid(result); return 0; } + +_public_ int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret) { + sd_id128_t id; + int r; + + assert_return(ret, -EINVAL); + + r = sd_id128_get_machine(&id); + if (r < 0) + return r; + + return get_app_specific(id, app_id, ret); +} + +_public_ int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret) { + sd_id128_t id; + int r; + + assert_return(ret, -EINVAL); + + r = sd_id128_get_boot(&id); + if (r < 0) + return r; + + return get_app_specific(id, app_id, ret); +} #endif /* NM_IGNORED */ diff --git a/src/systemd/src/shared/dns-domain.c b/src/systemd/src/shared/dns-domain.c index 048075f2..ebea861f 100644 --- a/src/systemd/src/shared/dns-domain.c +++ b/src/systemd/src/shared/dns-domain.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -#include "nm-sd-adapt.h" +#include "nm-sd-adapt-core.h" #if 0 /* NM_IGNORED */ #if HAVE_LIBIDN2 @@ -21,6 +21,7 @@ #include "dns-domain.h" #include "hashmap.h" #include "hexdecoct.h" +#include "hostname-util.h" #include "in-addr-util.h" #include "macro.h" #include "parse-util.h" @@ -28,9 +29,9 @@ #include "strv.h" #include "utf8.h" -int dns_label_unescape(const char **name, char *dest, size_t sz) { +int dns_label_unescape(const char **name, char *dest, size_t sz, DNSLabelFlags flags) { const char *n; - char *d; + char *d, last_char = 0; int r = 0; assert(name); @@ -40,13 +41,15 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) { d = dest; for (;;) { - if (*n == '.') { - n++; - break; - } + if (*n == 0 || *n == '.') { + if (FLAGS_SET(flags, DNS_LABEL_LDH) && last_char == '-') + /* Trailing dash */ + return -EINVAL; - if (*n == 0) + if (*n == '.') + n++; break; + } if (r >= DNS_LABEL_MAX) return -EINVAL; @@ -56,6 +59,8 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) { if (*n == '\\') { /* Escaped character */ + if (FLAGS_SET(flags, DNS_LABEL_NO_ESCAPES)) + return -EINVAL; n++; @@ -66,6 +71,10 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) { else if (IN_SET(*n, '\\', '.')) { /* Escaped backslash or dot */ + if (FLAGS_SET(flags, DNS_LABEL_LDH)) + return -EINVAL; + + last_char = *n; if (d) *(d++) = *n; sz--; @@ -94,6 +103,11 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) { if (k > 255) return -EINVAL; + if (FLAGS_SET(flags, DNS_LABEL_LDH) && + !valid_ldh_char((char) k)) + return -EINVAL; + + last_char = (char) k; if (d) *(d++) = (char) k; sz--; @@ -107,6 +121,15 @@ int dns_label_unescape(const char **name, char *dest, size_t sz) { /* Normal character */ + if (FLAGS_SET(flags, DNS_LABEL_LDH)) { + if (!valid_ldh_char(*n)) + return -EINVAL; + if (r == 0 && *n == '-') + /* Leading dash */ + return -EINVAL; + } + + last_char = *n; if (d) *(d++) = *n; sz--; @@ -189,7 +212,7 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha terminal--; } - r = dns_label_unescape(&name, dest, sz); + r = dns_label_unescape(&name, dest, sz, 0); if (r < 0) return r; @@ -386,7 +409,7 @@ int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, #endif #endif /* NM_IGNORED */ -int dns_name_concat(const char *a, const char *b, char **_ret) { +int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **_ret) { _cleanup_free_ char *ret = NULL; size_t n = 0, allocated = 0; const char *p; @@ -403,7 +426,7 @@ int dns_name_concat(const char *a, const char *b, char **_ret) { for (;;) { char label[DNS_LABEL_MAX]; - r = dns_label_unescape(&p, label, sizeof(label)); + r = dns_label_unescape(&p, label, sizeof label, flags); if (r < 0) return r; if (r == 0) { @@ -469,8 +492,7 @@ finish: } #if 0 /* NM_IGNORED */ -void dns_name_hash_func(const void *s, struct siphash *state) { - const char *p = s; +void dns_name_hash_func(const char *p, struct siphash *state) { int r; assert(p); @@ -478,7 +500,7 @@ void dns_name_hash_func(const void *s, struct siphash *state) { for (;;) { char label[DNS_LABEL_MAX+1]; - r = dns_label_unescape(&p, label, sizeof(label)); + r = dns_label_unescape(&p, label, sizeof label, 0); if (r < 0) break; if (r == 0) @@ -493,15 +515,15 @@ void dns_name_hash_func(const void *s, struct siphash *state) { string_hash_func("", state); } -int dns_name_compare_func(const void *a, const void *b) { +int dns_name_compare_func(const char *a, const char *b) { const char *x, *y; int r, q; assert(a); assert(b); - x = (const char *) a + strlen(a); - y = (const char *) b + strlen(b); + x = a + strlen(a); + y = b + strlen(b); for (;;) { char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX]; @@ -520,10 +542,7 @@ int dns_name_compare_func(const void *a, const void *b) { } } -const struct hash_ops dns_name_hash_ops = { - .hash = dns_name_hash_func, - .compare = dns_name_compare_func -}; +DEFINE_HASH_OPS(dns_name_hash_ops, char, dns_name_hash_func, dns_name_compare_func); int dns_name_equal(const char *x, const char *y) { int r, q; @@ -534,11 +553,11 @@ int dns_name_equal(const char *x, const char *y) { for (;;) { char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX]; - r = dns_label_unescape(&x, la, sizeof(la)); + r = dns_label_unescape(&x, la, sizeof la, 0); if (r < 0) return r; - q = dns_label_unescape(&y, lb, sizeof(lb)); + q = dns_label_unescape(&y, lb, sizeof lb, 0); if (q < 0) return q; @@ -565,14 +584,14 @@ int dns_name_endswith(const char *name, const char *suffix) { for (;;) { char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX]; - r = dns_label_unescape(&n, ln, sizeof(ln)); + r = dns_label_unescape(&n, ln, sizeof ln, 0); if (r < 0) return r; if (!saved_n) saved_n = n; - q = dns_label_unescape(&s, ls, sizeof(ls)); + q = dns_label_unescape(&s, ls, sizeof ls, 0); if (q < 0) return q; @@ -603,13 +622,13 @@ int dns_name_startswith(const char *name, const char *prefix) { for (;;) { char ln[DNS_LABEL_MAX], lp[DNS_LABEL_MAX]; - r = dns_label_unescape(&p, lp, sizeof(lp)); + r = dns_label_unescape(&p, lp, sizeof lp, 0); if (r < 0) return r; if (r == 0) return true; - q = dns_label_unescape(&n, ln, sizeof(ln)); + q = dns_label_unescape(&n, ln, sizeof ln, 0); if (q < 0) return q; @@ -638,14 +657,14 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char if (!saved_before) saved_before = n; - r = dns_label_unescape(&n, ln, sizeof(ln)); + r = dns_label_unescape(&n, ln, sizeof ln, 0); if (r < 0) return r; if (!saved_after) saved_after = n; - q = dns_label_unescape(&s, ls, sizeof(ls)); + q = dns_label_unescape(&s, ls, sizeof ls, 0); if (q < 0) return q; @@ -668,7 +687,7 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char /* Found it! Now generate the new name */ prefix = strndupa(name, saved_before - name); - r = dns_name_concat(prefix, new_suffix, ret); + r = dns_name_concat(prefix, new_suffix, 0, ret); if (r < 0) return r; @@ -746,7 +765,7 @@ int dns_name_address(const char *p, int *family, union in_addr_union *address) { for (i = 0; i < ELEMENTSOF(a); i++) { char label[DNS_LABEL_MAX+1]; - r = dns_label_unescape(&p, label, sizeof(label)); + r = dns_label_unescape(&p, label, sizeof label, 0); if (r < 0) return r; if (r == 0) @@ -783,7 +802,7 @@ int dns_name_address(const char *p, int *family, union in_addr_union *address) { char label[DNS_LABEL_MAX+1]; int x, y; - r = dns_label_unescape(&p, label, sizeof(label)); + r = dns_label_unescape(&p, label, sizeof label, 0); if (r <= 0) return r; if (r != 1) @@ -792,7 +811,7 @@ int dns_name_address(const char *p, int *family, union in_addr_union *address) { if (x < 0) return -EINVAL; - r = dns_label_unescape(&p, label, sizeof(label)); + r = dns_label_unescape(&p, label, sizeof label, 0); if (r <= 0) return r; if (r != 1) @@ -861,7 +880,7 @@ int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len, boo * dns_label_unescape() returns 0 when it hits the end * of the domain name, which we rely on here to encode * the trailing NUL byte. */ - r = dns_label_unescape(&domain, (char *) out, len); + r = dns_label_unescape(&domain, (char *) out, len, 0); if (r < 0) return r; @@ -929,7 +948,7 @@ bool dns_srv_type_is_valid(const char *name) { /* This more or less implements RFC 6335, Section 5.1 */ - r = dns_label_unescape(&name, label, sizeof(label)); + r = dns_label_unescape(&name, label, sizeof label, 0); if (r < 0) return false; if (r == 0) @@ -989,7 +1008,7 @@ int dns_service_join(const char *name, const char *type, const char *domain, cha return -EINVAL; if (!name) - return dns_name_concat(type, domain, ret); + return dns_name_concat(type, domain, 0, ret); if (!dns_service_name_is_valid(name)) return -EINVAL; @@ -998,11 +1017,11 @@ int dns_service_join(const char *name, const char *type, const char *domain, cha if (r < 0) return r; - r = dns_name_concat(type, domain, &n); + r = dns_name_concat(type, domain, 0, &n); if (r < 0) return r; - return dns_name_concat(escaped, n, ret); + return dns_name_concat(escaped, n, 0, ret); } static bool dns_service_name_label_is_valid(const char *label, size_t n) { @@ -1027,7 +1046,7 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do assert(joined); /* Get first label from the full name */ - an = dns_label_unescape(&p, a, sizeof(a)); + an = dns_label_unescape(&p, a, sizeof(a), 0); if (an < 0) return an; @@ -1035,7 +1054,7 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do x++; /* If there was a first label, try to get the second one */ - bn = dns_label_unescape(&p, b, sizeof(b)); + bn = dns_label_unescape(&p, b, sizeof(b), 0); if (bn < 0) return bn; @@ -1044,7 +1063,7 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do /* If there was a second label, try to get the third one */ q = p; - cn = dns_label_unescape(&p, c, sizeof(c)); + cn = dns_label_unescape(&p, c, sizeof(c), 0); if (cn < 0) return cn; @@ -1094,7 +1113,7 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do d = joined; finish: - r = dns_name_normalize(d, &domain); + r = dns_name_normalize(d, 0, &domain); if (r < 0) return r; @@ -1110,7 +1129,7 @@ finish: return 0; } -static int dns_name_build_suffix_table(const char *name, const char*table[]) { +static int dns_name_build_suffix_table(const char *name, const char *table[]) { const char *p; unsigned n = 0; int r; @@ -1239,12 +1258,12 @@ int dns_name_common_suffix(const char *a, const char *b, const char **ret) { } x = a_labels[n - 1 - k]; - r = dns_label_unescape(&x, la, sizeof(la)); + r = dns_label_unescape(&x, la, sizeof la, 0); if (r < 0) return r; y = b_labels[m - 1 - k]; - q = dns_label_unescape(&y, lb, sizeof(lb)); + q = dns_label_unescape(&y, lb, sizeof lb, 0); if (q < 0) return q; @@ -1295,7 +1314,7 @@ int dns_name_apply_idna(const char *name, char **ret) { log_debug("idn2_lookup_u8(\"%s\") failed: %d/%s", name, r, idn2_strerror(r)); if (r == IDN2_2HYPHEN) - /* The name has two hypens — forbidden by IDNA2008 in some cases */ + /* The name has two hyphens — forbidden by IDNA2008 in some cases */ return 0; if (IN_SET(r, IDN2_TOO_BIG_DOMAIN, IDN2_TOO_BIG_LABEL)) return -ENOSPC; @@ -1312,13 +1331,13 @@ int dns_name_apply_idna(const char *name, char **ret) { for (;;) { char label[DNS_LABEL_MAX]; - r = dns_label_unescape(&name, label, sizeof(label)); + r = dns_label_unescape(&name, label, sizeof label, 0); if (r < 0) return r; if (r == 0) break; - q = dns_label_apply_idna(label, r, label, sizeof(label)); + q = dns_label_apply_idna(label, r, label, sizeof label); if (q < 0) return q; if (q > 0) diff --git a/src/systemd/src/shared/dns-domain.h b/src/systemd/src/shared/dns-domain.h index 95f4069c..88b3eb11 100644 --- a/src/systemd/src/shared/dns-domain.h +++ b/src/systemd/src/shared/dns-domain.h @@ -24,13 +24,18 @@ /* Maximum number of labels per valid hostname */ #define DNS_N_LABELS_MAX 127 -int dns_label_unescape(const char **name, char *dest, size_t sz); +typedef enum DNSLabelFlags { + DNS_LABEL_LDH = 1 << 0, /* Follow the "LDH" rule — only letters, digits, and internal hyphens. */ + DNS_LABEL_NO_ESCAPES = 1 << 1, /* Do not treat backslashes specially */ +} DNSLabelFlags; + +int dns_label_unescape(const char **name, char *dest, size_t sz, DNSLabelFlags flags); int dns_label_unescape_suffix(const char *name, const char **label_end, char *dest, size_t sz); int dns_label_escape(const char *p, size_t l, char *dest, size_t sz); int dns_label_escape_new(const char *p, size_t l, char **ret); static inline int dns_name_parent(const char **name) { - return dns_label_unescape(name, NULL, DNS_LABEL_MAX); + return dns_label_unescape(name, NULL, DNS_LABEL_MAX, 0); } #if 0 /* NM_IGNORED */ @@ -40,18 +45,29 @@ int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, #endif #endif /* NM_IGNORED */ -int dns_name_concat(const char *a, const char *b, char **ret); +int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **ret); -static inline int dns_name_normalize(const char *s, char **ret) { +static inline int dns_name_normalize(const char *s, DNSLabelFlags flags, char **ret) { /* dns_name_concat() normalizes as a side-effect */ - return dns_name_concat(s, NULL, ret); + return dns_name_concat(s, NULL, flags, ret); } static inline int dns_name_is_valid(const char *s) { int r; /* dns_name_normalize() verifies as a side effect */ - r = dns_name_normalize(s, NULL); + r = dns_name_normalize(s, 0, NULL); + if (r == -EINVAL) + return 0; + if (r < 0) + return r; + return 1; +} + +static inline int dns_name_is_valid_ldh(const char *s) { + int r; + + r = dns_name_concat(s, NULL, DNS_LABEL_LDH|DNS_LABEL_NO_ESCAPES, NULL); if (r == -EINVAL) return 0; if (r < 0) @@ -59,8 +75,8 @@ static inline int dns_name_is_valid(const char *s) { return 1; } -void dns_name_hash_func(const void *s, struct siphash *state); -int dns_name_compare_func(const void *a, const void *b); +void dns_name_hash_func(const char *s, struct siphash *state); +int dns_name_compare_func(const char *a, const char *b); extern const struct hash_ops dns_name_hash_ops; int dns_name_between(const char *a, const char *b, const char *c); diff --git a/src/systemd/src/systemd/_sd-common.h b/src/systemd/src/systemd/_sd-common.h index 4742b2e7..b3ee7bbc 100644 --- a/src/systemd/src/systemd/_sd-common.h +++ b/src/systemd/src/systemd/_sd-common.h @@ -23,24 +23,26 @@ # error "Do not include _sd-common.h directly; it is a private header." #endif +typedef void (*_sd_destroy_t)(void *userdata); + #ifndef _sd_printf_ # if __GNUC__ >= 4 -# define _sd_printf_(a,b) __attribute__ ((format (printf, a, b))) +# define _sd_printf_(a,b) __attribute__((__format__(printf, a, b))) # else # define _sd_printf_(a,b) # endif #endif #ifndef _sd_sentinel_ -# define _sd_sentinel_ __attribute__((sentinel)) +# define _sd_sentinel_ __attribute__((__sentinel__)) #endif #ifndef _sd_packed_ -# define _sd_packed_ __attribute__((packed)) +# define _sd_packed_ __attribute__((__packed__)) #endif #ifndef _sd_pure_ -# define _sd_pure_ __attribute__((pure)) +# define _sd_pure_ __attribute__((__pure__)) #endif #ifndef _SD_STRINGIFY @@ -70,6 +72,14 @@ # endif #endif +#ifndef _SD_ARRAY_STATIC +# if __STDC_VERSION__ >= 199901L +# define _SD_ARRAY_STATIC static +# else +# define _SD_ARRAY_STATIC +# endif +#endif + #define _SD_DEFINE_POINTER_CLEANUP_FUNC(type, func) \ static __inline__ void func##p(type **p) { \ if (*p) \ diff --git a/src/systemd/src/systemd/sd-dhcp-client.h b/src/systemd/src/systemd/sd-dhcp-client.h index e3885520..bd0d429d 100644 --- a/src/systemd/src/systemd/sd-dhcp-client.h +++ b/src/systemd/src/systemd/sd-dhcp-client.h @@ -23,6 +23,7 @@ #include <net/ethernet.h> #include <netinet/in.h> #include <sys/types.h> +#include <stdbool.h> #include "sd-dhcp-lease.h" #include "sd-event.h" @@ -127,12 +128,14 @@ int sd_dhcp_client_set_client_id( size_t data_len); int sd_dhcp_client_set_iaid_duid( sd_dhcp_client *client, + bool iaid_set, uint32_t iaid, uint16_t duid_type, const void *duid, size_t duid_len); int sd_dhcp_client_set_iaid_duid_llt( sd_dhcp_client *client, + bool iaid_set, uint32_t iaid, uint64_t llt_time); int sd_dhcp_client_set_duid( diff --git a/src/systemd/src/systemd/sd-dhcp-lease.h b/src/systemd/src/systemd/sd-dhcp-lease.h index 2a60145f..4875f105 100644 --- a/src/systemd/src/systemd/sd-dhcp-lease.h +++ b/src/systemd/src/systemd/sd-dhcp-lease.h @@ -57,6 +57,7 @@ int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **timezone); int sd_dhcp_route_get_destination(sd_dhcp_route *route, struct in_addr *destination); int sd_dhcp_route_get_destination_prefix_length(sd_dhcp_route *route, uint8_t *length); int sd_dhcp_route_get_gateway(sd_dhcp_route *route, struct in_addr *gateway); +int sd_dhcp_route_get_option(sd_dhcp_route *route); _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_lease, sd_dhcp_lease_unref); diff --git a/src/systemd/src/systemd/sd-dhcp6-client.h b/src/systemd/src/systemd/sd-dhcp6-client.h index fa36dca9..43d38f5c 100644 --- a/src/systemd/src/systemd/sd-dhcp6-client.h +++ b/src/systemd/src/systemd/sd-dhcp6-client.h @@ -21,7 +21,6 @@ #include <inttypes.h> #include <net/ethernet.h> -#include <stdbool.h> #include <sys/types.h> #include "sd-dhcp6-lease.h" @@ -120,8 +119,15 @@ int sd_dhcp6_client_get_information_request( int sd_dhcp6_client_set_request_option( sd_dhcp6_client *client, uint16_t option); +int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, + int *delegation); int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, - bool delegation); + int delegation); +int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client, + int *request); +int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, + int request); +int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id); int sd_dhcp6_client_get_lease( sd_dhcp6_client *client, diff --git a/src/systemd/src/systemd/sd-event.h b/src/systemd/src/systemd/sd-event.h index eb35b834..787a12f2 100644 --- a/src/systemd/src/systemd/sd-event.h +++ b/src/systemd/src/systemd/sd-event.h @@ -33,7 +33,8 @@ - Supports event source prioritization - Scales better with a large number of time events because it does not require one timerfd each - Automatically tries to coalesce timer events system-wide - - Handles signals and child PIDs + - Handles signals, child PIDs, inotify events + - Supports systemd-style automatic watchdog event generation */ _SD_BEGIN_DECLARATIONS; @@ -76,7 +77,7 @@ typedef int (*sd_event_child_handler_t)(sd_event_source *s, const siginfo_t *si, typedef void* sd_event_child_handler_t; #endif typedef int (*sd_event_inotify_handler_t)(sd_event_source *s, const struct inotify_event *event, void *userdata); -typedef void (*sd_event_destroy_t)(void *userdata); +typedef _sd_destroy_t sd_event_destroy_t; int sd_event_default(sd_event **e); @@ -142,6 +143,8 @@ int sd_event_source_get_child_pid(sd_event_source *s, pid_t *pid); int sd_event_source_get_inotify_mask(sd_event_source *s, uint32_t *ret); int sd_event_source_set_destroy_callback(sd_event_source *s, sd_event_destroy_t callback); int sd_event_source_get_destroy_callback(sd_event_source *s, sd_event_destroy_t *ret); +int sd_event_source_get_floating(sd_event_source *s); +int sd_event_source_set_floating(sd_event_source *s, int b); /* Define helpers so that __attribute__((cleanup(sd_event_unrefp))) and similar may be used. */ _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_event, sd_event_unref); diff --git a/src/systemd/src/systemd/sd-id128.h b/src/systemd/src/systemd/sd-id128.h index 143a0ffb..bdf88ed5 100644 --- a/src/systemd/src/systemd/sd-id128.h +++ b/src/systemd/src/systemd/sd-id128.h @@ -35,24 +35,26 @@ union sd_id128 { #define SD_ID128_STRING_MAX 33 -char *sd_id128_to_string(sd_id128_t id, char s[SD_ID128_STRING_MAX]); +char *sd_id128_to_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_STRING_MAX]); int sd_id128_from_string(const char *s, sd_id128_t *ret); int sd_id128_randomize(sd_id128_t *ret); int sd_id128_get_machine(sd_id128_t *ret); -int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret); int sd_id128_get_boot(sd_id128_t *ret); int sd_id128_get_invocation(sd_id128_t *ret); -#define SD_ID128_MAKE(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \ - ((const sd_id128_t) { .bytes = { 0x##v0, 0x##v1, 0x##v2, 0x##v3, 0x##v4, 0x##v5, 0x##v6, 0x##v7, \ - 0x##v8, 0x##v9, 0x##v10, 0x##v11, 0x##v12, 0x##v13, 0x##v14, 0x##v15 }}) +int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret); +int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret); #define SD_ID128_ARRAY(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \ { .bytes = { 0x##v0, 0x##v1, 0x##v2, 0x##v3, 0x##v4, 0x##v5, 0x##v6, 0x##v7, \ 0x##v8, 0x##v9, 0x##v10, 0x##v11, 0x##v12, 0x##v13, 0x##v14, 0x##v15 }} +#define SD_ID128_MAKE(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \ + ((const sd_id128_t) SD_ID128_ARRAY(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15)) + + /* Note that SD_ID128_FORMAT_VAL will evaluate the passed argument 16 * times. It is hence not a good idea to call this macro with an * expensive function as parameter or an expression with side @@ -108,7 +110,12 @@ _sd_pure_ static __inline__ int sd_id128_is_null(sd_id128_t a) { return a.qwords[0] == 0 && a.qwords[1] == 0; } +_sd_pure_ static __inline__ int sd_id128_is_allf(sd_id128_t a) { + return a.qwords[0] == UINT64_C(0xFFFFFFFFFFFFFFFF) && a.qwords[1] == UINT64_C(0xFFFFFFFFFFFFFFFF); +} + #define SD_ID128_NULL ((const sd_id128_t) { .qwords = { 0, 0 }}) +#define SD_ID128_ALLF ((const sd_id128_t) { .qwords = { UINT64_C(0xFFFFFFFFFFFFFFFF), UINT64_C(0xFFFFFFFFFFFFFFFF) }}) _SD_END_DECLARATIONS; diff --git a/src/systemd/src/systemd/sd-lldp.h b/src/systemd/src/systemd/sd-lldp.h index d650794c..bf3afadc 100644 --- a/src/systemd/src/systemd/sd-lldp.h +++ b/src/systemd/src/systemd/sd-lldp.h @@ -27,7 +27,7 @@ _SD_BEGIN_DECLARATIONS; -/* IEEE 802.3AB Clause 9: TLV Types */ +/* IEEE 802.1AB-2009 Clause 8: TLV Types */ enum { SD_LLDP_TYPE_END = 0, SD_LLDP_TYPE_CHASSIS_ID = 1, @@ -41,7 +41,7 @@ enum { SD_LLDP_TYPE_PRIVATE = 127, }; -/* IEEE 802.3AB Clause 9.5.2: Chassis subtypes */ +/* IEEE 802.1AB-2009 Clause 8.5.2: Chassis subtypes */ enum { SD_LLDP_CHASSIS_SUBTYPE_RESERVED = 0, SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT = 1, @@ -53,7 +53,7 @@ enum { SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED = 7, }; -/* IEEE 802.3AB Clause 9.5.3: Port subtype */ +/* IEEE 802.1AB-2009 Clause 8.5.3: Port subtype */ enum { SD_LLDP_PORT_SUBTYPE_RESERVED = 0, SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS = 1, @@ -65,6 +65,7 @@ enum { SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED = 7, }; +/* IEEE 802.1AB-2009 Clause 8.5.8: System capabilities */ enum { SD_LLDP_SYSTEM_CAPABILITIES_OTHER = 1 << 0, SD_LLDP_SYSTEM_CAPABILITIES_REPEATER = 1 << 1, @@ -95,6 +96,7 @@ enum { #define SD_LLDP_OUI_802_1 (uint8_t[]) { 0x00, 0x80, 0xc2 } #define SD_LLDP_OUI_802_3 (uint8_t[]) { 0x00, 0x12, 0x0f } +/* IEEE 802.1AB-2009 Annex E */ enum { SD_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID = 1, SD_LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID = 2, @@ -105,14 +107,24 @@ enum { SD_LLDP_OUI_802_1_SUBTYPE_LINK_AGGREGATION = 7, }; +/* IEEE 802.1AB-2009 Annex F */ +enum { + SD_LLDP_OUI_802_3_SUBTYPE_MAC_PHY_CONFIG_STATUS = 1, + SD_LLDP_OUI_802_3_SUBTYPE_POWER_VIA_MDI = 2, + SD_LLDP_OUI_802_3_SUBTYPE_LINK_AGGREGATION = 3, + SD_LLDP_OUI_802_3_SUBTYPE_MAXIMUM_FRAME_SIZE = 4, +}; + typedef struct sd_lldp sd_lldp; typedef struct sd_lldp_neighbor sd_lldp_neighbor; typedef enum sd_lldp_event { - SD_LLDP_EVENT_ADDED = 'a', - SD_LLDP_EVENT_REMOVED = 'r', - SD_LLDP_EVENT_UPDATED = 'u', - SD_LLDP_EVENT_REFRESHED = 'f', + SD_LLDP_EVENT_ADDED, + SD_LLDP_EVENT_REMOVED, + SD_LLDP_EVENT_UPDATED, + SD_LLDP_EVENT_REFRESHED, + _SD_LLDP_EVENT_MAX, + _SD_LLDP_EVENT_INVALID = -1, } sd_lldp_event; typedef void (*sd_lldp_callback_t)(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n, void *userdata); @@ -166,8 +178,8 @@ int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n); int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n); int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type); int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type); -int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[3], uint8_t *subtype); -int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[3], uint8_t subtype); +int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t *subtype); +int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t subtype); int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size); _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp, sd_lldp_unref); diff --git a/src/systemd/src/systemd/sd-ndisc.h b/src/systemd/src/systemd/sd-ndisc.h index 6b6249ca..d1bee343 100644 --- a/src/systemd/src/systemd/sd-ndisc.h +++ b/src/systemd/src/systemd/sd-ndisc.h @@ -55,8 +55,10 @@ typedef struct sd_ndisc sd_ndisc; typedef struct sd_ndisc_router sd_ndisc_router; typedef enum sd_ndisc_event { - SD_NDISC_EVENT_TIMEOUT = 't', - SD_NDISC_EVENT_ROUTER = 'r', + SD_NDISC_EVENT_TIMEOUT, + SD_NDISC_EVENT_ROUTER, + _SD_NDISC_EVENT_MAX, + _SD_NDISC_EVENT_INVALID = -1, } sd_ndisc_event; typedef void (*sd_ndisc_callback_t)(sd_ndisc *nd, sd_ndisc_event event, sd_ndisc_router *rt, void *userdata); diff --git a/src/tests/config/NetworkManager-warn.conf b/src/tests/config/NetworkManager-warn.conf new file mode 100644 index 00000000..80df7c52 --- /dev/null +++ b/src/tests/config/NetworkManager-warn.conf @@ -0,0 +1,26 @@ +[main] +dhcp=dhclient +plugin=foo,bar,baz +no-auto-default=11:11:11:11:11:11 +rc-managed=unmanaged +dns=none + +[logging] +level=INFO + +[connectivity] +uri=http://example.com +interval=100 +response=Hello +audit=true + +[connection] +ipv4.route-metric=50 +ipv4.addresses=1.2.3.4 +ipv4.dad-timeout=100 + +[connection-wifi] +match-device=type:wifi +wifi.powersave=2 +ipv6.ip6-privacy=1 +wifi.tx-power=99 diff --git a/src/tests/config/NetworkManager.conf b/src/tests/config/NetworkManager.conf index da7b1fd4..a447b6d6 100644 --- a/src/tests/config/NetworkManager.conf +++ b/src/tests/config/NetworkManager.conf @@ -19,8 +19,8 @@ extra-key=some value [connection] ipv4.route-metric=50 ipv6.ip6_privacy=0 -dummy.test1=no -dummy.test2=no +ethernet.mtu=1400 +ipv4.dns-priority=60 ord.key00=A-0.0.00 ord.key01=A-0.0.01 @@ -37,7 +37,7 @@ ord.key09=A-0.0.09 match-device=mac:00:00:00:00:00:51 stop-match=yes ipv4.route-metric=51 -dummy.test1=yes +ethernet.mtu=9000 [connection.dev52] match-device=mac:00:00:00:00:00:52 diff --git a/src/tests/config/meson.build b/src/tests/config/meson.build index fd6c89b7..dc0a022c 100644 --- a/src/tests/config/meson.build +++ b/src/tests/config/meson.build @@ -2,7 +2,7 @@ test_unit = 'test-config' sources = files( 'nm-test-device.c', - 'test-config.c' + 'test-config.c', ) test_config_dir = meson.current_source_dir() @@ -16,5 +16,5 @@ exe = executable( test( 'config/' + test_unit, test_script, - args: test_args + [exe.full_path()] + args: test_args + [exe.full_path()], ) diff --git a/src/tests/config/test-config.c b/src/tests/config/test-config.c index 75fef4fa..10084b7f 100644 --- a/src/tests/config/test-config.c +++ b/src/tests/config/test-config.c @@ -25,6 +25,7 @@ #include "nm-config.h" #include "nm-test-device.h" #include "platform/nm-fake-platform.h" +#include "dhcp/nm-dhcp-manager.h" #include "nm-dbus-manager.h" #include "nm-connectivity.h" @@ -123,6 +124,24 @@ setup_config (GError **error, const char *config_file, const char *intern_config g_assert_no_error (local_error); } nm_config_cmd_line_options_free (cli); + + if (config) { + NMDhcpManager *dhcp_manager; + gpointer logging_old_state; + + logging_old_state = nmtst_logging_disable (FALSE); + + dhcp_manager = nm_dhcp_manager_get (); + g_test_assert_expected_messages (); + + nmtst_logging_reenable (logging_old_state); + + g_object_set_data_full (G_OBJECT (config), + "nmtst-config-keep-dhcp-manager-alive", + dhcp_manager, + nmtst_dhcp_manager_unget); + } + return config; } @@ -187,20 +206,20 @@ test_config_simple (void) g_assert_cmpstr (value, ==, "52"); g_free (value); - value = nm_config_data_get_connection_default (nm_config_get_data_orig (config), "dummy.test1", dev51); - g_assert_cmpstr (value, ==, "yes"); + value = nm_config_data_get_connection_default (nm_config_get_data_orig (config), "ethernet.mtu", dev51); + g_assert_cmpstr (value, ==, "9000"); g_free (value); - value = nm_config_data_get_connection_default (nm_config_get_data_orig (config), "dummy.test1", dev50); - g_assert_cmpstr (value, ==, "no"); + value = nm_config_data_get_connection_default (nm_config_get_data_orig (config), "ethernet.mtu", dev50); + g_assert_cmpstr (value, ==, "1400"); g_free (value); - value = nm_config_data_get_connection_default (nm_config_get_data_orig (config), "dummy.test2", dev51); + value = nm_config_data_get_connection_default (nm_config_get_data_orig (config), "ipv4.dns-priority", dev51); g_assert_cmpstr (value, ==, NULL); g_free (value); - value = nm_config_data_get_connection_default (nm_config_get_data_orig (config), "dummy.test2", dev50); - g_assert_cmpstr (value, ==, "no"); + value = nm_config_data_get_connection_default (nm_config_get_data_orig (config), "ipv4.dns-priority", dev50); + g_assert_cmpstr (value, ==, "60"); g_free (value); } @@ -487,17 +506,17 @@ test_config_confdir (void) gs_free char *_value = nm_config_data_get_connection_default (nm_config_get_data_orig (xconfig), (xname), NULL); \ g_assert_cmpstr (_value, ==, (xvalue)); \ } G_STMT_END - ASSERT_GET_CONN_DEFAULT (config, "ord.key00", "A-0.0.00"); - ASSERT_GET_CONN_DEFAULT (config, "ord.key01", "A-0.3.01"); - ASSERT_GET_CONN_DEFAULT (config, "ord.key02", "A-0.2.02"); - ASSERT_GET_CONN_DEFAULT (config, "ord.key03", "A-0.1.03"); - ASSERT_GET_CONN_DEFAULT (config, "ord.key04", "B-1.3.04"); - ASSERT_GET_CONN_DEFAULT (config, "ord.key05", "B-1.2.05"); - ASSERT_GET_CONN_DEFAULT (config, "ord.key06", "B-1.1.06"); - ASSERT_GET_CONN_DEFAULT (config, "ord.key07", "C-2.3.07"); - ASSERT_GET_CONN_DEFAULT (config, "ord.key08", "C-2.2.08"); - ASSERT_GET_CONN_DEFAULT (config, "ord.key09", "C-2.1.09"); - ASSERT_GET_CONN_DEFAULT (config, "ord.ovw01", "C-0.1.ovw01"); + ASSERT_GET_CONN_DEFAULT (config, NM_CON_DEFAULT ("ord.key00"), "A-0.0.00"); + ASSERT_GET_CONN_DEFAULT (config, NM_CON_DEFAULT ("ord.key01"), "A-0.3.01"); + ASSERT_GET_CONN_DEFAULT (config, NM_CON_DEFAULT ("ord.key02"), "A-0.2.02"); + ASSERT_GET_CONN_DEFAULT (config, NM_CON_DEFAULT ("ord.key03"), "A-0.1.03"); + ASSERT_GET_CONN_DEFAULT (config, NM_CON_DEFAULT ("ord.key04"), "B-1.3.04"); + ASSERT_GET_CONN_DEFAULT (config, NM_CON_DEFAULT ("ord.key05"), "B-1.2.05"); + ASSERT_GET_CONN_DEFAULT (config, NM_CON_DEFAULT ("ord.key06"), "B-1.1.06"); + ASSERT_GET_CONN_DEFAULT (config, NM_CON_DEFAULT ("ord.key07"), "C-2.3.07"); + ASSERT_GET_CONN_DEFAULT (config, NM_CON_DEFAULT ("ord.key08"), "C-2.2.08"); + ASSERT_GET_CONN_DEFAULT (config, NM_CON_DEFAULT ("ord.key09"), "C-2.1.09"); + ASSERT_GET_CONN_DEFAULT (config, NM_CON_DEFAULT ("ord.ovw01"), "C-0.1.ovw01"); value = nm_config_data_get_value (nm_config_get_data_orig (config), NM_CONFIG_KEYFILE_GROUPPREFIX_TEST_APPEND_STRINGLIST".1", "val1", NM_CONFIG_GET_VALUE_NONE); g_assert_cmpstr (value, ==, "a,c"); @@ -533,6 +552,36 @@ test_config_confdir_parse_error (void) g_clear_error (&error); } +static void +test_config_warnings (void) +{ + gs_unref_object NMConfig *config = NULL; + const char *const *warnings; + + config = setup_config (NULL, TEST_DIR "/NetworkManager-warn.conf", "", NULL, "/no/such/dir", "", NULL); + + warnings = nm_config_get_warnings (config); + +#define check_warning(str, group, key) \ + { \ + gs_free char *expected = NULL; \ + \ + expected = g_strdup_printf ("unknown key '%s' in section [%s] of file '" TEST_DIR "/NetworkManager-warn.conf'", \ + key, group); \ + g_assert_cmpstr (str, ==, expected); \ + } + + g_assert (warnings); + g_assert_cmpint (g_strv_length ((char **) warnings), ==, 5); + check_warning (warnings[0], "main", "plugin"); + check_warning (warnings[1], "main", "rc-managed"); + check_warning (warnings[2], "connectivity", "audit"); + check_warning (warnings[3], "connection-wifi", "wifi.tx-power"); + check_warning (warnings[4], "connection", "ipv4.addresses"); + +#undef check_warning +} + /*****************************************************************************/ typedef void (*TestSetValuesUserSetFcn) (NMConfig *config, gboolean is_user, GKeyFile *keyfile_user, NMConfigChangeFlags *out_expected_changes); @@ -603,7 +652,7 @@ _set_values_user (NMConfig *config, else NMTST_EXPECT_NM_INFO ("config: signal: SIGHUP (no changes from disk)*"); - nm_config_reload (config, NM_CONFIG_CHANGE_CAUSE_SIGHUP); + nm_config_reload (config, NM_CONFIG_CHANGE_CAUSE_SIGHUP, FALSE); g_test_assert_expected_messages (); @@ -907,15 +956,15 @@ test_config_signal (void) expected = NM_CONFIG_CHANGE_CAUSE_SIGUSR1; NMTST_EXPECT_NM_INFO ("config: signal: SIGUSR1"); - nm_config_reload (config, expected); + nm_config_reload (config, expected, FALSE); expected = NM_CONFIG_CHANGE_CAUSE_SIGUSR2; NMTST_EXPECT_NM_INFO ("config: signal: SIGUSR2"); - nm_config_reload (config, expected); + nm_config_reload (config, expected, FALSE); expected = NM_CONFIG_CHANGE_CAUSE_SIGHUP; NMTST_EXPECT_NM_INFO ("config: signal: SIGHUP (no changes from disk)*"); - nm_config_reload (config, expected); + nm_config_reload (config, expected, FALSE); /* test with subscribing two signals... * @@ -927,7 +976,7 @@ test_config_signal (void) &expected); expected = NM_CONFIG_CHANGE_CAUSE_SIGUSR2; NMTST_EXPECT_NM_INFO ("config: signal: SIGUSR2"); - nm_config_reload (config, NM_CONFIG_CHANGE_CAUSE_SIGUSR2); + nm_config_reload (config, NM_CONFIG_CHANGE_CAUSE_SIGUSR2, FALSE); g_signal_handlers_disconnect_by_func (config, _test_signal_config_changed_cb2, &expected); g_signal_handlers_disconnect_by_func (config, _test_signal_config_changed_cb, &expected); @@ -1045,6 +1094,7 @@ main (int argc, char **argv) g_test_add_func ("/config/no-auto-default", test_config_no_auto_default); g_test_add_func ("/config/confdir", test_config_confdir); g_test_add_func ("/config/confdir-parse-error", test_config_confdir_parse_error); + g_test_add_func ("/config/warnings", test_config_warnings); g_test_add_func ("/config/set-values", test_config_set_values); g_test_add_func ("/config/global-dns", test_config_global_dns); diff --git a/src/tests/meson.build b/src/tests/meson.build index 430ffe43..9c51e8d4 100644 --- a/src/tests/meson.build +++ b/src/tests/meson.build @@ -7,20 +7,20 @@ test_units = [ 'test-ip6-config', 'test-dcb', 'test-wired-defname', - 'test-utils' + 'test-utils', ] foreach test_unit: test_units exe = executable( test_unit, test_unit + '.c', - dependencies: test_nm_dep + dependencies: test_nm_dep, ) test( 'src/' + test_unit, test_script, - args: test_args + [exe.full_path()] + args: test_args + [exe.full_path()], ) endforeach @@ -37,11 +37,14 @@ exe = executable( include_directories: src_inc, dependencies: nm_core_dep, c_args: cflags, - link_with: libsystemd_nm + link_with: [ + libnm_systemd_core, + libnm_systemd_shared, + ], ) test( 'src/' + test_unit, test_script, - args: test_args + [exe.full_path()] + args: test_args + [exe.full_path()], ) diff --git a/src/tests/test-general.c b/src/tests/test-general.c index 1e7329d1..f065d127 100644 --- a/src/tests/test-general.c +++ b/src/tests/test-general.c @@ -22,12 +22,19 @@ #include <string.h> #include <errno.h> +#include <net/if.h> +#include <byteswap.h> /* need math.h for isinf() and INFINITY. No need to link with -lm */ #include <math.h> #include "NetworkManagerUtils.h" #include "nm-core-internal.h" +#include "nm-core-utils.h" +#include "systemd/nm-sd-utils-core.h" + +#include "dns/nm-dns-manager.h" +#include "nm-connectivity.h" #include "nm-test-utils-core.h" @@ -345,13 +352,13 @@ _match_connection (GSList *connections, gint64 default_v4_metric, gint64 default_v6_metric) { - NMConnection **list; + gs_free NMConnection **list = NULL; guint i, len; len = g_slist_length (connections); g_assert (len < 10); - list = g_alloca ((len + 1) * sizeof (NMConnection *)); + list = g_malloc ((len + 1) * sizeof (NMConnection *)); for (i = 0; i < len; i++, connections = connections->next) { g_assert (connections); g_assert (connections->data); @@ -1093,7 +1100,7 @@ static NMMatchSpecMatchType _test_match_spec_device (const GSList *specs, const char *match_str) { if (match_str && g_str_has_prefix (match_str, MATCH_S390)) - return nm_match_spec_device (specs, NULL, NULL, NULL, NULL, NULL, &match_str[NM_STRLEN (MATCH_S390)]); + return nm_match_spec_device (specs, NULL, NULL, NULL, NULL, NULL, &match_str[NM_STRLEN (MATCH_S390)], NULL); if (match_str && g_str_has_prefix (match_str, MATCH_DRIVER)) { gs_free char *s = g_strdup (&match_str[NM_STRLEN (MATCH_DRIVER)]); char *t; @@ -1103,13 +1110,16 @@ _test_match_spec_device (const GSList *specs, const char *match_str) t[0] = '\0'; t++; } - return nm_match_spec_device (specs, NULL, NULL, s, t, NULL, NULL); + return nm_match_spec_device (specs, NULL, NULL, s, t, NULL, NULL, NULL); } - return nm_match_spec_device (specs, match_str, NULL, NULL, NULL, NULL, NULL); + return nm_match_spec_device (specs, match_str, NULL, NULL, NULL, NULL, NULL, NULL); } static void -_do_test_match_spec_device (const char *spec_str, const char **matches, const char **no_matches, const char **neg_matches) +_do_test_match_spec_device (const char *spec_str, + const char *const *matches, + const char *const *no_matches, + const char *const *neg_matches) { GSList *specs, *specs_randperm = NULL, *specs_resplit, *specs_i, *specs_j; guint i; @@ -1181,98 +1191,100 @@ _do_test_match_spec_device (const char *spec_str, const char **matches, const ch static void test_match_spec_device (void) { -#define S(...) ((const char *[]) { __VA_ARGS__, NULL } ) _do_test_match_spec_device ("em1", - S ("em1"), + NM_MAKE_STRV ("em1"), NULL, NULL); _do_test_match_spec_device ("em1,em2", - S ("em1", "em2"), + NM_MAKE_STRV ("em1", "em2"), NULL, NULL); _do_test_match_spec_device ("em1,em2,interface-name:em2", - S ("em1", "em2"), + NM_MAKE_STRV ("em1", "em2"), NULL, NULL); _do_test_match_spec_device ("interface-name:em1", - S ("em1"), + NM_MAKE_STRV ("em1"), NULL, NULL); _do_test_match_spec_device ("interface-name:em*", - S ("em", "em*", "em\\", "em\\*", "em\\1", "em\\11", "em\\2", "em1", "em11", "em2", "em3"), + NM_MAKE_STRV ("em", "em*", "em\\", "em\\*", "em\\1", "em\\11", "em\\2", "em1", "em11", "em2", "em3"), NULL, NULL); _do_test_match_spec_device ("interface-name:em\\*", - S ("em\\", "em\\*", "em\\1", "em\\11", "em\\2"), + NM_MAKE_STRV ("em\\", "em\\*", "em\\1", "em\\11", "em\\2"), NULL, NULL); _do_test_match_spec_device ("interface-name:~em\\*", - S ("em\\", "em\\*", "em\\1", "em\\11", "em\\2"), + NM_MAKE_STRV ("em\\", "em\\*", "em\\1", "em\\11", "em\\2"), NULL, NULL); _do_test_match_spec_device ("except:*", NULL, - S (NULL), - S ("a")); + NM_MAKE_STRV (NULL), + NM_MAKE_STRV ("a")); _do_test_match_spec_device ("interface-name:=em*", - S ("em*"), + NM_MAKE_STRV ("em*"), NULL, NULL); _do_test_match_spec_device ("interface-name:em*,except:interface-name:em1*", - S ("em", "em*", "em\\", "em\\*", "em\\1", "em\\11", "em\\2", "em2", "em3"), + NM_MAKE_STRV ("em", "em*", "em\\", "em\\*", "em\\1", "em\\11", "em\\2", "em2", "em3"), NULL, - S ("em1", "em11")); + NM_MAKE_STRV ("em1", "em11")); _do_test_match_spec_device ("interface-name:em*,except:interface-name:=em*", - S ("em", "em\\", "em\\*", "em\\1", "em\\11", "em\\2", "em1", "em11", "em2", "em3"), + NM_MAKE_STRV ("em", "em\\", "em\\*", "em\\1", "em\\11", "em\\2", "em1", "em11", "em2", "em3"), NULL, - S ("em*")); + NM_MAKE_STRV ("em*")); + _do_test_match_spec_device ("except:interface-name:em*", + NM_MAKE_STRV ("", "eth", "eth1", "e1"), + NM_MAKE_STRV (NULL), + NM_MAKE_STRV ("em", "em\\", "em\\*", "em\\1", "em\\11", "em\\2", "em1", "em11", "em2", "em3")); _do_test_match_spec_device ("aa,bb,cc\\,dd,e,,", - S ("aa", "bb", "cc,dd", "e"), + NM_MAKE_STRV ("aa", "bb", "cc,dd", "e"), NULL, NULL); _do_test_match_spec_device ("aa;bb;cc\\;dd;e,;", - S ("aa", "bb", "cc;dd", "e"), + NM_MAKE_STRV ("aa", "bb", "cc;dd", "e"), NULL, NULL); _do_test_match_spec_device ("interface-name:em\\;1,em\\,2,\\,,\\\\,,em\\\\x", - S ("em;1", "em,2", ",", "\\", "em\\x"), + NM_MAKE_STRV ("em;1", "em,2", ",", "\\", "em\\x"), NULL, NULL); _do_test_match_spec_device ("\\s\\s,\\sinterface-name:a,\\s,", - S (" ", " ", " interface-name:a"), + NM_MAKE_STRV (" ", " ", " interface-name:a"), NULL, NULL); _do_test_match_spec_device (" aa ; bb ; cc\\;dd ;e , ; \t\\t , ", - S ("aa", "bb", "cc;dd", "e", "\t"), + NM_MAKE_STRV ("aa", "bb", "cc;dd", "e", "\t"), NULL, NULL); _do_test_match_spec_device ("s390-subchannels:0.0.1000\\,0.0.1001", - S (MATCH_S390"0.0.1000", MATCH_S390"0.0.1000,deadbeef", MATCH_S390"0.0.1000,0.0.1001", MATCH_S390"0.0.1000,0.0.1002"), - S (MATCH_S390"0.0.1001"), + NM_MAKE_STRV (MATCH_S390"0.0.1000", MATCH_S390"0.0.1000,deadbeef", MATCH_S390"0.0.1000,0.0.1001", MATCH_S390"0.0.1000,0.0.1002"), + NM_MAKE_STRV (MATCH_S390"0.0.1001"), NULL); _do_test_match_spec_device ("*,except:s390-subchannels:0.0.1000\\,0.0.1001", NULL, - S (NULL), - S (MATCH_S390"0.0.1000", MATCH_S390"0.0.1000,deadbeef", MATCH_S390"0.0.1000,0.0.1001", MATCH_S390"0.0.1000,0.0.1002")); + NM_MAKE_STRV (NULL), + NM_MAKE_STRV (MATCH_S390"0.0.1000", MATCH_S390"0.0.1000,deadbeef", MATCH_S390"0.0.1000,0.0.1001", MATCH_S390"0.0.1000,0.0.1002")); _do_test_match_spec_device ("driver:DRV", - S (MATCH_DRIVER"DRV", MATCH_DRIVER"DRV|1.6"), - S (MATCH_DRIVER"DR", MATCH_DRIVER"DR*"), + NM_MAKE_STRV (MATCH_DRIVER"DRV", MATCH_DRIVER"DRV|1.6"), + NM_MAKE_STRV (MATCH_DRIVER"DR", MATCH_DRIVER"DR*"), NULL); _do_test_match_spec_device ("driver:DRV//", - S (MATCH_DRIVER"DRV/"), - S (MATCH_DRIVER"DRV/|1.6", MATCH_DRIVER"DR", MATCH_DRIVER"DR*"), + NM_MAKE_STRV (MATCH_DRIVER"DRV/"), + NM_MAKE_STRV (MATCH_DRIVER"DRV/|1.6", MATCH_DRIVER"DR", MATCH_DRIVER"DR*"), NULL); _do_test_match_spec_device ("driver:DRV//*", - S (MATCH_DRIVER"DRV/", MATCH_DRIVER"DRV/|1.6"), - S (MATCH_DRIVER"DR", MATCH_DRIVER"DR*"), + NM_MAKE_STRV (MATCH_DRIVER"DRV/", MATCH_DRIVER"DRV/|1.6"), + NM_MAKE_STRV (MATCH_DRIVER"DR", MATCH_DRIVER"DR*"), NULL); _do_test_match_spec_device ("driver:DRV//1.5*", - S (MATCH_DRIVER"DRV/|1.5", MATCH_DRIVER"DRV/|1.5.2"), - S (MATCH_DRIVER"DRV/", MATCH_DRIVER"DRV/|1.6", MATCH_DRIVER"DR", MATCH_DRIVER"DR*"), + NM_MAKE_STRV (MATCH_DRIVER"DRV/|1.5", MATCH_DRIVER"DRV/|1.5.2"), + NM_MAKE_STRV (MATCH_DRIVER"DRV/", MATCH_DRIVER"DRV/|1.6", MATCH_DRIVER"DR", MATCH_DRIVER"DR*"), NULL); -#undef S } /*****************************************************************************/ @@ -1296,9 +1308,10 @@ _do_test_match_spec_config (const char *file, int line, const char *spec_str, gu match_result = nm_match_spec_config (specs, version, NULL); if (expected != match_result) - g_error ("%s:%d: faild comparing \"%s\" with %u.%u.%u. Expected %d, but got %d", file, line, spec_str, v_maj, v_min, v_mic, (int) expected, (int) match_result); + g_error ("%s:%d: failed comparing \"%s\" with %u.%u.%u. Expected %d, but got %d", file, line, spec_str, v_maj, v_min, v_mic, (int) expected, (int) match_result); - if (g_slist_length (specs) == 1 && match_result != NM_MATCH_SPEC_NEG_MATCH) { + if ( g_slist_length (specs) == 1 + && !g_str_has_prefix (specs->data, "except:")) { /* there is only one spec in the list... test that we match except: */ char *sss = g_strdup_printf ("except:%s", (char *) specs->data); GSList *specs2 = g_slist_append (NULL, sss); @@ -1306,7 +1319,7 @@ _do_test_match_spec_config (const char *file, int line, const char *spec_str, gu match_result2 = nm_match_spec_config (specs2, version, NULL); if (match_result == NM_MATCH_SPEC_NO_MATCH) - g_assert_cmpint (match_result2, ==, NM_MATCH_SPEC_NO_MATCH); + g_assert_cmpint (match_result2, ==, NM_MATCH_SPEC_MATCH); else g_assert_cmpint (match_result2, ==, NM_MATCH_SPEC_NEG_MATCH); @@ -1390,7 +1403,7 @@ test_match_spec_config (void) do_test_match_spec_config ("nm-version-max:1", 1, 4, 30, NM_MATCH_SPEC_MATCH); do_test_match_spec_config ("nm-version-max:1", 2, 4, 30, NM_MATCH_SPEC_NO_MATCH); - do_test_match_spec_config ("except:nm-version:1.4.8", 1, 6, 0, NM_MATCH_SPEC_NO_MATCH); + do_test_match_spec_config ("except:nm-version:1.4.8", 1, 6, 0, NM_MATCH_SPEC_MATCH); do_test_match_spec_config ("nm-version-min:1.6,except:nm-version:1.4.8", 1, 6, 0, NM_MATCH_SPEC_MATCH); do_test_match_spec_config ("nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", 1, 2, 0, NM_MATCH_SPEC_NO_MATCH); @@ -1427,6 +1440,82 @@ test_nm_utils_strbuf_append (void) char buf[NM_STRLEN (BUF_ORIG) + 1]; char str[NM_STRLEN (BUF_ORIG) + 1]; +#define _strbuf_append(buf, len, format, ...) \ + G_STMT_START { \ + char **_buf = (buf); \ + gsize *_len = (len); \ + const char *_str_iter; \ + gs_free char *_str = NULL; \ + \ + switch (nmtst_get_rand_int () % 4) { \ + case 0: \ + nm_utils_strbuf_append (_buf, _len, (format), __VA_ARGS__); \ + break; \ + case 1: \ + _str = g_strdup_printf ((format), __VA_ARGS__); \ + nm_utils_strbuf_append_str (_buf, _len, _str); \ + break; \ + case 2: \ + _str = g_strdup_printf ((format), __VA_ARGS__); \ + nm_utils_strbuf_append_bin (_buf, _len, _str, strlen (_str)); \ + break; \ + case 3: \ + _str = g_strdup_printf ((format), __VA_ARGS__); \ + if (!_str[0]) \ + nm_utils_strbuf_append_str (_buf, _len, _str); \ + for (_str_iter = _str; _str_iter[0]; _str_iter++) \ + nm_utils_strbuf_append_c (_buf, _len, _str_iter[0]); \ + break; \ + } \ + } G_STMT_END + +#define _strbuf_append_str(buf, len, str) \ + G_STMT_START { \ + char **_buf = (buf); \ + gsize *_len = (len); \ + const char *_str = (str); \ + \ + switch (nmtst_get_rand_int () % 4) { \ + case 0: \ + nm_utils_strbuf_append (_buf, _len, "%s", _str ?: ""); \ + break; \ + case 1: \ + nm_utils_strbuf_append_str (_buf, _len, _str); \ + break; \ + case 2: \ + nm_utils_strbuf_append_bin (_buf, _len, _str, _str ? strlen (_str) : 0); \ + break; \ + case 3: \ + if (!_str || !_str[0]) \ + nm_utils_strbuf_append_str (_buf, _len, _str); \ + for (; _str && _str[0]; _str++) \ + nm_utils_strbuf_append_c (_buf, _len, _str[0]); \ + break; \ + } \ + } G_STMT_END + +#define _strbuf_append_c(buf, len, ch) \ + G_STMT_START { \ + char **_buf = (buf); \ + gsize *_len = (len); \ + char _ch = (ch); \ + \ + switch (nmtst_get_rand_int () % 4) { \ + case 0: \ + nm_utils_strbuf_append (_buf, _len, "%c", _ch); \ + break; \ + case 1: \ + nm_utils_strbuf_append_str (_buf, _len, ((char[2]) { _ch, 0 })); \ + break; \ + case 2: \ + nm_utils_strbuf_append_bin (_buf, _len, &_ch, 1); \ + break; \ + case 3: \ + nm_utils_strbuf_append_c (_buf, _len, _ch); \ + break; \ + } \ + } G_STMT_END + for (buf_len = 0; buf_len < 10; buf_len++) { for (rep = 0; rep < 50; rep++) { const int s_len = nmtst_get_rand_int () % (sizeof (str) - 5); @@ -1450,21 +1539,21 @@ test_nm_utils_strbuf_append (void) switch (test_mode) { case 0: if (s_len == 1) { - nm_utils_strbuf_append_c (&t_buf, &t_len, str[0]); + _strbuf_append_c (&t_buf, &t_len, str[0]); break; } /* fall through */ case 1: - nm_utils_strbuf_append_str (&t_buf, &t_len, str); + _strbuf_append_str (&t_buf, &t_len, str); break; case 2: if (s_len == 1) { - nm_utils_strbuf_append (&t_buf, &t_len, "%c", str[0]); + _strbuf_append (&t_buf, &t_len, "%c", str[0]); break; } /* fall through */ case 3: - nm_utils_strbuf_append (&t_buf, &t_len, "%s", str); + _strbuf_append (&t_buf, &t_len, "%s", str); break; case 4: g_snprintf (t_buf, t_len, "%s", str); @@ -1733,7 +1822,7 @@ do_test_stable_id_parse (const char *stable_id, else g_assert (stable_id); - stable_type = nm_utils_stable_id_parse (stable_id, "_DEVICE", "_BOOT", "_CONNECTION", &generated); + stable_type = nm_utils_stable_id_parse (stable_id, "_DEVICE", "_MAC", "_BOOT", "_CONNECTION", &generated); g_assert_cmpint (expected_stable_type, ==, stable_type); @@ -1772,6 +1861,7 @@ test_stable_id_parse (void) _parse_generated ("x${BOOT}", "x${BOOT}=5{_BOOT}"); _parse_generated ("x${BOOT}${CONNECTION}", "x${BOOT}=5{_BOOT}${CONNECTION}=11{_CONNECTION}"); _parse_generated ("xX${BOOT}yY${CONNECTION}zZ", "xX${BOOT}=5{_BOOT}yY${CONNECTION}=11{_CONNECTION}zZ"); + _parse_generated ("${MAC}x", "${MAC}=4{_MAC}x"); _parse_random ("${RANDOM}"); _parse_random (" ${RANDOM}"); _parse_random ("${BOOT}${RANDOM}"); @@ -1845,6 +1935,246 @@ test_nm_utils_exp10 (void) /*****************************************************************************/ +static void +test_utils_file_is_in_path (void) +{ + g_assert (!nm_utils_file_is_in_path ("/", "/")); + g_assert (!nm_utils_file_is_in_path ("//", "/")); + g_assert (!nm_utils_file_is_in_path ("/a/", "/")); + g_assert ( nm_utils_file_is_in_path ("/a", "/")); + g_assert ( nm_utils_file_is_in_path ("///a", "/")); + g_assert ( nm_utils_file_is_in_path ("//b/a", "/b//")); + g_assert ( nm_utils_file_is_in_path ("//b///a", "/b//")); + g_assert (!nm_utils_file_is_in_path ("//b///a/", "/b//")); + g_assert (!nm_utils_file_is_in_path ("//b///a/", "/b/a/")); + g_assert (!nm_utils_file_is_in_path ("//b///a", "/b/a/")); + g_assert ( nm_utils_file_is_in_path ("//b///a/.", "/b/a/")); + g_assert ( nm_utils_file_is_in_path ("//b///a/..", "/b/a/")); +} + +/*****************************************************************************/ + +#define _TEST_RC(searches, nameservers, options, expected) \ + G_STMT_START { \ + const char *const*const _searches = (searches); \ + const char *const*const _nameservers = (nameservers); \ + const char *const*const _options = (options); \ + gs_free char *_content = NULL; \ + \ + _content = nmtst_dns_create_resolv_conf (_searches, _nameservers, _options); \ + g_assert_cmpstr (_content, ==, expected); \ + } G_STMT_END + +static void +test_dns_create_resolv_conf (void) +{ + _TEST_RC (NM_MAKE_STRV ("a"), + NULL, + NULL, + "# Generated by NetworkManager\n" + "search a\n" + ""); + + _TEST_RC (NM_MAKE_STRV ("a", "b.com"), + NM_MAKE_STRV ("192.168.55.1", "192.168.56.1"), + NM_MAKE_STRV ("opt1", "opt2"), + "# Generated by NetworkManager\n" + "search a b.com\n" + "nameserver 192.168.55.1\n" + "nameserver 192.168.56.1\n" + "options opt1 opt2\n" + ""); + + _TEST_RC (NM_MAKE_STRV ("a2x456789.b2x456789.c2x456789.d2x456789.e2x456789.f2x456789.g2x456789.h2x456789.i2x456789.j2x4567890", + "a2y456789.b2y456789.c2y456789.d2y456789.e2y456789.f2y456789.g2y456789.h2y456789.i2y456789.j2y4567890", + "a2z456789.b2z456789.c2z456789.d2z456789.e2z456789.f2z456789.g2z456789.h2z456789.i2z456789.j2z4567890"), + NULL, + NULL, + "# Generated by NetworkManager\n" + "search a2x456789.b2x456789.c2x456789.d2x456789.e2x456789.f2x456789.g2x456789.h2x456789.i2x456789.j2x4567890 a2y456789.b2y456789.c2y456789.d2y456789.e2y456789.f2y456789.g2y456789.h2y456789.i2y456789.j2y4567890 a2z456789.b2z456789.c2z456789.d2z456789.e2z456789.f2z456789.g2z456789.h2z456789.i2z456789.j2z4567890\n" + ""); + +} + +/*****************************************************************************/ + +static void +test_machine_id_read (void) +{ + NMUuid machine_id_sd; + const NMUuid *machine_id; + char machine_id_str[33]; + gpointer logstate; + + logstate = nmtst_logging_disable (FALSE); + /* If you run this test as root, without a valid /etc/machine-id, + * the code will try to get the secret-key. That is a bit ugly, + * but no real problem. */ + machine_id = nm_utils_machine_id_bin (); + nmtst_logging_reenable (logstate); + + g_assert (machine_id); + g_assert (_nm_utils_bin2hexstr_full (machine_id, + sizeof (NMUuid), + '\0', + FALSE, + machine_id_str) == machine_id_str); + g_assert (strlen (machine_id_str) == 32); + g_assert_cmpstr (machine_id_str, ==, nm_utils_machine_id_str ()); + + /* double check with systemd's implementation... */ + if (!nm_sd_utils_id128_get_machine (&machine_id_sd)) { + /* if systemd failed to read /etc/machine-id, the file likely + * is invalid. Our machine-id is fake, and we have nothing to + * compare against. */ + + /* NOTE: this test will fail, if you don't have /etc/machine-id, + * but a valid "LOCALSTATEDIR/lib/dbus/machine-id" file. + * Just don't do that. */ + g_assert (nm_utils_machine_id_is_fake ()); + } else { + g_assert (!nm_utils_machine_id_is_fake ()); + g_assert_cmpmem (&machine_id_sd, sizeof (NMUuid), machine_id, 16); + } +} + +/*****************************************************************************/ + +static void +test_nm_utils_dhcp_client_id_systemd_node_specific (gconstpointer test_data) +{ + const int TEST_IDX = GPOINTER_TO_INT (test_data); + const guint8 HASH_KEY[16] = { 0x80, 0x11, 0x8c, 0xc2, 0xfe, 0x4a, 0x03, 0xee, 0x3e, 0xd6, 0x0c, 0x6f, 0x36, 0x39, 0x14, 0x09 }; + const guint16 duid_type_en = htons (2); + const guint32 systemd_pen = htonl (43793); + const struct { + NMUuid machine_id; + const char *ifname; + guint64 ifname_hash_1; + guint32 iaid_ifname; + guint64 duid_id; + } d_array[] = { + [0] = { + .machine_id = { 0xcb, 0xc2, 0x2e, 0x47, 0x41, 0x8e, 0x40, 0x2a, 0xa7, 0xb3, 0x0d, 0xea, 0x92, 0x83, 0x94, 0xef }, + .ifname = "lo", + .ifname_hash_1 = 0x7297085c2b12c911llu, + .iaid_ifname = htobe32 (0x5985c14du), + .duid_id = htobe64 (0x3d769bb2c14d29e1u), + }, + [1] = { + .machine_id = { 0x11, 0x4e, 0xb4, 0xda, 0xd3, 0x22, 0x4a, 0xff, 0x9f, 0xc3, 0x30, 0x83, 0x38, 0xa0, 0xeb, 0xb7 }, + .ifname = "eth0", + .ifname_hash_1 = 0x9e1cb083b54cd7b6llu, + .iaid_ifname = htobe32 (0x2b506735u), + .duid_id = htobe64 (0x551572e0f2a2a10fu), + }, + }; + int i; + typeof (d_array[0]) *d = &d_array[TEST_IDX]; + gint64 u64; + gint32 u32; + + /* the test already hard-codes the expected values iaid_ifname and duid_id + * above. Still, redo the steps to derive them from the ifname/machine-id + * and double check. */ + u64 = c_siphash_hash (HASH_KEY, (const guint8 *) d->ifname, strlen (d->ifname)); + g_assert_cmpint (u64, ==, d->ifname_hash_1); + u32 = be32toh ((u64 & 0xffffffffu) ^ (u64 >> 32)); + g_assert_cmpint (u32, ==, d->iaid_ifname); + + u64 = htole64 (c_siphash_hash (HASH_KEY, (const guint8 *) &d->machine_id, sizeof (d->machine_id))); + g_assert_cmpint (u64, ==, d->duid_id); + + for (i = 0; i < 2; i++) { + const gboolean legacy_unstable_byteorder = (i != 0); + gs_unref_bytes GBytes *client_id = NULL; + const guint8 *cid; + guint32 iaid = d->iaid_ifname; + + client_id = nm_utils_dhcp_client_id_systemd_node_specific_full (legacy_unstable_byteorder, + (const guint8 *) d->ifname, + strlen (d->ifname), + (const guint8 *) &d->machine_id, + sizeof (d->machine_id)); + + g_assert (client_id); + g_assert_cmpint (g_bytes_get_size (client_id), ==, 19); + cid = g_bytes_get_data (client_id, NULL); + g_assert_cmpint (cid[0], ==, 255); +#if __BYTE_ORDER == __BIG_ENDIAN + if (legacy_unstable_byteorder) { + /* on non-little endian, the legacy behavior is to have the bytes + * swapped. */ + iaid = bswap_32 (iaid); + } +#endif + g_assert_cmpmem (&cid[1], 4, &iaid, sizeof (iaid)); + g_assert_cmpmem (&cid[5], 2, &duid_type_en, sizeof (duid_type_en)); + g_assert_cmpmem (&cid[7], 4, &systemd_pen, sizeof (systemd_pen)); + g_assert_cmpmem (&cid[11], 8, &d->duid_id, sizeof (d->duid_id)); + + g_assert_cmpint (iaid, ==, htonl (nm_utils_create_dhcp_iaid (legacy_unstable_byteorder, + (const guint8 *) d->ifname, + strlen (d->ifname)))); + } +} + +/*****************************************************************************/ + +static void +test_connectivity_state_cmp (void) +{ + NMConnectivityState a; + +#define _cmp(a, b, cmp) \ + G_STMT_START { \ + const NMConnectivityState _a = (a); \ + const NMConnectivityState _b = (b); \ + const int _cmp = (cmp); \ + \ + g_assert (NM_IN_SET (_cmp, -1, 0, 1)); \ + g_assert_cmpint (nm_connectivity_state_cmp (_a, _b), ==, _cmp); \ + g_assert_cmpint (nm_connectivity_state_cmp (_b, _a), ==, -_cmp); \ + } G_STMT_END + + for (a = NM_CONNECTIVITY_UNKNOWN; a <= NM_CONNECTIVITY_FULL; a++) + _cmp (a, a, 0); + + _cmp (NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_UNKNOWN, 0); + _cmp (NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_NONE, -1); + _cmp (NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_LIMITED, -1); + _cmp (NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_PORTAL, -1); + _cmp (NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_FULL, -1); + + _cmp (NM_CONNECTIVITY_NONE, NM_CONNECTIVITY_UNKNOWN, 1); + _cmp (NM_CONNECTIVITY_NONE, NM_CONNECTIVITY_NONE, 0); + _cmp (NM_CONNECTIVITY_NONE, NM_CONNECTIVITY_LIMITED, -1); + _cmp (NM_CONNECTIVITY_NONE, NM_CONNECTIVITY_PORTAL, -1); + _cmp (NM_CONNECTIVITY_NONE, NM_CONNECTIVITY_FULL, -1); + + _cmp (NM_CONNECTIVITY_LIMITED, NM_CONNECTIVITY_UNKNOWN, 1); + _cmp (NM_CONNECTIVITY_LIMITED, NM_CONNECTIVITY_NONE, 1); + _cmp (NM_CONNECTIVITY_LIMITED, NM_CONNECTIVITY_LIMITED, 0); + _cmp (NM_CONNECTIVITY_LIMITED, NM_CONNECTIVITY_PORTAL, -1); + _cmp (NM_CONNECTIVITY_LIMITED, NM_CONNECTIVITY_FULL, -1); + + _cmp (NM_CONNECTIVITY_PORTAL, NM_CONNECTIVITY_UNKNOWN, 1); + _cmp (NM_CONNECTIVITY_PORTAL, NM_CONNECTIVITY_NONE, 1); + _cmp (NM_CONNECTIVITY_PORTAL, NM_CONNECTIVITY_LIMITED, 1); + _cmp (NM_CONNECTIVITY_PORTAL, NM_CONNECTIVITY_PORTAL, 0); + _cmp (NM_CONNECTIVITY_PORTAL, NM_CONNECTIVITY_FULL, -1); + + _cmp (NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_UNKNOWN, 1); + _cmp (NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_NONE, 1); + _cmp (NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_LIMITED, 1); + _cmp (NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_PORTAL, 1); + _cmp (NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_FULL, 0); + +#undef _cmp +} + +/*****************************************************************************/ + NMTST_DEFINE (); int @@ -1891,6 +2221,17 @@ main (int argc, char **argv) g_test_add_func ("/general/stable-id/parse", test_stable_id_parse); g_test_add_func ("/general/stable-id/generated-complete", test_stable_id_generated_complete); + g_test_add_func ("/general/machine-id/read", test_machine_id_read); + + g_test_add_func ("/general/test_utils_file_is_in_path", test_utils_file_is_in_path); + + g_test_add_func ("/general/test_dns_create_resolv_conf", test_dns_create_resolv_conf); + + g_test_add_data_func ("/general/nm_utils_dhcp_client_id_systemd_node_specific/0", GINT_TO_POINTER (0), test_nm_utils_dhcp_client_id_systemd_node_specific); + g_test_add_data_func ("/general/nm_utils_dhcp_client_id_systemd_node_specific/1", GINT_TO_POINTER (1), test_nm_utils_dhcp_client_id_systemd_node_specific); + + g_test_add_func ("/core/general/test_connectivity_state_cmp", test_connectivity_state_cmp); + return g_test_run (); } diff --git a/src/tests/test-ip6-config.c b/src/tests/test-ip6-config.c index a03d89b0..51807dea 100644 --- a/src/tests/test-ip6-config.c +++ b/src/tests/test-ip6-config.c @@ -22,6 +22,7 @@ #include <string.h> #include <arpa/inet.h> +#include <linux/if_addr.h> #include "nm-ip6-config.h" @@ -251,7 +252,7 @@ test_nm_ip6_config_addresses_sort_check (NMIP6Config *config, NMSettingIP6Config copy2 = nm_ip6_config_clone (config); g_assert (copy2); - /* initialize the array of indeces, and keep shuffling them for every @repeat iteration. */ + /* initialize the array of indices, and keep shuffling them for every @repeat iteration. */ for (i = 0; i < addr_count; i++) idx[i] = i; diff --git a/src/tests/test-systemd.c b/src/tests/test-systemd.c index ab5fed22..05e22776 100644 --- a/src/tests/test-systemd.c +++ b/src/tests/test-systemd.c @@ -20,6 +20,7 @@ #include "nm-default.h" #include "systemd/nm-sd.h" +#include "systemd/nm-sd-utils-shared.h" #include "nm-test-utils-core.h" @@ -47,6 +48,13 @@ nm_utils_get_monotonic_timestamp_s (void) NMLogDomain _nm_logging_enabled_state[_LOGL_N_REAL]; +gboolean +_nm_log_enabled (NMLogLevel level, + NMLogDomain domain) +{ + return FALSE; +} + void _nm_log_impl (const char *file, guint line, @@ -173,6 +181,145 @@ test_sd_event (void) /*****************************************************************************/ +static void +test_path_equal (void) +{ +#define _path_equal_check1(path, kill_dots, expected) \ + G_STMT_START { \ + const gboolean _kill_dots = (kill_dots); \ + const char *_path0 = (path); \ + const char *_expected = (expected); \ + gs_free char *_path = g_strdup (_path0); \ + const char *_path_result; \ + \ + if ( !_kill_dots \ + && !nm_sd_utils_path_equal (_path0, _expected)) \ + g_error ("Paths \"%s\" and \"%s\" don't compare equal", _path0, _expected); \ + \ + _path_result = nm_sd_utils_path_simplify (_path, _kill_dots); \ + g_assert (_path_result == _path); \ + g_assert_cmpstr (_path, ==, _expected); \ + } G_STMT_END + +#define _path_equal_check(path, expected_no_kill_dots, expected_kill_dots) \ + G_STMT_START { \ + _path_equal_check1 (path, FALSE, expected_no_kill_dots); \ + _path_equal_check1 (path, TRUE, expected_kill_dots ?: expected_no_kill_dots); \ + } G_STMT_END + + _path_equal_check ("", "", NULL); + _path_equal_check (".", ".", NULL); + _path_equal_check ("..", "..", NULL); + _path_equal_check ("/..", "/..", NULL); + _path_equal_check ("//..", "/..", NULL); + _path_equal_check ("/.", "/.", "/"); + _path_equal_check ("./", ".", "."); + _path_equal_check ("./.", "./.", "."); + _path_equal_check (".///.", "./.", "."); + _path_equal_check (".///./", "./.", "."); + _path_equal_check (".////", ".", "."); + _path_equal_check ("//..//foo/", "/../foo", NULL); + _path_equal_check ("///foo//./bar/.", "/foo/./bar/.", "/foo/bar"); + _path_equal_check (".//./foo//./bar/.", "././foo/./bar/.", "foo/bar"); +} + +/*****************************************************************************/ + +static void +_test_unbase64char (char ch, gboolean maybe_invalid) +{ + int r; + + r = nm_sd_utils_unbase64char (ch, FALSE); + + if (ch == '=') { + g_assert (!maybe_invalid); + g_assert_cmpint (r, <, 0); + g_assert_cmpint (nm_sd_utils_unbase64char (ch, TRUE), ==, G_MAXINT); + } else { + g_assert_cmpint (r, ==, nm_sd_utils_unbase64char (ch, TRUE)); + if (r >= 0) + g_assert_cmpint (r, <=, 255); + if (!maybe_invalid) + g_assert_cmpint (r, >=, 0); + } +} + +static void +_test_unbase64mem_mem (const char *base64, const guint8 *expected_arr, gsize expected_len) +{ + gs_free char *expected_base64 = NULL; + int r; + gs_free guint8 *exp2_arr = NULL; + gs_free guint8 *exp3_arr = NULL; + gsize exp2_len; + gsize exp3_len; + gsize i; + + expected_base64 = g_base64_encode (expected_arr, expected_len); + + for (i = 0; expected_base64[i]; i++) + _test_unbase64char (expected_base64[i], FALSE); + + r = nm_sd_utils_unbase64mem (expected_base64, strlen (expected_base64), &exp2_arr, &exp2_len); + g_assert_cmpint (r, ==, 0); + g_assert_cmpmem (expected_arr, expected_len, exp2_arr, exp2_len); + + if (!nm_streq (base64, expected_base64)) { + r = nm_sd_utils_unbase64mem (base64, strlen (base64), &exp3_arr, &exp3_len); + g_assert_cmpint (r, ==, 0); + g_assert_cmpmem (expected_arr, expected_len, exp3_arr, exp3_len); + } +} + +#define _test_unbase64mem(base64, expected_str) _test_unbase64mem_mem (base64, (const guint8 *) ""expected_str"", NM_STRLEN (expected_str)) + +static void +_test_unbase64mem_inval (const char *base64) +{ + gs_free guint8 *exp_arr = NULL; + gsize exp_len = 0; + int r; + + r = nm_sd_utils_unbase64mem (base64, strlen (base64), &exp_arr, &exp_len); + g_assert_cmpint (r, <, 0); + g_assert (!exp_arr); + g_assert (exp_len == 0); +} + +static void +test_nm_sd_utils_unbase64mem (void) +{ + gs_free char *rnd_base64 = NULL; + guint8 rnd_buf[30]; + guint i, rnd_len; + + _test_unbase64mem ("", ""); + _test_unbase64mem (" ", ""); + _test_unbase64mem (" Y Q == ", "a"); + _test_unbase64mem (" Y WJjZGV mZ 2g = ", "abcdefgh"); + _test_unbase64mem_inval (" Y %WJjZGV mZ 2g = "); + _test_unbase64mem_inval (" Y %WJjZGV mZ 2g = a"); + _test_unbase64mem ("YQ==", "a"); + _test_unbase64mem_inval ("YQ==a"); + + rnd_len = nmtst_get_rand_int () % sizeof (rnd_buf); + for (i = 0; i < rnd_len; i++) + rnd_buf[i] = nmtst_get_rand_int () % 256; + rnd_base64 = g_base64_encode (rnd_buf, rnd_len); + _test_unbase64mem_mem (rnd_base64, rnd_buf, rnd_len); + + _test_unbase64char ('=', FALSE); + for (i = 0; i < 10; i++) { + char ch = nmtst_get_rand_int () % 256; + + if (ch != '=') + _test_unbase64char (ch, TRUE); + } +} + +/*****************************************************************************/ + NMTST_DEFINE (); int @@ -183,6 +330,8 @@ main (int argc, char **argv) g_test_add_func ("/systemd/dhcp/create", test_dhcp_create); g_test_add_func ("/systemd/lldp/create", test_lldp_create); g_test_add_func ("/systemd/sd-event", test_sd_event); + g_test_add_func ("/systemd/test_path_equal", test_path_equal); + g_test_add_func ("/systemd/test_nm_sd_utils_unbase64mem", test_nm_sd_utils_unbase64mem); return g_test_run (); } diff --git a/src/tests/test-utils.c b/src/tests/test-utils.c index 16eb3aec..9572ec7a 100644 --- a/src/tests/test-utils.c +++ b/src/tests/test-utils.c @@ -64,10 +64,10 @@ _do_test_hw_addr (NMUtilsStableType stable_type, const char *ifname, const char *current_mac_address, const char *generate_mac_address_mask, - const char **expected) + const char *const *expected) { gs_free char *generated = NULL; - const char **e; + const char *const *e; gboolean found = FALSE; for (e = expected; *e; e++) { @@ -95,7 +95,13 @@ _do_test_hw_addr (NMUtilsStableType stable_type, g_assert (found); } #define do_test_hw_addr(stable_type, stable_id, secret_key, ifname, current_mac_address, generate_mac_address_mask, ...) \ - _do_test_hw_addr ((stable_type), (stable_id), (const guint8 *) ""secret_key"", NM_STRLEN (secret_key), (ifname), ""current_mac_address"", generate_mac_address_mask, (const char *[]) { __VA_ARGS__, NULL }) + _do_test_hw_addr ((stable_type), \ + (stable_id), \ + (const guint8 *) ""secret_key"", \ + NM_STRLEN (secret_key), (ifname), \ + ""current_mac_address"", \ + generate_mac_address_mask, \ + NM_MAKE_STRV (__VA_ARGS__)) static void test_hw_addr_gen_stable_eth (void) diff --git a/src/vpn/nm-vpn-connection.c b/src/vpn/nm-vpn-connection.c index bd847d75..6626f64a 100644 --- a/src/vpn/nm-vpn-connection.c +++ b/src/vpn/nm-vpn-connection.c @@ -725,7 +725,7 @@ add_ip4_vpn_gateway_route (NMIP4Config *config, AF_INET, &vpn_gw, ifindex, - (NMPObject **) &route_resolved) == NM_PLATFORM_ERROR_SUCCESS) { + (NMPObject **) &route_resolved) >= 0) { const NMPlatformIP4Route *r = NMP_OBJECT_CAST_IP4_ROUTE (route_resolved); if (r->ifindex == ifindex) { @@ -799,7 +799,7 @@ add_ip6_vpn_gateway_route (NMIP6Config *config, AF_INET6, vpn_gw, ifindex, - (NMPObject **) &route_resolved) == NM_PLATFORM_ERROR_SUCCESS) { + (NMPObject **) &route_resolved) >= 0) { const NMPlatformIP6Route *r = NMP_OBJECT_CAST_IP6_ROUTE (route_resolved); if (r->ifindex == ifindex) { @@ -851,6 +851,7 @@ nm_vpn_connection_new (NMSettingsConnection *settings_connection, NMDevice *parent_device, const char *specific_object, NMActivationReason activation_reason, + NMActivationStateFlags initial_state_flags, NMAuthSubject *subject) { g_return_val_if_fail (!settings_connection || NM_IS_SETTINGS_CONNECTION (settings_connection), NULL); @@ -864,6 +865,7 @@ nm_vpn_connection_new (NMSettingsConnection *settings_connection, NM_ACTIVE_CONNECTION_INT_SUBJECT, subject, NM_ACTIVE_CONNECTION_INT_ACTIVATION_REASON, activation_reason, NM_ACTIVE_CONNECTION_VPN, TRUE, + NM_ACTIVE_CONNECTION_STATE_FLAGS, (guint) initial_state_flags, NULL); } @@ -882,14 +884,15 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_vpn_plugin_failure_to_string, NMVpnPluginFai NM_UTILS_LOOKUP_STR_ITEM (NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED, "connect-failed"), NM_UTILS_LOOKUP_STR_ITEM (NM_VPN_PLUGIN_FAILURE_BAD_IP_CONFIG, "bad-ip-config"), ); -#define vpn_plugin_failure_to_string(failure) NM_UTILS_LOOKUP_STR (_vpn_plugin_failure_to_string, failure) + +#define vpn_plugin_failure_to_string_a(failure) NM_UTILS_LOOKUP_STR_A (_vpn_plugin_failure_to_string, failure) static void plugin_failed (NMVpnConnection *self, guint reason) { NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); - _LOGW ("VPN plugin: failed: %s (%d)", vpn_plugin_failure_to_string (reason), reason); + _LOGW ("VPN plugin: failed: %s (%d)", vpn_plugin_failure_to_string_a (reason), reason); switch (reason) { case NM_VPN_PLUGIN_FAILURE_LOGIN_FAILED: @@ -914,7 +917,8 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_vpn_service_state_to_string, NMVpnServiceSta NM_UTILS_LOOKUP_STR_ITEM (NM_VPN_SERVICE_STATE_STOPPING, "stopping"), NM_UTILS_LOOKUP_STR_ITEM (NM_VPN_SERVICE_STATE_STOPPED, "stopped"), ); -#define vpn_service_state_to_string(state) NM_UTILS_LOOKUP_STR (_vpn_service_state_to_string, state) + +#define vpn_service_state_to_string_a(state) NM_UTILS_LOOKUP_STR_A (_vpn_service_state_to_string, state) NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_vpn_state_to_string, VpnState, NM_UTILS_LOOKUP_DEFAULT (NULL), @@ -930,7 +934,8 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_vpn_state_to_string, VpnState, NM_UTILS_LOOKUP_STR_ITEM (STATE_DISCONNECTED, "disconnected"), NM_UTILS_LOOKUP_STR_ITEM (STATE_FAILED, "failed"), ); -#define vpn_state_to_string(state) NM_UTILS_LOOKUP_STR (_vpn_state_to_string, state) + +#define vpn_state_to_string_a(state) NM_UTILS_LOOKUP_STR_A (_vpn_state_to_string, state) static void plugin_state_changed (NMVpnConnection *self, NMVpnServiceState new_service_state) @@ -939,7 +944,7 @@ plugin_state_changed (NMVpnConnection *self, NMVpnServiceState new_service_state NMVpnServiceState old_service_state = priv->service_state; _LOGI ("VPN plugin: state changed: %s (%d)", - vpn_service_state_to_string (new_service_state), new_service_state); + vpn_service_state_to_string_a (new_service_state), new_service_state); priv->service_state = new_service_state; if (new_service_state == NM_VPN_SERVICE_STATE_STOPPED) { @@ -979,15 +984,16 @@ print_vpn_config (NMVpnConnection *self) const NMPlatformIP6Address *address6; char *dns_domain = NULL; guint32 num, i; - char buf[NM_UTILS_INET_ADDRSTRLEN]; + char b1[NM_UTILS_INET_ADDRSTRLEN]; + char b2[NM_UTILS_INET_ADDRSTRLEN]; NMDedupMultiIter ipconf_iter; if (priv->ip4_external_gw) { _LOGI ("Data: VPN Gateway: %s", - nm_utils_inet4_ntop (priv->ip4_external_gw, NULL)); + nm_utils_inet4_ntop (priv->ip4_external_gw, b1)); } else if (priv->ip6_external_gw) { _LOGI ("Data: VPN Gateway: %s", - nm_utils_inet6_ntop (priv->ip6_external_gw, NULL)); + nm_utils_inet6_ntop (priv->ip6_external_gw, b1)); } _LOGI ("Data: Tunnel Device: %s%s%s", NM_PRINT_FMT_QUOTE_STRING (priv->ip_iface)); @@ -1001,22 +1007,22 @@ print_vpn_config (NMVpnConnection *self) nm_assert (address4); if (priv->ip4_internal_gw) - _LOGI ("Data: Internal Gateway: %s", nm_utils_inet4_ntop (priv->ip4_internal_gw, NULL)); - _LOGI ("Data: Internal Address: %s", address4 ? nm_utils_inet4_ntop (address4->address, NULL) : "??"); + _LOGI ("Data: Internal Gateway: %s", nm_utils_inet4_ntop (priv->ip4_internal_gw, b1)); + _LOGI ("Data: Internal Address: %s", address4 ? nm_utils_inet4_ntop (address4->address, b1) : "??"); _LOGI ("Data: Internal Prefix: %d", address4 ? (int) address4->plen : -1); - _LOGI ("Data: Internal Point-to-Point Address: %s", nm_utils_inet4_ntop (address4->peer_address, NULL)); + _LOGI ("Data: Internal Point-to-Point Address: %s", nm_utils_inet4_ntop (address4->peer_address, b1)); nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, priv->ip4_config, &route) { _LOGI ("Data: Static Route: %s/%d Next Hop: %s", - nm_utils_inet4_ntop (route->network, NULL), + nm_utils_inet4_ntop (route->network, b1), route->plen, - nm_utils_inet4_ntop (route->gateway, buf)); + nm_utils_inet4_ntop (route->gateway, b2)); } num = nm_ip4_config_get_num_nameservers (priv->ip4_config); for (i = 0; i < num; i++) { _LOGI ("Data: Internal DNS: %s", - nm_utils_inet4_ntop (nm_ip4_config_get_nameserver (priv->ip4_config, i), NULL)); + nm_utils_inet4_ntop (nm_ip4_config_get_nameserver (priv->ip4_config, i), b1)); } if (nm_ip4_config_get_num_domains (priv->ip4_config) > 0) @@ -1035,22 +1041,22 @@ print_vpn_config (NMVpnConnection *self) nm_assert (address6); if (priv->ip6_internal_gw) - _LOGI ("Data: Internal Gateway: %s", nm_utils_inet6_ntop (priv->ip6_internal_gw, NULL)); - _LOGI ("Data: Internal Address: %s", nm_utils_inet6_ntop (&address6->address, NULL)); + _LOGI ("Data: Internal Gateway: %s", nm_utils_inet6_ntop (priv->ip6_internal_gw, b1)); + _LOGI ("Data: Internal Address: %s", nm_utils_inet6_ntop (&address6->address, b1)); _LOGI ("Data: Internal Prefix: %d", address6->plen); - _LOGI ("Data: Internal Point-to-Point Address: %s", nm_utils_inet6_ntop (&address6->peer_address, NULL)); + _LOGI ("Data: Internal Point-to-Point Address: %s", nm_utils_inet6_ntop (&address6->peer_address, b1)); nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, priv->ip6_config, &route) { _LOGI ("Data: Static Route: %s/%d Next Hop: %s", - nm_utils_inet6_ntop (&route->network, NULL), + nm_utils_inet6_ntop (&route->network, b1), route->plen, - nm_utils_inet6_ntop (&route->gateway, buf)); + nm_utils_inet6_ntop (&route->gateway, b2)); } num = nm_ip6_config_get_num_nameservers (priv->ip6_config); for (i = 0; i < num; i++) { _LOGI ("Data: Internal DNS: %s", - nm_utils_inet6_ntop (nm_ip6_config_get_nameserver (priv->ip6_config, i), NULL)); + nm_utils_inet6_ntop (nm_ip6_config_get_nameserver (priv->ip6_config, i), b1)); } if (nm_ip6_config_get_num_domains (priv->ip6_config) > 0) @@ -1878,7 +1884,7 @@ connect_success (NMVpnConnection *self) timeout = nm_setting_vpn_get_timeout (s_vpn); if (timeout == 0) { timeout = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA, - "vpn.timeout", + NM_CON_DEFAULT ("vpn.timeout"), NULL, 1, G_MAXUINT32, 60); } @@ -2687,12 +2693,12 @@ plugin_interactive_secrets_required (NMVpnConnection *self, if (!NM_IN_SET (priv->vpn_state, STATE_CONNECT, STATE_NEED_AUTH)) { _LOGD ("VPN plugin: requested secrets; state %s (%d); ignore request in current state", - vpn_state_to_string (priv->vpn_state), priv->vpn_state); + vpn_state_to_string_a (priv->vpn_state), priv->vpn_state); return; } _LOGI ("VPN plugin: requested secrets; state %s (%d)", - vpn_state_to_string (priv->vpn_state), priv->vpn_state); + vpn_state_to_string_a (priv->vpn_state), priv->vpn_state); priv->secrets_idx = SECRETS_REQ_INTERACTIVE; _set_vpn_state (self, STATE_NEED_AUTH, NM_ACTIVE_CONNECTION_STATE_REASON_NONE, FALSE); diff --git a/src/vpn/nm-vpn-connection.h b/src/vpn/nm-vpn-connection.h index e409cc31..5482fb0d 100644 --- a/src/vpn/nm-vpn-connection.h +++ b/src/vpn/nm-vpn-connection.h @@ -53,6 +53,7 @@ NMVpnConnection * nm_vpn_connection_new (NMSettingsConnection *settings_connecti NMDevice *parent_device, const char *specific_object, NMActivationReason activation_reason, + NMActivationStateFlags initial_state_flags, NMAuthSubject *subject); void nm_vpn_connection_activate (NMVpnConnection *self, |