diff options
Diffstat (limited to 'src')
416 files changed, 17461 insertions, 12192 deletions
diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c index 13cb1dae..1773405d 100644 --- a/src/NetworkManagerUtils.c +++ b/src/NetworkManagerUtils.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -229,6 +228,7 @@ nm_utils_connection_has_default_route (NMConnection *connection, goto out; } else { if (NM_IN_STRSET (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_METHOD_DISABLED, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) goto out; } @@ -249,13 +249,15 @@ nm_utils_complete_generic (NMPlatform *platform, const char *preferred_id, const char *fallback_id_prefix, const char *ifname_prefix, + const char *ifname, gboolean default_enable_ipv6) { NMSettingConnection *s_con; - char *id, *ifname; + char *id, *generated_ifname; GHashTable *parameters; g_assert (fallback_id_prefix); + g_return_if_fail (ifname_prefix == NULL || ifname == NULL); s_con = nm_connection_get_setting_connection (connection); if (!s_con) { @@ -278,10 +280,12 @@ nm_utils_complete_generic (NMPlatform *platform, } /* Add an interface name, if requested */ - if (ifname_prefix && !nm_setting_connection_get_interface_name (s_con)) { - ifname = get_new_connection_ifname (platform, existing_connections, ifname_prefix); + if (ifname) { g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_INTERFACE_NAME, ifname, NULL); - g_free (ifname); + } else if (ifname_prefix && !nm_setting_connection_get_interface_name (s_con)) { + generated_ifname = get_new_connection_ifname (platform, existing_connections, ifname_prefix); + g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_INTERFACE_NAME, generated_ifname, NULL); + g_free (generated_ifname); } /* Normalize */ @@ -919,29 +923,30 @@ nm_ip_routing_rule_to_platform (const NMIPRoutingRule *rule, nm_assert (out_pl); *out_pl = (NMPlatformRoutingRule) { - .addr_family = nm_ip_routing_rule_get_addr_family (rule), - .flags = ( nm_ip_routing_rule_get_invert (rule) - ? FIB_RULE_INVERT - : 0), - .priority = nm_ip_routing_rule_get_priority (rule), - .tos = nm_ip_routing_rule_get_tos (rule), - .ip_proto = nm_ip_routing_rule_get_ipproto (rule), - .fwmark = nm_ip_routing_rule_get_fwmark (rule), - .fwmask = nm_ip_routing_rule_get_fwmask (rule), - .sport_range = { - .start = nm_ip_routing_rule_get_source_port_start (rule), - .end = nm_ip_routing_rule_get_source_port_end (rule), + .addr_family = nm_ip_routing_rule_get_addr_family (rule), + .flags = ( nm_ip_routing_rule_get_invert (rule) + ? FIB_RULE_INVERT + : 0), + .priority = nm_ip_routing_rule_get_priority (rule), + .tos = nm_ip_routing_rule_get_tos (rule), + .ip_proto = nm_ip_routing_rule_get_ipproto (rule), + .fwmark = nm_ip_routing_rule_get_fwmark (rule), + .fwmask = nm_ip_routing_rule_get_fwmask (rule), + .sport_range = { + .start = nm_ip_routing_rule_get_source_port_start (rule), + .end = nm_ip_routing_rule_get_source_port_end (rule), }, - .dport_range = { - .start = nm_ip_routing_rule_get_destination_port_start (rule), - .end = nm_ip_routing_rule_get_destination_port_end (rule), + .dport_range = { + .start = nm_ip_routing_rule_get_destination_port_start (rule), + .end = nm_ip_routing_rule_get_destination_port_end (rule), }, - .src = *(nm_ip_routing_rule_get_from_bin (rule) ?: &nm_ip_addr_zero), - .dst = *(nm_ip_routing_rule_get_to_bin (rule) ?: &nm_ip_addr_zero), - .src_len = nm_ip_routing_rule_get_from_len (rule), - .dst_len = nm_ip_routing_rule_get_to_len (rule), - .action = nm_ip_routing_rule_get_action (rule), - .table = nm_ip_routing_rule_get_table (rule), + .src = *(nm_ip_routing_rule_get_from_bin (rule) ?: &nm_ip_addr_zero), + .dst = *(nm_ip_routing_rule_get_to_bin (rule) ?: &nm_ip_addr_zero), + .src_len = nm_ip_routing_rule_get_from_len (rule), + .dst_len = nm_ip_routing_rule_get_to_len (rule), + .action = nm_ip_routing_rule_get_action (rule), + .table = nm_ip_routing_rule_get_table (rule), + .suppress_prefixlen_inverse = ~((guint32) nm_ip_routing_rule_get_suppress_prefixlength (rule)), }; nm_ip_routing_rule_get_xifname_bin (rule, TRUE, out_pl->iifname); @@ -955,7 +960,8 @@ nm_ip_routing_rule_to_platform (const NMIPRoutingRule *rule, struct _NMShutdownWaitObjHandle { CList lst; GObject *watched_obj; - const char *msg_reason; + char *msg_reason; + bool free_msg_reason:1; }; static CList _shutdown_waitobj_lst_head; @@ -964,6 +970,8 @@ static void _shutdown_waitobj_unregister (NMShutdownWaitObjHandle *handle) { c_list_unlink_stale (&handle->lst); + if (handle->free_msg_reason) + g_free (handle->msg_reason); g_slice_free (NMShutdownWaitObjHandle, handle); /* FIXME(shutdown): check whether the object list is empty, and @@ -982,13 +990,14 @@ _shutdown_waitobj_cb (gpointer user_data, } /** - * _nm_shutdown_wait_obj_register: + * nm_shutdown_wait_obj_register_full: * @watched_obj: the object to watch. Takes a weak reference on the object * to be notified when it gets destroyed. - * @msg_reason: a reason message, for debugging and logging purposes. It - * must be a static string. Or at least, be alive at least as long as - * @watched_obj. So, theoretically, if you need a dynamic @msg_reason, - * you could attach it to @watched_obj's user-data. + * @msg_reason: a reason message, for debugging and logging purposes. + * @free_msg_reason: if %TRUE, then ownership of @msg_reason will be taken + * and the string will be freed with g_free() afterwards. If %FALSE, + * the caller must ensure that @msg_reason string outlives the watched + * objects (e.g. being a static strings). * * Keep track of @watched_obj until it gets destroyed. During shutdown, * we wait until all watched objects are destroyed. This is useful, if @@ -1005,8 +1014,9 @@ _shutdown_waitobj_cb (gpointer user_data, * once it gets destroyed. */ NMShutdownWaitObjHandle * -_nm_shutdown_wait_obj_register (GObject *watched_obj, - const char *msg_reason) +nm_shutdown_wait_obj_register_full (GObject *watched_obj, + char *msg_reason, + gboolean free_msg_reason) { NMShutdownWaitObjHandle *handle; @@ -1016,11 +1026,14 @@ _nm_shutdown_wait_obj_register (GObject *watched_obj, c_list_init (&_shutdown_waitobj_lst_head); handle = g_slice_new (NMShutdownWaitObjHandle); - handle->watched_obj = watched_obj; - /* we don't clone the string. We require the caller to use pass a static message. - * If he really cannot do that, he should attach the string to the watched_obj - * as user-data. */ - handle->msg_reason = msg_reason; + *handle = (NMShutdownWaitObjHandle) { + /* depending on @free_msg_reason, we take ownership of @msg_reason. + * In either case, we just reference the string without cloning + * it. */ + .watched_obj = watched_obj, + .msg_reason = msg_reason, + .free_msg_reason = free_msg_reason, + }; c_list_link_tail (&_shutdown_waitobj_lst_head, &handle->lst); g_object_weak_ref (watched_obj, _shutdown_waitobj_cb, handle); return handle; diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h index 2b1f5ed8..778dc001 100644 --- a/src/NetworkManagerUtils.h +++ b/src/NetworkManagerUtils.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -41,6 +40,7 @@ void nm_utils_complete_generic (NMPlatform *platform, const char *preferred_id, const char *fallback_id_prefix, const char *ifname_prefix, + const char *ifname, gboolean default_enable_ipv6); typedef gboolean (NMUtilsMatchFilterFunc) (NMConnection *connection, gpointer user_data); @@ -78,7 +78,7 @@ NMPlatformRoutingRule *nm_ip_routing_rule_to_platform (const NMIPRoutingRule *ru * SIGKILL. * * After NM_SHUTDOWN_TIMEOUT_MS, NetworkManager will however not yet terminate right - * away. It iterates the mainloop for another NM_SHUTDOWN_TIMEOUT_MS_EXTRA. This + * away. It iterates the mainloop for another NM_SHUTDOWN_TIMEOUT_MS_WATCHDOG. This * should give time to reap the child process (after SIGKILL). * * So, the maximum time we should wait before sending SIGKILL should be at most @@ -89,10 +89,11 @@ NMPlatformRoutingRule *nm_ip_routing_rule_to_platform (const NMIPRoutingRule *ru typedef struct _NMShutdownWaitObjHandle NMShutdownWaitObjHandle; -NMShutdownWaitObjHandle *_nm_shutdown_wait_obj_register (GObject *watched_obj, - const char *msg_reason); +NMShutdownWaitObjHandle *nm_shutdown_wait_obj_register_full (GObject *watched_obj, + char *msg_reason, + gboolean free_msg_reason); -#define nm_shutdown_wait_obj_register(watched_obj, msg_reason) _nm_shutdown_wait_obj_register((watched_obj), (""msg_reason"")) +#define nm_shutdown_wait_obj_register(watched_obj, msg_reason) nm_shutdown_wait_obj_register_full((watched_obj), (""msg_reason""), FALSE) void nm_shutdown_wait_obj_unregister (NMShutdownWaitObjHandle *handle); diff --git a/src/devices/adsl/nm-atm-manager.c b/src/devices/adsl/nm-atm-manager.c index f312b5a3..bc0bf5c6 100644 --- a/src/devices/adsl/nm-atm-manager.c +++ b/src/devices/adsl/nm-atm-manager.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/adsl/nm-device-adsl.c b/src/devices/adsl/nm-device-adsl.c index b3b87dc7..59c87851 100644 --- a/src/devices/adsl/nm-device-adsl.c +++ b/src/devices/adsl/nm-device-adsl.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -131,6 +130,7 @@ complete_connection (NMDevice *device, NULL, _("ADSL connection"), NULL, + NULL, FALSE); /* No IPv6 yet by default */ return TRUE; } @@ -487,9 +487,9 @@ act_stage3_ip4_config_start (NMDevice *device, if (priv->ppp_manager) { nm_ppp_manager_set_route_parameters (priv->ppp_manager, - nm_device_get_route_table (device, AF_INET, TRUE), + nm_device_get_route_table (device, AF_INET), nm_device_get_route_metric (device, AF_INET), - nm_device_get_route_table (device, AF_INET6, TRUE), + nm_device_get_route_table (device, AF_INET6), nm_device_get_route_metric (device, AF_INET6)); } diff --git a/src/devices/adsl/nm-device-adsl.h b/src/devices/adsl/nm-device-adsl.h index 351c8e7a..2d3a9fc7 100644 --- a/src/devices/adsl/nm-device-adsl.h +++ b/src/devices/adsl/nm-device-adsl.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/bluetooth/nm-bluez-common.h b/src/devices/bluetooth/nm-bluez-common.h index d72bea81..956375bb 100644 --- a/src/devices/bluetooth/nm-bluez-common.h +++ b/src/devices/bluetooth/nm-bluez-common.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/bluetooth/nm-bluez-device.c b/src/devices/bluetooth/nm-bluez-device.c index 377ee478..fb0a72c4 100644 --- a/src/devices/bluetooth/nm-bluez-device.c +++ b/src/devices/bluetooth/nm-bluez-device.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -236,15 +235,18 @@ pan_connection_check_create (NMBluezDevice *self) * which then already finds the suitable connection in priv->connections. This is confusing, * so block the signal. check_emit_usable will succeed after this function call returns. */ g_signal_handlers_block_by_func (priv->settings, cp_connection_added, self); - added = nm_settings_add_connection (priv->settings, connection, FALSE, &error); + nm_settings_add_connection (priv->settings, + connection, + NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY, + NM_SETTINGS_CONNECTION_ADD_REASON_NONE, + NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED, + &added, + &error); g_signal_handlers_unblock_by_func (priv->settings, cp_connection_added, self); if (added) { nm_assert (!g_slist_find (priv->connections, added)); nm_assert (connection_compatible (self, added)); - - nm_settings_connection_set_flags (added, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED, TRUE); - priv->connections = g_slist_prepend (priv->connections, g_object_ref (added)); priv->pan_connection = added; nm_log_dbg (LOGD_BT, "bluez[%s] added new Bluetooth connection for NAP device: '%s' (%s)", priv->path, id, uuid); @@ -393,7 +395,7 @@ cp_connection_removed (NMSettings *settings, static void cp_connection_updated (NMSettings *settings, NMSettingsConnection *sett_conn, - gboolean by_user, + guint update_reason_u, NMBluezDevice *self) { if (_internal_track_connection (self, sett_conn, @@ -1226,7 +1228,7 @@ dispose (GObject *object) if (to_delete) { nm_log_dbg (LOGD_BT, "bluez[%s] removing Bluetooth connection for NAP device: '%s' (%s)", priv->path, nm_settings_connection_get_id (to_delete), nm_settings_connection_get_uuid (to_delete)); - nm_settings_connection_delete (to_delete, NULL); + nm_settings_connection_delete (to_delete, FALSE); g_object_unref (to_delete); } diff --git a/src/devices/bluetooth/nm-bluez-device.h b/src/devices/bluetooth/nm-bluez-device.h index d2d0beb0..e8dd2ced 100644 --- a/src/devices/bluetooth/nm-bluez-device.h +++ b/src/devices/bluetooth/nm-bluez-device.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/bluetooth/nm-bluez-manager.c b/src/devices/bluetooth/nm-bluez-manager.c index 9cd3a519..7577ab8b 100644 --- a/src/devices/bluetooth/nm-bluez-manager.c +++ b/src/devices/bluetooth/nm-bluez-manager.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/bluetooth/nm-bluez4-adapter.c b/src/devices/bluetooth/nm-bluez4-adapter.c index 3a456deb..bd230e90 100644 --- a/src/devices/bluetooth/nm-bluez4-adapter.c +++ b/src/devices/bluetooth/nm-bluez4-adapter.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/bluetooth/nm-bluez4-adapter.h b/src/devices/bluetooth/nm-bluez4-adapter.h index 0aa4ff91..82bd2de8 100644 --- a/src/devices/bluetooth/nm-bluez4-adapter.h +++ b/src/devices/bluetooth/nm-bluez4-adapter.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/bluetooth/nm-bluez4-manager.c b/src/devices/bluetooth/nm-bluez4-manager.c index 82d995be..8327776d 100644 --- a/src/devices/bluetooth/nm-bluez4-manager.c +++ b/src/devices/bluetooth/nm-bluez4-manager.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/bluetooth/nm-bluez5-dun.c b/src/devices/bluetooth/nm-bluez5-dun.c index ff3a0da9..04859f9a 100644 --- a/src/devices/bluetooth/nm-bluez5-dun.c +++ b/src/devices/bluetooth/nm-bluez5-dun.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/bluetooth/nm-bluez5-dun.h b/src/devices/bluetooth/nm-bluez5-dun.h index b75e4399..b605414b 100644 --- a/src/devices/bluetooth/nm-bluez5-dun.h +++ b/src/devices/bluetooth/nm-bluez5-dun.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/bluetooth/nm-bluez5-manager.c b/src/devices/bluetooth/nm-bluez5-manager.c index be15d824..7bcd04e5 100644 --- a/src/devices/bluetooth/nm-bluez5-manager.c +++ b/src/devices/bluetooth/nm-bluez5-manager.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/bluetooth/nm-bluez5-manager.h b/src/devices/bluetooth/nm-bluez5-manager.h index 43694435..14ac842e 100644 --- a/src/devices/bluetooth/nm-bluez5-manager.h +++ b/src/devices/bluetooth/nm-bluez5-manager.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/bluetooth/nm-bt-error.c b/src/devices/bluetooth/nm-bt-error.c index 66c65b6d..bc9e5aa4 100644 --- a/src/devices/bluetooth/nm-bt-error.c +++ b/src/devices/bluetooth/nm-bt-error.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/bluetooth/nm-bt-error.h b/src/devices/bluetooth/nm-bt-error.h index fa3a9570..ed7ed90d 100644 --- a/src/devices/bluetooth/nm-bt-error.h +++ b/src/devices/bluetooth/nm-bt-error.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/bluetooth/nm-device-bt.c b/src/devices/bluetooth/nm-device-bt.c index e79251ce..0853c926 100644 --- a/src/devices/bluetooth/nm-device-bt.c +++ b/src/devices/bluetooth/nm-device-bt.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -341,6 +340,7 @@ complete_connection (NMDevice *device, preferred, fallback_prefix, NULL, + NULL, is_dun ? FALSE : TRUE); /* No IPv6 yet for DUN */ setting_bdaddr = nm_setting_bluetooth_get_bdaddr (s_bt); diff --git a/src/devices/bluetooth/nm-device-bt.h b/src/devices/bluetooth/nm-device-bt.h index b90dbd2a..6c8a5773 100644 --- a/src/devices/bluetooth/nm-device-bt.h +++ b/src/devices/bluetooth/nm-device-bt.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/nm-acd-manager.c b/src/devices/nm-acd-manager.c index f437ce3a..16d3df8a 100644 --- a/src/devices/nm-acd-manager.c +++ b/src/devices/nm-acd-manager.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/nm-acd-manager.h b/src/devices/nm-acd-manager.h index 08c0b798..53c88e4b 100644 --- a/src/devices/nm-acd-manager.h +++ b/src/devices/nm-acd-manager.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/nm-device-6lowpan.c b/src/devices/nm-device-6lowpan.c index 40103747..7a279431 100644 --- a/src/devices/nm-device-6lowpan.c +++ b/src/devices/nm-device-6lowpan.c @@ -190,6 +190,7 @@ complete_connection (NMDevice *device, NULL, _("6LOWPAN connection"), NULL, + NULL, TRUE); s_6lowpan = NM_SETTING_6LOWPAN (nm_connection_get_setting (connection, NM_TYPE_SETTING_6LOWPAN)); diff --git a/src/devices/nm-device-bond.c b/src/devices/nm-device-bond.c index 37159fca..fd79348d 100644 --- a/src/devices/nm-device-bond.c +++ b/src/devices/nm-device-bond.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -70,6 +69,7 @@ complete_connection (NMDevice *device, NULL, _("Bond connection"), "bond", + NULL, TRUE); s_bond = nm_connection_get_setting_bond (connection); @@ -413,6 +413,12 @@ release_slave (NMDevice *device, NMDeviceBond *self = NM_DEVICE_BOND (device); gboolean success; gs_free char *address = NULL; + int ifindex_slave; + + ifindex_slave = nm_device_get_ip_ifindex (slave); + + if (ifindex_slave <= 0) + _LOGD (LOGD_TEAM, "bond slave %s is already released", nm_device_get_ip_iface (slave)); if (configure) { /* When the last slave is released the bond MAC will be set to a random @@ -420,16 +426,18 @@ release_slave (NMDevice *device, */ address = g_strdup (nm_device_get_hw_address (device)); - success = nm_platform_link_release (nm_device_get_platform (device), - nm_device_get_ip_ifindex (device), - nm_device_get_ip_ifindex (slave)); - - if (success) { - _LOGI (LOGD_BOND, "released bond slave %s", - nm_device_get_ip_iface (slave)); - } else { - _LOGW (LOGD_BOND, "failed to release bond slave %s", - nm_device_get_ip_iface (slave)); + if (ifindex_slave > 0) { + success = nm_platform_link_release (nm_device_get_platform (device), + nm_device_get_ip_ifindex (device), + ifindex_slave); + + if (success) { + _LOGI (LOGD_BOND, "released bond slave %s", + nm_device_get_ip_iface (slave)); + } else { + _LOGW (LOGD_BOND, "failed to release bond slave %s", + nm_device_get_ip_iface (slave)); + } } nm_platform_process_events (nm_device_get_platform (device)); @@ -440,11 +448,15 @@ release_slave (NMDevice *device, * IFF_UP), so we must bring it back up here to ensure carrier changes and * other state is noticed by the now-released slave. */ - if (!nm_device_bring_up (slave, TRUE, NULL)) - _LOGW (LOGD_BOND, "released bond slave could not be brought up."); + if (ifindex_slave > 0) { + if (!nm_device_bring_up (slave, TRUE, NULL)) + _LOGW (LOGD_BOND, "released bond slave could not be brought up."); + } } else { - _LOGI (LOGD_BOND, "bond slave %s was released", - nm_device_get_ip_iface (slave)); + if (ifindex_slave > 0) { + _LOGI (LOGD_BOND, "bond slave %s was released", + nm_device_get_ip_iface (slave)); + } } } diff --git a/src/devices/nm-device-bond.h b/src/devices/nm-device-bond.h index b4e9b0e8..9448950b 100644 --- a/src/devices/nm-device-bond.h +++ b/src/devices/nm-device-bond.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c index 4275af91..ade9eb0d 100644 --- a/src/devices/nm-device-bridge.c +++ b/src/devices/nm-device-bridge.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -153,6 +152,7 @@ complete_connection (NMDevice *device, NULL, _("Bridge connection"), "bridge", + NULL, TRUE); s_bridge = nm_connection_get_setting_bridge (connection); @@ -625,11 +625,19 @@ release_slave (NMDevice *device, { NMDeviceBridge *self = NM_DEVICE_BRIDGE (device); gboolean success; + int ifindex_slave; + + ifindex_slave = nm_device_get_ip_ifindex (slave); + + if (ifindex_slave <= 0) { + _LOGD (LOGD_TEAM, "bond slave %s is already released", nm_device_get_ip_iface (slave)); + return; + } if (configure) { success = nm_platform_link_release (nm_device_get_platform (device), nm_device_get_ip_ifindex (device), - nm_device_get_ip_ifindex (slave)); + ifindex_slave); if (success) { _LOGI (LOGD_BRIDGE, "detached bridge port %s", diff --git a/src/devices/nm-device-bridge.h b/src/devices/nm-device-bridge.h index 44b4ed72..bc5ed04c 100644 --- a/src/devices/nm-device-bridge.h +++ b/src/devices/nm-device-bridge.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/nm-device-dummy.c b/src/devices/nm-device-dummy.c index 47a45342..9c0c3035 100644 --- a/src/devices/nm-device-dummy.c +++ b/src/devices/nm-device-dummy.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -66,6 +65,7 @@ complete_connection (NMDevice *device, NULL, _("Dummy connection"), NULL, + NULL, TRUE); s_dummy = nm_connection_get_setting_dummy (connection); diff --git a/src/devices/nm-device-dummy.h b/src/devices/nm-device-dummy.h index cc89a847..a380d366 100644 --- a/src/devices/nm-device-dummy.h +++ b/src/devices/nm-device-dummy.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/nm-device-ethernet-utils.c b/src/devices/nm-device-ethernet-utils.c index b18eadfa..1d7a060e 100644 --- a/src/devices/nm-device-ethernet-utils.c +++ b/src/devices/nm-device-ethernet-utils.c @@ -1,4 +1,3 @@ -/* -*- 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 of the License, or diff --git a/src/devices/nm-device-ethernet-utils.h b/src/devices/nm-device-ethernet-utils.h index 7e5c8b31..6355c4ec 100644 --- a/src/devices/nm-device-ethernet-utils.h +++ b/src/devices/nm-device-ethernet-utils.h @@ -1,4 +1,3 @@ -/* -*- 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 of the License, or diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c index 170c7e28..3e84847e 100644 --- a/src/devices/nm-device-ethernet.c +++ b/src/devices/nm-device-ethernet.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -561,7 +560,7 @@ build_supplicant_config (NMDeviceEthernet *self, 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); + config = nm_supplicant_config_new (FALSE, FALSE, FALSE, FALSE); security = nm_connection_get_setting_802_1x (connection); if (!nm_supplicant_config_add_setting_8021x (config, security, con_uuid, mtu, TRUE, error)) { @@ -1008,9 +1007,9 @@ pppoe_stage3_ip4_config_start (NMDeviceEthernet *self, NMDeviceStateReason *out_ if (priv->ppp_manager) { nm_ppp_manager_set_route_parameters (priv->ppp_manager, - nm_device_get_route_table (device, AF_INET, TRUE), + nm_device_get_route_table (device, AF_INET), nm_device_get_route_metric (device, AF_INET), - nm_device_get_route_table (device, AF_INET6, TRUE), + nm_device_get_route_table (device, AF_INET6), nm_device_get_route_metric (device, AF_INET6)); } @@ -1390,9 +1389,6 @@ complete_connection (NMDevice *device, { NMSettingWired *s_wired; NMSettingPppoe *s_pppoe; - const char *setting_mac; - const char *perm_hw_addr; - gboolean perm_hw_addr_is_fake; s_pppoe = nm_connection_get_setting_pppoe (connection); @@ -1402,6 +1398,12 @@ complete_connection (NMDevice *device, if (s_pppoe && !nm_setting_verify (NM_SETTING (s_pppoe), NULL, error)) return FALSE; + s_wired = nm_connection_get_setting_wired (connection); + if (!s_wired) { + s_wired = (NMSettingWired *) nm_setting_wired_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + } + /* Default to an ethernet-only connection, but if a PPPoE setting was given * then PPPoE should be our connection type. */ @@ -1412,34 +1414,9 @@ complete_connection (NMDevice *device, NULL, s_pppoe ? _("PPPoE connection") : _("Wired connection"), NULL, + nm_setting_wired_get_mac_address (s_wired) ? NULL : nm_device_get_iface (device), s_pppoe ? FALSE : TRUE); /* No IPv6 by default yet for PPPoE */ - s_wired = nm_connection_get_setting_wired (connection); - if (!s_wired) { - s_wired = (NMSettingWired *) nm_setting_wired_new (); - nm_connection_add_setting (connection, NM_SETTING (s_wired)); - } - - perm_hw_addr = nm_device_get_permanent_hw_address_full (device, TRUE, &perm_hw_addr_is_fake); - if (perm_hw_addr && !perm_hw_addr_is_fake) { - setting_mac = nm_setting_wired_get_mac_address (s_wired); - if (setting_mac) { - /* Make sure the setting MAC (if any) matches the device's permanent MAC */ - if (!nm_utils_hwaddr_matches (setting_mac, -1, perm_hw_addr, -1)) { - g_set_error_literal (error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("connection does not match device")); - g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_MAC_ADDRESS); - return FALSE; - } - } else { - g_object_set (G_OBJECT (s_wired), - NM_SETTING_WIRED_MAC_ADDRESS, perm_hw_addr, - NULL); - } - } - return TRUE; } @@ -1452,17 +1429,14 @@ new_default_connection (NMDevice *self) gs_unref_hashtable GHashTable *existing_ids = NULL; struct udev_device *dev; const char *perm_hw_addr; + const char *iface; const char *uprop = "0"; gs_free char *defname = NULL; gs_free char *uuid = NULL; guint i, n_connections; - if (nm_config_get_no_auto_default_for_device (nm_config_get (), self)) - return NULL; - perm_hw_addr = nm_device_get_permanent_hw_address (self); - if (!perm_hw_addr) - return NULL; + iface = nm_device_get_iface (self); connection = nm_simple_connection_new (); setting = nm_setting_connection_new (); @@ -1483,7 +1457,7 @@ new_default_connection (NMDevice *self) uuid = _nm_utils_uuid_generate_from_strings ("default-wired", nm_utils_machine_id_str (), defname, - perm_hw_addr, + perm_hw_addr ?: iface, NULL); g_object_set (setting, @@ -1493,13 +1467,9 @@ new_default_connection (NMDevice *self) NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY, NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_MIN, NM_SETTING_CONNECTION_UUID, uuid, NM_SETTING_CONNECTION_TIMESTAMP, (guint64) time (NULL), + NM_SETTING_CONNECTION_INTERFACE_NAME, iface, NULL); - /* Lock the connection to the device */ - setting = nm_setting_wired_new (); - g_object_set (setting, NM_SETTING_WIRED_MAC_ADDRESS, perm_hw_addr, NULL); - nm_connection_add_setting (connection, setting); - /* Check if we should create a Link-Local only connection */ dev = nm_platform_link_get_udev_device (nm_device_get_platform (NM_DEVICE (self)), nm_device_get_ip_ifindex (self)); if (dev) @@ -1575,11 +1545,11 @@ update_connection (NMDevice *device, NMConnection *connection) g_object_set (s_wired, NM_SETTING_WIRED_S390_SUBCHANNELS, priv->subchannels_dbus, NULL); if (priv->s390_nettype) g_object_set (s_wired, NM_SETTING_WIRED_S390_NETTYPE, priv->s390_nettype, NULL); + + _nm_setting_wired_clear_s390_options (s_wired); g_hash_table_iter_init (&iter, priv->s390_options); - while (g_hash_table_iter_next (&iter, &key, &value)) { + while (g_hash_table_iter_next (&iter, &key, &value)) nm_setting_wired_add_s390_option (s_wired, (const char *) key, (const char *) value); - } - } static void diff --git a/src/devices/nm-device-ethernet.h b/src/devices/nm-device-ethernet.h index 22fae293..d7de519c 100644 --- a/src/devices/nm-device-ethernet.h +++ b/src/devices/nm-device-ethernet.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/nm-device-factory.c b/src/devices/nm-device-factory.c index 30aca038..c0ec1d2e 100644 --- a/src/devices/nm-device-factory.c +++ b/src/devices/nm-device-factory.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/nm-device-factory.h b/src/devices/nm-device-factory.h index 33b596e6..7fe371ad 100644 --- a/src/devices/nm-device-factory.h +++ b/src/devices/nm-device-factory.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/nm-device-generic.c b/src/devices/nm-device-generic.c index d00aa93b..39232acf 100644 --- a/src/devices/nm-device-generic.c +++ b/src/devices/nm-device-generic.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/nm-device-generic.h b/src/devices/nm-device-generic.h index 42442180..9c59851a 100644 --- a/src/devices/nm-device-generic.h +++ b/src/devices/nm-device-generic.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/nm-device-infiniband.c b/src/devices/nm-device-infiniband.c index 4db7d8a7..89db2af2 100644 --- a/src/devices/nm-device-infiniband.c +++ b/src/devices/nm-device-infiniband.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -161,8 +160,12 @@ complete_connection (NMDevice *device, GError **error) { NMSettingInfiniband *s_infiniband; - const char *setting_mac; - const char *hw_address; + + s_infiniband = nm_connection_get_setting_infiniband (connection); + if (!s_infiniband) { + s_infiniband = (NMSettingInfiniband *) nm_setting_infiniband_new (); + nm_connection_add_setting (connection, NM_SETTING (s_infiniband)); + } nm_utils_complete_generic (nm_device_get_platform (device), connection, @@ -171,31 +174,9 @@ complete_connection (NMDevice *device, NULL, _("InfiniBand connection"), NULL, + nm_setting_infiniband_get_mac_address (s_infiniband) ? NULL : nm_device_get_iface (device), TRUE); - s_infiniband = nm_connection_get_setting_infiniband (connection); - if (!s_infiniband) { - s_infiniband = (NMSettingInfiniband *) nm_setting_infiniband_new (); - nm_connection_add_setting (connection, NM_SETTING (s_infiniband)); - } - - setting_mac = nm_setting_infiniband_get_mac_address (s_infiniband); - hw_address = nm_device_get_permanent_hw_address (device); - if (setting_mac) { - /* Make sure the setting MAC (if any) matches the device's MAC */ - if (!nm_utils_hwaddr_matches (setting_mac, -1, hw_address, -1)) { - g_set_error_literal (error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("connection does not match device")); - g_prefix_error (error, "%s.%s: ", NM_SETTING_INFINIBAND_SETTING_NAME, NM_SETTING_INFINIBAND_MAC_ADDRESS); - return FALSE; - } - } else { - /* Lock the connection to this device by default */ - g_object_set (G_OBJECT (s_infiniband), NM_SETTING_INFINIBAND_MAC_ADDRESS, hw_address, NULL); - } - if (!nm_setting_infiniband_get_transport_mode (s_infiniband)) g_object_set (G_OBJECT (s_infiniband), NM_SETTING_INFINIBAND_TRANSPORT_MODE, "datagram", NULL); diff --git a/src/devices/nm-device-infiniband.h b/src/devices/nm-device-infiniband.h index 719c9cc9..55d0aadc 100644 --- a/src/devices/nm-device-infiniband.h +++ b/src/devices/nm-device-infiniband.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/nm-device-ip-tunnel.c b/src/devices/nm-device-ip-tunnel.c index 96275947..ede4487e 100644 --- a/src/devices/nm-device-ip-tunnel.c +++ b/src/devices/nm-device-ip-tunnel.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -421,6 +420,7 @@ complete_connection (NMDevice *device, NULL, _("IP tunnel connection"), NULL, + NULL, TRUE); s_ip_tunnel = nm_connection_get_setting_ip_tunnel (connection); diff --git a/src/devices/nm-device-ip-tunnel.h b/src/devices/nm-device-ip-tunnel.h index 1109ace4..e38d36eb 100644 --- a/src/devices/nm-device-ip-tunnel.h +++ b/src/devices/nm-device-ip-tunnel.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/nm-device-logging.h b/src/devices/nm-device-logging.h index f0c7e591..c45a0552 100644 --- a/src/devices/nm-device-logging.h +++ b/src/devices/nm-device-logging.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/nm-device-macsec.c b/src/devices/nm-device-macsec.c index 1a6b64a4..e3e3a895 100644 --- a/src/devices/nm-device-macsec.c +++ b/src/devices/nm-device-macsec.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -225,7 +224,7 @@ build_supplicant_config (NMDeviceMacsec *self, GError **error) 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); + config = nm_supplicant_config_new (FALSE, FALSE, FALSE, FALSE); s_macsec = nm_device_get_applied_setting (NM_DEVICE (self), NM_TYPE_SETTING_MACSEC); diff --git a/src/devices/nm-device-macsec.h b/src/devices/nm-device-macsec.h index 23e9d2c9..e6919d4c 100644 --- a/src/devices/nm-device-macsec.h +++ b/src/devices/nm-device-macsec.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/nm-device-macvlan.c b/src/devices/nm-device-macvlan.c index aa2a0ac0..709f98da 100644 --- a/src/devices/nm-device-macvlan.c +++ b/src/devices/nm-device-macvlan.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -364,6 +363,7 @@ complete_connection (NMDevice *device, NULL, _("MACVLAN connection"), NULL, + NULL, TRUE); s_macvlan = nm_connection_get_setting_macvlan (connection); diff --git a/src/devices/nm-device-macvlan.h b/src/devices/nm-device-macvlan.h index c7d4be7c..9e76f5c9 100644 --- a/src/devices/nm-device-macvlan.h +++ b/src/devices/nm-device-macvlan.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/nm-device-ppp.c b/src/devices/nm-device-ppp.c index 3c310146..f2e68f73 100644 --- a/src/devices/nm-device-ppp.c +++ b/src/devices/nm-device-ppp.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -117,6 +116,25 @@ ppp_ip4_config (NMPPPManager *ppp_manager, } } +static gboolean +check_connection_compatible (NMDevice *device, NMConnection *connection, GError **error) +{ + NMSettingPppoe *s_pppoe; + + if (!NM_DEVICE_CLASS (nm_device_ppp_parent_class)->check_connection_compatible (device, connection, error)) + return FALSE; + + s_pppoe = nm_connection_get_setting_pppoe (connection); + if ( !s_pppoe + || !nm_setting_pppoe_get_parent (s_pppoe)) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_INCOMPATIBLE, + "the connection doesn't specify a PPPoE parent interface"); + return FALSE; + } + + return TRUE; +} + static NMActStageReturn act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) { @@ -140,9 +158,9 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) if (priv->ppp_manager) { nm_ppp_manager_set_route_parameters (priv->ppp_manager, - nm_device_get_route_table (device, AF_INET, TRUE), + nm_device_get_route_table (device, AF_INET), nm_device_get_route_metric (device, AF_INET), - nm_device_get_route_table (device, AF_INET6, TRUE), + nm_device_get_route_table (device, AF_INET6), nm_device_get_route_metric (device, AF_INET6)); } @@ -280,6 +298,7 @@ nm_device_ppp_class_init (NMDevicePppClass *klass) device_class->act_stage2_config = act_stage2_config; device_class->act_stage3_ip_config_start = act_stage3_ip_config_start; + device_class->check_connection_compatible = check_connection_compatible; device_class->create_and_realize = create_and_realize; device_class->deactivate = deactivate; device_class->get_generic_capabilities = get_generic_capabilities; diff --git a/src/devices/nm-device-ppp.h b/src/devices/nm-device-ppp.h index aaa18b9b..097f4b97 100644 --- a/src/devices/nm-device-ppp.h +++ b/src/devices/nm-device-ppp.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/nm-device-private.h b/src/devices/nm-device-private.h index dd98e92f..0a414af9 100644 --- a/src/devices/nm-device-private.h +++ b/src/devices/nm-device-private.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/nm-device-tun.c b/src/devices/nm-device-tun.c index 90360c9e..afe83f50 100644 --- a/src/devices/nm-device-tun.c +++ b/src/devices/nm-device-tun.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -156,6 +155,7 @@ complete_connection (NMDevice *device, NULL, _("TUN connection"), NULL, + NULL, TRUE); s_tun = nm_connection_get_setting_tun (connection); diff --git a/src/devices/nm-device-tun.h b/src/devices/nm-device-tun.h index b0d309bb..f665b942 100644 --- a/src/devices/nm-device-tun.h +++ b/src/devices/nm-device-tun.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/nm-device-veth.c b/src/devices/nm-device-veth.c index d7a59bae..0d6425d4 100644 --- a/src/devices/nm-device-veth.c +++ b/src/devices/nm-device-veth.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/nm-device-veth.h b/src/devices/nm-device-veth.h index 53ccf925..07217ff9 100644 --- a/src/devices/nm-device-veth.h +++ b/src/devices/nm-device-veth.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c index ace6a24b..0467a6e4 100644 --- a/src/devices/nm-device-vlan.c +++ b/src/devices/nm-device-vlan.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -399,6 +398,7 @@ complete_connection (NMDevice *device, NULL, _("VLAN connection"), NULL, + NULL, TRUE); s_vlan = nm_connection_get_setting_vlan (connection); diff --git a/src/devices/nm-device-vlan.h b/src/devices/nm-device-vlan.h index 375e8fa4..c463db58 100644 --- a/src/devices/nm-device-vlan.h +++ b/src/devices/nm-device-vlan.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/nm-device-vxlan.c b/src/devices/nm-device-vxlan.c index fc73c099..1a700ba4 100644 --- a/src/devices/nm-device-vxlan.c +++ b/src/devices/nm-device-vxlan.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -368,6 +367,7 @@ complete_connection (NMDevice *device, NULL, _("VXLAN connection"), NULL, + NULL, TRUE); s_vxlan = nm_connection_get_setting_vxlan (connection); diff --git a/src/devices/nm-device-vxlan.h b/src/devices/nm-device-vxlan.h index 511b7156..dd14d910 100644 --- a/src/devices/nm-device-vxlan.h +++ b/src/devices/nm-device-vxlan.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/nm-device-wireguard.c b/src/devices/nm-device-wireguard.c index f8c7d1c3..d36573cf 100644 --- a/src/devices/nm-device-wireguard.c +++ b/src/devices/nm-device-wireguard.c @@ -21,12 +21,16 @@ #include "nm-device-wireguard.h" +#include <linux/rtnetlink.h> +#include <linux/fib_rules.h> + #include "nm-setting-wireguard.h" #include "nm-core-internal.h" #include "nm-glib-aux/nm-secret-utils.h" #include "nm-device-private.h" #include "platform/nm-platform.h" #include "platform/nmp-object.h" +#include "platform/nmp-rules-manager.h" #include "nm-device-factory.h" #include "nm-active-connection.h" #include "nm-act-request.h" @@ -134,10 +138,21 @@ typedef struct { GHashTable *peers; gint64 resolve_next_try_at; - guint resolve_next_try_id; - gint64 link_config_last_at; + + guint resolve_next_try_id; guint link_config_delayed_id; + + guint32 auto_default_route_fwmark; + + guint32 auto_default_route_priority; + + bool auto_default_route_enabled_4:1; + bool auto_default_route_enabled_6:1; + bool auto_default_route_initialized:1; + bool auto_default_route_refresh:1; + bool auto_default_route_priority_initialized:1; + } NMDeviceWireGuardPrivate; struct _NMDeviceWireGuard { @@ -177,6 +192,394 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_link_config_mode_to_string, LinkConfigMode, /*****************************************************************************/ +static void +_auto_default_route_get_enabled (NMSettingWireGuard *s_wg, + NMConnection *connection, + gboolean *out_enabled_v4, + gboolean *out_enabled_v6) +{ + NMTernary enabled_v4; + NMTernary enabled_v6; + + enabled_v4 = nm_setting_wireguard_get_ip4_auto_default_route (s_wg); + enabled_v6 = nm_setting_wireguard_get_ip6_auto_default_route (s_wg); + + if (enabled_v4 == NM_TERNARY_DEFAULT) { + if (nm_setting_ip_config_get_never_default (nm_connection_get_setting_ip_config (connection, AF_INET))) + enabled_v4 = FALSE; + } + if (enabled_v6 == NM_TERNARY_DEFAULT) { + if (nm_setting_ip_config_get_never_default (nm_connection_get_setting_ip_config (connection, AF_INET6))) + enabled_v6 = FALSE; + } + + if ( enabled_v4 == NM_TERNARY_DEFAULT + || enabled_v6 == NM_TERNARY_DEFAULT) { + guint i, n_peers; + + n_peers = nm_setting_wireguard_get_peers_len (s_wg); + for (i = 0; i < n_peers; i++) { + NMWireGuardPeer *peer = nm_setting_wireguard_get_peer (s_wg, i); + guint n_aips; + guint j; + + n_aips = nm_wireguard_peer_get_allowed_ips_len (peer); + for (j = 0; j < n_aips; j++) { + const char *aip; + gboolean valid; + int prefix; + int addr_family; + + aip = nm_wireguard_peer_get_allowed_ip (peer, j, &valid); + if (!valid) + continue; + if (!nm_utils_parse_inaddr_prefix_bin (AF_UNSPEC, + aip, + &addr_family, + NULL, + &prefix)) + continue; + if (prefix != 0) + continue; + + if (addr_family == AF_INET) { + if (enabled_v4 == NM_TERNARY_DEFAULT) { + enabled_v4 = TRUE; + if (enabled_v6 != NM_TERNARY_DEFAULT) + goto done; + } + } else { + if (enabled_v6 == NM_TERNARY_DEFAULT) { + enabled_v6 = TRUE; + if (enabled_v4 != NM_TERNARY_DEFAULT) + goto done; + } + } + } + } +done: + ; + } + + *out_enabled_v4 = (enabled_v4 == TRUE); + *out_enabled_v6 = (enabled_v6 == TRUE); +} + +static guint32 +_auto_default_route_find_unused_table (NMPlatform *platform) +{ + guint32 table; + int is_ipv4; + + for (table = 51820; TRUE; table++) { + const NMDedupMultiHeadEntry *head_entry; + const guint32 table_coerced = nm_platform_route_table_coerce (table); + NMDedupMultiIter iter; + const NMPObject *plobj; + + /* find a table/fwmark that is not yet in use. */ + + for (is_ipv4 = 0; is_ipv4 < 2; is_ipv4++) { + head_entry = nm_platform_lookup_object (platform, + is_ipv4 + ? NMP_OBJECT_TYPE_IP4_ROUTE + : NMP_OBJECT_TYPE_IP6_ROUTE, + -1); + nmp_cache_iter_for_each (&iter, head_entry, &plobj) { + if (NMP_OBJECT_CAST_IP_ROUTE (plobj)->table_coerced == table_coerced) + goto try_next_table; + } + } + + head_entry = nm_platform_lookup_object_by_addr_family (platform, + NMP_OBJECT_TYPE_ROUTING_RULE, + AF_UNSPEC); + nmp_cache_iter_for_each (&iter, head_entry, &plobj) { + const NMPlatformRoutingRule *rr = NMP_OBJECT_CAST_ROUTING_RULE (plobj); + + if (rr->fwmark == table) + goto try_next_table; + } + + head_entry = nm_platform_lookup_obj_type (platform, NMP_OBJECT_TYPE_LINK); + nmp_cache_iter_for_each (&iter, head_entry, &plobj) { + const NMPObject *lnk_wg; + + if (plobj->link.type != NM_LINK_TYPE_WIREGUARD) + continue; + + lnk_wg = plobj->_link.netlink.lnk; + + if (!lnk_wg) + continue; + + if (NMP_OBJECT_GET_TYPE (lnk_wg) != NMP_OBJECT_TYPE_LNK_WIREGUARD) + continue; + + if (NMP_OBJECT_CAST_LNK_WIREGUARD (lnk_wg)->fwmark == table) + goto try_next_table; + } + + return table; +try_next_table: + ; + } +} + +#define PRIO_WIDTH ((guint32) 2) + +static gboolean +_auto_default_route_find_priority_exists (const NMDedupMultiHeadEntry *head_entry, + guint32 priority) +{ + NMDedupMultiIter iter; + const NMPObject *plobj; + + nmp_cache_iter_for_each (&iter, head_entry, &plobj) { + const NMPlatformRoutingRule *rr = NMP_OBJECT_CAST_ROUTING_RULE (plobj); + + /* we don't differenciate between IPv4 vs. IPv6. There should be no + * conflicting rules with the same priority. */ + if ( rr->priority >= priority + && rr->priority < priority + PRIO_WIDTH) + return TRUE; + } + + return FALSE; +} + +static guint32 +_auto_default_route_find_priority (NMPlatform *platform, + const char *uuid) +{ + const NMDedupMultiHeadEntry *head_entry; + guint64 rnd_seed; + const guint32 PRIME_NUMBER = 1111567573u; + const guint32 RANGE_TOP = ((32766u - 2u * PRIO_WIDTH) / PRIO_WIDTH); + const guint32 RANGE_LEN1 = 200u; + const guint32 RANGE_LEN2 = (RANGE_TOP - 100u) - RANGE_LEN1; + guint32 range_len; + guint32 range_top; + guint32 prio_candidate = 0; + guint32 i_step; + guint32 i; + + /* For the auto-default-route policy routing rule we add 4 rules (2 Ipv4 and 2 IPv6). + * Hence, we choose a priority for the first (of the two rules) and the second + * rule gets priority + 1. + * We want a priority that is + * - unused so far. + * - smaller than 32766u (which is the priority of the default rules for IPv4 and IPv6) + * - stable for each connection but different between connections (we hash the UUID + * as a "random" seed) + * - if possible, close to 32766u (RANGE_LEN1). Only otherwise fallback to the entire + * range (RANGE_LEN2). + */ + + rnd_seed = c_siphash_hash ((const guint8 [16]) { 0xb9, 0x39, 0x8e, 0xed, 0x15, 0xb3, 0xd1, 0xc4, 0x5f, 0x45, 0x00, 0x4f, 0xec, 0xc2, 0x2b, 0x7e }, + (const guint8 *) uuid, + uuid ? strlen (uuid) + 1u : 0u); + + head_entry = nm_platform_lookup_object_by_addr_family (platform, + NMP_OBJECT_TYPE_ROUTING_RULE, + AF_UNSPEC); + + range_len = RANGE_LEN1; + range_top = RANGE_TOP; + +again: + i_step = ((guint32) rnd_seed) % range_len; + for (i = 0; i < range_len; i++) { + + /* we sample the range in a stable, but somewhat arbitrary order to + * find an unused priority. */ + i_step = (i_step + PRIME_NUMBER) % range_len; + + nm_assert (i_step < range_top); + + prio_candidate = (range_top - i_step) * PRIO_WIDTH; + + nm_assert (prio_candidate < 32766u); + + if (!_auto_default_route_find_priority_exists (head_entry, prio_candidate)) + return prio_candidate; + } + + if (range_len == RANGE_LEN1) { + /* within the narrow range close to RANGE_TOP we couldn't find any unused + * priority. Retry with the entire range... */ + range_len = RANGE_LEN2; + range_top -= RANGE_LEN1; + goto again; + } + + /* Couldn't find an unused one? Very odd, this really should not happen unless there + * are thousands of rules already. Just pick the last one we sampled. */ + return prio_candidate; +} + +static void +_auto_default_route_init (NMDeviceWireGuard *self) +{ + NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE (self); + NMConnection *connection; + NMSettingWireGuard *s_wg; + gboolean enabled_v4; + gboolean enabled_v6; + gboolean refreshing_only; + guint32 old_fwmark; + char sbuf1[100]; + + if (G_LIKELY ( priv->auto_default_route_initialized + && !priv->auto_default_route_refresh)) + return; + + refreshing_only = priv->auto_default_route_initialized + && priv->auto_default_route_refresh; + priv->auto_default_route_refresh = FALSE; + + connection = nm_device_get_applied_connection (NM_DEVICE (self)); + + s_wg = _nm_connection_get_setting (connection, NM_TYPE_SETTING_WIREGUARD); + + old_fwmark = priv->auto_default_route_fwmark; + + priv->auto_default_route_fwmark = nm_setting_wireguard_get_fwmark (s_wg); + + _auto_default_route_get_enabled (s_wg, + connection, + &enabled_v4, + &enabled_v6); + priv->auto_default_route_enabled_4 = enabled_v4; + priv->auto_default_route_enabled_6 = enabled_v6; + priv->auto_default_route_initialized = TRUE; + + if ( ( priv->auto_default_route_enabled_4 + || priv->auto_default_route_enabled_6) + && priv->auto_default_route_fwmark == 0u) { + if (refreshing_only) + priv->auto_default_route_fwmark = old_fwmark; + else + priv->auto_default_route_fwmark = _auto_default_route_find_unused_table (nm_device_get_platform (NM_DEVICE (self))); + } + + _LOGT (LOGD_DEVICE, + "auto-default-route is %s for IPv4 and %s for IPv6%s", + priv->auto_default_route_enabled_4 ? "enabled" : "disabled", + priv->auto_default_route_enabled_6 ? "enabled" : "disabled", + priv->auto_default_route_enabled_4 || priv->auto_default_route_enabled_6 + ? nm_sprintf_buf (sbuf1, " (fwmark 0x%x)", priv->auto_default_route_fwmark) + : ""); +} + +static GPtrArray * +get_extra_rules (NMDevice *device) +{ + NMDeviceWireGuard *self = NM_DEVICE_WIREGUARD (device); + NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE (self); + gs_unref_ptrarray GPtrArray *extra_rules = NULL; + guint32 priority = 0; + int is_ipv4; + NMConnection *connection; + + _auto_default_route_init (self); + + connection = nm_device_get_applied_connection (device); + if (!connection) + return NULL; + + for (is_ipv4 = 0; is_ipv4 < 2; is_ipv4++) { + NMSettingIPConfig *s_ip; + int addr_family = is_ipv4 ? AF_INET : AF_INET6; + guint32 table_main; + guint32 fwmark; + + if (is_ipv4) { + if (!priv->auto_default_route_enabled_4) + continue; + } else { + if (!priv->auto_default_route_enabled_6) + continue; + } + + if (!extra_rules) { + if (priv->auto_default_route_priority_initialized) + priority = priv->auto_default_route_priority; + else { + priority = _auto_default_route_find_priority (nm_device_get_platform (device), + nm_connection_get_uuid (connection)); + priv->auto_default_route_priority = priority; + priv->auto_default_route_priority_initialized = TRUE; + } + extra_rules = g_ptr_array_new_with_free_func ((GDestroyNotify) nmp_object_unref); + } + + s_ip = nm_connection_get_setting_ip_config (connection, addr_family); + table_main = nm_setting_ip_config_get_route_table (s_ip); + if (table_main == 0) + table_main = RT_TABLE_MAIN; + + fwmark = priv->auto_default_route_fwmark; + + G_STATIC_ASSERT_EXPR (PRIO_WIDTH == 2); + + g_ptr_array_add (extra_rules, + nmp_object_new (NMP_OBJECT_TYPE_ROUTING_RULE, + &((const NMPlatformRoutingRule) { + .priority = priority, + .addr_family = addr_family, + .action = FR_ACT_TO_TBL, + .table = table_main, + .suppress_prefixlen_inverse = ~((guint32) 0u), + }))); + + g_ptr_array_add (extra_rules, + nmp_object_new (NMP_OBJECT_TYPE_ROUTING_RULE, + &((const NMPlatformRoutingRule) { + .priority = priority + 1u, + .addr_family = addr_family, + .action = FR_ACT_TO_TBL, + .table = fwmark, + .flags = FIB_RULE_INVERT, + .fwmark = fwmark, + .fwmask = 0xFFFFFFFFu, + }))); + } + + return g_steal_pointer (&extra_rules); +} + +static guint32 +coerce_route_table (NMDevice *device, + int addr_family, + guint32 route_table, + gboolean is_user_config) +{ + NMDeviceWireGuard *self = NM_DEVICE_WIREGUARD (device); + NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE (self); + gboolean auto_default_route_enabled; + + if (route_table != 0u) + return route_table; + + _auto_default_route_init (self); + + auto_default_route_enabled = (addr_family == AF_INET) + ? priv->auto_default_route_enabled_4 + : priv->auto_default_route_enabled_6; + + if (auto_default_route_enabled) { + /* we need to enable full-sync mode of all routing tables. */ + _LOGT (LOGD_DEVICE, "coerce ipv%c.route-table setting to \"main\" (table 254) as we enable auto-default-route handling", + nm_utils_addr_family_to_char (addr_family)); + return RT_TABLE_MAIN; + } + + return 0; +} + +/*****************************************************************************/ + static gboolean _peer_data_equal (gconstpointer ptr_a, gconstpointer ptr_b) { @@ -1084,6 +1487,8 @@ link_config (NMDeviceWireGuard *self, _LOGT (LOGD_DEVICE, "wireguard link config (%s, %s)...", reason, _link_config_mode_to_string (config_mode)); + _auto_default_route_init (self); + if (!priv->dns_manager) { priv->dns_manager = g_object_ref (nm_dns_manager_get ()); g_signal_connect (priv->dns_manager, NM_DNS_MANAGER_CONFIG_CHANGED, G_CALLBACK (_dns_config_changed), self); @@ -1127,10 +1532,10 @@ link_config (NMDeviceWireGuard *self, if (NM_IN_SET (config_mode, LINK_CONFIG_MODE_FULL, LINK_CONFIG_MODE_REAPPLY)) { - wg_lnk.listen_port = nm_setting_wireguard_get_listen_port (s_wg), + wg_lnk.listen_port = nm_setting_wireguard_get_listen_port (s_wg); wg_change_flags |= NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_LISTEN_PORT; - wg_lnk.fwmark = nm_setting_wireguard_get_fwmark (s_wg), + wg_lnk.fwmark = priv->auto_default_route_fwmark; wg_change_flags |= NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_FWMARK; if (nm_utils_base64secret_decode (nm_setting_wireguard_get_private_key (s_wg), @@ -1256,6 +1661,7 @@ static NMIPConfig * _get_dev2_ip_config (NMDeviceWireGuard *self, int addr_family) { + NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE (self); gs_unref_object NMIPConfig *ip_config = NULL; NMConnection *connection; NMSettingWireGuard *s_wg; @@ -1264,6 +1670,9 @@ _get_dev2_ip_config (NMDeviceWireGuard *self, int ip_ifindex; guint32 route_metric; guint32 route_table_coerced; + gboolean auto_default_route_enabled; + + _auto_default_route_init (self); connection = nm_device_get_applied_connection (NM_DEVICE (self)); @@ -1301,7 +1710,11 @@ _get_dev2_ip_config (NMDeviceWireGuard *self, route_metric = nm_device_get_route_metric (NM_DEVICE (self), addr_family); - route_table_coerced = nm_platform_route_table_coerce (nm_device_get_route_table (NM_DEVICE (self), addr_family, TRUE)); + route_table_coerced = nm_platform_route_table_coerce (nm_device_get_route_table (NM_DEVICE (self), addr_family)); + + auto_default_route_enabled = (addr_family == AF_INET) + ? priv->auto_default_route_enabled_4 + : priv->auto_default_route_enabled_6; n_peers = nm_setting_wireguard_get_peers_len (s_wg); for (i = 0; i < n_peers; i++) { @@ -1316,6 +1729,7 @@ _get_dev2_ip_config (NMDeviceWireGuard *self, const char *aip; gboolean valid; int prefix; + guint32 rtable_coerced; aip = nm_wireguard_peer_get_allowed_ip (peer, j, &valid); @@ -1335,13 +1749,24 @@ _get_dev2_ip_config (NMDeviceWireGuard *self, nm_utils_ipx_address_clear_host_address (addr_family, &addrbin, NULL, prefix); + rtable_coerced = route_table_coerced; + + if ( prefix == 0 + && auto_default_route_enabled) { + /* In auto-default-route mode, we place the default route in a table that + * has the same number as the fwmark. wg-quick does that too. If you don't + * like that, configure the rules and the default-route explicitly in the + * connection profile. */ + rtable_coerced = nm_platform_route_table_coerce (priv->auto_default_route_fwmark); + } + if (addr_family == AF_INET) { rt.r4 = (NMPlatformIP4Route) { .network = addrbin.addr4, .plen = prefix, .ifindex = ip_ifindex, .rt_source = NM_IP_CONFIG_SOURCE_USER, - .table_coerced = route_table_coerced, + .table_coerced = rtable_coerced, .metric = route_metric, }; } else { @@ -1350,7 +1775,7 @@ _get_dev2_ip_config (NMDeviceWireGuard *self, .plen = prefix, .ifindex = ip_ifindex, .rt_source = NM_IP_CONFIG_SOURCE_USER, - .table_coerced = route_table_coerced, + .table_coerced = rtable_coerced, .metric = route_metric, }; } @@ -1406,20 +1831,28 @@ get_configured_mtu (NMDevice *device, NMDeviceMtuSource *out_source) } static void +_device_cleanup (NMDeviceWireGuard *self) +{ + NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE (self); + + _peers_remove_all (priv); + + _secrets_cancel (self); + + priv->auto_default_route_initialized = FALSE; + priv->auto_default_route_priority_initialized = FALSE; +} + +static void device_state_changed (NMDevice *device, NMDeviceState new_state, NMDeviceState old_state, NMDeviceStateReason reason) { - NMDeviceWireGuardPrivate *priv; - if (new_state <= NM_DEVICE_STATE_ACTIVATED) return; - priv = NM_DEVICE_WIREGUARD_GET_PRIVATE (device); - - _peers_remove_all (priv); - _secrets_cancel (NM_DEVICE_WIREGUARD (device)); + _device_cleanup (NM_DEVICE_WIREGUARD (device)); } /*****************************************************************************/ @@ -1440,6 +1873,8 @@ can_reapply_change (NMDevice *device, NM_SETTING_WIREGUARD_SETTING_NAME, error, NM_SETTING_WIREGUARD_FWMARK, + NM_SETTING_WIREGUARD_IP4_AUTO_DEFAULT_ROUTE, + NM_SETTING_WIREGUARD_IP6_AUTO_DEFAULT_ROUTE, NM_SETTING_WIREGUARD_LISTEN_PORT, NM_SETTING_WIREGUARD_PEERS, NM_SETTING_WIREGUARD_PEER_ROUTES, @@ -1461,9 +1896,12 @@ reapply_connection (NMDevice *device, NMConnection *con_new) { NMDeviceWireGuard *self = NM_DEVICE_WIREGUARD (device); + NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE (self); gs_unref_object NMIPConfig *ip4_config = NULL; gs_unref_object NMIPConfig *ip6_config = NULL; + priv->auto_default_route_refresh = TRUE; + ip4_config = _get_dev2_ip_config (self, AF_INET); ip6_config = _get_dev2_ip_config (self, AF_INET6); @@ -1567,11 +2005,8 @@ static void dispose (GObject *object) { NMDeviceWireGuard *self = NM_DEVICE_WIREGUARD (object); - NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE (self); - _secrets_cancel (self); - - _peers_remove_all (priv); + _device_cleanup (self); G_OBJECT_CLASS (nm_device_wireguard_parent_class)->dispose (object); } @@ -1589,6 +2024,8 @@ finalize (GObject *object) g_object_unref (priv->dns_manager); } + g_hash_table_destroy (priv->peers); + G_OBJECT_CLASS (nm_device_wireguard_parent_class)->finalize (object); } @@ -1631,6 +2068,8 @@ nm_device_wireguard_class_init (NMDeviceWireGuardClass *klass) device_class->can_reapply_change = can_reapply_change; device_class->reapply_connection = reapply_connection; device_class->get_configured_mtu = get_configured_mtu; + device_class->get_extra_rules = get_extra_rules; + device_class->coerce_route_table = coerce_route_table; obj_properties[PROP_PUBLIC_KEY] = g_param_spec_variant (NM_DEVICE_WIREGUARD_PUBLIC_KEY, diff --git a/src/devices/nm-device-wpan.c b/src/devices/nm-device-wpan.c index cdfd1f70..88234412 100644 --- a/src/devices/nm-device-wpan.c +++ b/src/devices/nm-device-wpan.c @@ -67,6 +67,7 @@ complete_connection (NMDevice *device, NULL, _("WPAN connection"), NULL, + NULL, TRUE); s_wpan = NM_SETTING_WPAN (nm_connection_get_setting (connection, NM_TYPE_SETTING_WPAN)); diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index bd4fbcc3..feb5110d 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -128,6 +127,15 @@ typedef struct { int ifindex; } DeleteOnDeactivateData; +typedef struct { + NMDevice *device; + GCancellable *cancellable; + NMPlatformAsyncCallback callback; + gpointer callback_data; + guint num_vfs; + NMTernary autoprobe; +} SriovOp; + typedef void (*AcdCallback) (NMDevice *, NMIP4Config **, gboolean); typedef struct { @@ -328,16 +336,18 @@ typedef struct _NMDevicePrivate { ActivationHandleData act_handle4; /* for layer2 and IPv4. */ ActivationHandleData act_handle6; guint recheck_assume_id; + struct { guint call_id; NMDeviceStateReason available_reason; NMDeviceStateReason unavailable_reason; - } recheck_available; + } recheck_available; + struct { - guint call_id; + NMDispatcherCallId *call_id; NMDeviceState post_state; NMDeviceStateReason post_state_reason; - } dispatcher; + } dispatcher; /* Link stuff */ guint link_connected_id; @@ -378,6 +388,9 @@ typedef struct _NMDevicePrivate { bool v4_route_table_initialized:1; bool v6_route_table_initialized:1; + bool v4_route_table_full_sync_before:1; + bool v6_route_table_full_sync_before:1; + NMDeviceAutoconnectBlockedFlags autoconnect_blocked_flags:5; bool is_enslaved:1; @@ -397,8 +410,7 @@ typedef struct _NMDevicePrivate { /* Proxy Configuration */ NMProxyConfig *proxy_config; - NMPacrunnerManager *pacrunner_manager; - NMPacrunnerCallId *pacrunner_call_id; + NMPacrunnerConfId *pacrunner_conf_id; /* IP configuration info. Combined config from VPN, settings, and device */ union { @@ -575,12 +587,16 @@ typedef struct _NMDevicePrivate { guint check_delete_unrealized_id; struct { + SriovOp *pending; /* SR-IOV operation currently running */ + SriovOp *next; /* next SR-IOV operation scheduled */ + } sriov; + + struct { guint timeout_id; guint refresh_rate_ms; guint64 tx_bytes; guint64 rx_bytes; } stats; - } NMDevicePrivate; G_DEFINE_ABSTRACT_TYPE (NMDevice, nm_device, NM_TYPE_DBUS_OBJECT) @@ -636,7 +652,8 @@ static void realize_start_setup (NMDevice *self, gboolean assume_state_guess_assume, const char *assume_state_connection_uuid, gboolean set_nm_owned, - NMUnmanFlagOp unmanaged_user_explicit); + NMUnmanFlagOp unmanaged_user_explicit, + gboolean force_platform_init); static void _set_mtu (NMDevice *self, guint32 mtu); static void _commit_mtu (NMDevice *self, const NMIP4Config *config); static void _cancel_activation (NMDevice *self); @@ -1898,7 +1915,7 @@ get_ip_iface_identifier (NMDevice *self, NMUtilsIPv6IfaceId *out_iid) const NMPlatformLink *pllink; const guint8 *hwaddr; guint8 pseudo_hwaddr[ETH_ALEN]; - guint hwaddr_len; + gsize hwaddr_len; int ifindex; gboolean success; @@ -1911,13 +1928,9 @@ get_ip_iface_identifier (NMDevice *self, NMUtilsIPv6IfaceId *out_iid) || NM_IN_SET (pllink->type, NM_LINK_TYPE_NONE, NM_LINK_TYPE_UNKNOWN)) return FALSE; - if (pllink->addr.len <= 0) + hwaddr = nmp_link_address_get (&pllink->l_address, &hwaddr_len); + if (hwaddr_len <= 0) return FALSE; - if (pllink->addr.len > NM_UTILS_HWADDR_LEN_MAX) - g_return_val_if_reached (FALSE); - - hwaddr = pllink->addr.data; - hwaddr_len = pllink->addr.len; if (pllink->type == NM_LINK_TYPE_6LOWPAN) { /* If the underlying IEEE 802.15.4 device has a short address we generate @@ -1949,7 +1962,7 @@ get_ip_iface_identifier (NMDevice *self, NMUtilsIPv6IfaceId *out_iid) out_iid); if (!success) { _LOGW (LOGD_PLATFORM, "failed to generate interface identifier " - "for link type %u hwaddr_len %u", pllink->type, (unsigned) pllink->addr.len); + "for link type %u hwaddr_len %zu", pllink->type, hwaddr_len); } return success; } @@ -2247,30 +2260,27 @@ _get_llmnr (NMDevice *self) NM_SETTING_CONNECTION_LLMNR_DEFAULT); } -guint32 -nm_device_get_route_table (NMDevice *self, - int addr_family, - gboolean fallback_main) +static guint32 +_get_route_table (NMDevice *self, + int addr_family) { - NMDevicePrivate *priv; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMDeviceClass *klass; NMConnection *connection; NMSettingIPConfig *s_ip; guint32 route_table = 0; + gboolean is_user_config = TRUE; nm_assert_addr_family (addr_family); - g_return_val_if_fail (NM_IS_DEVICE (self), RT_TABLE_MAIN); - - priv = NM_DEVICE_GET_PRIVATE (self); - /* the route table setting affects how we sync routes. We shall * not change it while the device is active, hence, cache it. */ if (addr_family == AF_INET) { if (priv->v4_route_table_initialized) - return priv->v4_route_table ?: (fallback_main ? RT_TABLE_MAIN : 0); + return priv->v4_route_table; } else { if (priv->v6_route_table_initialized) - return priv->v6_route_table ?: (fallback_main ? RT_TABLE_MAIN : 0); + return priv->v6_route_table; } connection = nm_device_get_applied_connection (self); @@ -2278,23 +2288,28 @@ nm_device_get_route_table (NMDevice *self, s_ip = nm_connection_get_setting_ip_config (connection, addr_family); if (s_ip) route_table = nm_setting_ip_config_get_route_table (s_ip); - - /* we only lookup the global default if we also have an applied - * connection. Otherwise, the connection is not active, and the - * connection default doesn't matter. */ - if (route_table == 0) { - const char *property; - - 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, - 0, G_MAXUINT32, 0); + } + if (route_table == 0u) { + gint64 v; + + v = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA, + addr_family == AF_INET + ? NM_CON_DEFAULT ("ipv4.route-table") + : NM_CON_DEFAULT ("ipv6.route-table"), + self, + 0, + G_MAXUINT32, + -1); + if (v != -1) { + route_table = v; + is_user_config = FALSE; } } + klass = NM_DEVICE_GET_CLASS (self); + if (klass->coerce_route_table) + route_table = klass->coerce_route_table (self, addr_family, route_table, is_user_config); + if (addr_family == AF_INET) { priv->v4_route_table_initialized = TRUE; priv->v4_route_table = route_table; @@ -2307,9 +2322,57 @@ nm_device_get_route_table (NMDevice *self, "ipv%c.route-table = %u%s", addr_family == AF_INET ? '4' : '6', (guint) (route_table ?: RT_TABLE_MAIN), - route_table ? "" : " (policy routing not enabled)"); + route_table != 0u ? "" : " (policy routing not enabled)"); - return route_table ?: (fallback_main ? RT_TABLE_MAIN : 0); + return route_table; +} + +guint32 +nm_device_get_route_table (NMDevice *self, + int addr_family) +{ + guint32 route_table; + + g_return_val_if_fail (NM_IS_DEVICE (self), RT_TABLE_MAIN); + + route_table = _get_route_table (self, addr_family); + return route_table ?: (guint32) RT_TABLE_MAIN; +} + +static NMIPRouteTableSyncMode +_get_route_table_sync_mode_stateful (NMDevice *self, + int addr_family) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + gboolean full_sync_now; + gboolean full_sync_eff; + + full_sync_now = _get_route_table (self, addr_family) != 0u; + + if (full_sync_now) + full_sync_eff = TRUE; + else { + /* When we change from full-sync to no full-sync, we do a last full-sync one + * more time. For that, we determine the effective full-state based on the + * cached/previous full-sync flag. + * + * The purpose of this is to support reapply of route-table (and thus the + * full-sync mode). If reapply toggles from full-sync to no-full-sync, we must + * sync one last time. */ + if (addr_family == AF_INET) + full_sync_eff = priv->v4_route_table_full_sync_before; + else + full_sync_eff = priv->v6_route_table_full_sync_before; + } + + if (addr_family == AF_INET) + priv->v4_route_table_full_sync_before = full_sync_now; + else + priv->v6_route_table_full_sync_before = full_sync_now; + + return full_sync_eff + ? NM_IP_ROUTE_TABLE_SYNC_MODE_FULL + : NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN; } const NMPObject * @@ -2801,6 +2864,7 @@ concheck_update_state (NMDevice *self, NM_CONNECTIVITY_PORTAL, NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_FAKE, + NM_CONNECTIVITY_NONE, NM_CONNECTIVITY_ERROR)); if (state == NM_CONNECTIVITY_ERROR) { @@ -2823,14 +2887,6 @@ concheck_update_state (NMDevice *self, state = NM_CONNECTIVITY_LIMITED; } else state = NM_CONNECTIVITY_NONE; - } else if (state == NM_CONNECTIVITY_LIMITED) { - /* NMConnectivity cannot distinguish between NONE and LIMITED connectivity. In both - * cases, it just failed to fetch the URL. - * - * NMDevice coerces a LIMITED state to NONE here, if the logical state of the device - * is disconnected. */ - if (priv->state <= NM_DEVICE_STATE_DISCONNECTED) - state = NM_CONNECTIVITY_NONE; } if (priv->concheck_x[IS_IPv4].state == state) { @@ -3096,28 +3152,17 @@ concheck_start (NMDevice *self, && !priv->concheck_rp_filter_checked) { if ((ifname = nm_device_get_ip_iface_from_platform (self))) { - int val, val_all; - - val = nm_platform_sysctl_ip_conf_get_int_checked (nm_device_get_platform (self), - AF_INET, - ifname, - "rp_filter", - 10, 0, 2, 3); - if (val < 2) { - val_all = nm_platform_sysctl_ip_conf_get_int_checked (nm_device_get_platform (self), - AF_INET, - "all", - "rp_filter", - 10, 0, 2, val); - if (val_all > val) { - val = val_all; - ifname = "all"; - } - } + gboolean due_to_all; + int val; + val = nm_platform_sysctl_ip_conf_get_rp_filter_ipv4 (nm_device_get_platform (self), + ifname, + TRUE, + &due_to_all); if (val == 1) { _LOGW (LOGD_CONCHECK, "connectivity: \"/proc/sys/net/ipv4/conf/%s/rp_filter\" is set to \"1\". " - "This might break connectivity checking for IPv4 on this device", ifname); + "This might break connectivity checking for IPv4 on this device", + due_to_all ? "all" : ifname); } } @@ -3127,6 +3172,7 @@ concheck_start (NMDevice *self, handle->c_handle = nm_connectivity_check_start (concheck_get_mgr (self), handle->addr_family, + nm_device_get_platform (self), nm_device_get_ip_ifindex (self), nm_device_get_ip_iface (self), concheck_cb, @@ -3588,7 +3634,7 @@ nm_device_set_carrier (NMDevice *self, gboolean carrier) now_ms = nm_utils_get_monotonic_timestamp_ms (); until_ms = NM_MAX (now_ms + _get_carrier_wait_ms (self), priv->carrier_wait_until_ms); priv->carrier_defer_id = g_timeout_add (until_ms - now_ms, carrier_disconnected_action_cb, self); - _LOGD (LOGD_DEVICE, "carrier: link disconnected (deferring action for %ld milli seconds) (id=%u)", + _LOGD (LOGD_DEVICE, "carrier: link disconnected (deferring action for %ld milliseconds) (id=%u)", (long) (until_ms - now_ms), priv->carrier_defer_id); } } @@ -3890,16 +3936,12 @@ device_link_changed (NMDevice *self) 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->ip_state_4 == NM_DEVICE_IP_STATE_DONE) { - if (!ip_config_merge_and_apply (self, AF_INET, TRUE)) - _LOGW (LOGD_IP4, "failed applying IP4 config after link comes up again"); - } + if (!ip_config_merge_and_apply (self, AF_INET, TRUE)) + _LOGW (LOGD_IP4, "failed applying IP4 config after link comes up again"); priv->linklocal6_dad_counter = 0; - if (priv->ip_state_6 == NM_DEVICE_IP_STATE_DONE) { - if (!ip_config_merge_and_apply (self, AF_INET6, TRUE)) - _LOGW (LOGD_IP6, "failed applying IP6 config after link comes up again"); - } + if (!ip_config_merge_and_apply (self, AF_INET6, TRUE)) + _LOGW (LOGD_IP6, "failed applying IP6 config after link comes up again"); } if (update_unmanaged_specs) @@ -4086,7 +4128,8 @@ nm_device_realize_start (NMDevice *self, assume_state_guess_assume, assume_state_connection_uuid, set_nm_owned, - unmanaged_user_explicit); + unmanaged_user_explicit, + FALSE); return TRUE; } @@ -4131,7 +4174,8 @@ nm_device_create_and_realize (NMDevice *self, plink, FALSE, /* assume_state_guess_assume */ NULL, /* assume_state_connection_uuid */ - FALSE, NM_UNMAN_FLAG_OP_FORGET); + FALSE, NM_UNMAN_FLAG_OP_FORGET, + TRUE); nm_device_realize_finish (self, plink); if (nm_device_get_managed (self, FALSE)) { @@ -4192,6 +4236,86 @@ nm_device_update_from_platform_link (NMDevice *self, const NMPlatformLink *plink } } +static void sriov_op_cb (GError *error, gpointer user_data); + +static void +sriov_op_start (NMDevice *self, SriovOp *op) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + nm_assert (!priv->sriov.pending); + + op->cancellable = g_cancellable_new (); + op->device = g_object_ref (self); + priv->sriov.pending = op; + + nm_platform_link_set_sriov_params_async (nm_device_get_platform (self), + priv->ifindex, + op->num_vfs, + op->autoprobe, + sriov_op_cb, + op, + op->cancellable); +} + +static void +sriov_op_cb (GError *error, gpointer user_data) +{ + SriovOp *op = user_data; + gs_unref_object NMDevice *self = op->device; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + nm_assert (op == priv->sriov.pending); + + priv->sriov.pending = NULL; + + if (op->callback) + op->callback (error, op->callback_data); + + g_clear_object (&op->cancellable); + g_slice_free (SriovOp, op); + + if (priv->sriov.next) { + sriov_op_start (self, + g_steal_pointer (&priv->sriov.next)); + } +} + +static void +sriov_op_queue (NMDevice *self, + guint num_vfs, + NMTernary autoprobe, + NMPlatformAsyncCallback callback, + gpointer callback_data) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + GError *error = NULL; + SriovOp *op; + + op = g_slice_new0 (SriovOp); + op->num_vfs = num_vfs; + op->autoprobe = autoprobe; + op->callback = callback; + op->callback_data = callback_data; + + if (priv->sriov.next) { + /* Cancel the next operation immediately */ + if (priv->sriov.next->callback) { + nm_utils_error_set_cancelled (&error, FALSE, NULL); + priv->sriov.next->callback (error, priv->sriov.next->callback_data); + g_clear_error (&error); + } + g_slice_free (SriovOp, priv->sriov.next); + priv->sriov.next = NULL; + } + + if (priv->sriov.pending) { + priv->sriov.next = op; + g_cancellable_cancel (priv->sriov.pending->cancellable); + } else + sriov_op_start (self, op); +} + static void device_init_static_sriov_num_vfs (NMDevice *self) { @@ -4206,10 +4330,8 @@ device_init_static_sriov_num_vfs (NMDevice *self) self, NULL); 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, NM_TERNARY_DEFAULT); - } + if (num_vfs >= 0) + sriov_op_queue (self, num_vfs, NM_TERNARY_DEFAULT, NULL, NULL); } } @@ -4223,11 +4345,11 @@ config_changed (NMConfig *config, NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); if ( priv->state <= NM_DEVICE_STATE_DISCONNECTED - || priv->state > NM_DEVICE_STATE_ACTIVATED) + || priv->state > NM_DEVICE_STATE_ACTIVATED) { priv->ignore_carrier = nm_config_data_get_ignore_carrier (config_data, self); - - if (NM_FLAGS_HAS (changes, NM_CONFIG_CHANGE_VALUES)) - device_init_static_sriov_num_vfs (self); + if (NM_FLAGS_HAS (changes, NM_CONFIG_CHANGE_VALUES)) + device_init_static_sriov_num_vfs (self); + } } static void @@ -4248,6 +4370,8 @@ realize_start_notify (NMDevice *self, * @set_nm_owned: if TRUE and device is a software-device, set nm-owned. * TRUE. * @unmanaged_user_explicit: the user-explict unmanaged flag to set. + * @force_platform_init: if TRUE the platform-init unmanaged flag is + * forcefully cleared. * * Update the device from backing resource properties (like hardware * addresses, carrier states, driver/firmware info, etc). This function @@ -4261,14 +4385,17 @@ realize_start_setup (NMDevice *self, gboolean assume_state_guess_assume, const char *assume_state_connection_uuid, gboolean set_nm_owned, - NMUnmanFlagOp unmanaged_user_explicit) + NMUnmanFlagOp unmanaged_user_explicit, + gboolean force_platform_init) { NMDevicePrivate *priv; NMDeviceClass *klass; + NMPlatform *platform; static guint32 id = 0; NMDeviceCapabilities capabilities = 0; NMConfig *config; guint real_rate; + gboolean unmanaged; /* plink is a NMPlatformLink type, however, we require it to come from the platform * cache (where else would it come from?). */ @@ -4289,6 +4416,7 @@ realize_start_setup (NMDevice *self, _LOGD (LOGD_DEVICE, "start setup of %s, kernel ifindex %d", G_OBJECT_TYPE_NAME (self), plink ? plink->ifindex : 0); klass = NM_DEVICE_GET_CLASS (self); + platform = nm_device_get_platform (self); /* Balanced by a thaw in nm_device_realize_finish() */ g_object_freeze_notify (G_OBJECT (self)); @@ -4307,19 +4435,19 @@ realize_start_setup (NMDevice *self, nm_device_update_from_platform_link (self, plink); if (priv->ifindex > 0) { - priv->physical_port_id = nm_platform_link_get_physical_port_id (nm_device_get_platform (self), priv->ifindex); + priv->physical_port_id = nm_platform_link_get_physical_port_id (platform, priv->ifindex); _notify (self, PROP_PHYSICAL_PORT_ID); - priv->dev_id = nm_platform_link_get_dev_id (nm_device_get_platform (self), priv->ifindex); + priv->dev_id = nm_platform_link_get_dev_id (platform, priv->ifindex); - if (nm_platform_link_is_software (nm_device_get_platform (self), priv->ifindex)) + if (nm_platform_link_is_software (platform, priv->ifindex)) capabilities |= NM_DEVICE_CAP_IS_SOFTWARE; _set_mtu (self, - nm_platform_link_get_mtu (nm_device_get_platform (self), + nm_platform_link_get_mtu (platform, priv->ifindex)); - nm_platform_link_get_driver_info (nm_device_get_platform (self), + nm_platform_link_get_driver_info (platform, priv->ifindex, NULL, &priv->driver_version, @@ -4330,9 +4458,9 @@ realize_start_setup (NMDevice *self, _notify (self, PROP_FIRMWARE_VERSION); if (nm_platform_kernel_support_get (NM_PLATFORM_KERNEL_SUPPORT_TYPE_USER_IPV6LL)) - priv->ipv6ll_handle = nm_platform_link_get_user_ipv6ll_enabled (nm_device_get_platform (self), priv->ifindex); + priv->ipv6ll_handle = nm_platform_link_get_user_ipv6ll_enabled (platform, priv->ifindex); - if (nm_platform_link_supports_sriov (nm_device_get_platform (self), priv->ifindex)) + if (nm_platform_link_supports_sriov (platform, priv->ifindex)) capabilities |= NM_DEVICE_CAP_SRIOV; } @@ -4398,8 +4526,12 @@ realize_start_setup (NMDevice *self, nm_device_set_unmanaged_by_user_udev (self); nm_device_set_unmanaged_by_user_conf (self); + unmanaged = plink + && !plink->initialized + && !force_platform_init; + nm_device_set_unmanaged_flags (self, NM_UNMANAGED_PLATFORM_INIT, - plink && !plink->initialized); + unmanaged); } /** @@ -4941,7 +5073,7 @@ static void check_ip_state (NMDevice *self, gboolean may_fail, gboolean full_state_update) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - gboolean ip4_disabled = FALSE, ip6_ignore = FALSE; + gboolean ip4_disabled = FALSE, ip6_disabled = FALSE; NMSettingIPConfig *s_ip4, *s_ip6; NMDeviceState state; @@ -4961,9 +5093,10 @@ check_ip_state (NMDevice *self, gboolean may_fail, gboolean full_state_update) ip4_disabled = TRUE; 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; + if (s_ip6 && NM_IN_STRSET (nm_setting_ip_config_get_method (s_ip6), + NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_METHOD_DISABLED)) + ip6_disabled = TRUE; if ( priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE && priv->ip_state_6 == NM_DEVICE_IP_STATE_DONE) { @@ -4973,7 +5106,7 @@ check_ip_state (NMDevice *self, gboolean may_fail, gboolean full_state_update) } if ( (priv->ip_state_4 == NM_DEVICE_IP_STATE_FAIL || (ip4_disabled && priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE)) - && (priv->ip_state_6 == NM_DEVICE_IP_STATE_FAIL || (ip6_ignore && priv->ip_state_6 == NM_DEVICE_IP_STATE_DONE))) { + && (priv->ip_state_6 == NM_DEVICE_IP_STATE_FAIL || (ip6_disabled && priv->ip_state_6 == NM_DEVICE_IP_STATE_DONE))) { /* Either both methods failed, or only one failed and the other is * disabled */ if (nm_device_sys_iface_state_is_external_or_assume (self)) { @@ -5010,7 +5143,7 @@ check_ip_state (NMDevice *self, gboolean may_fail, gboolean full_state_update) /* If at least a method has completed, proceed with activation */ if ( (priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE && !ip4_disabled) - || (priv->ip_state_6 == NM_DEVICE_IP_STATE_DONE && !ip6_ignore)) { + || (priv->ip_state_6 == NM_DEVICE_IP_STATE_DONE && !ip6_disabled)) { if (full_state_update) nm_device_state_changed (self, NM_DEVICE_STATE_IP_CHECK, NM_DEVICE_STATE_REASON_NONE); return; @@ -5565,8 +5698,9 @@ nm_device_generate_connection (NMDevice *self, */ 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 + if ( nm_streq0 (ip4_method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) + && NM_IN_STRSET (ip6_method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_METHOD_DISABLED) && !nm_setting_connection_get_master (NM_SETTING_CONNECTION (s_con)) && c_list_is_empty (&priv->slaves)) { NM_SET_OUT (out_maybe_later, TRUE); @@ -5631,7 +5765,10 @@ nm_device_complete_connection (NMDevice *self, error)) return FALSE; - return nm_connection_verify (connection, error); + if (!nm_connection_normalize (connection, NULL, NULL, error)) + return FALSE; + + return nm_device_check_connection_compatible (self, connection, error); } gboolean @@ -5652,11 +5789,9 @@ nm_device_match_parent (NMDevice *self, const char *parent) * no connection active on the device or when a connection with * that UUID is active. */ - connection = nm_device_get_applied_connection (self); - if (!connection) - return TRUE; - - if (!nm_streq0 (parent, nm_connection_get_uuid (connection))) + connection = nm_device_get_applied_connection (parent_device); + if ( connection + && !nm_streq0 (parent, nm_connection_get_uuid (connection))) return FALSE; } else { /* Interface name */ @@ -6255,6 +6390,41 @@ sriov_vf_config_to_platform (NMDevice *self, return g_steal_pointer (&plat_vf); } +static void +sriov_params_cb (GError *error, gpointer data) +{ + NMDevice *self; + NMDevicePrivate *priv; + nm_auto_freev NMPlatformVF **plat_vfs = NULL; + + nm_utils_user_data_unpack (data, &self, &plat_vfs); + + if (nm_utils_error_is_cancelled (error, TRUE)) + return; + + priv = NM_DEVICE_GET_PRIVATE (self); + + if (error) { + _LOGE (LOGD_DEVICE, "failed to set SR-IOV parameters: %s", error->message); + nm_device_state_changed (self, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED); + return; + } + + if (!nm_platform_link_set_sriov_vfs (nm_device_get_platform (self), + priv->ifindex, + (const NMPlatformVF *const *) plat_vfs)) { + _LOGE (LOGD_DEVICE, "failed to apply SR-IOV VFs"); + nm_device_state_changed (self, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED); + return; + } + + nm_device_activate_schedule_stage2_device_config (self); +} + static NMActStageReturn act_stage1_prepare (NMDevice *self, NMDeviceStateReason *out_failure_reason) { @@ -6269,6 +6439,7 @@ act_stage1_prepare (NMDevice *self, NMDeviceStateReason *out_failure_reason) gs_free_error GError *error = NULL; NMSriovVF *vf; NMTernary autoprobe; + gpointer *data; autoprobe = nm_setting_sriov_get_autoprobe_drivers (s_sriov); if (autoprobe == NM_TERNARY_DEFAULT) { @@ -6295,24 +6466,19 @@ act_stage1_prepare (NMDevice *self, NMDeviceStateReason *out_failure_reason) } } - if (!nm_platform_link_set_sriov_params (nm_device_get_platform (self), - priv->ifindex, - nm_setting_sriov_get_total_vfs (s_sriov), - autoprobe)) { - _LOGE (LOGD_DEVICE, "failed to apply SR-IOV parameters"); - NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED); - return NM_ACT_STAGE_RETURN_FAILURE; - } - - if (!nm_platform_link_set_sriov_vfs (nm_device_get_platform (self), - priv->ifindex, - (const NMPlatformVF *const *) plat_vfs)) { - _LOGE (LOGD_DEVICE, "failed to apply SR-IOV VFs"); - NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED); - return NM_ACT_STAGE_RETURN_FAILURE; - } + /* When changing the number of VFs the kernel can block + * for very long time in the write to sysfs, especially + * if autoprobe-drivers is enabled. Do it asynchronously + * to avoid blocking the entire NM process. + */ + data = nm_utils_user_data_pack (self, g_steal_pointer (&plat_vfs)); + sriov_op_queue (self, + nm_setting_sriov_get_total_vfs (s_sriov), + autoprobe, + sriov_params_cb, + data); + return NM_ACT_STAGE_RETURN_POSTPONE; } - return NM_ACT_STAGE_RETURN_SUCCESS; } @@ -6328,6 +6494,9 @@ activate_stage1_device_prepare (NMDevice *self) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS; + priv->v4_route_table_initialized = FALSE; + priv->v6_route_table_initialized = FALSE; + _set_ip_state (self, AF_INET, NM_DEVICE_IP_STATE_NONE); _set_ip_state (self, AF_INET6, NM_DEVICE_IP_STATE_NONE); @@ -6430,11 +6599,15 @@ _routing_rules_sync (NMDevice *self, { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMPRulesManager *rules_manager = nm_netns_get_rules_manager (nm_device_get_netns (self)); + NMDeviceClass *klass = NM_DEVICE_GET_CLASS (self); gboolean untrack_only_dirty = FALSE; gboolean keep_deleted_rules; - gpointer user_tag; + gpointer user_tag_1; + gpointer user_tag_2; - user_tag = priv; + /* take two arbitrary user-tag pointers that belong to @self. */ + user_tag_1 = &priv->v4_route_table; + user_tag_2 = &priv->v6_route_table; if (set_mode == NM_TERNARY_TRUE) { NMConnection *applied_connection; @@ -6443,7 +6616,9 @@ _routing_rules_sync (NMDevice *self, int is_ipv4; untrack_only_dirty = TRUE; - nmp_rules_manager_set_dirty (rules_manager, user_tag); + nmp_rules_manager_set_dirty (rules_manager, user_tag_1); + if (klass->get_extra_rules) + nmp_rules_manager_set_dirty (rules_manager, user_tag_2); applied_connection = nm_device_get_applied_connection (self); @@ -6461,15 +6636,37 @@ _routing_rules_sync (NMDevice *self, rule = nm_setting_ip_config_get_routing_rule (s_ip, i); nm_ip_routing_rule_to_platform (rule, &plrule); + + /* We track this rule, but we also make it explicitly not weakly-tracked + * (meaning to untrack NMP_RULES_MANAGER_EXTERN_WEAKLY_TRACKED_USER_TAG at + * the same time). */ nmp_rules_manager_track (rules_manager, &plrule, 10, - user_tag); + user_tag_1, + NMP_RULES_MANAGER_EXTERN_WEAKLY_TRACKED_USER_TAG); + } + } + + if (klass->get_extra_rules) { + gs_unref_ptrarray GPtrArray *extra_rules = NULL; + + extra_rules = klass->get_extra_rules (self); + if (extra_rules) { + for (i = 0; i < extra_rules->len; i++) { + nmp_rules_manager_track (rules_manager, + NMP_OBJECT_CAST_ROUTING_RULE (extra_rules->pdata[i]), + 10, + user_tag_2, + NMP_RULES_MANAGER_EXTERN_WEAKLY_TRACKED_USER_TAG); + } } } } - nmp_rules_manager_untrack_all (rules_manager, user_tag, !untrack_only_dirty); + nmp_rules_manager_untrack_all (rules_manager, user_tag_1, !untrack_only_dirty); + if (klass->get_extra_rules) + nmp_rules_manager_untrack_all (rules_manager, user_tag_2, !untrack_only_dirty); keep_deleted_rules = FALSE; if (set_mode == NM_TERNARY_DEFAULT) { @@ -6500,7 +6697,7 @@ tc_commit (NMDevice *self) NMSettingTCConfig *s_tc = NULL; int ip_ifindex; guint nqdiscs, ntfilters; - int i; + guint i; connection = nm_device_get_applied_connection (self); if (connection) @@ -6508,7 +6705,7 @@ tc_commit (NMDevice *self) ip_ifindex = nm_device_get_ip_ifindex (self); if (!ip_ifindex) - return s_tc == NULL; + return s_tc == NULL; if (s_tc) { nqdiscs = nm_setting_tc_config_get_num_qdiscs (s_tc); @@ -6520,12 +6717,40 @@ tc_commit (NMDevice *self) NMPlatformQdisc *qdisc = NMP_OBJECT_CAST_QDISC (q); qdisc->ifindex = ip_ifindex; + + /* Note: kind string is still owned by NMTCTfilter. + * This qdisc instance must not be kept alive beyond this function. + * nm_platform_qdisc_sync() promises to do that. */ qdisc->kind = nm_tc_qdisc_get_kind (s_qdisc); + qdisc->addr_family = AF_UNSPEC; qdisc->handle = nm_tc_qdisc_get_handle (s_qdisc); qdisc->parent = nm_tc_qdisc_get_parent (s_qdisc); qdisc->info = 0; +#define GET_ATTR(name, dst, variant_type, type, dflt) G_STMT_START { \ + GVariant *_variant = nm_tc_qdisc_get_attribute (s_qdisc, ""name""); \ + \ + if ( _variant \ + && g_variant_is_of_type (_variant, G_VARIANT_TYPE_ ## variant_type)) \ + (dst) = g_variant_get_ ## type (_variant); \ + else \ + (dst) = (dflt); \ +} G_STMT_END + + if (strcmp (qdisc->kind, "fq_codel") == 0) { + GET_ATTR ("limit", qdisc->fq_codel.limit, UINT32, uint32, 0); + GET_ATTR ("flows", qdisc->fq_codel.flows, UINT32, uint32, 0); + GET_ATTR ("target", qdisc->fq_codel.target, UINT32, uint32, 0); + GET_ATTR ("interval", qdisc->fq_codel.interval, UINT32, uint32, 0); + GET_ATTR ("quantum", qdisc->fq_codel.quantum, UINT32, uint32, 0); + GET_ATTR ("ce_threshold", qdisc->fq_codel.ce_threshold, UINT32, uint32, NM_PLATFORM_FQ_CODEL_CE_THRESHOLD_DISABLED); + GET_ATTR ("memory_limit", qdisc->fq_codel.memory_limit, UINT32, uint32, NM_PLATFORM_FQ_CODEL_MEMORY_LIMIT_UNSET); + GET_ATTR ("ecn", qdisc->fq_codel.ecn, BOOLEAN, boolean, FALSE); + } + +#undef GET_ADDR + g_ptr_array_add (qdiscs, q); } @@ -6539,7 +6764,12 @@ tc_commit (NMDevice *self) NMPlatformTfilter *tfilter = NMP_OBJECT_CAST_TFILTER (q); tfilter->ifindex = ip_ifindex; + + /* Note: kind string is still owned by NMTCTfilter. + * This tfilter instance must not be kept alive beyond this function. + * nm_platform_tfilter_sync() promises to do that. */ tfilter->kind = nm_tc_tfilter_get_kind (s_tfilter); + tfilter->addr_family = AF_UNSPEC; tfilter->handle = nm_tc_tfilter_get_handle (s_tfilter); tfilter->parent = nm_tc_tfilter_get_parent (s_tfilter); @@ -6547,16 +6777,42 @@ tc_commit (NMDevice *self) action = nm_tc_tfilter_get_action (s_tfilter); if (action) { + GVariant *var; + + /* Note: kind string is still owned by NMTCAction. + * This tfilter instance must not be kept alive beyond this function. + * nm_platform_tfilter_sync() promises to do that. */ tfilter->action.kind = nm_tc_action_get_kind (action); - if (strcmp (tfilter->action.kind, "simple") == 0) { - GVariant *sdata; - sdata = nm_tc_action_get_attribute (action, "sdata"); - if (sdata && g_variant_is_of_type (sdata, G_VARIANT_TYPE_BYTESTRING)) { + if (strcmp (tfilter->action.kind, "simple") == 0) { + var = nm_tc_action_get_attribute (action, "sdata"); + if (var && g_variant_is_of_type (var, G_VARIANT_TYPE_BYTESTRING)) { g_strlcpy (tfilter->action.simple.sdata, - g_variant_get_bytestring (sdata), + g_variant_get_bytestring (var), sizeof (tfilter->action.simple.sdata)); } + } else if (strcmp (tfilter->action.kind, "mirred") == 0) { + if (nm_tc_action_get_attribute (action, "egress")) + tfilter->action.mirred.egress = TRUE; + + if (nm_tc_action_get_attribute (action, "ingress")) + tfilter->action.mirred.ingress = TRUE; + + if (nm_tc_action_get_attribute (action, "mirror")) + tfilter->action.mirred.mirror = TRUE; + + if (nm_tc_action_get_attribute (action, "redirect")) + tfilter->action.mirred.redirect = TRUE; + + var = nm_tc_action_get_attribute (action, "dev"); + if (var && g_variant_is_of_type (var, G_VARIANT_TYPE_STRING)) { + int ifindex; + + ifindex = nm_platform_link_get_ifindex (nm_device_get_platform (self), + g_variant_get_string (var, NULL)); + if (ifindex > 0) + tfilter->action.mirred.ifindex = ifindex; + } } } @@ -6941,7 +7197,7 @@ ipv4ll_get_ip4_config (NMDevice *self, guint32 lla) route.network = htonl (0xE0000000L); route.plen = 4; route.rt_source = NM_IP_CONFIG_SOURCE_IP4LL; - route.table_coerced = nm_platform_route_table_coerce (nm_device_get_route_table (self, AF_INET, TRUE)); + route.table_coerced = nm_platform_route_table_coerce (nm_device_get_route_table (self, AF_INET)); route.metric = nm_device_get_route_metric (self, AF_INET); nm_ip4_config_add_route (config, &route, NULL); @@ -7110,12 +7366,12 @@ ensure_con_ip_config (NMDevice *self, int addr_family) nm_connection_get_setting_ip4_config (connection), _get_mdns (self), _get_llmnr (self), - nm_device_get_route_table (self, addr_family, TRUE), + nm_device_get_route_table (self, addr_family), nm_device_get_route_metric (self, addr_family)); } else { nm_ip6_config_merge_setting (NM_IP6_CONFIG (con_ip_config), nm_connection_get_setting_ip6_config (connection), - nm_device_get_route_table (self, addr_family, TRUE), + nm_device_get_route_table (self, addr_family), nm_device_get_route_metric (self, addr_family)); } @@ -7332,12 +7588,12 @@ ip_config_merge_and_apply (NMDevice *self, if (commit) { if (IS_IPv4) { nm_ip4_config_add_dependent_routes (NM_IP4_CONFIG (composite), - nm_device_get_route_table (self, addr_family, TRUE), + nm_device_get_route_table (self, addr_family), nm_device_get_route_metric (self, addr_family), &ip4_dev_route_blacklist); } else { nm_ip6_config_add_dependent_routes (NM_IP6_CONFIG (composite), - nm_device_get_route_table (self, addr_family, TRUE), + nm_device_get_route_table (self, addr_family), nm_device_get_route_metric (self, addr_family)); } } @@ -7350,15 +7606,23 @@ ip_config_merge_and_apply (NMDevice *self, } if (!IS_IPv4) { + const NMPlatformLink *link; NMUtilsIPv6IfaceId iid; + NMPlatform *platform; + int ifindex; 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); + platform = nm_device_get_platform (self); + ifindex = nm_device_get_ip_ifindex (self); + link = nm_platform_link_get (platform, ifindex); + + if (link && link->inet6_token.id == iid.id) + _LOGT (LOGD_DEVICE | LOGD_IP6, "token %s already set", ip6_addr_gen_token); + else + nm_platform_link_set_ipv6_token (platform, ifindex, iid); } } @@ -7470,9 +7734,13 @@ clear_config: static void dhcp4_dad_cb (NMDevice *self, NMIP4Config **configs, gboolean success) { - if (success) + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + if (success) { + nm_dhcp_client_accept (priv->dhcp4.client, NULL); nm_device_activate_schedule_ip_config_result (self, AF_INET, NM_IP_CONFIG_CAST (configs[1])); - else { + } else { + nm_dhcp_client_decline (priv->dhcp4.client, "Address conflict detected", NULL); nm_device_ip_method_failed (self, AF_INET, NM_DEVICE_STATE_REASON_IP_ADDRESS_DUPLICATE); } @@ -7531,7 +7799,7 @@ dhcp4_state_changed (NMDhcpClient *client, nm_connection_get_setting_ip4_config (connection), NM_SETTING_CONNECTION_MDNS_DEFAULT, NM_SETTING_CONNECTION_LLMNR_DEFAULT, - nm_device_get_route_table (self, AF_INET, TRUE), + nm_device_get_route_table (self, AF_INET), nm_device_get_route_metric (self, AF_INET)); configs = g_new0 (NMIP4Config *, 3); @@ -7738,9 +8006,11 @@ dhcp4_start (NMDevice *self) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMSettingIPConfig *s_ip4; gs_unref_bytes GBytes *hwaddr = NULL; + gs_unref_bytes GBytes *bcast_hwaddr = NULL; gs_unref_bytes GBytes *client_id = NULL; NMConnection *connection; GError *error = NULL; + const NMPlatformLink *pllink; connection = nm_device_get_applied_connection (self); g_return_val_if_fail (connection, FALSE); @@ -7751,8 +8021,11 @@ dhcp4_start (NMDevice *self) nm_dbus_object_clear_and_unexport (&priv->dhcp4.config); priv->dhcp4.config = nm_dhcp4_config_new (); - hwaddr = nm_platform_link_get_address_as_bytes (nm_device_get_platform (self), - nm_device_get_ip_ifindex (self)); + pllink = nm_platform_link_get (nm_device_get_platform (self), nm_device_get_ip_ifindex (self)); + if (pllink) { + hwaddr = nmp_link_address_get_as_bytes (&pllink->l_address); + bcast_hwaddr = nmp_link_address_get_as_bytes (&pllink->l_broadcast); + } client_id = dhcp4_get_client_id (self, connection, hwaddr); @@ -7762,8 +8035,9 @@ dhcp4_start (NMDevice *self) nm_device_get_ip_iface (self), nm_device_get_ip_ifindex (self), hwaddr, + bcast_hwaddr, nm_connection_get_uuid (connection), - nm_device_get_route_table (self, AF_INET, TRUE), + nm_device_get_route_table (self, AF_INET), nm_device_get_route_metric (self, AF_INET), nm_setting_ip_config_get_dhcp_send_hostname (s_ip4), nm_setting_ip_config_get_dhcp_hostname (s_ip4), @@ -7892,7 +8166,9 @@ connection_ip_method_requires_carrier (NMConnection *connection, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL); } - NM_SET_OUT (out_ip_enabled, !nm_streq (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)); + NM_SET_OUT (out_ip_enabled, + !NM_IN_STRSET (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_METHOD_DISABLED)); return NM_IN_STRSET (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO, NM_SETTING_IP6_CONFIG_METHOD_DHCP, NM_SETTING_IP6_CONFIG_METHOD_SHARED, @@ -8537,8 +8813,10 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMSettingIPConfig *s_ip6; gs_unref_bytes GBytes *hwaddr = NULL; + gs_unref_bytes GBytes *bcast_hwaddr = NULL; gs_unref_bytes GBytes *duid = NULL; gboolean enforce_duid = FALSE; + const NMPlatformLink *pllink; GError *error = NULL; const NMPlatformIP6Address *ll_addr = NULL; @@ -8558,8 +8836,11 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection) return FALSE; } - hwaddr = nm_platform_link_get_address_as_bytes (nm_device_get_platform (self), - nm_device_get_ip_ifindex (self)); + pllink = nm_platform_link_get (nm_device_get_platform (self), nm_device_get_ip_ifindex (self)); + if (pllink) { + hwaddr = nmp_link_address_get_as_bytes (&pllink->l_address); + bcast_hwaddr = nmp_link_address_get_as_bytes (&pllink->l_broadcast); + } duid = dhcp6_get_duid (self, connection, hwaddr, &enforce_duid); priv->dhcp6.client = nm_dhcp_manager_start_ip6 (nm_dhcp_manager_get (), @@ -8567,9 +8848,10 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection) nm_device_get_ip_iface (self), nm_device_get_ip_ifindex (self), hwaddr, + bcast_hwaddr, &ll_addr->address, nm_connection_get_uuid (connection), - nm_device_get_route_table (self, AF_INET6, TRUE), + nm_device_get_route_table (self, AF_INET6), nm_device_get_route_metric (self, AF_INET6), nm_setting_ip_config_get_dhcp_send_hostname (s_ip6), nm_setting_ip_config_get_dhcp_hostname (s_ip6), @@ -9087,7 +9369,8 @@ _commit_mtu (NMDevice *self, const NMIP4Config *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)) { + NM_SETTING_IP6_CONFIG_METHOD_IGNORE + NM_SETTING_IP6_CONFIG_METHOD_DISABLED)) { /* the interface has IPv6 enabled. The MTU with IPv6 cannot be smaller * then 1280. * @@ -9262,7 +9545,7 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in rdata->gateways_n, rdata->routes, rdata->routes_n, - nm_device_get_route_table (self, AF_INET6, TRUE), + nm_device_get_route_table (self, AF_INET6), nm_device_get_route_metric (self, AF_INET6), nm_platform_kernel_support_get (NM_PLATFORM_KERNEL_SUPPORT_TYPE_RTA_PREF)); if (priv->ac_ip6_config.current) { @@ -9271,7 +9554,7 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in rdata->gateways_n, rdata->routes, rdata->routes_n, - nm_device_get_route_table (self, AF_INET6, TRUE), + nm_device_get_route_table (self, AF_INET6), nm_device_get_route_metric (self, AF_INET6), nm_platform_kernel_support_get (NM_PLATFORM_KERNEL_SUPPORT_TYPE_RTA_PREF)); } @@ -9763,7 +10046,7 @@ act_stage3_ip_config_start (NMDevice *self, nm_connection_get_setting_ip4_config (connection), NM_SETTING_CONNECTION_MDNS_DEFAULT, NM_SETTING_CONNECTION_LLMNR_DEFAULT, - nm_device_get_route_table (self, AF_INET, TRUE), + nm_device_get_route_table (self, AF_INET), nm_device_get_route_metric (self, AF_INET)); configs = g_new0 (NMIP4Config *, 2); configs[0] = config; @@ -9795,6 +10078,11 @@ act_stage3_ip_config_start (NMDevice *self, NMSettingIP6ConfigPrivacy ip6_privacy = NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN; const char *ip6_privacy_str = "0"; + if (nm_streq (method, NM_SETTING_IP6_CONFIG_METHOD_DISABLED)) { + nm_device_sysctl_ip_conf_set (self, AF_INET6, "disable_ipv6", "1"); + return NM_ACT_STAGE_RETURN_IP_DONE; + } + if (nm_streq (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) { if ( !priv->master && !nm_device_sys_iface_state_is_external (self)) { @@ -10948,7 +11236,7 @@ nm_device_reactivate_ip4_config (NMDevice *self, s_ip4_new, _get_mdns (self), _get_llmnr (self), - nm_device_get_route_table (self, AF_INET, TRUE), + nm_device_get_route_table (self, AF_INET), nm_device_get_route_metric (self, AF_INET)); method_old = s_ip4_old @@ -11022,7 +11310,7 @@ nm_device_reactivate_ip6_config (NMDevice *self, priv->con_ip_config_6 = nm_device_ip6_config_new (self); nm_ip6_config_merge_setting (priv->con_ip_config_6, s_ip6_new, - nm_device_get_route_table (self, AF_INET6, TRUE), + nm_device_get_route_table (self, AF_INET6), nm_device_get_route_metric (self, AF_INET6)); method_old = s_ip6_old @@ -11073,21 +11361,17 @@ nm_device_reactivate_ip6_config (NMDevice *self, } static void -_pacrunner_manager_send (NMDevice *self) +_pacrunner_manager_add (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - nm_pacrunner_manager_remove_clear (priv->pacrunner_manager, - &priv->pacrunner_call_id); + nm_pacrunner_manager_remove_clear (&priv->pacrunner_conf_id); - if (!priv->pacrunner_manager) - priv->pacrunner_manager = g_object_ref (nm_pacrunner_manager_get ()); - - priv->pacrunner_call_id = nm_pacrunner_manager_send (priv->pacrunner_manager, - nm_device_get_ip_iface (self), - priv->proxy_config, - NULL, - NULL); + priv->pacrunner_conf_id = nm_pacrunner_manager_add (nm_pacrunner_manager_get (), + priv->proxy_config, + nm_device_get_ip_iface (self), + NULL, + NULL); } static void @@ -11095,16 +11379,19 @@ reactivate_proxy_config (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (!priv->pacrunner_call_id) + if (!priv->pacrunner_conf_id) return; nm_device_set_proxy_config (self, priv->dhcp4.pac_url); - _pacrunner_manager_send (self); + _pacrunner_manager_add (self); } static gboolean -can_reapply_change (NMDevice *self, const char *setting_name, - NMSetting *s_old, NMSetting *s_new, - GHashTable *diffs, GError **error) +can_reapply_change (NMDevice *self, + const char *setting_name, + NMSetting *s_old, + NMSetting *s_new, + GHashTable *diffs, + GError **error) { if (nm_streq (setting_name, NM_SETTING_CONNECTION_SETTING_NAME)) { /* Whitelist allowed properties from "connection" setting which are @@ -11127,32 +11414,9 @@ can_reapply_change (NMDevice *self, const char *setting_name, NM_SETTING_CONNECTION_MDNS, NM_SETTING_CONNECTION_LLMNR); } else if (NM_IN_STRSET (setting_name, - NM_SETTING_PROXY_SETTING_NAME)) { - return TRUE; - } else if (NM_IN_STRSET (setting_name, + NM_SETTING_PROXY_SETTING_NAME, NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_SETTING_IP6_CONFIG_SETTING_NAME)) { - if (g_hash_table_contains (diffs, NM_SETTING_IP_CONFIG_ROUTE_TABLE)) { - /* changing the route-table setting is complicated, because it affects - * how we sync the routes. Don't support changing it without full - * re-activation. - * - * The problem is really that changing the setting also affects the sync - * mode. So, switching from NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN to - * NM_IP_ROUTE_TABLE_SYNC_MODE_FULL would somehow require us to get rid - * of additional routes, but we don't know which routes were added by NM - * and which should be removed. - * - * Note how nm_device_get_route_table() caches the value for the duration of the - * activation. */ - g_set_error (error, - NM_DEVICE_ERROR, - NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, - "Can't reapply changes to '%s.%s' setting", - setting_name, - NM_SETTING_IP_CONFIG_ROUTE_TABLE); - return FALSE; - } return TRUE; } else { g_set_error (error, @@ -11278,7 +11542,7 @@ check_and_reapply_connection (NMDevice *self, || !nm_streq0 (nm_setting_connection_get_stable_id (s_con_a), nm_setting_connection_get_stable_id (s_con_n))) { connection_clean_free = nm_simple_connection_new_clone (connection); connection_clean = connection_clean_free; - s_con_n = nm_connection_get_setting_connection (connection); + s_con_n = nm_connection_get_setting_connection (connection_clean); g_object_set (s_con_n, NM_SETTING_CONNECTION_ID, nm_setting_connection_get_id (s_con_a), NM_SETTING_CONNECTION_UUID, nm_setting_connection_get_uuid (s_con_a), @@ -11298,8 +11562,17 @@ check_and_reapply_connection (NMDevice *self, priv->v4_commit_first_time = TRUE; priv->v6_commit_first_time = TRUE; + priv->v4_route_table_initialized = FALSE; + priv->v6_route_table_initialized = FALSE; + /************************************************************************** * Reapply changes + * + * Note that reapply_connection() is called as very first. This is for example + * important for NMDeviceWireGuard, which implements coerce_route_table() + * and get_extra_rules(). + * That is because NMDeviceWireGuard caches settings, so during reapply that + * cache must be updated *first*. *************************************************************************/ klass->reapply_connection (self, con_old, con_new); @@ -11312,9 +11585,14 @@ check_and_reapply_connection (NMDevice *self, s_ip6_old = nm_connection_get_setting_ip6_config (con_old); s_ip6_new = nm_connection_get_setting_ip6_config (con_new); + /* Allow reapply of MTU */ + priv->mtu_source = NM_DEVICE_MTU_SOURCE_NONE; + nm_device_reactivate_ip4_config (self, s_ip4_old, s_ip4_new); nm_device_reactivate_ip6_config (self, s_ip6_old, s_ip6_new); + _routing_rules_sync (self, NM_TERNARY_TRUE); + reactivate_proxy_config (self); return TRUE; @@ -12097,9 +12375,7 @@ nm_device_set_ip_config (NMDevice *self, if (IS_IPv4) { success = nm_ip4_config_commit (NM_IP4_CONFIG (new_config), nm_device_get_platform (self), - nm_device_get_route_table (self, addr_family, FALSE) - ? NM_IP_ROUTE_TABLE_SYNC_MODE_FULL - : NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN); + _get_route_table_sync_mode_stateful (self, addr_family)); nm_platform_ip4_dev_route_blacklist_set (nm_device_get_platform (self), nm_ip_config_get_ifindex (new_config), ip4_dev_route_blacklist); @@ -12108,9 +12384,7 @@ nm_device_set_ip_config (NMDevice *self, success = nm_ip6_config_commit (NM_IP6_CONFIG (new_config), nm_device_get_platform (self), - nm_device_get_route_table (self, addr_family, FALSE) - ? NM_IP_ROUTE_TABLE_SYNC_MODE_FULL - : NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN, + _get_route_table_sync_mode_stateful (self, addr_family), &temporary_not_available); if (!_rt6_temporary_not_available_set (self, temporary_not_available)) @@ -12173,16 +12447,28 @@ nm_device_set_ip_config (NMDevice *self, if ( nm_device_sys_iface_state_is_external (self) && (settings_connection = nm_device_get_settings_connection (self)) - && NM_FLAGS_HAS (nm_settings_connection_get_flags (settings_connection), - NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED) + && NM_FLAGS_ALL (nm_settings_connection_get_flags (settings_connection), + NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED + | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE + | NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED) && nm_active_connection_get_activation_type (NM_ACTIVE_CONNECTION (priv->act_request.obj)) == NM_ACTIVATION_TYPE_EXTERNAL) { - g_object_freeze_notify (G_OBJECT (settings_connection)); - /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */ - nm_connection_add_setting (nm_settings_connection_get_connection (settings_connection), + gs_unref_object NMConnection *new_connection = NULL; + + new_connection = nm_simple_connection_new_clone (nm_settings_connection_get_connection (settings_connection)); + + nm_connection_add_setting (new_connection, IS_IPv4 ? nm_ip4_config_create_setting (priv->ip_config_4) : nm_ip6_config_create_setting (priv->ip_config_6)); - g_object_thaw_notify (G_OBJECT (settings_connection)); + + nm_settings_connection_update (settings_connection, + new_connection, + NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY, + NM_SETTINGS_CONNECTION_INT_FLAGS_NONE, + NM_SETTINGS_CONNECTION_INT_FLAGS_NONE, + NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE, + "update-external", + NULL); } nm_device_queue_recheck_assume (self); @@ -12307,29 +12593,31 @@ nm_device_get_ip6_config (NMDevice *self) /*****************************************************************************/ -static void +static gboolean dispatcher_cleanup (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (priv->dispatcher.call_id) { - nm_dispatcher_call_cancel (priv->dispatcher.call_id); - priv->dispatcher.call_id = 0; - priv->dispatcher.post_state = NM_DEVICE_STATE_UNKNOWN; - priv->dispatcher.post_state_reason = NM_DEVICE_STATE_REASON_NONE; - } + if (!priv->dispatcher.call_id) + return FALSE; + + nm_dispatcher_call_cancel (g_steal_pointer (&priv->dispatcher.call_id)); + priv->dispatcher.post_state = NM_DEVICE_STATE_UNKNOWN; + priv->dispatcher.post_state_reason = NM_DEVICE_STATE_REASON_NONE; + return TRUE; } static void -dispatcher_complete_proceed_state (guint call_id, gpointer user_data) +dispatcher_complete_proceed_state (NMDispatcherCallId *call_id, gpointer user_data) { NMDevice *self = NM_DEVICE (user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); g_return_if_fail (call_id == priv->dispatcher.call_id); - priv->dispatcher.call_id = 0; - nm_device_queue_state (self, priv->dispatcher.post_state, + priv->dispatcher.call_id = NULL; + nm_device_queue_state (self, + priv->dispatcher.post_state, priv->dispatcher.post_state_reason); priv->dispatcher.post_state = NM_DEVICE_STATE_UNKNOWN; priv->dispatcher.post_state_reason = NM_DEVICE_STATE_REASON_NONE; @@ -12342,10 +12630,8 @@ ip_check_pre_up (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (priv->dispatcher.call_id != 0) { - g_warn_if_reached (); - dispatcher_cleanup (self); - } + if (dispatcher_cleanup (self)) + nm_assert_not_reached (); priv->dispatcher.post_state = NM_DEVICE_STATE_SECONDARIES; priv->dispatcher.post_state_reason = NM_DEVICE_STATE_REASON_NONE; @@ -12883,7 +13169,8 @@ update_ext_ip_config (NMDevice *self, int addr_family, gboolean intersect_config for (iter = priv->vpn_configs_6; iter; iter = iter->next) nm_ip6_config_intersect (iter->data, priv->ext_ip_config_6, is_up, is_up, 0); - if ( priv->ipv6ll_has + if ( is_up + && priv->ipv6ll_has && !nm_ip6_config_lookup_address (priv->ext_ip_config_6, &priv->ipv6ll_addr)) priv->ipv6ll_has = FALSE; } @@ -13218,7 +13505,7 @@ _get_managed_by_flags(NMUnmanagedFlags flags, NMUnmanagedFlags mask, gboolean fo return TRUE; /* A for-user-request, is effectively the same as pretending - * that user-dbus flag is cleared. */ + * that user-explicit flag is cleared. */ mask |= NM_UNMANAGED_USER_EXPLICIT; flags &= ~NM_UNMANAGED_USER_EXPLICIT; } @@ -13270,6 +13557,9 @@ _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 explicit user-request + * Setting this to %TRUE has the same effect as if %NM_UNMANAGED_USER_EXPLICIT + * unmanaged flag would be unset (meaning: explicitly not-unmanaged). + * If this parameter is %TRUE, the device can only appear more managed. * * Whether the device is unmanaged according to the unmanaged flags. * @@ -13839,13 +14129,17 @@ _nm_device_check_connection_available (NMDevice *self, return FALSE; } if (state < NM_DEVICE_STATE_UNAVAILABLE) { - if (!nm_device_get_managed (self, TRUE)) { - if (!nm_device_get_managed (self, FALSE)) { + if (nm_device_get_managed (self, FALSE)) { + /* device is managed, both for user-requests and non-user-requests alike. */ + } else { + if (!nm_device_get_managed (self, TRUE)) { + /* device is strictly unmanaged by authoritative unmanaged reasons. */ nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_UNMANAGED_DEVICE, "device is strictly unmanaged"); return FALSE; } if (!NM_FLAGS_HAS (flags, _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_OVERRULE_UNMANAGED)) { + /* device could be managed for an explict user-request, but this is not such a request. */ nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_UNMANAGED_DEVICE, "device is currently unmanaged"); return FALSE; @@ -14126,7 +14420,7 @@ cp_connection_added (NMSettings *settings, NMSettingsConnection *sett_conn, gpoi } static void -cp_connection_updated (NMSettings *settings, NMSettingsConnection *sett_conn, gboolean by_user, gpointer user_data) +cp_connection_updated (NMSettings *settings, NMSettingsConnection *sett_conn, guint update_reason_u, gpointer user_data) { cp_connection_added_or_updated (user_data, sett_conn); } @@ -14339,6 +14633,9 @@ _cleanup_generic_post (NMDevice *self, CleanupType cleanup_type) priv->v4_route_table_initialized = FALSE; priv->v6_route_table_initialized = FALSE; + priv->v4_route_table_full_sync_before = FALSE; + priv->v6_route_table_full_sync_before = FALSE; + priv->default_route_metric_penalty_ip4_has = FALSE; priv->default_route_metric_penalty_ip6_has = FALSE; @@ -14732,6 +15029,34 @@ ip6_managed_setup (NMDevice *self) } static void +deactivate_ready (NMDevice *self, NMDeviceStateReason reason) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + if (priv->dispatcher.call_id) + return; + + if (priv->sriov.pending) + return; + nm_assert (!priv->sriov.next); + + nm_device_queue_state (self, NM_DEVICE_STATE_DISCONNECTED, reason); +} + +static void +sriov_deactivate_cb (GError *error, gpointer user_data) +{ + NMDevice *self; + gpointer reason; + + if (nm_utils_error_is_cancelled (error, TRUE)) + return; + + nm_utils_user_data_unpack (user_data, &self, &reason); + deactivate_ready (self, (NMDeviceStateReason) reason); +} + +static void deactivate_async_ready (NMDevice *self, GError *error, gpointer user_data) @@ -14751,11 +15076,12 @@ deactivate_async_ready (NMDevice *self, _LOGW (LOGD_DEVICE, "Deactivation failed: %s", error->message); } - nm_device_queue_state (self, NM_DEVICE_STATE_DISCONNECTED, reason); + + deactivate_ready (self, reason); } static void -deactivate_dispatcher_complete (guint call_id, gpointer user_data) +deactivate_dispatcher_complete (NMDispatcherCallId *call_id, gpointer user_data) { NMDevice *self = NM_DEVICE (user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); @@ -14764,9 +15090,9 @@ deactivate_dispatcher_complete (guint call_id, gpointer user_data) g_return_if_fail (call_id == priv->dispatcher.call_id); g_return_if_fail (priv->dispatcher.post_state == NM_DEVICE_STATE_DISCONNECTED); - reason = priv->dispatcher.post_state_reason; + reason = priv->state_reason; - priv->dispatcher.call_id = 0; + priv->dispatcher.call_id = NULL; priv->dispatcher.post_state = NM_DEVICE_STATE_UNKNOWN; priv->dispatcher.post_state_reason = NM_DEVICE_STATE_REASON_NONE; @@ -14780,7 +15106,7 @@ deactivate_dispatcher_complete (guint call_id, gpointer user_data) deactivate_async_ready, GUINT_TO_POINTER (reason)); } else - nm_device_queue_state (self, NM_DEVICE_STATE_DISCONNECTED, reason); + deactivate_ready (self, reason); } static void @@ -14993,12 +15319,6 @@ _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 @@ -15020,10 +15340,18 @@ _set_state_full (NMDevice *self, /* Just proceed on errors */ deactivate_dispatcher_complete (0, self); } + + if ( priv->ifindex > 0 + && (s_sriov = nm_device_get_applied_setting (self, NM_TYPE_SETTING_SRIOV))) { + sriov_op_queue (self, + 0, + NM_TERNARY_TRUE, + sriov_deactivate_cb, + nm_utils_user_data_pack (self, (gpointer) reason)); + } } - nm_pacrunner_manager_remove_clear (priv->pacrunner_manager, - &priv->pacrunner_call_id); + nm_pacrunner_manager_remove_clear (&priv->pacrunner_conf_id); break; case NM_DEVICE_STATE_DISCONNECTED: if ( priv->queued_act_request @@ -15043,7 +15371,7 @@ _set_state_full (NMDevice *self, NULL, NULL, NULL); if (priv->proxy_config) - _pacrunner_manager_send (self); + _pacrunner_manager_add (self); break; case NM_DEVICE_STATE_FAILED: /* Usually upon failure the activation chain is interrupted in @@ -15076,7 +15404,7 @@ _set_state_full (NMDevice *self, * and those we haven't tried yet (no timestamp). */ if (sett_conn && !nm_settings_connection_get_timestamp (sett_conn, NULL)) - nm_settings_connection_update_timestamp (sett_conn, (guint64) 0, TRUE); + nm_settings_connection_update_timestamp (sett_conn, (guint64) 0); /* Schedule the transition to DISCONNECTED. The device can't transition * immediately because we can't change states again from the state @@ -16286,9 +16614,7 @@ dispose (GObject *object) dispatcher_cleanup (self); - nm_pacrunner_manager_remove_clear (priv->pacrunner_manager, - &priv->pacrunner_call_id); - g_clear_object (&priv->pacrunner_manager); + nm_pacrunner_manager_remove_clear (&priv->pacrunner_conf_id); _cleanup_generic_pre (self, CLEANUP_TYPE_KEEP); diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index d11c33ab..ae6aab39 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -321,6 +320,12 @@ typedef struct _NMDeviceClass { void (* set_enabled) (NMDevice *self, gboolean enabled); + /* let the subclass return additional NMPlatformRoutingRule (in form of NMPObject + * pointers) that shall be added to the rules provided by this device. + * The returned GPtrArray will be g_ptr_array_unref()'ed. The subclass may or + * may not keep an additional reference and return this array again and again. */ + GPtrArray *(*get_extra_rules) (NMDevice *self); + /* allow derived classes to override the result of nm_device_autoconnect_allowed(). * If the value changes, the class should call nm_device_emit_recheck_auto_activate(), * which emits NM_DEVICE_RECHECK_AUTO_ACTIVATE signal. */ @@ -332,6 +337,13 @@ typedef struct _NMDeviceClass { guint32 (*get_configured_mtu) (NMDevice *self, NMDeviceMtuSource *out_source); + /* allow the subclass to overwrite the routing table. This is mainly useful + * to change from partial mode (route-table=0) to full-sync mode (route-table=254). */ + guint32 (*coerce_route_table) (NMDevice *self, + int addr_family, + guint32 route_table, + gboolean is_user_config); + const char *(*get_auto_ip_config_method) (NMDevice *self, int addr_family); /* Checks whether the connection is compatible with the device using @@ -497,7 +509,7 @@ NMDeviceType nm_device_get_device_type (NMDevice *dev); NMLinkType nm_device_get_link_type (NMDevice *dev); NMMetered nm_device_get_metered (NMDevice *dev); -guint32 nm_device_get_route_table (NMDevice *self, int addr_family, gboolean fallback_main); +guint32 nm_device_get_route_table (NMDevice *self, int addr_family); guint32 nm_device_get_route_metric (NMDevice *dev, int addr_family); guint32 nm_device_get_route_metric_default (NMDeviceType device_type); diff --git a/src/devices/nm-lldp-listener.c b/src/devices/nm-lldp-listener.c index 831054ba..c0d78315 100644 --- a/src/devices/nm-lldp-listener.c +++ b/src/devices/nm-lldp-listener.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/nm-lldp-listener.h b/src/devices/nm-lldp-listener.h index 1afa8456..c44ca59f 100644 --- a/src/devices/nm-lldp-listener.h +++ b/src/devices/nm-lldp-listener.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/ovs/nm-device-ovs-interface.c b/src/devices/ovs/nm-device-ovs-interface.c index e3d3f9ee..83de3c3d 100644 --- a/src/devices/ovs/nm-device-ovs-interface.c +++ b/src/devices/ovs/nm-device-ovs-interface.c @@ -95,6 +95,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection, GError s_ovs_iface = nm_connection_get_setting_ovs_interface (connection); if (!NM_IN_STRSET (nm_setting_ovs_interface_get_interface_type (s_ovs_iface), + "dpdk", "internal", "patch")) { nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, diff --git a/src/devices/ovs/nm-device-ovs-port.c b/src/devices/ovs/nm-device-ovs-port.c index 35eb739f..8a93a5a9 100644 --- a/src/devices/ovs/nm-device-ovs-port.c +++ b/src/devices/ovs/nm-device-ovs-port.c @@ -140,13 +140,18 @@ del_iface_cb (GError *error, gpointer user_data) static void release_slave (NMDevice *device, NMDevice *slave, gboolean configure) { - nm_ovsdb_del_interface (nm_ovsdb_get (), nm_device_get_iface (slave), - del_iface_cb, g_object_ref (slave)); - - /* Open VSwitch is going to delete this one. We must ignore what happens - * next with the interface. */ - if (NM_IS_DEVICE_OVS_INTERFACE (slave)) - nm_device_update_from_platform_link (slave, NULL); + NMDeviceOvsPort *self = NM_DEVICE_OVS_PORT (device); + + if (configure) { + _LOGI (LOGD_DEVICE, "releasing ovs interface %s", nm_device_get_ip_iface (slave)); + nm_ovsdb_del_interface (nm_ovsdb_get (), nm_device_get_iface (slave), + del_iface_cb, g_object_ref (slave)); + /* Open VSwitch is going to delete this one. We must ignore what happens + * next with the interface. */ + if (NM_IS_DEVICE_OVS_INTERFACE (slave)) + nm_device_update_from_platform_link (slave, NULL); + } else + _LOGI (LOGD_DEVICE, "ovs interface %s was released", nm_device_get_ip_iface (slave)); } /*****************************************************************************/ diff --git a/src/devices/ovs/nm-ovs-factory.c b/src/devices/ovs/nm-ovs-factory.c index 2124b2a0..fdf07bd3 100644 --- a/src/devices/ovs/nm-ovs-factory.c +++ b/src/devices/ovs/nm-ovs-factory.c @@ -26,7 +26,9 @@ #include "nm-device-ovs-bridge.h" #include "platform/nm-platform.h" #include "nm-core-internal.h" +#include "settings/nm-settings.h" #include "devices/nm-device-factory.h" +#include "devices/nm-device-private.h" /*****************************************************************************/ @@ -51,7 +53,13 @@ G_DEFINE_TYPE (NMOvsFactory, nm_ovs_factory, NM_TYPE_DEVICE_FACTORY) /*****************************************************************************/ #define _NMLOG_DOMAIN LOGD_DEVICE -#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "ovs", __VA_ARGS__) +#define _NMLOG(level, ifname, con_uuid, ...) \ + G_STMT_START { \ + nm_log ((level), _NMLOG_DOMAIN, (ifname), (con_uuid), \ + "ovs: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__) \ + _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } G_STMT_END + /*****************************************************************************/ @@ -139,6 +147,36 @@ ovsdb_device_removed (NMOvsdb *ovsdb, const char *name, NMDeviceType device_type } static void +ovsdb_interface_failed (NMOvsdb *ovsdb, + const char *name, + const char *connection_uuid, + const char *error, + NMDeviceFactory *self) +{ + NMDevice *device = NULL; + NMSettingsConnection *connection = NULL; + + _LOGI (name, connection_uuid, "ovs interface \"%s\" (%s) failed: %s", name, connection_uuid, error); + + device = nm_manager_get_device (nm_manager_get (), name, NM_DEVICE_TYPE_OVS_INTERFACE); + if (!device) + return; + + if (connection_uuid) + connection = nm_settings_get_connection_by_uuid (nm_device_get_settings (device), connection_uuid); + + if (connection) { + nm_settings_connection_autoconnect_blocked_reason_set (connection, + NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_FAILED, + TRUE); + } + + nm_device_state_changed (device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_OVSDB_FAILED); +} + +static void start (NMDeviceFactory *self) { NMOvsdb *ovsdb; @@ -147,6 +185,7 @@ start (NMDeviceFactory *self) g_signal_connect_object (ovsdb, NM_OVSDB_DEVICE_ADDED, G_CALLBACK (ovsdb_device_added), self, (GConnectFlags) 0); g_signal_connect_object (ovsdb, NM_OVSDB_DEVICE_REMOVED, G_CALLBACK (ovsdb_device_removed), self, (GConnectFlags) 0); + g_signal_connect_object (ovsdb, NM_OVSDB_INTERFACE_FAILED, G_CALLBACK (ovsdb_interface_failed), self, (GConnectFlags) 0); } static NMDevice * diff --git a/src/devices/ovs/nm-ovsdb.c b/src/devices/ovs/nm-ovsdb.c index 5b50f840..e5de466c 100644 --- a/src/devices/ovs/nm-ovsdb.c +++ b/src/devices/ovs/nm-ovsdb.c @@ -25,8 +25,7 @@ #include <gio/gunixsocketaddress.h> #include "nm-glib-aux/nm-jansson.h" -#include "devices/nm-device.h" -#include "platform/nm-platform.h" +#include "nm-core-utils.h" #include "nm-core-internal.h" /*****************************************************************************/ @@ -58,7 +57,7 @@ typedef struct { enum { DEVICE_ADDED, DEVICE_REMOVED, - DEVICE_CHANGED, + INTERFACE_FAILED, LAST_SIGNAL }; @@ -341,6 +340,7 @@ _insert_interface (json_t *params, NMConnection *interface) { const char *type = NULL; NMSettingOvsInterface *s_ovs_iface; + NMSettingOvsDpdk *s_ovs_dpdk; NMSettingOvsPatch *s_ovs_patch; json_t *options = json_array (); @@ -349,11 +349,20 @@ _insert_interface (json_t *params, NMConnection *interface) type = nm_setting_ovs_interface_get_interface_type (s_ovs_iface); json_array_append_new (options, json_string ("map")); - s_ovs_patch = nm_connection_get_setting_ovs_patch (interface); - if (s_ovs_patch) { + + s_ovs_dpdk = (NMSettingOvsDpdk *) nm_connection_get_setting (interface, + NM_TYPE_SETTING_OVS_DPDK); + if (!s_ovs_dpdk) + s_ovs_patch = nm_connection_get_setting_ovs_patch (interface); + + if (s_ovs_dpdk) { + json_array_append_new (options, json_pack ("[[s, s]]", + "dpdk-devargs", + nm_setting_ovs_dpdk_get_devargs (s_ovs_dpdk))); + } else if (s_ovs_patch) { json_array_append_new (options, json_pack ("[[s, s]]", - "peer", - nm_setting_ovs_patch_get_peer (s_ovs_patch))); + "peer", + nm_setting_ovs_patch_get_peer (s_ovs_patch))); } else { json_array_append_new (options, json_array ()); } @@ -436,6 +445,7 @@ _insert_bridge (json_t *params, NMConnection *bridge, json_t *new_ports) gboolean mcast_snooping_enable = FALSE; gboolean rstp_enable = FALSE; gboolean stp_enable = FALSE; + const char *datapath_type = NULL; json_t *row; s_ovs_bridge = nm_connection_get_setting_ovs_bridge (bridge); @@ -447,6 +457,7 @@ _insert_bridge (json_t *params, NMConnection *bridge, json_t *new_ports) mcast_snooping_enable = nm_setting_ovs_bridge_get_mcast_snooping_enable (s_ovs_bridge); rstp_enable = nm_setting_ovs_bridge_get_rstp_enable (s_ovs_bridge); stp_enable = nm_setting_ovs_bridge_get_stp_enable (s_ovs_bridge); + datapath_type = nm_setting_ovs_bridge_get_datapath_type (s_ovs_bridge); } if (fail_mode) @@ -457,6 +468,8 @@ _insert_bridge (json_t *params, NMConnection *bridge, json_t *new_ports) json_object_set_new (row, "rstp_enable", json_boolean (rstp_enable)); if (stp_enable) json_object_set_new (row, "stp_enable", json_boolean (stp_enable)); + if (datapath_type) + json_object_set_new (row, "datapath_type", json_string (datapath_type)); json_object_set_new (row, "name", json_string (nm_connection_get_interface_name (bridge))); json_object_set_new (row, "ports", json_pack ("[s, O]", "set", new_ports)); @@ -534,9 +547,14 @@ _add_interface (NMOvsdb *self, json_t *params, json_array_append_new (ports, json_pack ("[s, s]", "uuid", port_uuid)); - if ( g_strcmp0 (ovs_port->name, nm_connection_get_interface_name (port)) != 0 - || g_strcmp0 (ovs_port->connection_uuid, nm_connection_get_uuid (port)) != 0) + if (!ovs_port) { + /* This would be a violation of ovsdb's reference integrity (a bug). */ + _LOGW ("Unknown port '%s' in bridge '%s'", port_uuid, bridge_uuid); continue; + } else if ( strcmp (ovs_port->name, nm_connection_get_interface_name (port)) != 0 + || g_strcmp0 (ovs_port->connection_uuid, nm_connection_get_uuid (port)) != 0) { + continue; + } for (ii = 0; ii < ovs_port->interfaces->len; ii++) { interface_uuid = g_ptr_array_index (ovs_port->interfaces, ii); @@ -544,9 +562,13 @@ _add_interface (NMOvsdb *self, json_t *params, json_array_append_new (interfaces, json_pack ("[s, s]", "uuid", interface_uuid)); - if ( g_strcmp0 (ovs_interface->name, nm_connection_get_interface_name (interface)) == 0 - && g_strcmp0 (ovs_interface->connection_uuid, nm_connection_get_uuid (interface)) == 0) + if (!ovs_interface) { + /* This would be a violation of ovsdb's reference integrity (a bug). */ + _LOGW ("Unknown interface '%s' in port '%s'", interface_uuid, port_uuid); + } else if ( strcmp (ovs_interface->name, nm_connection_get_interface_name (interface)) == 0 + && g_strcmp0 (ovs_interface->connection_uuid, nm_connection_get_uuid (interface)) == 0) { has_interface = TRUE; + } } break; @@ -642,16 +664,27 @@ _delete_interface (NMOvsdb *self, json_t *params, const char *ifname) interfaces_changed = FALSE; + if (!ovs_port) { + /* This would be a violation of ovsdb's reference integrity (a bug). */ + _LOGW ("Unknown port '%s' in bridge '%s'", port_uuid, bridge_uuid); + continue; + } + for (ii = 0; ii < ovs_port->interfaces->len; ii++) { interface_uuid = g_ptr_array_index (ovs_port->interfaces, ii); ovs_interface = g_hash_table_lookup (priv->interfaces, interface_uuid); json_array_append_new (interfaces, json_pack ("[s,s]", "uuid", interface_uuid)); - if (strcmp (ovs_interface->name, ifname) == 0) { - /* skip the interface */ - interfaces_changed = TRUE; - continue; + if (ovs_interface) { + if (strcmp (ovs_interface->name, ifname) == 0) { + /* skip the interface */ + interfaces_changed = TRUE; + continue; + } + } else { + /* This would be a violation of ovsdb's reference integrity (a bug). */ + _LOGW ("Unknown interface '%s' in port '%s'", interface_uuid, port_uuid); } json_array_append_new (new_interfaces, json_pack ("[s,s]", "uuid", interface_uuid)); @@ -718,14 +751,14 @@ ovsdb_next_command (NMOvsdb *self) msg = json_pack ("{s:i, s:s, s:[s, n, {" " s:[{s:[s, s, s]}]," " s:[{s:[s, s, s]}]," - " s:[{s:[s, s, s]}]," + " s:[{s:[s, s, s, s]}]," " s:[{s:[]}]" "}]}", "id", call->id, "method", "monitor", "params", "Open_vSwitch", "Bridge", "columns", "name", "ports", "external_ids", "Port", "columns", "name", "interfaces", "external_ids", - "Interface", "columns", "name", "type", "external_ids", + "Interface", "columns", "name", "type", "external_ids", "error", "Open_vSwitch", "columns"); break; case OVSDB_ADD_INTERFACE: @@ -864,21 +897,25 @@ ovsdb_got_update (NMOvsdb *self, json_t *msg) /* Interfaces */ json_object_foreach (interface, key, value) { + json_t *error = NULL; gboolean old = FALSE; gboolean new = FALSE; if (json_unpack (value, "{s:{}}", "old") == 0) old = TRUE; - if (json_unpack (value, "{s:{s:s, s:s, s:o}}", "new", + if (json_unpack (value, "{s:{s:s, s:s, s?:o, s:o}}", "new", "name", &name, "type", &type, + "error", &error, "external_ids", &external_ids) == 0) new = TRUE; if (old) { ovs_interface = g_hash_table_lookup (priv->interfaces, key); - if (!new || g_strcmp0 (ovs_interface->name, name) != 0) { + if (!ovs_interface) { + _LOGW ("Interface '%s' was not seen", key); + } else if (!new || strcmp (ovs_interface->name, name) != 0) { old = FALSE; _LOGT ("removed an '%s' interface: %s%s%s", ovs_interface->type, ovs_interface->name, @@ -899,12 +936,11 @@ ovsdb_got_update (NMOvsdb *self, json_t *msg) ovs_interface->name = g_strdup (name); ovs_interface->type = g_strdup (type); ovs_interface->connection_uuid = _connection_uuid_from_external_ids (external_ids); + g_hash_table_insert (priv->interfaces, g_strdup (key), ovs_interface); if (old) { _LOGT ("changed an '%s' interface: %s%s%s", type, ovs_interface->name, ovs_interface->connection_uuid ? ", " : "", ovs_interface->connection_uuid ?: ""); - g_signal_emit (self, signals[DEVICE_CHANGED], 0, - "ovs-interface", ovs_interface->name); } else { _LOGT ("added an '%s' interface: %s%s%s", ovs_interface->type, ovs_interface->name, @@ -917,7 +953,14 @@ ovsdb_got_update (NMOvsdb *self, json_t *msg) ovs_interface->name, NM_DEVICE_TYPE_OVS_INTERFACE); } } - g_hash_table_insert (priv->interfaces, g_strdup (key), ovs_interface); + /* The error is a string. No error is indicated by an empty set, + * because why the fuck not: [ "set": [] ] */ + if (error && json_is_string (error)) { + g_signal_emit (self, signals[INTERFACE_FAILED], 0, + ovs_interface->name, + ovs_interface->connection_uuid, + json_string_value (error)); + } } } @@ -954,12 +997,11 @@ ovsdb_got_update (NMOvsdb *self, json_t *msg) ovs_port->connection_uuid = _connection_uuid_from_external_ids (external_ids); ovs_port->interfaces = g_ptr_array_new_with_free_func (g_free); _uuids_to_array (ovs_port->interfaces, items); + g_hash_table_insert (priv->ports, g_strdup (key), ovs_port); if (old) { _LOGT ("changed a port: %s%s%s", ovs_port->name, ovs_port->connection_uuid ? ", " : "", ovs_port->connection_uuid ?: ""); - g_signal_emit (self, signals[DEVICE_CHANGED], 0, - NM_SETTING_OVS_PORT_SETTING_NAME, ovs_port->name); } else { _LOGT ("added a port: %s%s%s", ovs_port->name, ovs_port->connection_uuid ? ", " : "", @@ -967,7 +1009,6 @@ ovsdb_got_update (NMOvsdb *self, json_t *msg) g_signal_emit (self, signals[DEVICE_ADDED], 0, ovs_port->name, NM_DEVICE_TYPE_OVS_PORT); } - g_hash_table_insert (priv->ports, g_strdup (key), ovs_port); } } @@ -1004,12 +1045,11 @@ ovsdb_got_update (NMOvsdb *self, json_t *msg) ovs_bridge->connection_uuid = _connection_uuid_from_external_ids (external_ids); ovs_bridge->ports = g_ptr_array_new_with_free_func (g_free); _uuids_to_array (ovs_bridge->ports, items); + g_hash_table_insert (priv->bridges, g_strdup (key), ovs_bridge); if (old) { _LOGT ("changed a bridge: %s%s%s", ovs_bridge->name, ovs_bridge->connection_uuid ? ", " : "", ovs_bridge->connection_uuid ?: ""); - g_signal_emit (self, signals[DEVICE_CHANGED], 0, - NM_SETTING_OVS_BRIDGE_SETTING_NAME, ovs_bridge->name); } else { _LOGT ("added a bridge: %s%s%s", ovs_bridge->name, ovs_bridge->connection_uuid ? ", " : "", @@ -1017,7 +1057,6 @@ ovsdb_got_update (NMOvsdb *self, json_t *msg) g_signal_emit (self, signals[DEVICE_ADDED], 0, ovs_bridge->name, NM_DEVICE_TYPE_OVS_BRIDGE); } - g_hash_table_insert (priv->bridges, g_strdup (key), ovs_bridge); } } @@ -1562,19 +1601,19 @@ nm_ovsdb_class_init (NMOvsdbClass *klass) G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, - G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_UINT); + G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_UINT); signals[DEVICE_REMOVED] = g_signal_new (NM_OVSDB_DEVICE_REMOVED, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, - G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_UINT); + G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_UINT); - signals[DEVICE_CHANGED] = - g_signal_new (NM_OVSDB_DEVICE_CHANGED, + signals[INTERFACE_FAILED] = + g_signal_new (NM_OVSDB_INTERFACE_FAILED, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, - G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_UINT); + G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); } diff --git a/src/devices/ovs/nm-ovsdb.h b/src/devices/ovs/nm-ovsdb.h index cf9fe2a2..279155a4 100644 --- a/src/devices/ovs/nm-ovsdb.h +++ b/src/devices/ovs/nm-ovsdb.h @@ -27,9 +27,9 @@ #define NM_IS_OVSDB_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_OVSDB)) #define NM_OVSDB_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_OVSDB, NMOvsdbClass)) -#define NM_OVSDB_DEVICE_ADDED "device-added" -#define NM_OVSDB_DEVICE_REMOVED "device-removed" -#define NM_OVSDB_DEVICE_CHANGED "device-changed" +#define NM_OVSDB_DEVICE_ADDED "device-added" +#define NM_OVSDB_DEVICE_REMOVED "device-removed" +#define NM_OVSDB_INTERFACE_FAILED "interface-failed" typedef struct _NMOvsdb NMOvsdb; typedef struct _NMOvsdbClass NMOvsdbClass; diff --git a/src/devices/team/nm-device-team.c b/src/devices/team/nm-device-team.c index 287f4d1b..a60a9fda 100644 --- a/src/devices/team/nm-device-team.c +++ b/src/devices/team/nm-device-team.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -101,6 +100,7 @@ complete_connection (NMDevice *device, NULL, _("Team connection"), "team", + NULL, TRUE); s_team = nm_connection_get_setting_team (connection); @@ -694,6 +694,9 @@ deactivate (NMDevice *device) NMDeviceTeam *self = NM_DEVICE_TEAM (device); NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self); + if (nm_device_sys_iface_state_is_external (device)) + return; + if (priv->teamd_pid || priv->tdc) _LOGI (LOGD_TEAM, "deactivation: stopping teamd..."); @@ -771,11 +774,19 @@ release_slave (NMDevice *device, NMDeviceTeam *self = NM_DEVICE_TEAM (device); NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self); gboolean success; + int ifindex_slave; + + ifindex_slave = nm_device_get_ip_ifindex (slave); + + if (ifindex_slave <= 0) { + _LOGD (LOGD_TEAM, "team port %s is already released", nm_device_get_ip_iface (slave)); + return; + } if (configure) { success = nm_platform_link_release (nm_device_get_platform (device), nm_device_get_ip_ifindex (device), - nm_device_get_ip_ifindex (slave)); + ifindex_slave); if (success) _LOGI (LOGD_TEAM, "released team port %s", nm_device_get_ip_iface (slave)); @@ -786,9 +797,10 @@ release_slave (NMDevice *device, * IFF_UP), so we must bring it back up here to ensure carrier changes and * other state is noticed by the now-released port. */ - if (!nm_device_bring_up (slave, TRUE, NULL)) + if (!nm_device_bring_up (slave, TRUE, NULL)) { _LOGW (LOGD_TEAM, "released team port %s could not be brought up", nm_device_get_ip_iface (slave)); + } nm_clear_g_source (&priv->teamd_read_timeout); priv->teamd_read_timeout = g_timeout_add_seconds (5, diff --git a/src/devices/team/nm-device-team.h b/src/devices/team/nm-device-team.h index bbb1417f..0c0b5101 100644 --- a/src/devices/team/nm-device-team.h +++ b/src/devices/team/nm-device-team.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * Copyright (C) 2013 Jiri Pirko <jiri@resnulli.us> diff --git a/src/devices/team/nm-team-factory.c b/src/devices/team/nm-team-factory.c index f18b943d..21a85701 100644 --- a/src/devices/team/nm-team-factory.c +++ b/src/devices/team/nm-team-factory.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/tests/meson.build b/src/devices/tests/meson.build index 4702c656..4b4c5c7e 100644 --- a/src/devices/tests/meson.build +++ b/src/devices/tests/meson.build @@ -9,7 +9,6 @@ foreach test_unit: test_units test_unit + '.c', dependencies: test_nm_dep, ) - test( 'devices/' + test_unit, test_script, diff --git a/src/devices/tests/test-acd.c b/src/devices/tests/test-acd.c index 8b52ee2c..42accfaf 100644 --- a/src/devices/tests/test-acd.c +++ b/src/devices/tests/test-acd.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-platform.c - Handle runtime kernel networking configuration * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/tests/test-lldp.c b/src/devices/tests/test-lldp.c index 7b135f58..ad157b7e 100644 --- a/src/devices/tests/test-lldp.c +++ b/src/devices/tests/test-lldp.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-platform.c - Handle runtime kernel networking configuration * * This program is free software; you can redistribute it and/or modify @@ -457,7 +456,7 @@ _test_recv_fixture_setup (TestRecvFixture *fixture, gconstpointer user_data) fixture->ifindex = link->ifindex; fixture->fd = nm_steal_fd (&fd); - memcpy (fixture->mac, link->addr.data, ETH_ALEN); + memcpy (fixture->mac, link->l_address.data, ETH_ALEN); } typedef struct { diff --git a/src/devices/wifi/nm-device-iwd.c b/src/devices/wifi/nm-device-iwd.c index da7d0685..9cfe5f70 100644 --- a/src/devices/wifi/nm-device-iwd.c +++ b/src/devices/wifi/nm-device-iwd.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -780,12 +779,10 @@ complete_connection (NMDevice *device, NMDeviceIwd *self = NM_DEVICE_IWD (device); NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); NMSettingWireless *s_wifi; - const char *setting_mac; gs_free char *ssid_utf8 = NULL; NMWifiAP *ap; GBytes *ssid; GBytes *setting_ssid = NULL; - const char *perm_hw_addr; const char *mode; s_wifi = nm_connection_get_setting_wireless (connection); @@ -875,36 +872,9 @@ complete_connection (NMDevice *device, ssid_utf8, ssid_utf8, NULL, + nm_setting_wireless_get_mac_address (s_wifi) ? NULL : nm_device_get_iface (device), TRUE); - perm_hw_addr = nm_device_get_permanent_hw_address (device); - if (perm_hw_addr) { - setting_mac = nm_setting_wireless_get_mac_address (s_wifi); - if (setting_mac) { - /* Make sure the setting MAC (if any) matches the device's permanent MAC */ - if (!nm_utils_hwaddr_matches (setting_mac, -1, perm_hw_addr, -1)) { - g_set_error_literal (error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - "connection does not match 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); - } - } - } - return TRUE; } diff --git a/src/devices/wifi/nm-device-iwd.h b/src/devices/wifi/nm-device-iwd.h index 4a2bd31e..aab45b7b 100644 --- a/src/devices/wifi/nm-device-iwd.h +++ b/src/devices/wifi/nm-device-iwd.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/wifi/nm-device-olpc-mesh.c b/src/devices/wifi/nm-device-olpc-mesh.c index 1172a613..3c865c55 100644 --- a/src/devices/wifi/nm-device-olpc-mesh.c +++ b/src/devices/wifi/nm-device-olpc-mesh.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * Dan Williams <dcbw@redhat.com> @@ -133,6 +132,7 @@ complete_connection (NMDevice *device, NULL, _("Mesh"), NULL, + NULL, FALSE); /* No IPv6 by default */ return TRUE; diff --git a/src/devices/wifi/nm-device-olpc-mesh.h b/src/devices/wifi/nm-device-olpc-mesh.h index bf0e4da9..619fc46a 100644 --- a/src/devices/wifi/nm-device-olpc-mesh.h +++ b/src/devices/wifi/nm-device-olpc-mesh.h @@ -1,5 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ - /* NetworkManager -- Network link manager * * Dan Williams <dcbw@redhat.com> diff --git a/src/devices/wifi/nm-device-wifi-p2p.c b/src/devices/wifi/nm-device-wifi-p2p.c index 8381ebc7..c5826e24 100644 --- a/src/devices/wifi/nm-device-wifi-p2p.c +++ b/src/devices/wifi/nm-device-wifi-p2p.c @@ -331,6 +331,7 @@ complete_connection (NMDevice *device, setting_name, setting_name, NULL, + NULL, TRUE); return TRUE; diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c index 3a58c620..2ffba47a 100644 --- a/src/devices/wifi/nm-device-wifi.c +++ b/src/devices/wifi/nm-device-wifi.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -545,6 +544,60 @@ wake_on_wlan_restore (NMDeviceWifi *self) } static void +disconnect_cb (NMSupplicantInterface *iface, GError *error, gpointer user_data) +{ + gs_unref_object NMDeviceWifi *self = NULL; + NMDeviceDeactivateCallback callback; + gpointer callback_user_data; + + nm_utils_user_data_unpack (user_data, &self, &callback, &callback_user_data); + + /* error will be freed by sup_iface */ + callback (NM_DEVICE (self), error, callback_user_data); +} + +static void +disconnect_cb_on_idle (gpointer user_data, + GCancellable *cancellable) +{ + gs_unref_object NMDeviceWifi *self = NULL; + NMDeviceDeactivateCallback callback; + gpointer callback_user_data; + gs_free_error GError *cancelled_error = NULL; + + nm_utils_user_data_unpack (user_data, &self, &callback, &callback_user_data); + + 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, + NMDeviceDeactivateCallback callback, + gpointer callback_user_data) { + NMDeviceWifi *self = NM_DEVICE_WIFI (device); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + gpointer user_data; + + nm_assert (G_IS_CANCELLABLE (cancellable)); + nm_assert (callback); + + user_data = nm_utils_user_data_pack (g_object_ref (self), callback, callback_user_data); + if (!priv->sup_iface) { + nm_utils_invoke_on_idle (disconnect_cb_on_idle, user_data, cancellable); + return; + } + + cleanup_association_attempt (self, FALSE); + + nm_supplicant_interface_disconnect_async (priv->sup_iface, + cancellable, + disconnect_cb, + user_data); +} + +static void deactivate (NMDevice *device) { NMDeviceWifi *self = NM_DEVICE_WIFI (device); @@ -695,6 +748,20 @@ check_connection_compatible (NMDevice *device, NMConnection *connection, GError return FALSE; } } + } else if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_MESH) == 0) { + if (!(priv->capabilities & NM_WIFI_DEVICE_CAP_MESH)) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "the device does not support Mesh mode"); + return FALSE; + } + + if (priv->sup_iface) { + if (nm_supplicant_interface_get_mesh_support (priv->sup_iface) == NM_SUPPLICANT_FEATURE_NO) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "wpa_supplicant does not support Mesh mode"); + return FALSE; + } + } } // FIXME: check channel/freq/band against bands the hardware supports @@ -739,12 +806,13 @@ check_connection_available (NMDevice *device, return TRUE; } - /* Ad-Hoc and AP connections are always available because they may be + /* Ad-Hoc, AP and Mesh connections are always available because they may be * started at any time. */ mode = nm_setting_wireless_get_mode (s_wifi); if ( g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_ADHOC) == 0 - || g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_AP) == 0) + || g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_AP) == 0 + || g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_MESH) == 0) return TRUE; /* Hidden SSIDs obviously don't always appear in the scan list either. @@ -778,13 +846,11 @@ complete_connection (NMDevice *device, NMDeviceWifi *self = NM_DEVICE_WIFI (device); NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); NMSettingWireless *s_wifi; - const char *setting_mac; gs_free char *ssid_utf8 = NULL; NMWifiAP *ap; GBytes *ssid = NULL; GBytes *setting_ssid = NULL; gboolean hidden = FALSE; - const char *perm_hw_addr; const char *mode; s_wifi = nm_connection_get_setting_wireless (connection); @@ -908,27 +974,12 @@ complete_connection (NMDevice *device, ssid_utf8, ssid_utf8, NULL, + nm_setting_wireless_get_mac_address (s_wifi) ? NULL : nm_device_get_iface (device), TRUE); if (hidden) g_object_set (s_wifi, NM_SETTING_WIRELESS_HIDDEN, TRUE, NULL); - perm_hw_addr = nm_device_get_permanent_hw_address (device); - if (perm_hw_addr) { - setting_mac = nm_setting_wireless_get_mac_address (s_wifi); - if (setting_mac) { - /* Make sure the setting MAC (if any) matches the device's permanent MAC */ - if (!nm_utils_hwaddr_matches (setting_mac, -1, perm_hw_addr, -1)) { - g_set_error_literal (error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("connection does not match device")); - g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_MAC_ADDRESS); - return FALSE; - } - } - } - return TRUE; } @@ -972,7 +1023,8 @@ can_auto_connect (NMDevice *device, NMConnection *connection; NMSettingWireless *s_wifi; NMWifiAP *ap; - const char *method, *mode; + const char *method6, *mode; + gboolean auto4, auto6; guint64 timestamp = 0; nm_assert (!specific_object || !*specific_object); @@ -985,13 +1037,20 @@ can_auto_connect (NMDevice *device, s_wifi = nm_connection_get_setting_wireless (connection); 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, AF_INET); + /* Always allow autoconnect for AP and non-autoconf Ad-Hoc or Mesh */ + auto4 = nm_streq0 (nm_utils_get_ip_config_method (connection, AF_INET), + NM_SETTING_IP4_CONFIG_METHOD_AUTO); + method6 = nm_utils_get_ip_config_method (connection, AF_INET6); + auto6 = nm_streq0 (method6, NM_SETTING_IP6_CONFIG_METHOD_AUTO) + || nm_streq0 (method6, NM_SETTING_IP6_CONFIG_METHOD_DHCP); + mode = nm_setting_wireless_get_mode (s_wifi); + if (nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_AP)) return TRUE; - else if ( nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_ADHOC) - && !nm_streq0 (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) + else if (!auto4 && nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_ADHOC)) + return TRUE; + else if (!auto4 && !auto6 && nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_MESH)) return TRUE; /* Don't autoconnect to networks that have been tried at least once @@ -1531,13 +1590,14 @@ try_fill_ssid_for_hidden_ap (NMDeviceWifi *self, NMSettingsConnection *sett_conn = connections[i]; NMSettingWireless *s_wifi; + if (!nm_settings_connection_has_seen_bssid (sett_conn, bssid)) + continue; s_wifi = nm_connection_get_setting_wireless (nm_settings_connection_get_connection (sett_conn)); - if (s_wifi) { - if (nm_settings_connection_has_seen_bssid (sett_conn, bssid)) { - nm_wifi_ap_set_ssid (ap, nm_setting_wireless_get_ssid (s_wifi)); - break; - } - } + if (!s_wifi) + continue; + + nm_wifi_ap_set_ssid (ap, nm_setting_wireless_get_ssid (s_wifi)); + break; } } @@ -1777,9 +1837,10 @@ supplicant_iface_wps_credentials_cb (NMSupplicantInterface *iface, } if (secrets) { if (nm_settings_connection_new_secrets (nm_act_request_get_settings_connection (req), - nm_act_request_get_applied_connection (req), - NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, - secrets, &error)) { + nm_act_request_get_applied_connection (req), + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + secrets, + &error)) { wifi_secrets_cancel (self); nm_device_activate_schedule_stage1_device_prepare (NM_DEVICE (self)); } else { @@ -2398,6 +2459,7 @@ supplicant_connection_timeout_cb (gpointer user_data) g_assert (connection); if ( priv->mode == NM_802_11_MODE_ADHOC + || priv->mode == NM_802_11_MODE_MESH || priv->mode == NM_802_11_MODE_AP) { /* In Ad-Hoc and AP modes there's nothing to check the encryption key * (if any), so supplicant timeouts here are almost certainly the wifi @@ -2468,7 +2530,9 @@ build_supplicant_config (NMDeviceWifi *self, config = nm_supplicant_config_new ( nm_supplicant_interface_get_pmf_support (priv->sup_iface) == NM_SUPPLICANT_FEATURE_YES, - nm_supplicant_interface_get_fils_support (priv->sup_iface) == NM_SUPPLICANT_FEATURE_YES); + nm_supplicant_interface_get_fils_support (priv->sup_iface) == NM_SUPPLICANT_FEATURE_YES, + nm_supplicant_interface_get_ft_support (priv->sup_iface) == NM_SUPPLICANT_FEATURE_YES, + nm_supplicant_interface_get_sha384_support (priv->sup_iface) == NM_SUPPLICANT_FEATURE_YES); /* Warn if AP mode may not be supported */ if ( g_strcmp0 (nm_setting_wireless_get_mode (s_wireless), NM_SETTING_WIRELESS_MODE_AP) == 0 @@ -2635,7 +2699,8 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) /* Scanning not done in AP mode; clear the scan list */ remove_all_aps (self); - } + } else if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_MESH) == 0) + priv->mode = NM_802_11_MODE_MESH; _notify (self, PROP_MODE); /* The kernel doesn't support Ad-Hoc WPA connections well at this time, @@ -2655,8 +2720,8 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) if (!nm_device_hw_addr_set_cloned (device, connection, TRUE)) return NM_ACT_STAGE_RETURN_FAILURE; - /* AP mode never uses a specific object or existing scanned AP */ - if (priv->mode != NM_802_11_MODE_AP) { + /* AP and Mesh modes never use a specific object or existing scanned AP */ + if (priv->mode != NM_802_11_MODE_AP && priv->mode != NM_802_11_MODE_MESH) { 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) @@ -2672,10 +2737,10 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) } /* 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 (Hotspot). + * (hidden network or something), starting a Hotspot or joining a Mesh, + * create a fake APfrom 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 (Hotspot). */ ap = nm_wifi_ap_new_fake_from_connection (connection); g_return_val_if_fail (ap != NULL, NM_ACT_STAGE_RETURN_FAILURE); @@ -2763,6 +2828,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; NMSupplicantConfig *config = NULL; + NM80211Mode ap_mode; NMActRequest *req; NMWifiAP *ap; NMConnection *connection; @@ -2783,6 +2849,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); goto out; } + ap_mode = nm_wifi_ap_get_mode (ap); connection = nm_act_request_get_applied_connection (req); g_assert (connection); @@ -2822,14 +2889,16 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) priv->ssid_found = FALSE; - /* Supplicant requires an initial frequency for Ad-Hoc and Hotspot; if the user - * didn't specify one and we didn't find an AP that matched the connection, - * just pick a frequency the device supports. + /* Supplicant requires an initial frequency for Ad-Hoc, Hotspot and Mesh; + * if the user didn't specify one and we didn't find an AP that matched + * the connection, just pick a frequency the device supports. */ - if ((nm_wifi_ap_get_mode (ap) == NM_802_11_MODE_ADHOC) || nm_wifi_ap_is_hotspot (ap)) + if ( ap_mode == NM_802_11_MODE_ADHOC + || ap_mode == NM_802_11_MODE_MESH + || nm_wifi_ap_is_hotspot (ap)) ensure_hotspot_frequency (self, s_wireless, ap); - if (nm_wifi_ap_get_mode (ap) == NM_802_11_MODE_INFRA) + if (ap_mode == NM_802_11_MODE_INFRA) set_powersave (device); /* Build up the supplicant configuration */ @@ -3386,6 +3455,7 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass) device_class->get_configured_mtu = get_configured_mtu; device_class->act_stage3_ip_config_start = act_stage3_ip_config_start; device_class->act_stage4_ip_config_timeout = act_stage4_ip_config_timeout; + device_class->deactivate_async = deactivate_async; device_class->deactivate = deactivate; device_class->deactivate_reset_hw_addr = deactivate_reset_hw_addr; device_class->unmanaged_on_quit = unmanaged_on_quit; diff --git a/src/devices/wifi/nm-device-wifi.h b/src/devices/wifi/nm-device-wifi.h index 116ad11e..82f62be6 100644 --- a/src/devices/wifi/nm-device-wifi.h +++ b/src/devices/wifi/nm-device-wifi.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/wifi/nm-iwd-manager.c b/src/devices/wifi/nm-iwd-manager.c index 494fca32..5b550ea4 100644 --- a/src/devices/wifi/nm-iwd-manager.c +++ b/src/devices/wifi/nm-iwd-manager.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -437,7 +436,6 @@ mirror_8021x_connection (NMIwdManager *self, NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_CONNECTION_ID, name, NM_SETTING_CONNECTION_UUID, nm_utils_uuid_generate_buf (uuid), - NM_SETTING_CONNECTION_READ_ONLY, TRUE, NULL)); nm_connection_add_setting (connection, setting); @@ -470,19 +468,19 @@ mirror_8021x_connection (NMIwdManager *self, if (!nm_connection_normalize (connection, NULL, NULL, NULL)) return NULL; - settings_connection = nm_settings_add_connection (priv->settings, connection, - FALSE, &error); - if (!settings_connection) { + if (!nm_settings_add_connection (priv->settings, + connection, + NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY, + NM_SETTINGS_CONNECTION_ADD_REASON_NONE, + NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED, + &settings_connection, + &error)) { _LOGW ("failed to add a mirror NMConnection for IWD's Known Network '%s': %s", name, error->message); g_error_free (error); return NULL; } - nm_settings_connection_set_flags (settings_connection, - NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED | - NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED, - TRUE); return settings_connection; } @@ -499,7 +497,7 @@ mirror_8021x_connection_take_and_delete (NMSettingsConnection *sett_conn) /* If connection has not been saved since we created it * in interface_added it too can be removed now. */ if (NM_FLAGS_HAS (flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)) - nm_settings_connection_delete (sett_conn, NULL); + nm_settings_connection_delete (sett_conn, FALSE); g_object_unref (sett_conn); } @@ -900,7 +898,7 @@ 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 ()); + priv->settings = g_object_ref (NM_SETTINGS_GET); g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_REMOVED, G_CALLBACK (connection_removed), self); diff --git a/src/devices/wifi/nm-iwd-manager.h b/src/devices/wifi/nm-iwd-manager.h index 57b7007a..b410e4ce 100644 --- a/src/devices/wifi/nm-iwd-manager.h +++ b/src/devices/wifi/nm-iwd-manager.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/wifi/nm-wifi-ap.c b/src/devices/wifi/nm-wifi-ap.c index 87daff59..c7ab7f04 100644 --- a/src/devices/wifi/nm-wifi-ap.c +++ b/src/devices/wifi/nm-wifi-ap.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -260,7 +259,8 @@ nm_wifi_ap_set_mode (NMWifiAP *ap, const NM80211Mode mode) g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE); g_return_val_if_fail ( mode == NM_802_11_MODE_ADHOC - || mode == NM_802_11_MODE_INFRA, FALSE); + || mode == NM_802_11_MODE_INFRA + || mode == NM_802_11_MODE_MESH, FALSE); priv = NM_WIFI_AP_GET_PRIVATE (ap); @@ -418,9 +418,11 @@ security_from_vardict (GVariant *security) if ( g_variant_lookup (security, "KeyMgmt", "^a&s", &array) && array) { - if (g_strv_contains (array, "wpa-psk")) + if (g_strv_contains (array, "wpa-psk") || + g_strv_contains (array, "wpa-ft-psk")) flags |= NM_802_11_AP_SEC_KEY_MGMT_PSK; if (g_strv_contains (array, "wpa-eap") || + g_strv_contains (array, "wpa-ft-eap") || g_strv_contains (array, "wpa-fils-sha256") || g_strv_contains (array, "wpa-fils-sha384")) flags |= NM_802_11_AP_SEC_KEY_MGMT_802_1X; @@ -815,6 +817,8 @@ nm_wifi_ap_update_from_properties (NMWifiAP *ap, changed |= nm_wifi_ap_set_mode (ap, NM_802_11_MODE_INFRA); else if (!g_strcmp0 (s, "ad-hoc")) changed |= nm_wifi_ap_set_mode (ap, NM_802_11_MODE_ADHOC); + else if (!g_strcmp0 (s, "mesh")) + changed |= nm_wifi_ap_set_mode (ap, NM_802_11_MODE_MESH); } if (g_variant_lookup (properties, "Signal", "n", &i16)) @@ -1007,7 +1011,9 @@ nm_wifi_ap_to_string (const NMWifiAP *self, ? '#' : (priv->fake ? 'f' - : 'a'))), + : (priv->mode == NM_802_11_MODE_MESH + ? 'm' + : 'a')))), chan, priv->strength, priv->flags & NM_802_11_AP_FLAGS_PRIVACY ? 'P' : '_', @@ -1072,6 +1078,8 @@ nm_wifi_ap_check_compatible (NMWifiAP *self, if ( !strcmp (mode, "ap") && (priv->mode != NM_802_11_MODE_INFRA || priv->hotspot != TRUE)) return FALSE; + if (!strcmp (mode, "mesh") && (priv->mode != NM_802_11_MODE_MESH)) + return FALSE; } band = nm_setting_wireless_get_band (s_wireless); @@ -1115,6 +1123,7 @@ nm_wifi_ap_complete_connection (NMWifiAP *self, return nm_wifi_utils_complete_connection (priv->ssid, priv->address, priv->mode, + priv->freq, priv->flags, priv->wpa_flags, priv->rsn_flags, @@ -1245,6 +1254,8 @@ nm_wifi_ap_new_fake_from_connection (NMConnection *connection) nm_wifi_ap_set_mode (ap, NM_802_11_MODE_INFRA); else if (!strcmp (mode, "adhoc")) nm_wifi_ap_set_mode (ap, NM_802_11_MODE_ADHOC); + else if (!strcmp (mode, "mesh")) + nm_wifi_ap_set_mode (ap, NM_802_11_MODE_MESH); else if (!strcmp (mode, "ap")) { nm_wifi_ap_set_mode (ap, NM_802_11_MODE_INFRA); NM_WIFI_AP_GET_PRIVATE (ap)->hotspot = TRUE; diff --git a/src/devices/wifi/nm-wifi-ap.h b/src/devices/wifi/nm-wifi-ap.h index 7462e9d1..755e722c 100644 --- a/src/devices/wifi/nm-wifi-ap.h +++ b/src/devices/wifi/nm-wifi-ap.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/wifi/nm-wifi-common.c b/src/devices/wifi/nm-wifi-common.c index c95620e7..96828d59 100644 --- a/src/devices/wifi/nm-wifi-common.c +++ b/src/devices/wifi/nm-wifi-common.c @@ -1,4 +1,3 @@ -/*-*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/src/devices/wifi/nm-wifi-common.h b/src/devices/wifi/nm-wifi-common.h index 91cbeb55..81d657ec 100644 --- a/src/devices/wifi/nm-wifi-common.h +++ b/src/devices/wifi/nm-wifi-common.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/src/devices/wifi/nm-wifi-factory.c b/src/devices/wifi/nm-wifi-factory.c index 9a89bbe9..2f069882 100644 --- a/src/devices/wifi/nm-wifi-factory.c +++ b/src/devices/wifi/nm-wifi-factory.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/wifi/nm-wifi-utils.c b/src/devices/wifi/nm-wifi-utils.c index c6e8b3e0..426eeea8 100644 --- a/src/devices/wifi/nm-wifi-utils.c +++ b/src/devices/wifi/nm-wifi-utils.c @@ -1,4 +1,3 @@ -/*-*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -25,6 +24,7 @@ #include <stdlib.h> #include "nm-utils.h" +#include "nm-core-internal.h" static gboolean verify_no_wep (NMSettingWirelessSecurity *s_wsec, const char *tag, GError **error) @@ -527,6 +527,7 @@ gboolean nm_wifi_utils_complete_connection (GBytes *ap_ssid, const char *bssid, NM80211Mode ap_mode, + guint32 ap_freq, guint32 ap_flags, guint32 ap_wpa_flags, guint32 ap_rsn_flags, @@ -540,6 +541,7 @@ nm_wifi_utils_complete_connection (GBytes *ap_ssid, GBytes *ssid; const char *mode, *key_mgmt, *auth_alg, *leap_username; gboolean adhoc = FALSE; + gboolean mesh = FALSE; s_wifi = nm_connection_get_setting_wireless (connection); g_assert (s_wifi); @@ -576,6 +578,10 @@ nm_wifi_utils_complete_connection (GBytes *ap_ssid, if (ap_mode == NM_802_11_MODE_ADHOC) valid = TRUE; adhoc = TRUE; + } else if (!strcmp (mode, NM_SETTING_WIRELESS_MODE_MESH)) { + if (ap_mode == NM_802_11_MODE_MESH) + valid = TRUE; + mesh = TRUE; } if (valid == FALSE) { @@ -591,10 +597,57 @@ nm_wifi_utils_complete_connection (GBytes *ap_ssid, if (ap_mode == NM_802_11_MODE_ADHOC) { mode = NM_SETTING_WIRELESS_MODE_ADHOC; adhoc = TRUE; + } else if (ap_mode == NM_802_11_MODE_MESH) { + mode = NM_SETTING_WIRELESS_MODE_MESH; + mesh = TRUE; } g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_MODE, mode, NULL); } + /* For now mesh requires channel and band, fill them only if both not present. + * Do not check existing values against an existing ap/mesh point, + * mesh join will start a new network if required */ + if (mesh) { + const char *band; + guint32 channel; + gboolean band_valid = TRUE; + gboolean chan_valid = TRUE; + gboolean valid; + + band = nm_setting_wireless_get_band (s_wifi); + channel = nm_setting_wireless_get_channel (s_wifi); + + valid = ((band == NULL) && (channel == 0)) + || ((band != NULL) && (channel != 0)); + + if ((band == NULL) && (channel == 0)) { + channel = nm_utils_wifi_freq_to_channel (ap_freq); + if (channel) { + g_object_set (s_wifi, + NM_SETTING_WIRELESS_CHANNEL, channel, + NULL); + } else { + chan_valid = FALSE; + } + + band = nm_utils_wifi_freq_to_band (ap_freq); + if (band) { + g_object_set (s_wifi, NM_SETTING_WIRELESS_BAND, band, NULL); + } else { + band_valid = FALSE; + } + } + + if (!valid || !chan_valid || !band_valid) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("connection does not match mesh point")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_MODE); + return FALSE; + } + } + /* Security */ /* Open */ diff --git a/src/devices/wifi/nm-wifi-utils.h b/src/devices/wifi/nm-wifi-utils.h index 03238c24..251d122f 100644 --- a/src/devices/wifi/nm-wifi-utils.h +++ b/src/devices/wifi/nm-wifi-utils.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -37,6 +36,7 @@ typedef enum { gboolean nm_wifi_utils_complete_connection (GBytes *ssid, const char *bssid, NM80211Mode mode, + guint32 ap_freq, guint32 flags, guint32 wpa_flags, guint32 rsn_flags, diff --git a/src/devices/wifi/tests/meson.build b/src/devices/wifi/tests/meson.build index 895853c4..ba756d53 100644 --- a/src/devices/wifi/tests/meson.build +++ b/src/devices/wifi/tests/meson.build @@ -1,13 +1,13 @@ -test_unit = 'test-general' +test_unit = 'test-devices-wifi' exe = executable( - 'wifi-' + test_unit, + test_unit, [test_unit + '.c'] + common_sources, dependencies: test_nm_dep, ) test( - 'devices/wifi/' + test_unit, + test_unit, test_script, args: test_args + [exe.full_path()], timeout: default_test_timeout, diff --git a/src/devices/wifi/tests/test-general.c b/src/devices/wifi/tests/test-devices-wifi.c index e0899837..a0b3e17f 100644 --- a/src/devices/wifi/tests/test-general.c +++ b/src/devices/wifi/tests/test-devices-wifi.c @@ -1,4 +1,3 @@ -/* -*- 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 @@ -86,6 +85,7 @@ complete_connection (const char *ssid, return nm_wifi_utils_complete_connection (ssid_b, bssid, mode, + 0, flags, wpa_flags, rsn_flags, diff --git a/src/devices/wwan/libnm-wwan.ver b/src/devices/wwan/libnm-wwan.ver index ea966afe..7ccebcb5 100644 --- a/src/devices/wwan/libnm-wwan.ver +++ b/src/devices/wwan/libnm-wwan.ver @@ -7,13 +7,16 @@ global: nm_modem_deactivate; nm_modem_deactivate_async; nm_modem_device_state_changed; + nm_modem_get_apn; nm_modem_get_capabilities; nm_modem_get_configured_mtu; nm_modem_get_control_port; + nm_modem_get_device_id; nm_modem_get_driver; nm_modem_get_iid; nm_modem_get_path; nm_modem_get_ip_ifindex; + nm_modem_get_operator_code; nm_modem_get_secrets; nm_modem_get_state; nm_modem_get_type; diff --git a/src/devices/wwan/nm-device-modem.c b/src/devices/wwan/nm-device-modem.c index 1e316280..042a6ca4 100644 --- a/src/devices/wwan/nm-device-modem.c +++ b/src/devices/wwan/nm-device-modem.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -15,7 +14,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2009 - 2011 Red Hat, Inc. + * Copyright (C) 2009 - 2019 Red Hat, Inc. */ #include "nm-default.h" @@ -36,10 +35,13 @@ _LOG_DECLARE_SELF(NMDeviceModem); /*****************************************************************************/ -NM_GOBJECT_PROPERTIES_DEFINE_BASE ( +NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceModem, PROP_MODEM, PROP_CAPABILITIES, PROP_CURRENT_CAPABILITIES, + PROP_DEVICE_ID, + PROP_OPERATOR_CODE, + PROP_APN, ); typedef struct { @@ -47,6 +49,9 @@ typedef struct { NMDeviceModemCapabilities caps; NMDeviceModemCapabilities current_caps; gboolean rf_enabled; + char *device_id; + char *operator_code; + char *apn; } NMDeviceModemPrivate; struct _NMDeviceModem { @@ -308,6 +313,34 @@ ip_ifindex_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data) } static void +operator_code_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data) +{ + NMDeviceModem *self = NM_DEVICE_MODEM (user_data); + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (self); + const char *operator_code = nm_modem_get_operator_code (modem); + + if (g_strcmp0 (priv->operator_code, operator_code) != 0) { + g_free (priv->operator_code); + priv->operator_code = g_strdup (operator_code); + _notify (self, PROP_OPERATOR_CODE); + } +} + +static void +apn_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data) +{ + NMDeviceModem *self = NM_DEVICE_MODEM (user_data); + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (self); + const char *apn = nm_modem_get_apn (modem); + + if (g_strcmp0 (priv->apn, apn) != 0) { + g_free (priv->apn); + priv->apn = g_strdup (apn); + _notify (self, PROP_APN); + } +} + +static void ids_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data) { nm_device_recheck_available_connections (NM_DEVICE (user_data)); @@ -499,7 +532,11 @@ complete_connection (NMDevice *device, { NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) device); - return nm_modem_complete_connection (priv->modem, connection, existing_connections, error); + return nm_modem_complete_connection (priv->modem, + nm_device_get_iface (device), + connection, + existing_connections, + error); } static void @@ -688,6 +725,8 @@ set_modem (NMDeviceModem *self, NMModem *modem) g_signal_connect (modem, "notify::" NM_MODEM_DEVICE_ID, G_CALLBACK (ids_changed_cb), self); g_signal_connect (modem, "notify::" NM_MODEM_SIM_ID, G_CALLBACK (ids_changed_cb), self); g_signal_connect (modem, "notify::" NM_MODEM_SIM_OPERATOR_ID, G_CALLBACK (ids_changed_cb), self); + g_signal_connect (modem, "notify::" NM_MODEM_OPERATOR_CODE, G_CALLBACK (operator_code_changed_cb), self); + g_signal_connect (modem, "notify::" NM_MODEM_APN, G_CALLBACK (apn_changed_cb), self); } static guint32 @@ -718,6 +757,15 @@ get_property (GObject *object, guint prop_id, case PROP_CURRENT_CAPABILITIES: g_value_set_uint (value, priv->current_caps); break; + case PROP_DEVICE_ID: + g_value_set_string (value, priv->device_id); + break; + case PROP_OPERATOR_CODE: + g_value_set_string (value, priv->operator_code); + break; + case PROP_APN: + g_value_set_string (value, priv->apn); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -741,6 +789,10 @@ set_property (GObject *object, guint prop_id, case PROP_CURRENT_CAPABILITIES: priv->current_caps = g_value_get_uint (value); break; + case PROP_DEVICE_ID: + /* construct-only */ + priv->device_id = g_value_dup_string (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -775,6 +827,7 @@ nm_device_modem_new (NMModem *modem) NM_DEVICE_MODEM_MODEM, modem, NM_DEVICE_MODEM_CAPABILITIES, caps, NM_DEVICE_MODEM_CURRENT_CAPABILITIES, current_caps, + NM_DEVICE_MODEM_DEVICE_ID, nm_modem_get_device_id (modem), NULL); } @@ -788,6 +841,10 @@ dispose (GObject *object) g_clear_object (&priv->modem); } + g_clear_pointer (&priv->device_id, g_free); + g_clear_pointer (&priv->operator_code, g_free); + g_clear_pointer (&priv->apn, g_free); + G_OBJECT_CLASS (nm_device_modem_parent_class)->dispose (object); } @@ -800,6 +857,9 @@ static const NMDBusInterfaceInfoExtended interface_info_device_modem = { .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("ModemCapabilities", "u", NM_DEVICE_MODEM_CAPABILITIES), NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("CurrentCapabilities", "u", NM_DEVICE_MODEM_CURRENT_CAPABILITIES), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("DeviceId", "s", NM_DEVICE_MODEM_DEVICE_ID), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("OperatorCode", "s", NM_DEVICE_MODEM_OPERATOR_CODE), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("Apn", "s", NM_DEVICE_MODEM_APN), ), ), .legacy_property_changed = TRUE, @@ -857,5 +917,23 @@ nm_device_modem_class_init (NMDeviceModemClass *klass) G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_DEVICE_ID] = + g_param_spec_string (NM_DEVICE_MODEM_DEVICE_ID, "", "", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_OPERATOR_CODE] = + g_param_spec_string (NM_DEVICE_MODEM_OPERATOR_CODE, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_APN] = + g_param_spec_string (NM_DEVICE_MODEM_APN, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); } diff --git a/src/devices/wwan/nm-device-modem.h b/src/devices/wwan/nm-device-modem.h index 65bda8b6..0a557f57 100644 --- a/src/devices/wwan/nm-device-modem.h +++ b/src/devices/wwan/nm-device-modem.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -34,6 +33,9 @@ #define NM_DEVICE_MODEM_MODEM "modem" #define NM_DEVICE_MODEM_CAPABILITIES "modem-capabilities" #define NM_DEVICE_MODEM_CURRENT_CAPABILITIES "current-capabilities" +#define NM_DEVICE_MODEM_DEVICE_ID "device-id" +#define NM_DEVICE_MODEM_OPERATOR_CODE "operator-code" +#define NM_DEVICE_MODEM_APN "apn" typedef struct _NMDeviceModem NMDeviceModem; typedef struct _NMDeviceModemClass NMDeviceModemClass; diff --git a/src/devices/wwan/nm-modem-broadband.c b/src/devices/wwan/nm-modem-broadband.c index ffdc61a6..5716500a 100644 --- a/src/devices/wwan/nm-modem-broadband.c +++ b/src/devices/wwan/nm-modem-broadband.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -79,6 +78,7 @@ typedef struct { MMObject *modem_object; /* Per-interface objects */ MMModem *modem_iface; + MMModem3gpp *modem_3gpp_iface; MMModemSimple *simple_iface; MMSim *sim_iface; @@ -213,12 +213,13 @@ owns_port (NMModem *_self, const char *iface) NMModemBroadband *self = NM_MODEM_BROADBAND (_self); const MMModemPortInfo *ports = NULL; guint n_ports = 0, i; - gboolean owns = FALSE; mm_modem_peek_ports (self->_priv.modem_iface, &ports, &n_ports); - for (i = 0; i < n_ports && !owns; i++) - owns = (g_strcmp0 (iface, ports[i].name) == 0); - return owns; + for (i = 0; i < n_ports; i++) { + if (nm_streq0 (iface, ports[i].name)) + return TRUE; + } + return FALSE; } /*****************************************************************************/ @@ -547,6 +548,8 @@ connect_context_step (NMModemBroadband *self) else g_assert_not_reached (); + _nm_modem_set_apn (NM_MODEM (self), mm_simple_connect_properties_get_apn (ctx->connect_properties)); + _LOGD ("launching connection with ip type '%s' (try %d)", nm_modem_ip_type_to_string (current), ctx->ip_type_tries + 1); @@ -656,12 +659,13 @@ check_connection_compatible_with_modem (NMModem *_self, NMConnection *connection /*****************************************************************************/ static gboolean -complete_connection (NMModem *_self, +complete_connection (NMModem *modem, + const char *iface, NMConnection *connection, NMConnection *const*existing_connections, GError **error) { - NMModemBroadband *self = NM_MODEM_BROADBAND (_self); + NMModemBroadband *self = NM_MODEM_BROADBAND (modem); MMModemCapability modem_caps; NMSettingPpp *s_ppp; @@ -679,6 +683,20 @@ complete_connection (NMModem *_self, } if (MODEM_CAPS_3GPP (modem_caps)) { + NMSettingGsm *s_gsm; + + s_gsm = nm_connection_get_setting_gsm (connection); + if (!s_gsm) { + s_gsm = (NMSettingGsm *) nm_setting_gsm_new (); + nm_connection_add_setting (connection, NM_SETTING (s_gsm)); + } + + if (!nm_setting_gsm_get_device_id (s_gsm)) { + g_object_set (G_OBJECT (s_gsm), + NM_SETTING_GSM_DEVICE_ID, nm_modem_get_device_id (modem), + NULL); + } + nm_utils_complete_generic (NM_PLATFORM_GET, connection, NM_SETTING_GSM_SETTING_NAME, @@ -686,6 +704,7 @@ complete_connection (NMModem *_self, NULL, _("GSM connection"), NULL, + NULL, FALSE); /* No IPv6 yet by default */ return TRUE; @@ -710,6 +729,7 @@ complete_connection (NMModem *_self, NULL, _("CDMA connection"), NULL, + iface, FALSE); /* No IPv6 yet by default */ return TRUE; @@ -1140,6 +1160,7 @@ disconnect (NMModem *modem, DisconnectContext *ctx; connect_context_clear (self); + _nm_modem_set_apn (NM_MODEM (self), NULL); ctx = g_slice_new0 (DisconnectContext); ctx->self = g_object_ref (self); @@ -1320,6 +1341,15 @@ supported_ip_families_changed (MMModem *modem, GParamSpec *pspec, gpointer user_ NULL); } +static void +operator_code_changed (MMModem3gpp *modem_3gpp, GParamSpec *pspec, gpointer user_data) +{ + NMModemBroadband *self = NM_MODEM_BROADBAND (user_data); + + g_return_if_fail (modem_3gpp == self->_priv.modem_3gpp_iface); + _nm_modem_set_operator_code (NM_MODEM (self), mm_modem_3gpp_get_operator_code (modem_3gpp)); +} + /*****************************************************************************/ static void @@ -1353,6 +1383,7 @@ set_property (GObject *object, /* construct-only */ self->_priv.modem_object = g_value_dup_object (value); self->_priv.modem_iface = mm_object_get_modem (self->_priv.modem_object); + self->_priv.modem_3gpp_iface = mm_object_get_modem_3gpp (self->_priv.modem_object); g_assert (self->_priv.modem_iface != NULL); g_signal_connect (self->_priv.modem_iface, "state-changed", @@ -1368,6 +1399,13 @@ set_property (GObject *object, G_CALLBACK (supported_ip_families_changed), self); + if (self->_priv.modem_3gpp_iface) { + g_signal_connect (self->_priv.modem_3gpp_iface, + "notify::operator-code", + G_CALLBACK (operator_code_changed), + self); + } + /* Note: don't grab the Simple iface here; the Modem interface is the * only one assumed to be always valid and available */ break; @@ -1389,7 +1427,9 @@ nm_modem_broadband_new (GObject *object, GError **error) { MMObject *modem_object; MMModem *modem_iface; + MMModem3gpp *modem_3gpp_iface; const char *const*drivers; + const char *operator_code = NULL; gs_free char *driver = NULL; g_return_val_if_fail (MM_IS_OBJECT (object), NULL); @@ -1405,6 +1445,10 @@ nm_modem_broadband_new (GObject *object, GError **error) if (drivers) driver = g_strjoinv (", ", (char **) drivers); + modem_3gpp_iface = mm_object_peek_modem_3gpp (modem_object); + if (modem_3gpp_iface) + operator_code = mm_modem_3gpp_get_operator_code (modem_3gpp_iface); + return g_object_new (NM_TYPE_MODEM_BROADBAND, NM_MODEM_PATH, mm_object_get_path (modem_object), NM_MODEM_UID, mm_modem_get_primary_port (modem_iface), @@ -1414,6 +1458,7 @@ nm_modem_broadband_new (GObject *object, GError **error) NM_MODEM_DEVICE_ID, mm_modem_get_device_identifier (modem_iface), NM_MODEM_BROADBAND_MODEM, modem_object, NM_MODEM_DRIVER, driver, + NM_MODEM_OPERATOR_CODE, operator_code, NULL); } @@ -1430,7 +1475,17 @@ dispose (GObject *object) g_clear_object (&self->_priv.ipv4_config); g_clear_object (&self->_priv.ipv6_config); g_clear_object (&self->_priv.bearer); - g_clear_object (&self->_priv.modem_iface); + + if (self->_priv.modem_iface) { + g_signal_handlers_disconnect_by_data (self->_priv.modem_iface, self); + g_clear_object (&self->_priv.modem_iface); + } + + if (self->_priv.modem_3gpp_iface) { + g_signal_handlers_disconnect_by_data (self->_priv.modem_3gpp_iface, self); + g_clear_object (&self->_priv.modem_3gpp_iface); + } + g_clear_object (&self->_priv.simple_iface); g_clear_object (&self->_priv.sim_iface); g_clear_object (&self->_priv.modem_object); diff --git a/src/devices/wwan/nm-modem-broadband.h b/src/devices/wwan/nm-modem-broadband.h index 4948cb3b..9404f0b9 100644 --- a/src/devices/wwan/nm-modem-broadband.h +++ b/src/devices/wwan/nm-modem-broadband.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/wwan/nm-modem-manager.c b/src/devices/wwan/nm-modem-manager.c index 4be089c0..db2c0192 100644 --- a/src/devices/wwan/nm-modem-manager.c +++ b/src/devices/wwan/nm-modem-manager.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/wwan/nm-modem-manager.h b/src/devices/wwan/nm-modem-manager.h index 5f913083..1a26fd9f 100644 --- a/src/devices/wwan/nm-modem-manager.h +++ b/src/devices/wwan/nm-modem-manager.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/wwan/nm-modem-ofono.c b/src/devices/wwan/nm-modem-ofono.c index 78d9a9f0..31111b62 100644 --- a/src/devices/wwan/nm-modem-ofono.c +++ b/src/devices/wwan/nm-modem-ofono.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/wwan/nm-modem-ofono.h b/src/devices/wwan/nm-modem-ofono.h index d9cb68ac..1dcd79b0 100644 --- a/src/devices/wwan/nm-modem-ofono.h +++ b/src/devices/wwan/nm-modem-ofono.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c index 17794229..617096a7 100644 --- a/src/devices/wwan/nm-modem.c +++ b/src/devices/wwan/nm-modem.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -52,6 +51,8 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMModem, PROP_SIM_ID, PROP_IP_TYPES, PROP_SIM_OPERATOR_ID, + PROP_OPERATOR_CODE, + PROP_APN, ); enum { @@ -90,6 +91,8 @@ typedef struct _NMModemPrivate { char *sim_id; NMModemIPType ip_types; char *sim_operator_id; + char *operator_code; + char *apn; NMPPPManager *ppp_manager; @@ -344,7 +347,8 @@ nm_modem_get_connection_ip_type (NMModem *self, s_ip6 = nm_connection_get_setting_ip6_config (connection); if (s_ip6) { method = nm_setting_ip_config_get_method (s_ip6); - if (g_strcmp0 (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0) + if (NM_IN_STRSET (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_METHOD_DISABLED)) ip6 = FALSE; ip6_may_fail = nm_setting_ip_config_get_may_fail (s_ip6); } @@ -436,6 +440,18 @@ nm_modem_get_sim_operator_id (NMModem *self) return NM_MODEM_GET_PRIVATE (self)->sim_operator_id; } +const char * +nm_modem_get_operator_code (NMModem *self) +{ + return NM_MODEM_GET_PRIVATE (self)->operator_code; +} + +const char * +nm_modem_get_apn (NMModem *self) +{ + return NM_MODEM_GET_PRIVATE (self)->apn; +} + /*****************************************************************************/ /* IP method PPP */ @@ -824,8 +840,9 @@ nm_modem_stage3_ip6_config_start (NMModem *self, method = nm_utils_get_ip_config_method (connection, AF_INET6); - /* Only Ignore and Auto methods make sense for WWAN */ - if (nm_streq (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) + /* Only Ignore, Disabled and Auto methods make sense for WWAN */ + if (NM_IN_STRSET (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_METHOD_DISABLED)) return NM_ACT_STAGE_RETURN_IP_DONE; if (!nm_streq (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) { @@ -1086,6 +1103,7 @@ nm_modem_check_connection_compatible (NMModem *self, NMConnection *connection, G gboolean nm_modem_complete_connection (NMModem *self, + const char *iface, NMConnection *connection, NMConnection *const*existing_connections, GError **error) @@ -1100,7 +1118,7 @@ nm_modem_complete_connection (NMModem *self, return FALSE; } - return klass->complete_connection (self, connection, existing_connections, error); + return klass->complete_connection (self, iface, connection, existing_connections, error); } /*****************************************************************************/ @@ -1556,9 +1574,9 @@ nm_modem_set_route_parameters_from_device (NMModem *self, g_return_if_fail (NM_IS_DEVICE (device)); nm_modem_set_route_parameters (self, - nm_device_get_route_table (device, AF_INET, TRUE), + nm_device_get_route_table (device, AF_INET), nm_device_get_route_metric (device, AF_INET), - nm_device_get_route_table (device, AF_INET6, TRUE), + nm_device_get_route_table (device, AF_INET6), nm_device_get_route_metric (device, AF_INET6)); } @@ -1576,6 +1594,30 @@ nm_modem_get_capabilities (NMModem *self, /*****************************************************************************/ +void +_nm_modem_set_operator_code (NMModem *self, const char *operator_code) +{ + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); + + if (g_strcmp0 (priv->operator_code, operator_code) != 0) { + g_free (priv->operator_code); + priv->operator_code = g_strdup (operator_code); + _notify (self, PROP_OPERATOR_CODE); + } +} + +void +_nm_modem_set_apn (NMModem *self, const char *apn) +{ + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); + + if (g_strcmp0 (priv->apn, apn) != 0) { + g_free (priv->apn); + priv->apn = g_strdup (apn); + _notify (self, PROP_APN); + } +} + static void get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) @@ -1614,6 +1656,12 @@ get_property (GObject *object, guint prop_id, case PROP_SIM_OPERATOR_ID: g_value_set_string (value, priv->sim_operator_id); break; + case PROP_OPERATOR_CODE: + g_value_set_string (value, priv->operator_code); + break; + case PROP_APN: + g_value_set_string (value, priv->apn); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1666,6 +1714,10 @@ set_property (GObject *object, guint prop_id, if (s && s[0]) priv->sim_operator_id = g_strdup (s); break; + case PROP_OPERATOR_CODE: + /* construct-only */ + priv->operator_code = g_value_dup_string (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1727,6 +1779,8 @@ finalize (GObject *object) g_free (priv->device_id); g_free (priv->sim_id); g_free (priv->sim_operator_id); + g_free (priv->operator_code); + g_free (priv->apn); G_OBJECT_CLASS (nm_modem_parent_class)->finalize (object); } @@ -1810,6 +1864,18 @@ nm_modem_class_init (NMModemClass *klass) G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_OPERATOR_CODE] = + g_param_spec_string (NM_MODEM_OPERATOR_CODE, "", "", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_APN] = + g_param_spec_string (NM_MODEM_APN, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); signals[PPP_STATS] = diff --git a/src/devices/wwan/nm-modem.h b/src/devices/wwan/nm-modem.h index f7b6bfe9..f5b386e8 100644 --- a/src/devices/wwan/nm-modem.h +++ b/src/devices/wwan/nm-modem.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -43,6 +42,8 @@ #define NM_MODEM_SIM_ID "sim-id" #define NM_MODEM_IP_TYPES "ip-types" /* Supported IP types */ #define NM_MODEM_SIM_OPERATOR_ID "sim-operator-id" +#define NM_MODEM_OPERATOR_CODE "operator-code" +#define NM_MODEM_APN "apn" /* Signals */ #define NM_MODEM_PPP_STATS "ppp-stats" @@ -130,6 +131,7 @@ typedef struct { GError **error); gboolean (*complete_connection) (NMModem *modem, + const char *iface, NMConnection *connection, NMConnection *const*existing_connections, GError **error); @@ -174,6 +176,8 @@ const char *nm_modem_get_device_id (NMModem *modem); const char *nm_modem_get_sim_id (NMModem *modem); const char *nm_modem_get_sim_operator_id (NMModem *modem); gboolean nm_modem_get_iid (NMModem *modem, NMUtilsIPv6IfaceId *out_iid); +const char *nm_modem_get_operator_code (NMModem *modem); +const char *nm_modem_get_apn (NMModem *modem); gboolean nm_modem_set_data_port (NMModem *self, NMPlatform *platform, @@ -194,6 +198,7 @@ gboolean nm_modem_check_connection_compatible (NMModem *self, GError **error); gboolean nm_modem_complete_connection (NMModem *self, + const char *iface, NMConnection *connection, NMConnection *const*existing_connections, GError **error); @@ -284,4 +289,7 @@ const char *nm_modem_ip_type_to_string (NMModemIPType ip_type); guint32 nm_modem_get_configured_mtu (NMDevice *self, NMDeviceMtuSource *out_source); +void _nm_modem_set_operator_code (NMModem *self, const char *operator_code); +void _nm_modem_set_apn (NMModem *self, const char *apn); + #endif /* __NETWORKMANAGER_MODEM_H__ */ diff --git a/src/devices/wwan/nm-wwan-factory.c b/src/devices/wwan/nm-wwan-factory.c index c9ee27ff..a0e5c160 100644 --- a/src/devices/wwan/nm-wwan-factory.c +++ b/src/devices/wwan/nm-wwan-factory.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/dhcp/nm-dhcp-client-logging.h b/src/dhcp/nm-dhcp-client-logging.h index 1ed47170..d9b53f58 100644 --- a/src/dhcp/nm-dhcp-client-logging.h +++ b/src/dhcp/nm-dhcp-client-logging.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/dhcp/nm-dhcp-client.c b/src/dhcp/nm-dhcp-client.c index d494eff0..9f585c5d 100644 --- a/src/dhcp/nm-dhcp-client.c +++ b/src/dhcp/nm-dhcp-client.c @@ -1,4 +1,3 @@ -/* -*- 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) @@ -52,6 +51,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDhcpClient, PROP_ADDR_FAMILY, PROP_FLAGS, PROP_HWADDR, + PROP_BROADCAST_HWADDR, PROP_IFACE, PROP_IFINDEX, PROP_MULTI_IDX, @@ -66,6 +66,7 @@ typedef struct _NMDhcpClientPrivate { NMDedupMultiIndex *multi_idx; char * iface; GBytes * hwaddr; + GBytes * bcast_hwaddr; char * uuid; GBytes * client_id; char * hostname; @@ -144,6 +145,14 @@ nm_dhcp_client_get_hw_addr (NMDhcpClient *self) return NM_DHCP_CLIENT_GET_PRIVATE (self)->hwaddr; } +GBytes * +nm_dhcp_client_get_broadcast_hw_addr (NMDhcpClient *self) +{ + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL); + + return NM_DHCP_CLIENT_GET_PRIVATE (self)->bcast_hwaddr; +} + guint32 nm_dhcp_client_get_route_table (NMDhcpClient *self) { @@ -476,8 +485,9 @@ nm_dhcp_client_start_timeout (NMDhcpClient *self) { NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self); + g_return_if_fail (priv->timeout_id == 0); + /* Set up a timeout on the transaction to kill it after the timeout */ - g_assert (priv->timeout_id == 0); if (priv->timeout == NM_DHCP_TIMEOUT_INFINITY) return; @@ -530,6 +540,36 @@ nm_dhcp_client_start_ip4 (NMDhcpClient *self, error); } +gboolean +nm_dhcp_client_accept (NMDhcpClient *self, + GError **error) +{ + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE); + + if (NM_DHCP_CLIENT_GET_CLASS (self)->accept) { + return NM_DHCP_CLIENT_GET_CLASS (self)->accept (self, + error); + } + + return TRUE; +} + +gboolean +nm_dhcp_client_decline (NMDhcpClient *self, + const char *error_message, + GError **error) +{ + g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE); + + if (NM_DHCP_CLIENT_GET_CLASS (self)->decline) { + return NM_DHCP_CLIENT_GET_CLASS (self)->decline (self, + error_message, + error); + } + + return TRUE; +} + static GBytes * get_duid (NMDhcpClient *self) { @@ -690,6 +730,23 @@ bytearray_variant_to_string (NMDhcpClient *self, GVariant *value, const char *ke return converted; } +static int +label_is_unknown_xyz (const char *label) +{ + if (!NM_STR_HAS_PREFIX (label, "unknown_")) + return -EINVAL; + + label += NM_STRLEN ("unknown_"); + if ( label[0] != '2' + || !g_ascii_isdigit (label[1]) + || !g_ascii_isdigit (label[2]) + || label[3] != '\0') + return -EINVAL; + + return _nm_utils_ascii_str_to_int64 (label, 10, 224, 254, -EINVAL); +} + + #define OLD_TAG "old_" #define NEW_TAG "new_" @@ -713,14 +770,41 @@ maybe_add_option (NMDhcpClient *self, "dhcp_message_type")) return; - if (g_str_has_prefix (key, NEW_TAG)) + if (NM_STR_HAS_PREFIX (key, NEW_TAG)) key += NM_STRLEN (NEW_TAG); - if (!key[0]) + if (NM_STR_HAS_PREFIX (key, "private_") || !key[0]) return; str_value = bytearray_variant_to_string (self, value, key); - if (str_value) + if (str_value) { + int priv_opt_num; + g_hash_table_insert (hash, g_strdup (key), str_value); + + /* dhclient has no special labels for private dhcp options: it uses "unknown_xyz" + * labels for that. We need to identify those to alias them to our "private_xyz" + * format unsed in the internal dchp plugins. + */ + if ((priv_opt_num = label_is_unknown_xyz (key)) > 0) { + gs_free guint8 *check_val = NULL; + char *hex_str = NULL; + gsize len; + + /* dhclient passes values from dhcp private options in its own "string" format: + * if the raw values are printable as ascii strings, it will pass the string + * representation; if the values are not printable as an ascii string, it will + * pass a string displaying the hex values (hex string). Try to enforce passing + * always an hex string, converting string representation if needed. + */ + check_val = nm_utils_hexstr2bin_alloc (str_value, FALSE, TRUE, ":", 0, &len); + hex_str = nm_utils_bin2hexstr_full (check_val ?: (guint8 *) str_value, + check_val ? len : strlen (str_value), + ':', FALSE, NULL); + g_hash_table_insert (hash, + g_strdup_printf ("private_%d", priv_opt_num), + hex_str); + } + } } gboolean @@ -839,6 +923,9 @@ get_property (GObject *object, guint prop_id, case PROP_HWADDR: g_value_set_boxed (value, priv->hwaddr); break; + case PROP_BROADCAST_HWADDR: + g_value_set_boxed (value, priv->bcast_hwaddr); + break; case PROP_ADDR_FAMILY: g_value_set_int (value, priv->addr_family); break; @@ -900,6 +987,10 @@ set_property (GObject *object, guint prop_id, /* construct-only */ priv->hwaddr = g_value_dup_boxed (value); break; + case PROP_BROADCAST_HWADDR: + /* construct-only */ + priv->bcast_hwaddr = g_value_dup_boxed (value); + break; case PROP_ADDR_FAMILY: /* construct-only */ priv->addr_family = g_value_get_int (value); @@ -966,6 +1057,7 @@ 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->bcast_hwaddr, g_bytes_unref); G_OBJECT_CLASS (nm_dhcp_client_parent_class)->dispose (object); @@ -1010,6 +1102,12 @@ nm_dhcp_client_class_init (NMDhcpClientClass *client_class) G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_BROADCAST_HWADDR] = + g_param_spec_boxed (NM_DHCP_CLIENT_BROADCAST_HWADDR, "", "", + G_TYPE_BYTES, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + obj_properties[PROP_ADDR_FAMILY] = g_param_spec_int (NM_DHCP_CLIENT_ADDR_FAMILY, "", "", 0, G_MAXINT, AF_UNSPEC, diff --git a/src/dhcp/nm-dhcp-client.h b/src/dhcp/nm-dhcp-client.h index 1db7eac6..9eb76f33 100644 --- a/src/dhcp/nm-dhcp-client.h +++ b/src/dhcp/nm-dhcp-client.h @@ -1,4 +1,3 @@ -/* -*- 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) @@ -35,17 +34,18 @@ #define NM_IS_DHCP_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DHCP_CLIENT)) #define NM_DHCP_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_CLIENT, NMDhcpClientClass)) -#define NM_DHCP_CLIENT_ADDR_FAMILY "addr-family" -#define NM_DHCP_CLIENT_FLAGS "flags" -#define NM_DHCP_CLIENT_HWADDR "hwaddr" -#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" -#define NM_DHCP_CLIENT_UUID "uuid" +#define NM_DHCP_CLIENT_ADDR_FAMILY "addr-family" +#define NM_DHCP_CLIENT_FLAGS "flags" +#define NM_DHCP_CLIENT_HWADDR "hwaddr" +#define NM_DHCP_CLIENT_BROADCAST_HWADDR "broadcast-hwaddr" +#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" +#define NM_DHCP_CLIENT_UUID "uuid" #define NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED "state-changed" #define NM_DHCP_CLIENT_SIGNAL_PREFIX_DELEGATED "prefix-delegated" @@ -83,6 +83,13 @@ typedef struct { const char *last_ip4_address, GError **error); + gboolean (*accept) (NMDhcpClient *self, + GError **error); + + gboolean (*decline) (NMDhcpClient *self, + const char *error_message, + GError **error); + gboolean (*ip6_start) (NMDhcpClient *self, const char *anycast_addr, const struct in6_addr *ll_addr, @@ -123,6 +130,8 @@ GBytes *nm_dhcp_client_get_duid (NMDhcpClient *self); GBytes *nm_dhcp_client_get_hw_addr (NMDhcpClient *self); +GBytes *nm_dhcp_client_get_broadcast_hw_addr (NMDhcpClient *self); + guint32 nm_dhcp_client_get_route_table (NMDhcpClient *self); void nm_dhcp_client_set_route_table (NMDhcpClient *self, guint32 route_table); @@ -156,6 +165,13 @@ gboolean nm_dhcp_client_start_ip6 (NMDhcpClient *self, guint needed_prefixes, GError **error); +gboolean nm_dhcp_client_accept (NMDhcpClient *self, + GError **error); + +gboolean nm_dhcp_client_decline (NMDhcpClient *self, + const char *error_message, + GError **error); + void nm_dhcp_client_stop (NMDhcpClient *self, gboolean release); /* Backend helpers for subclasses */ @@ -200,5 +216,6 @@ extern const NMDhcpClientFactory _nm_dhcp_client_factory_dhcpcanon; extern const NMDhcpClientFactory _nm_dhcp_client_factory_dhclient; extern const NMDhcpClientFactory _nm_dhcp_client_factory_dhcpcd; extern const NMDhcpClientFactory _nm_dhcp_client_factory_internal; +extern const NMDhcpClientFactory _nm_dhcp_client_factory_nettools; #endif /* __NETWORKMANAGER_DHCP_CLIENT_H__ */ diff --git a/src/dhcp/nm-dhcp-dhclient-utils.c b/src/dhcp/nm-dhcp-dhclient-utils.c index 85ca3704..bf3df399 100644 --- a/src/dhcp/nm-dhcp-dhclient-utils.c +++ b/src/dhcp/nm-dhcp-dhclient-utils.c @@ -1,4 +1,3 @@ -/* -*- 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 diff --git a/src/dhcp/nm-dhcp-dhclient-utils.h b/src/dhcp/nm-dhcp-dhclient-utils.h index 57a711db..8ca893c3 100644 --- a/src/dhcp/nm-dhcp-dhclient-utils.h +++ b/src/dhcp/nm-dhcp-dhclient-utils.h @@ -1,4 +1,3 @@ -/* -*- 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) diff --git a/src/dhcp/nm-dhcp-dhclient.c b/src/dhcp/nm-dhcp-dhclient.c index b655a1eb..54b50479 100644 --- a/src/dhcp/nm-dhcp-dhclient.c +++ b/src/dhcp/nm-dhcp-dhclient.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-dhcp-dhclient.c - dhclient specific hooks for NetworkManager * * This program is free software; you can redistribute it and/or modify @@ -376,7 +375,7 @@ dhclient_start (NMDhcpClient *client, iface = nm_dhcp_client_get_iface (client); uuid = nm_dhcp_client_get_uuid (client); - pid_file = g_strdup_printf (RUNSTATEDIR "/dhclient%s-%s.pid", + pid_file = g_strdup_printf (NMRUNDIR "/dhclient%s-%s.pid", _addr_family_to_path_part (addr_family), iface); diff --git a/src/dhcp/nm-dhcp-dhcpcanon.c b/src/dhcp/nm-dhcp-dhcpcanon.c index 868cc9dd..2d2113cc 100644 --- a/src/dhcp/nm-dhcp-dhcpcanon.c +++ b/src/dhcp/nm-dhcp-dhcpcanon.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-dhcp-dhcpcanon.c - dhcpcanon specific hooks for NetworkManager * * This program is free software; you can redistribute it and/or modify @@ -186,18 +185,6 @@ ip4_start (NMDhcpClient *client, error); } -static gboolean -ip6_start (NMDhcpClient *client, - const char *dhcp_anycast_addr, - const struct in6_addr *ll_addr, - NMSettingIP6ConfigPrivacy privacy, - 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) { @@ -257,7 +244,6 @@ nm_dhcp_dhcpcanon_class_init (NMDhcpDhcpcanonClass *dhcpcanon_class) object_class->dispose = dispose; client_class->ip4_start = ip4_start; - client_class->ip6_start = ip6_start; client_class->stop = stop; } diff --git a/src/dhcp/nm-dhcp-dhcpcd.c b/src/dhcp/nm-dhcp-dhcpcd.c index 2a7482b1..c300bbe2 100644 --- a/src/dhcp/nm-dhcp-dhcpcd.c +++ b/src/dhcp/nm-dhcp-dhcpcd.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-dhcp-dhcpcd.c - dhcpcd specific hooks for NetworkManager * * Copyright (C) 2008 Roy Marples @@ -100,9 +99,9 @@ ip4_start (NMDhcpClient *client, iface = nm_dhcp_client_get_iface (client); /* dhcpcd does not allow custom pidfiles; the pidfile is always - * RUNDIR "dhcpcd-<ifname>.pid". + * RUNSTATEDIR "dhcpcd-<ifname>.pid". */ - priv->pid_file = g_strdup_printf (RUNDIR "/dhcpcd-%s.pid", iface); + priv->pid_file = g_strdup_printf (RUNSTATEDIR "/dhcpcd-%s.pid", iface); dhcpcd_path = nm_dhcp_dhcpcd_get_path (); if (!dhcpcd_path) { @@ -180,18 +179,6 @@ ip4_start (NMDhcpClient *client, return TRUE; } -static gboolean -ip6_start (NMDhcpClient *client, - const char *dhcp_anycast_addr, - const struct in6_addr *ll_addr, - NMSettingIP6ConfigPrivacy privacy, - guint needed_prefixes, - GError **error) -{ - nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, "dhcpcd plugin does not support IPv6"); - return FALSE; -} - static void stop (NMDhcpClient *client, gboolean release) { @@ -251,7 +238,6 @@ nm_dhcp_dhcpcd_class_init (NMDhcpDhcpcdClass *dhcpcd_class) object_class->dispose = dispose; client_class->ip4_start = ip4_start; - client_class->ip6_start = ip6_start; client_class->stop = stop; } diff --git a/src/dhcp/nm-dhcp-helper-api.h b/src/dhcp/nm-dhcp-helper-api.h index 58344215..c1a3c71c 100644 --- a/src/dhcp/nm-dhcp-helper-api.h +++ b/src/dhcp/nm-dhcp-helper-api.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This library is free software; you can redistribute it and/or diff --git a/src/dhcp/nm-dhcp-helper.c b/src/dhcp/nm-dhcp-helper.c index 8f753a61..9acc4045 100644 --- a/src/dhcp/nm-dhcp-helper.c +++ b/src/dhcp/nm-dhcp-helper.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/dhcp/nm-dhcp-listener.c b/src/dhcp/nm-dhcp-listener.c index 049c4e55..88aafeb0 100644 --- a/src/dhcp/nm-dhcp-listener.c +++ b/src/dhcp/nm-dhcp-listener.c @@ -1,4 +1,3 @@ -/* -*- 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) @@ -39,7 +38,7 @@ /*****************************************************************************/ -const NMDhcpClientFactory *const _nm_dhcp_manager_factories[4] = { +const NMDhcpClientFactory *const _nm_dhcp_manager_factories[5] = { /* the order here matters, as we will try the plugins in this order to find * the first available plugin. */ @@ -53,15 +52,16 @@ const NMDhcpClientFactory *const _nm_dhcp_manager_factories[4] = { &_nm_dhcp_client_factory_dhcpcd, #endif &_nm_dhcp_client_factory_internal, + &_nm_dhcp_client_factory_nettools, }; /*****************************************************************************/ typedef struct { - NMDBusManager * dbus_mgr; - gulong new_conn_id; - gulong dis_conn_id; - GHashTable * connections; + NMDBusManager *dbus_mgr; + gulong new_conn_id; + gulong dis_conn_id; + GHashTable *connections; } NMDhcpListenerPrivate; struct _NMDhcpListener { @@ -282,7 +282,7 @@ nm_dhcp_listener_init (NMDhcpListener *self) /* Maps GDBusConnection :: signal-id */ priv->connections = g_hash_table_new (nm_direct_hash, NULL); - priv->dbus_mgr = nm_dbus_manager_get (); + priv->dbus_mgr = g_object_ref (nm_dbus_manager_get ()); /* Register the socket our DHCP clients will return lease info on */ nm_dbus_manager_private_server_register (priv->dbus_mgr, PRIV_SOCK_PATH, PRIV_SOCK_TAG); @@ -303,10 +303,11 @@ dispose (GObject *object) nm_clear_g_signal_handler (priv->dbus_mgr, &priv->new_conn_id); nm_clear_g_signal_handler (priv->dbus_mgr, &priv->dis_conn_id); - priv->dbus_mgr = NULL; g_clear_pointer (&priv->connections, g_hash_table_destroy); + g_clear_object (&priv->dbus_mgr); + G_OBJECT_CLASS (nm_dhcp_listener_parent_class)->dispose (object); } diff --git a/src/dhcp/nm-dhcp-listener.h b/src/dhcp/nm-dhcp-listener.h index 3018b97a..d9724062 100644 --- a/src/dhcp/nm-dhcp-listener.h +++ b/src/dhcp/nm-dhcp-listener.h @@ -1,4 +1,3 @@ -/* -*- 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) diff --git a/src/dhcp/nm-dhcp-manager.c b/src/dhcp/nm-dhcp-manager.c index fe843a2c..304a7b99 100644 --- a/src/dhcp/nm-dhcp-manager.c +++ b/src/dhcp/nm-dhcp-manager.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-dhcp-manager.c - Handle the DHCP daemon for NetworkManager * * This program is free software; you can redistribute it and/or modify @@ -90,6 +89,54 @@ _client_factory_available (const NMDhcpClientFactory *client_factory) return NULL; } +static const NMDhcpClientFactory * +_client_factory_get_effective (const NMDhcpClientFactory *client_factory, + int addr_family) +{ + nm_auto_unref_gtypeclass NMDhcpClientClass *klass = NULL; + + nm_assert (client_factory); + nm_assert_addr_family (addr_family); + + /* currently, the chosen DHCP plugin for IPv4 and IPv6 is configured in NetworkManager.conf + * and cannot be reloaded. It would be nice to configure the plugin per address family + * or to be able to reload it. + * + * Note that certain options in NetworkManager.conf depend on the chosen DHCP plugin. + * See "dhcp-plugin:" in "Device List Format" (`man NetworkManager.conf`). + * Supporting reloading the plugin would also require to re-evalate the decisions from + * the "Device List Format". Likewise, having per-address family plugins would make the + * "main.dhcp" setting and "dhcp-plugin:" match non-sensical because these configurations + * currently are address family independet. + * + * So actually, we don't want that complexity. We want to phase out all plugins in favor + * of the internal plugin. + * However, certain existing plugins are well known to not support an address family. + * In those cases, we should just silently fallback to the internal plugin. + * + * This could be a problem with forward compatibility if we ever intended to add IPv6 support + * to those plugins. But we don't intend to do so. The internal plugin is the way forward and + * not extending other plugins. */ + + if (client_factory == &_nm_dhcp_client_factory_internal) { + /* already using internal plugin. Nothing to do. */ + return client_factory; + } + + klass = g_type_class_ref (client_factory->get_type ()); + + nm_assert (NM_IS_DHCP_CLIENT_CLASS (klass)); + + if (addr_family == AF_INET6) { + return klass->ip6_start + ? client_factory + : &_nm_dhcp_client_factory_internal; + } + return klass->ip4_start + ? client_factory + : &_nm_dhcp_client_factory_internal; +} + /*****************************************************************************/ static NMDhcpClient * @@ -157,6 +204,7 @@ client_start (NMDhcpManager *self, const char *iface, int ifindex, GBytes *hwaddr, + GBytes *bcast_hwaddr, const char *uuid, guint32 route_table, guint32 route_metric, @@ -177,6 +225,7 @@ client_start (NMDhcpManager *self, NMDhcpClient *client; gboolean success = FALSE; gsize hwaddr_len; + const NMDhcpClientFactory *client_factory; g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL); g_return_val_if_fail (iface, NULL); @@ -185,10 +234,11 @@ client_start (NMDhcpManager *self, g_return_val_if_fail (!dhcp_client_id || g_bytes_get_size (dhcp_client_id) >= 2, NULL); g_return_val_if_fail (!error || !*error, NULL); - if (!hwaddr) { + if (!hwaddr || !bcast_hwaddr) { nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, - "missing MAC address"); + "missing %s address", + hwaddr ? "broadcast" : "MAC"); return NULL; } @@ -201,24 +251,33 @@ client_start (NMDhcpManager *self, g_return_val_if_reached (NULL) ; } + nm_assert (g_bytes_get_size (hwaddr) == g_bytes_get_size (bcast_hwaddr)); + priv = NM_DHCP_MANAGER_GET_PRIVATE (self); - nm_assert (priv->client_factory); + client_factory = _client_factory_get_effective (priv->client_factory, addr_family); /* Kill any old client instance */ client = get_client_for_ifindex (self, addr_family, ifindex); if (client) { + /* FIXME: we cannot just call synchronously "stop()" and forget about the client. + * We need to wait for the client to be fully stopped because most/all clients + * cannot quit right away. + * + * FIXME(shutdown): also fix this during shutdown, to wait for all DHCP clients + * to be fully stopped. */ remove_client (self, client); nm_dhcp_client_stop (client, FALSE); g_object_unref (client); } - client = g_object_new (priv->client_factory->get_type (), + client = g_object_new (client_factory->get_type (), NM_DHCP_CLIENT_MULTI_IDX, multi_idx, NM_DHCP_CLIENT_ADDR_FAMILY, addr_family, NM_DHCP_CLIENT_INTERFACE, iface, NM_DHCP_CLIENT_IFINDEX, ifindex, NM_DHCP_CLIENT_HWADDR, hwaddr, + NM_DHCP_CLIENT_BROADCAST_HWADDR, bcast_hwaddr, NM_DHCP_CLIENT_UUID, uuid, NM_DHCP_CLIENT_HOSTNAME, hostname, NM_DHCP_CLIENT_ROUTE_TABLE, (guint) route_table, @@ -291,6 +350,7 @@ nm_dhcp_manager_start_ip4 (NMDhcpManager *self, const char *iface, int ifindex, GBytes *hwaddr, + GBytes *bcast_hwaddr, const char *uuid, guint32 route_table, guint32 route_metric, @@ -341,6 +401,7 @@ nm_dhcp_manager_start_ip4 (NMDhcpManager *self, iface, ifindex, hwaddr, + bcast_hwaddr, uuid, route_table, route_metric, @@ -365,6 +426,7 @@ nm_dhcp_manager_start_ip6 (NMDhcpManager *self, const char *iface, int ifindex, GBytes *hwaddr, + GBytes *bcast_hwaddr, const struct in6_addr *ll_addr, const char *uuid, guint32 route_table, @@ -396,6 +458,7 @@ nm_dhcp_manager_start_ip6 (NMDhcpManager *self, iface, ifindex, hwaddr, + bcast_hwaddr, uuid, route_table, route_metric, diff --git a/src/dhcp/nm-dhcp-manager.h b/src/dhcp/nm-dhcp-manager.h index f8f39e53..ff0d6f54 100644 --- a/src/dhcp/nm-dhcp-manager.h +++ b/src/dhcp/nm-dhcp-manager.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-dhcp-manager.c - Handle the DHCP daemon for NetworkManager * * This program is free software; you can redistribute it and/or modify @@ -50,6 +49,7 @@ NMDhcpClient * nm_dhcp_manager_start_ip4 (NMDhcpManager *manager, const char *iface, int ifindex, GBytes *hwaddr, + GBytes *bcast_hwaddr, const char *uuid, guint32 route_table, guint32 route_metric, @@ -67,6 +67,7 @@ NMDhcpClient * nm_dhcp_manager_start_ip6 (NMDhcpManager *manager, const char *iface, int ifindex, GBytes *hwaddr, + GBytes *bcast_hwaddr, const struct in6_addr *ll_addr, const char *uuid, guint32 route_table, @@ -85,7 +86,7 @@ NMDhcpClient * nm_dhcp_manager_start_ip6 (NMDhcpManager *manager, /* For testing only */ extern const char* nm_dhcp_helper_path; -extern const NMDhcpClientFactory *const _nm_dhcp_manager_factories[4]; +extern const NMDhcpClientFactory *const _nm_dhcp_manager_factories[5]; void nmtst_dhcp_manager_unget (gpointer singleton_instance); diff --git a/src/dhcp/nm-dhcp-nettools.c b/src/dhcp/nm-dhcp-nettools.c new file mode 100644 index 00000000..3aa92180 --- /dev/null +++ b/src/dhcp/nm-dhcp-nettools.c @@ -0,0 +1,1278 @@ +/* + * This program 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 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-2019 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <ctype.h> +#include <net/if_arp.h> + +#include "nm-sd-adapt-shared.h" +#include "hostname-util.h" + +#include "nm-glib-aux/nm-dedup-multi.h" +#include "nm-std-aux/unaligned.h" + +#include "nm-utils.h" +#include "nm-config.h" +#include "nm-dhcp-utils.h" +#include "nm-dhcp-options.h" +#include "nm-core-utils.h" +#include "NetworkManagerUtils.h" +#include "platform/nm-platform.h" +#include "nm-dhcp-client-logging.h" +#include "n-dhcp4/src/n-dhcp4.h" +#include "systemd/nm-sd-utils-shared.h" + +/*****************************************************************************/ + +#define NM_TYPE_DHCP_NETTOOLS (nm_dhcp_nettools_get_type ()) +#define NM_DHCP_NETTOOLS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_NETTOOLS, NMDhcpNettools)) +#define NM_DHCP_NETTOOLS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP_NETTOOLS, NMDhcpNettoolsClass)) +#define NM_IS_DHCP_NETTOOLS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DHCP_NETTOOLS)) +#define NM_IS_DHCP_NETTOOLS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DHCP_NETTOOLS)) +#define NM_DHCP_NETTOOLS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_NETTOOLS, NMDhcpNettoolsClass)) + +typedef struct _NMDhcpNettools NMDhcpNettools; +typedef struct _NMDhcpNettoolsClass NMDhcpNettoolsClass; + +static GType nm_dhcp_nettools_get_type (void); + +/*****************************************************************************/ + +typedef struct { + NDhcp4Client *client; + NDhcp4ClientProbe *probe; + NDhcp4ClientLease *lease; + GIOChannel *channel; + guint event_id; +} NMDhcpNettoolsPrivate; + +struct _NMDhcpNettools { + NMDhcpClient parent; + NMDhcpNettoolsPrivate _priv; +}; + +struct _NMDhcpNettoolsClass { + NMDhcpClientClass parent; +}; + +G_DEFINE_TYPE (NMDhcpNettools, nm_dhcp_nettools, NM_TYPE_DHCP_CLIENT) + +#define NM_DHCP_NETTOOLS_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDhcpNettools, NM_IS_DHCP_NETTOOLS) + +/*****************************************************************************/ + +#define DHCP_MAX_FQDN_LENGTH 255 + +enum { + DHCP_FQDN_FLAG_S = (1 << 0), + DHCP_FQDN_FLAG_O = (1 << 1), + DHCP_FQDN_FLAG_E = (1 << 2), + DHCP_FQDN_FLAG_N = (1 << 3), +}; + +enum { + NM_IN_ADDR_CLASS_A, + NM_IN_ADDR_CLASS_B, + NM_IN_ADDR_CLASS_C, + NM_IN_ADDR_CLASS_INVALID, +}; + +static int +in_addr_class (struct in_addr addr) +{ + switch (ntohl (addr.s_addr) >> 24) { + case 0 ... 127: + return NM_IN_ADDR_CLASS_A; + case 128 ... 191: + return NM_IN_ADDR_CLASS_B; + case 192 ... 223: + return NM_IN_ADDR_CLASS_C; + default: + return NM_IN_ADDR_CLASS_INVALID; + } +} + +static gboolean +lease_option_consume (void *out, + size_t n_out, + uint8_t **datap, + size_t *n_datap) +{ + if (*n_datap < n_out) + return FALSE; + + memcpy (out, *datap, n_out); + *datap += n_out; + *n_datap -= n_out; + return TRUE; +} + +static gboolean +lease_option_next_in_addr (struct in_addr *addrp, + uint8_t **datap, + size_t *n_datap) +{ + return lease_option_consume (addrp, sizeof (struct in_addr), datap, n_datap); +} + +static gboolean +lease_option_next_route (struct in_addr *destp, + uint8_t *plenp, + struct in_addr *gatewayp, + gboolean classless, + uint8_t **datap, + size_t *n_datap) +{ + struct in_addr dest = {}, gateway; + uint8_t *data = *datap; + size_t n_data = *n_datap; + uint8_t plen; + + if (classless) { + if (!lease_option_consume (&plen, sizeof (plen), &data, &n_data)) + return FALSE; + + if (plen > 32) + return FALSE; + + if (!lease_option_consume (&dest, plen / 8, &data, &n_data)) + return FALSE; + } else { + if (!lease_option_next_in_addr (&dest, &data, &n_data)) + return FALSE; + + switch (in_addr_class (dest)) { + case NM_IN_ADDR_CLASS_A: + plen = 8; + break; + case NM_IN_ADDR_CLASS_B: + plen = 16; + break; + case NM_IN_ADDR_CLASS_C: + plen = 24; + break; + case NM_IN_ADDR_CLASS_INVALID: + return FALSE; + } + } + + dest.s_addr = nm_utils_ip4_address_clear_host_address (dest.s_addr, plen); + + if (!lease_option_next_in_addr (&gateway, &data, &n_data)) + return FALSE; + + *destp = dest; + *plenp = plen; + *gatewayp = gateway; + *datap = data; + *n_datap = n_data; + return TRUE; +} + +static gboolean +lease_option_print_label (GString *str, size_t n_label, uint8_t **datap, size_t *n_datap) +{ + for (size_t i = 0; i < n_label; ++i) { + uint8_t c; + + if (!lease_option_consume(&c, sizeof (c), datap, n_datap)) + return FALSE; + + switch (c) { + case 'a' ... 'z': + case 'A' ... 'Z': + case '0' ... '9': + case '-': + case '_': + g_string_append_c(str, c); + break; + case '.': + case '\\': + g_string_append_printf(str, "\\%c", c); + break; + default: + g_string_append_printf(str, "\\%3d", c); + } + } + + return TRUE; +} + +static gboolean +lease_option_print_domain_name (GString *str, uint8_t *cache, size_t *n_cachep, uint8_t **datap, size_t *n_datap) +{ + uint8_t *domain; + size_t n_domain, n_cache = *n_cachep; + uint8_t **domainp = datap; + size_t *n_domainp = n_datap; + gboolean first = TRUE; + uint8_t c; + + /* + * We are given two adjacent memory regions. The @cache contains alreday parsed + * domain names, and the @datap contains the remaining data to parse. + * + * A domain name is formed from a sequence of labels. Each label start with + * a length byte, where the two most significant bits are unset. A zero-length + * label indicates the end of the domain name. + * + * Alternatively, a label can be followed by an offset (indicated by the two + * most significant bits being set in the next byte that is read). The offset + * is an offset into the cache, where the next label of the domain name can + * be found. + * + * Note, that each time a jump to an offset is performed, the size of the + * cache shrinks, so this is guaranteed to terminate. + */ + if (cache + n_cache != *datap) + return FALSE; + + for (;;) { + if (!lease_option_consume(&c, sizeof (c), domainp, n_domainp)) + return FALSE; + + switch (c & 0xC0) { + case 0x00: /* label length */ + { + size_t n_label = c; + + if (n_label == 0) { + /* + * We reached the final label of the domain name. Adjust + * the cache to include the consumed data, and return. + */ + *n_cachep = *datap - cache; + return TRUE; + } + + if (!first) { + g_string_append_c(str, '.'); + first = FALSE; + } + + if (!lease_option_print_label (str, n_label, domainp, n_domainp)) + return FALSE; + + break; + } + case 0xC0: /* back pointer */ + { + size_t offset = (c & 0x3F) << 16; + + /* + * The offset is given as two bytes (in big endian), where the + * two high bits are masked out. + */ + + if (!lease_option_consume (&c, sizeof (c), domainp, n_domainp)) + return FALSE; + + offset += c; + + if (offset >= n_cache) + return FALSE; + + domain = cache + offset; + n_domain = n_cache - offset; + n_cache = offset; + + domainp = &domain; + n_domainp = &n_domain; + + break; + } + default: + return FALSE; + } + } +} + +static gboolean +lease_get_in_addr (NDhcp4ClientLease *lease, + guint8 option, + struct in_addr *addrp) { + struct in_addr addr; + uint8_t *data; + size_t n_data; + int r; + + r = n_dhcp4_client_lease_query (lease, option, &data, &n_data); + if (r) + return FALSE; + + if (!lease_option_next_in_addr (&addr, &data, &n_data)) + return FALSE; + + if (n_data != 0) + return FALSE; + + *addrp = addr; + return TRUE; +} + +static gboolean +lease_get_u16 (NDhcp4ClientLease *lease, + uint8_t option, + uint16_t *u16p) +{ + uint8_t *data; + size_t n_data; + uint16_t be16; + int r; + + r = n_dhcp4_client_lease_query (lease, option, &data, &n_data); + if (r) + return FALSE; + + if (n_data != sizeof (be16)) + return FALSE; + + memcpy (&be16, data, sizeof (be16)); + + *u16p = ntohs(be16); + return TRUE; +} + +#define LOG_LEASE(domain, ...) \ + G_STMT_START { \ + _LOG2I ((domain), (iface), " "__VA_ARGS__); \ + } G_STMT_END + +static gboolean +lease_parse_address (NDhcp4ClientLease *lease, + const char *iface, + NMIP4Config *ip4_config, + GHashTable *options, + GError **error) +{ + char addr_str[NM_UTILS_INET_ADDRSTRLEN]; + const gint64 ts = nm_utils_get_monotonic_timestamp_ns (); + struct in_addr a_address; + struct in_addr a_netmask; + guint32 a_plen; + guint64 a_lifetime; + + n_dhcp4_client_lease_get_yiaddr (lease, &a_address); + if (a_address.s_addr == INADDR_ANY) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, "could not get address from lease"); + return FALSE; + } + + /* n_dhcp4_client_lease_get_lifetime() never fails */ + n_dhcp4_client_lease_get_lifetime (lease, &a_lifetime); + + if (!lease_get_in_addr (lease, NM_DHCP_OPTION_DHCP4_SUBNET_MASK, &a_netmask)) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, "could not get netmask from lease"); + return FALSE; + } + + nm_utils_inet4_ntop (a_address.s_addr, addr_str); + a_plen = nm_utils_ip4_netmask_to_prefix (a_netmask.s_addr); + + LOG_LEASE (LOGD_DHCP4, "address %s/%u", addr_str, a_plen); + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS, + addr_str); + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_SUBNET_MASK, + nm_utils_inet4_ntop (a_netmask.s_addr, addr_str)); + + LOG_LEASE (LOGD_DHCP4, "expires in %u seconds", + (guint) ((a_lifetime - ts)/1000000000)); + nm_dhcp_option_add_option_u64 (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_IP_ADDRESS_LEASE_TIME, + (guint64) (a_lifetime / 1000000000)); + + 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 / 1000000000, + .lifetime = (a_lifetime - ts) / 1000000000, + .preferred = (a_lifetime - ts) / 1000000000, + })); + + return TRUE; +} + +static void +lease_parse_domain_name_servers (NDhcp4ClientLease *lease, + const char *iface, + NMIP4Config *ip4_config, + GHashTable *options) +{ + nm_auto_free_gstring GString *str = NULL; + char addr_str[NM_UTILS_INET_ADDRSTRLEN]; + struct in_addr addr; + uint8_t *data; + size_t n_data; + int r; + + r = n_dhcp4_client_lease_query (lease, NM_DHCP_OPTION_DHCP4_DOMAIN_NAME_SERVER, &data, &n_data); + if (r) + return; + + nm_gstring_prepare (&str); + + while (lease_option_next_in_addr (&addr, &data, &n_data)) { + + nm_utils_inet4_ntop (addr.s_addr, addr_str); + g_string_append (nm_gstring_add_space_delimiter (str), addr_str); + + if ( addr.s_addr == 0 + || nm_ip4_addr_is_localhost (addr.s_addr)) { + /* Skip localhost addresses, like also networkd does. + * See https://github.com/systemd/systemd/issues/4524. */ + continue; + } + nm_ip4_config_add_nameserver (ip4_config, addr.s_addr); + } + + LOG_LEASE (LOGD_DHCP4, "nameserver '%s'", str->str); + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_DOMAIN_NAME_SERVER, + str->str); +} + +static void +lease_parse_routes (NDhcp4ClientLease *lease, + const char *iface, + NMIP4Config *ip4_config, + GHashTable *options, + guint32 route_table, + guint32 route_metric) +{ + nm_auto_free_gstring GString *str = NULL; + char dest_str[NM_UTILS_INET_ADDRSTRLEN]; + char gateway_str[NM_UTILS_INET_ADDRSTRLEN]; + const char *s; + struct in_addr dest, gateway; + uint8_t plen; + guint32 m; + gboolean has_router_from_classless = FALSE, has_classless = FALSE; + guint32 default_route_metric = route_metric; + uint8_t *data; + size_t n_data; + int r; + + r = n_dhcp4_client_lease_query (lease, NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE, &data, &n_data); + if (!r) { + nm_gstring_prepare (&str); + + has_classless = TRUE; + + while (lease_option_next_route (&dest, &plen, &gateway, TRUE, &data, &n_data)) { + + nm_utils_inet4_ntop (dest.s_addr, dest_str); + nm_utils_inet4_ntop (gateway.s_addr, gateway_str); + + LOG_LEASE (LOGD_DHCP4, + "classless static route %s/%d gw %s", + dest_str, + (int) plen, + gateway_str); + g_string_append_printf (nm_gstring_add_space_delimiter (str), + "%s/%d %s", + dest_str, + (int) plen, + gateway_str); + + if (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 = dest.s_addr, + .plen = plen, + .gateway = gateway.s_addr, + .rt_source = NM_IP_CONFIG_SOURCE_DHCP, + .metric = m, + .table_coerced = nm_platform_route_table_coerce (route_table), + }), + NULL); + } + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE, + str->str); + } + + r = n_dhcp4_client_lease_query (lease, NM_DHCP_OPTION_DHCP4_STATIC_ROUTE, &data, &n_data); + if (!r) { + nm_gstring_prepare (&str); + + while (lease_option_next_route (&dest, &plen, &gateway, FALSE, &data, &n_data)) { + + nm_utils_inet4_ntop (dest.s_addr, dest_str); + nm_utils_inet4_ntop (gateway.s_addr, gateway_str); + + LOG_LEASE (LOGD_DHCP4, + "static route %s/%d gw %s", + dest_str, + (int) plen, + gateway_str); + g_string_append_printf (nm_gstring_add_space_delimiter (str), + "%s/%d %s", + dest_str, + (int) plen, + gateway_str); + + if (has_classless) { + /* 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 (plen == 0) { + /* for option 33 (static route), RFC 2132 says: + * + * The default route (0.0.0.0) is an illegal destination for a static + * route. */ + continue; + } + + nm_ip4_config_add_route (ip4_config, + &((const NMPlatformIP4Route) { + .network = dest.s_addr, + .plen = plen, + .gateway = gateway.s_addr, + .rt_source = NM_IP_CONFIG_SOURCE_DHCP, + .metric = route_metric, + .table_coerced = nm_platform_route_table_coerce (route_table), + }), + NULL); + } + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_STATIC_ROUTE, + str->str); + } + + r = n_dhcp4_client_lease_query (lease, NM_DHCP_OPTION_DHCP4_ROUTER, &data, &n_data); + if (!r) { + nm_gstring_prepare (&str); + + while (lease_option_next_in_addr (&gateway, &data, &n_data)) { + s = nm_utils_inet4_ntop (gateway.s_addr, gateway_str); + g_string_append (nm_gstring_add_space_delimiter (str), s); + + if (gateway.s_addr == 0) { + /* silently skip 0.0.0.0 */ + continue; + } + + if (has_router_from_classless) { + /* 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). + */ + continue; + } + + /* 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++; + + nm_ip4_config_add_route (ip4_config, + &((const NMPlatformIP4Route) { + .rt_source = NM_IP_CONFIG_SOURCE_DHCP, + .gateway = gateway.s_addr, + .table_coerced = nm_platform_route_table_coerce (route_table), + .metric = m, + }), + NULL); + } + LOG_LEASE (LOGD_DHCP4, "router %s", str->str); + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_ROUTER, + str->str); + } +} + +static void +lease_parse_mtu (NDhcp4ClientLease *lease, + const char *iface, + NMIP4Config *ip4_config, + GHashTable *options) +{ + uint16_t mtu; + + if (!lease_get_u16 (lease, NM_DHCP_OPTION_DHCP4_INTERFACE_MTU, &mtu)) + return; + + if (mtu < 68) + return; + + LOG_LEASE (LOGD_DHCP4, "mtu %u", mtu); + nm_dhcp_option_add_option_u64 (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_INTERFACE_MTU, + mtu); + nm_ip4_config_set_mtu (ip4_config, mtu, NM_IP_CONFIG_SOURCE_DHCP); +} + +static void +lease_parse_metered (NDhcp4ClientLease *lease, + const char *iface, + NMIP4Config *ip4_config, + GHashTable *options) +{ + gboolean metered = FALSE; + uint8_t *data; + size_t n_data; + int r; + + r = n_dhcp4_client_lease_query (lease, NM_DHCP_OPTION_DHCP4_VENDOR_SPECIFIC, &data, &n_data); + if (r) { + metered = FALSE; + } else { + metered = !!memmem (data, n_data, "ANDROID_METERED", NM_STRLEN ("ANDROID_METERED")); + } + + LOG_LEASE (LOGD_DHCP4, "%s", metered ? "metered" : "unmetered"); + nm_ip4_config_set_metered (ip4_config, metered); +} + +static void +lease_parse_ntps (NDhcp4ClientLease *lease, + const char *iface, + GHashTable *options) +{ + nm_auto_free_gstring GString *str = NULL; + char addr_str[NM_UTILS_INET_ADDRSTRLEN]; + struct in_addr addr; + uint8_t *data; + size_t n_data; + int r; + + r = n_dhcp4_client_lease_query (lease, NM_DHCP_OPTION_DHCP4_NTP_SERVER, &data, &n_data); + if (r) + return; + + nm_gstring_prepare (&str); + + while (lease_option_next_in_addr (&addr, &data, &n_data)) { + nm_utils_inet4_ntop (addr.s_addr, addr_str); + g_string_append (nm_gstring_add_space_delimiter (str), addr_str); + } + + LOG_LEASE (LOGD_DHCP4, "ntp server '%s'", str->str); + nm_dhcp_option_add_option (options, _nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_NTP_SERVER, str->str); +} + +static void +lease_parse_hostname (NDhcp4ClientLease *lease, + const char *iface, + GHashTable *options) +{ + nm_auto_free_gstring GString *str = NULL; + uint8_t *data; + size_t n_data; + int r; + + r = n_dhcp4_client_lease_query (lease, NM_DHCP_OPTION_DHCP4_HOST_NAME, &data, &n_data); + if (r) + return; + + str = g_string_new_len ((char *)data, n_data); + + if (is_localhost(str->str)) + return; + + LOG_LEASE (LOGD_DHCP4, "hostname '%s'", str->str); + nm_dhcp_option_add_option (options, _nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_HOST_NAME, str->str); +} + +static void +lease_parse_domainname (NDhcp4ClientLease *lease, + const char *iface, + NMIP4Config *ip4_config, + GHashTable *options) +{ + nm_auto_free_gstring GString *str = NULL; + gs_strfreev char **domains = NULL; + uint8_t *data; + size_t n_data; + int r; + + r = n_dhcp4_client_lease_query (lease, NM_DHCP_OPTION_DHCP4_DOMAIN_NAME, &data, &n_data); + if (r) + return; + + str = g_string_new_len ((char *)data, n_data); + + /* Multiple domains sometimes stuffed into option 15 "Domain Name". */ + domains = g_strsplit (str->str, " ", 0); + nm_gstring_prepare (&str); + + for (char **d = domains; *d; d++) { + if (is_localhost(*d)) + return; + + g_string_append (nm_gstring_add_space_delimiter (str), *d); + nm_ip4_config_add_domain (ip4_config, *d); + } + LOG_LEASE (LOGD_DHCP4, "domain name '%s'", str->str); + nm_dhcp_option_add_option (options, _nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_DOMAIN_NAME, str->str); +} + +static void +lease_parse_search_domains (NDhcp4ClientLease *lease, + const char *iface, + NMIP4Config *ip4_config, + GHashTable *options) +{ + nm_auto_free_gstring GString *str = NULL; + uint8_t *data, *cache; + size_t n_data, n_cache = 0; + int r; + + r = n_dhcp4_client_lease_query (lease, NM_DHCP_OPTION_DHCP4_DOMAIN_SEARCH_LIST, &data, &n_data); + if (r) + return; + + cache = data; + + nm_gstring_prepare (&str); + + for (;;) { + nm_auto_free_gstring GString *domain = NULL; + + nm_gstring_prepare (&domain); + + if (!lease_option_print_domain_name (domain, cache, &n_cache, &data, &n_data)) + break; + + g_string_append (nm_gstring_add_space_delimiter (str), domain->str); + nm_ip4_config_add_search (ip4_config, domain->str); + } + LOG_LEASE (LOGD_DHCP4, "domain search '%s'", str->str); + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_DOMAIN_SEARCH_LIST, + str->str); +} + +static void +lease_parse_root_path (NDhcp4ClientLease *lease, + const char *iface, + GHashTable *options) +{ + nm_auto_free_gstring GString *str = NULL; + uint8_t *data; + size_t n_data; + int r; + + r = n_dhcp4_client_lease_query (lease, NM_DHCP_OPTION_DHCP4_ROOT_PATH, &data, &n_data); + if (r) + return; + + str = g_string_new_len ((char *)data, n_data); + LOG_LEASE (LOGD_DHCP4, "root path '%s'", str->str); + nm_dhcp_option_add_option (options, _nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_ROOT_PATH, str->str); +} + +static void +lease_parse_wpad (NDhcp4ClientLease *lease, + const char *iface, + GHashTable *options) +{ + nm_auto_free_gstring GString *str = NULL; + uint8_t *data; + size_t n_data; + int r; + + r = n_dhcp4_client_lease_query (lease, NM_DHCP_OPTION_DHCP4_PRIVATE_PROXY_AUTODISCOVERY, &data, &n_data); + if (r) + return; + + str = g_string_new_len ((char *)data, n_data); + LOG_LEASE (LOGD_DHCP4, "wpad '%s'", str->str); + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_PRIVATE_PROXY_AUTODISCOVERY, + str->str); +} + +static NMIP4Config * +lease_to_ip4_config (NMDedupMultiIndex *multi_idx, + const char *iface, + int ifindex, + NDhcp4ClientLease *lease, + guint32 route_table, + guint32 route_metric, + GHashTable **out_options, + GError **error) +{ + gs_unref_object NMIP4Config *ip4_config = NULL; + gs_unref_hashtable GHashTable *options = NULL; + + g_return_val_if_fail (lease != NULL, NULL); + + ip4_config = nm_ip4_config_new (multi_idx, ifindex); + options = out_options ? nm_dhcp_option_create_options_dict () : NULL; + + if (!lease_parse_address (lease, iface, ip4_config, options, error)) + return NULL; + + lease_parse_routes (lease, iface, ip4_config, options, route_table, route_metric); + lease_parse_domain_name_servers (lease, iface, ip4_config, options); + lease_parse_domainname (lease, iface, ip4_config, options); + lease_parse_search_domains (lease, iface, ip4_config, options); + lease_parse_mtu (lease, iface, ip4_config, options); + lease_parse_metered (lease, iface, ip4_config, options); + + lease_parse_hostname (lease, iface, options); + lease_parse_ntps (lease, iface, options); + lease_parse_root_path (lease, iface, options); + lease_parse_wpad (lease, iface, options); + + NM_SET_OUT (out_options, g_steal_pointer (&options)); + return g_steal_pointer (&ip4_config); +} + +/*****************************************************************************/ + +static void +bound4_handle (NMDhcpNettools *self, NDhcp4ClientLease *lease) +{ + const char *iface = nm_dhcp_client_get_iface (NM_DHCP_CLIENT (self)); + gs_unref_object NMIP4Config *ip4_config = NULL; + gs_unref_hashtable GHashTable *options = NULL; + GError *error = NULL; + + _LOGT ("lease available"); + + 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, + nm_dhcp_client_get_route_table (NM_DHCP_CLIENT (self)), + nm_dhcp_client_get_route_metric (NM_DHCP_CLIENT (self)), + &options, + &error); + if (!ip4_config) { + _LOGW ("%s", error->message); + g_clear_error (&error); + nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), NM_DHCP_STATE_FAIL, NULL, NULL); + return; + } + + nm_dhcp_option_add_requests_to_options (options, _nm_dhcp_option_dhcp4_options); + + nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), + NM_DHCP_STATE_BOUND, + NM_IP_CONFIG_CAST (ip4_config), + options); +} + +static gboolean +dhcp4_event_handle (NMDhcpNettools *self, + NDhcp4ClientEvent *event) +{ + NMDhcpNettoolsPrivate *priv = NM_DHCP_NETTOOLS_GET_PRIVATE (self); + int r; + + _LOGT ("client event %d", event->event); + + switch (event->event) { + case N_DHCP4_CLIENT_EVENT_OFFER: + /* always accept the first lease */ + r = n_dhcp4_client_lease_select (event->offer.lease); + if (r) { + _LOGW ("selecting lease failed: %d", r); + } + break; + case N_DHCP4_CLIENT_EVENT_EXPIRED: + nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), NM_DHCP_STATE_EXPIRE, NULL, NULL); + break; + case N_DHCP4_CLIENT_EVENT_RETRACTED: + case N_DHCP4_CLIENT_EVENT_CANCELLED: + nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), NM_DHCP_STATE_FAIL, NULL, NULL); + break; + case N_DHCP4_CLIENT_EVENT_GRANTED: + priv->lease = n_dhcp4_client_lease_ref (event->granted.lease); + bound4_handle (self, event->granted.lease); + break; + case N_DHCP4_CLIENT_EVENT_EXTENDED: + bound4_handle (self, event->extended.lease); + break; + case N_DHCP4_CLIENT_EVENT_DOWN: + /* ignore down events, they are purely informational */ + break; + default: + _LOGW ("unhandled DHCP event %d", event->event); + break; + } + + return TRUE; +} + +static gboolean +dhcp4_event_cb (GIOChannel *source, + GIOCondition condition, + gpointer data) +{ + NMDhcpNettools *self = data; + NMDhcpNettoolsPrivate *priv = NM_DHCP_NETTOOLS_GET_PRIVATE (self); + NDhcp4ClientEvent *event; + int r; + + r = n_dhcp4_client_dispatch (priv->client); + if (r < 0) + return G_SOURCE_CONTINUE; + + while (!n_dhcp4_client_pop_event (priv->client, &event) && event) { + dhcp4_event_handle (self, event); + } + + return G_SOURCE_CONTINUE; +} + +static gboolean +nettools_create (NMDhcpNettools *self, + const char *dhcp_anycast_addr, + GError **error) +{ + NMDhcpNettoolsPrivate *priv = NM_DHCP_NETTOOLS_GET_PRIVATE (self); + nm_auto (n_dhcp4_client_config_freep) NDhcp4ClientConfig *config = NULL; + nm_auto (n_dhcp4_client_unrefp) NDhcp4Client *client = NULL; + GBytes *hwaddr; + GBytes *bcast_hwaddr; + const uint8_t *hwaddr_arr; + const uint8_t *bcast_hwaddr_arr; + gsize hwaddr_len; + gsize bcast_hwaddr_len; + GBytes *client_id; + gs_unref_bytes GBytes *client_id_new = NULL; + const uint8_t *client_id_arr; + size_t client_id_len; + int r, fd, arp_type, transport; + + g_return_val_if_fail (!priv->client, FALSE); + + hwaddr = nm_dhcp_client_get_hw_addr (NM_DHCP_CLIENT (self)); + 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; + } + + bcast_hwaddr = nm_dhcp_client_get_broadcast_hw_addr (NM_DHCP_CLIENT (self)); + bcast_hwaddr_arr = g_bytes_get_data (bcast_hwaddr, &bcast_hwaddr_len); + + switch (arp_type) { + case ARPHRD_ETHER: + transport = N_DHCP4_TRANSPORT_ETHERNET; + break; + case ARPHRD_INFINIBAND: + transport = N_DHCP4_TRANSPORT_INFINIBAND; + break; + default: + nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, "unsupported ARP type"); + 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 . */ + client_id = nm_dhcp_client_get_client_id (NM_DHCP_CLIENT (self)); + 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; + } + + r = n_dhcp4_client_config_new (&config); + if (r) { + nm_utils_error_set_errno (error, r, "failed to create client-config: %s"); + return FALSE; + } + + n_dhcp4_client_config_set_ifindex (config, nm_dhcp_client_get_ifindex (NM_DHCP_CLIENT (self))); + n_dhcp4_client_config_set_transport (config, transport); + n_dhcp4_client_config_set_mac (config, hwaddr_arr, hwaddr_len); + n_dhcp4_client_config_set_broadcast_mac (config, bcast_hwaddr_arr, bcast_hwaddr_len); + r = n_dhcp4_client_config_set_client_id (config, client_id_arr, client_id_len); + if (r) { + nm_utils_error_set_errno (error, r, "failed to set client-id: %s"); + return FALSE; + } + + r = n_dhcp4_client_new (&client, config); + if (r) { + nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, "failed to create client: error %d", r); + return FALSE; + } + + priv->client = client; + client = NULL; + + n_dhcp4_client_get_fd (priv->client, &fd); + priv->channel = g_io_channel_unix_new (fd); + priv->event_id = g_io_add_watch (priv->channel, G_IO_IN, dhcp4_event_cb, self); + + return TRUE; +} + +static gboolean +_accept (NMDhcpClient *client, + GError **error) +{ + NMDhcpNettools *self = NM_DHCP_NETTOOLS (client); + NMDhcpNettoolsPrivate *priv = NM_DHCP_NETTOOLS_GET_PRIVATE (self); + int r; + + g_return_val_if_fail (priv->lease, FALSE); + + _LOGT ("accept"); + + r = n_dhcp4_client_lease_accept (priv->lease); + if (r) { + nm_utils_error_set_errno (error, r, "failed to accept lease: %s"); + return FALSE; + } + + priv->lease = n_dhcp4_client_lease_unref (priv->lease); + + return TRUE; +} + +static gboolean +decline (NMDhcpClient *client, + const char *error_message, + GError **error) +{ + NMDhcpNettools *self = NM_DHCP_NETTOOLS (client); + NMDhcpNettoolsPrivate *priv = NM_DHCP_NETTOOLS_GET_PRIVATE (self); + int r; + + g_return_val_if_fail (priv->lease, FALSE); + + _LOGT ("dhcp4-client: decline"); + + r = n_dhcp4_client_lease_decline (priv->lease, error_message); + if (r) { + nm_utils_error_set_errno (error, r, "failed to decline lease: %s"); + return FALSE; + } + + priv->lease = n_dhcp4_client_lease_unref (priv->lease); + + return TRUE; +} + +static gboolean +ip4_start (NMDhcpClient *client, + const char *dhcp_anycast_addr, + const char *last_ip4_address, + GError **error) +{ + nm_auto (n_dhcp4_client_probe_config_freep) NDhcp4ClientProbeConfig *config = NULL; + NMDhcpNettools *self = NM_DHCP_NETTOOLS (client); + NMDhcpNettoolsPrivate *priv = NM_DHCP_NETTOOLS_GET_PRIVATE (self); + struct in_addr last_addr = { 0 }; + const char *hostname; + int r, i; + + g_return_val_if_fail (!priv->probe, FALSE); + + if (!nettools_create (self, dhcp_anycast_addr, error)) + return FALSE; + + r = n_dhcp4_client_probe_config_new (&config); + if (r) { + nm_utils_error_set_errno (error, r, "failed to create dhcp-client-probe-config: %s"); + return FALSE; + } + + /* + * FIXME: + * Select, or configure, a reasonable start delay, to protect poor servers beeing flooded. + */ + n_dhcp4_client_probe_config_set_start_delay (config, 1); + + if (last_ip4_address) { + inet_pton (AF_INET, last_ip4_address, &last_addr); + n_dhcp4_client_probe_config_set_requested_ip (config, last_addr); + } + + /* Add requested options */ + for (i = 0; _nm_dhcp_option_dhcp4_options[i].name; i++) { + if (_nm_dhcp_option_dhcp4_options[i].include) { + nm_assert (_nm_dhcp_option_dhcp4_options[i].option_num <= 255); + n_dhcp4_client_probe_config_request_option (config, + _nm_dhcp_option_dhcp4_options[i].option_num); + } + } + + hostname = nm_dhcp_client_get_hostname (client); + if (hostname) { + if (nm_dhcp_client_get_use_fqdn (client)) { + uint8_t buffer[3 + DHCP_MAX_FQDN_LENGTH]; + + buffer[0] = DHCP_FQDN_FLAG_S | /* Request server to perform A RR DNS updates */ + DHCP_FQDN_FLAG_E; /* Canonical wire format */ + buffer[1] = 0; /* RCODE1 (deprecated) */ + buffer[2] = 0; /* RCODE2 (deprecated) */ + + r = nm_sd_dns_name_to_wire_format (hostname, + buffer + 3, + sizeof (buffer) - 3, + FALSE); + if (r < 0) { + nm_utils_error_set_errno (error, r, "failed to convert DHCP FQDN: %s"); + return FALSE; + } + + r = n_dhcp4_client_probe_config_append_option (config, + NM_DHCP_OPTION_DHCP4_CLIENT_FQDN, + buffer, + 3 + r); + if (r) { + nm_utils_error_set_errno (error, r, "failed to set DHCP FQDN: %s"); + return FALSE; + } + } else { + r = n_dhcp4_client_probe_config_append_option (config, + NM_DHCP_OPTION_DHCP4_HOST_NAME, + hostname, + strlen (hostname)); + if (r) { + nm_utils_error_set_errno (error, r, "failed to set DHCP hostname: %s"); + return FALSE; + } + } + } + + r = n_dhcp4_client_probe (priv->client, &priv->probe, config); + if (r) { + nm_utils_error_set_errno (error, r, "failed to start DHCP client: %s"); + return FALSE; + } + + _LOGT ("dhcp-client4: start %p", (gpointer) priv->client); + + nm_dhcp_client_start_timeout (client); + return TRUE; +} + +static gboolean +ip6_start (NMDhcpClient *client, + const char *dhcp_anycast_addr, + const struct in6_addr *ll_addr, + NMSettingIP6ConfigPrivacy privacy, + guint needed_prefixes, + GError **error) +{ + nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, "nettools plugin does not support IPv6"); + return FALSE; +} + +static void +stop (NMDhcpClient *client, + gboolean release) +{ + NMDhcpNettools *self = NM_DHCP_NETTOOLS (client); + NMDhcpNettoolsPrivate *priv = NM_DHCP_NETTOOLS_GET_PRIVATE (self); + + NM_DHCP_CLIENT_CLASS (nm_dhcp_nettools_parent_class)->stop (client, release); + + _LOGT ("dhcp-client4: stop %p", + (gpointer) priv->client); + + priv->probe = n_dhcp4_client_probe_free (priv->probe); +} + +/*****************************************************************************/ + +static void +nm_dhcp_nettools_init (NMDhcpNettools *self) +{ +} + +static void +dispose (GObject *object) +{ + NMDhcpNettoolsPrivate *priv = NM_DHCP_NETTOOLS_GET_PRIVATE ((NMDhcpNettools *) object); + + nm_clear_pointer (&priv->channel, g_io_channel_unref); + nm_clear_g_source (&priv->event_id); + nm_clear_pointer (&priv->lease, n_dhcp4_client_lease_unref); + nm_clear_pointer (&priv->probe, n_dhcp4_client_probe_free); + nm_clear_pointer (&priv->client, n_dhcp4_client_unref); + + G_OBJECT_CLASS (nm_dhcp_nettools_parent_class)->dispose (object); +} + +static void +nm_dhcp_nettools_class_init (NMDhcpNettoolsClass *class) +{ + NMDhcpClientClass *client_class = NM_DHCP_CLIENT_CLASS (class); + GObjectClass *object_class = G_OBJECT_CLASS (class); + + object_class->dispose = dispose; + + client_class->ip4_start = ip4_start; + client_class->ip6_start = ip6_start; + client_class->accept = _accept; + client_class->decline = decline; + client_class->stop = stop; +} + +const NMDhcpClientFactory _nm_dhcp_client_factory_nettools = { + .name = "nettools", + .get_type = nm_dhcp_nettools_get_type, + .get_path = NULL, +}; diff --git a/src/dhcp/nm-dhcp-options.c b/src/dhcp/nm-dhcp-options.c new file mode 100644 index 00000000..21963070 --- /dev/null +++ b/src/dhcp/nm-dhcp-options.c @@ -0,0 +1,278 @@ +/* + * 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. + * + * (C) Copyright 2019 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-dhcp-options.h" + + +#define REQPREFIX "requested_" + +#define REQ(_num, _name, _include) \ + { \ + .name = REQPREFIX""_name, \ + .option_num = _num, \ + .include = _include, \ + } + +const NMDhcpOption _nm_dhcp_option_dhcp4_options[] = { + REQ (NM_DHCP_OPTION_DHCP4_SUBNET_MASK, "subnet_mask", TRUE ), + REQ (NM_DHCP_OPTION_DHCP4_TIME_OFFSET, "time_offset", TRUE ), + REQ (NM_DHCP_OPTION_DHCP4_DOMAIN_NAME_SERVER, "domain_name_servers", TRUE ), + REQ (NM_DHCP_OPTION_DHCP4_HOST_NAME, "host_name", TRUE ), + REQ (NM_DHCP_OPTION_DHCP4_DOMAIN_NAME, "domain_name", TRUE ), + REQ (NM_DHCP_OPTION_DHCP4_INTERFACE_MTU, "interface_mtu", TRUE ), + REQ (NM_DHCP_OPTION_DHCP4_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 (NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE, "rfc3442_classless_static_routes", TRUE ), + REQ (NM_DHCP_OPTION_DHCP4_ROUTER, "routers", TRUE ), + REQ (NM_DHCP_OPTION_DHCP4_STATIC_ROUTE, "static_routes", TRUE ), + REQ (NM_DHCP_OPTION_DHCP4_NIS_DOMAIN, "nis_domain", TRUE ), + REQ (NM_DHCP_OPTION_DHCP4_NIS_SERVERS, "nis_servers", TRUE ), + REQ (NM_DHCP_OPTION_DHCP4_NTP_SERVER, "ntp_servers", TRUE ), + REQ (NM_DHCP_OPTION_DHCP4_SERVER_ID, "dhcp_server_identifier", TRUE ), + REQ (NM_DHCP_OPTION_DHCP4_DOMAIN_SEARCH_LIST, "domain_search", TRUE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_CLASSLESS_STATIC_ROUTE, "ms_classless_static_routes", TRUE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_PROXY_AUTODISCOVERY, "wpad", TRUE ), + REQ (NM_DHCP_OPTION_DHCP4_ROOT_PATH, "root_path", TRUE ), + + REQ (NM_DHCP_OPTION_DHCP4_TIME_SERVERS, "time_servers", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_IEN116_NAME_SERVERS, "ien116_name_servers", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_LOG_SERVERS, "log_servers", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_COOKIE_SERVERS, "cookie_servers", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_LPR_SERVERS, "lpr_servers", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_IMPRESS_SERVERS, "impress_servers", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_RESOURCE_LOCATION_SERVERS, "resource_location_servers", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_BOOT_FILE_SIZE, "boot_size", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_MERIT_DUMP, "merit_dump", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_SWAP_SERVER, "swap_server", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_EXTENSIONS_PATH, "extensions_path", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_ENABLE_IP_FORWARDING, "ip_forwarding", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_ENABLE_SRC_ROUTING, "non_local_source_routing", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_POLICY_FILTER, "policy_filter", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_INTERFACE_MDR, "max_dgram_reassembly", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_INTERFACE_TTL, "default_ip_ttl", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_INTERFACE_MTU_AGING_TIMEOUT, "path_mtu_aging_timeout", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PATH_MTU_PLATEAU_TABLE, "path_mtu_plateau_table", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_ALL_SUBNETS_LOCAL, "all_subnets_local", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PERFORM_MASK_DISCOVERY, "perform_mask_discovery", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_MASK_SUPPLIER, "mask_supplier", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_ROUTER_DISCOVERY, "router_discovery", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_ROUTER_SOLICITATION_ADDR, "router_solicitation_address", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_TRAILER_ENCAPSULATION, "trailer_encapsulation", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_ARP_CACHE_TIMEOUT, "arp_cache_timeout", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_IEEE802_3_ENCAPSULATION, "ieee802_3_encapsulation", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_DEFAULT_TCP_TTL, "default_tcp_ttl", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_TCP_KEEPALIVE_INTERVAL, "tcp_keepalive_internal", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_TCP_KEEPALIVE_GARBAGE, "tcp_keepalive_garbage", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_VENDOR_SPECIFIC, "vendor_encapsulated_options", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_NETBIOS_NAMESERVER, "netbios_name_servers", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_NETBIOS_DD_SERVER, "netbios_dd_server", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_FONT_SERVERS, "font_servers", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_X_DISPLAY_MANAGER, "x_display_manager", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_IP_ADDRESS_LEASE_TIME, "dhcp_lease_time", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_RENEWAL_T1_TIME, "dhcp_renewal_time", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_REBINDING_T2_TIME, "dhcp_rebinding_time", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_CLIENT_ID, "dhcp_client_identifier", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_NEW_TZDB_TIMEZONE, "tcode", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_NWIP_DOMAIN, "nwip_domain", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_NWIP_SUBOPTIONS, "nwip_suboptions", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_NISPLUS_DOMAIN, "nisplus_domain", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_NISPLUS_SERVERS, "nisplus_servers", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_TFTP_SERVER_NAME, "tftp_server_name", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_BOOTFILE_NAME, "bootfile_name", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_MOBILE_IP_HOME_AGENT, "mobile_ip_home_agent", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_SMTP_SERVER, "smtp_server", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_POP_SERVER, "pop_server", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_NNTP_SERVER, "nntp_server", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_WWW_SERVER, "www_server", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_FINGER_SERVER, "finger_server", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_IRC_SERVER, "irc_server", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_STREETTALK_SERVER, "streettalk_server", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_STREETTALK_DIR_ASSIST_SERVER, "streettalk_directory_assistance_server", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_SLP_DIRECTORY_AGENT, "slp_directory_agent", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_SLP_SERVICE_SCOPE, "slp_service_scope", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_CLIENT_FQDN, "fqdn", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_RELAY_AGENT_INFORMATION, "relay_agent_information", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_NDS_SERVERS, "nds_servers", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_NDS_TREE_NAME, "nds_tree_name", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_NDS_CONTEXT, "nds_context", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_BCMS_CONTROLLER_NAMES, "bcms_controller_names", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_BCMS_CONTROLLER_ADDRESS, "bcms_controller_address", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_CLIENT_LAST_TRANSACTION, "client_last_transaction_time", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_ASSOCIATED_IP, "associated_ip", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PXE_SYSTEM_TYPE, "pxe_system_type", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PXE_INTERFACE_ID, "pxe_interface_id", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PXE_CLIENT_ID, "pxe_client_id", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_UAP_SERVERS, "uap_servers", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_GEOCONF_CIVIC, "geoconf_civic", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_NETINFO_SERVER_ADDRESS, "netinfo_server_address", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_NETINFO_SERVER_TAG, "netinfo_server_tag", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_DEFAULT_URL, "default_url", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_AUTO_CONFIG, "auto_config", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_NAME_SERVICE_SEARCH, "name_service_search", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_SUBNET_SELECTION, "subnet_selection", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_VIVCO, "vivco", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_VIVSO, "vivso", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PANA_AGENT, "pana_agent", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_V4_LOST, "v4_lost", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_SIP_UA_CS_DOMAINS, "sip_ua_cs_domains", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_IPV4_ADDRESS_ANDSF, "ipv4_address_andsf", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_RDNSS_SELECTION, "rndss_selection", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_TFTP_SERVER_ADDRESS, "tftp_server_address", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_V4_PORTPARAMS, "v4_portparams", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_V4_CAPTIVE_PORTAL, "v4_captive_portal", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_LOADER_CONFIGFILE, "loader_configfile", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_LOADER_PATHPREFIX, "loader_pathprefix", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_LOADER_REBOOTTIME, "loader_reboottime", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_OPTION_6RD, "option_6rd", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_V4_ACCESS_DOMAIN, "v4_access_domain", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_224, "private_224", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_225, "private_225", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_226, "private_226", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_227, "private_227", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_228, "private_228", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_229, "private_229", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_230, "private_230", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_231, "private_231", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_232, "private_232", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_233, "private_233", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_234, "private_234", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_235, "private_235", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_236, "private_236", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_237, "private_237", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_238, "private_238", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_239, "private_239", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_240, "private_240", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_241, "private_241", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_242, "private_242", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_243, "private_243", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_244, "private_244", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_245, "private_245", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_246, "private_246", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_247, "private_247", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_248, "private_248", FALSE ), + /* NM_DHCP_OPTION_DHCP4_PRIVATE_CLASSLESS_STATIC_ROUTE */ + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_250, "private_250", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_251, "private_251", FALSE ), + /* NM_DHCP_OPTION_DHCP4_PRIVATE_PROXY_AUTODISCOVERY */ + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_253, "private_253", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_254, "private_254", FALSE ), + + /* Internal values */ + REQ (NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS, "ip_address", FALSE ), + REQ (NM_DHCP_OPTION_DHCP4_NM_EXPIRY, "expiry", FALSE ), + + { 0 } +}; + +const NMDhcpOption _nm_dhcp_option_dhcp6_options[] = { + REQ (NM_DHCP_OPTION_DHCP6_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. + */ + REQ (NM_DHCP_OPTION_DHCP6_SERVERID, "dhcp6_server_id", FALSE ), + + REQ (NM_DHCP_OPTION_DHCP6_DNS_SERVERS, "dhcp6_name_servers", TRUE ), + REQ (NM_DHCP_OPTION_DHCP6_DOMAIN_LIST, "dhcp6_domain_search", TRUE ), + REQ (NM_DHCP_OPTION_DHCP6_SNTP_SERVERS, "dhcp6_sntp_servers", TRUE ), + + /* Internal values */ + REQ (NM_DHCP_OPTION_DHCP6_NM_IP_ADDRESS, "ip6_address", FALSE ), + REQ (NM_DHCP_OPTION_DHCP6_NM_PREFIXLEN, "ip6_prefixlen", FALSE ), + REQ (NM_DHCP_OPTION_DHCP6_NM_PREFERRED_LIFE, "preferred_life", FALSE ), + REQ (NM_DHCP_OPTION_DHCP6_NM_MAX_LIFE, "max_life", FALSE ), + REQ (NM_DHCP_OPTION_DHCP6_NM_STARTS, "starts", FALSE ), + REQ (NM_DHCP_OPTION_DHCP6_NM_LIFE_STARTS, "life_starts", FALSE ), + REQ (NM_DHCP_OPTION_DHCP6_NM_RENEW, "renew", FALSE ), + REQ (NM_DHCP_OPTION_DHCP6_NM_REBIND, "rebind", FALSE ), + REQ (NM_DHCP_OPTION_DHCP6_NM_IAID, "iaid", FALSE ), + + { 0 } +}; + + +const char * +nm_dhcp_option_request_string (const NMDhcpOption *requests, guint option) +{ + guint i = 0; + + while (requests[i].name) { + if (requests[i].option_num == option) + return requests[i].name + NM_STRLEN (REQPREFIX); + i++; + } + + /* Option should always be found */ + nm_assert_not_reached (); + return NULL; +} + +void +nm_dhcp_option_take_option (GHashTable *options, + const NMDhcpOption *requests, + guint option, + char *value) +{ + nm_assert (options); + nm_assert (requests); + nm_assert (value); + + g_hash_table_insert (options, + (gpointer) nm_dhcp_option_request_string (requests, option), + value); +} + +void +nm_dhcp_option_add_option (GHashTable *options, const NMDhcpOption *requests, guint option, const char *value) +{ + if (options) + nm_dhcp_option_take_option (options, requests, option, g_strdup (value)); +} + +void +nm_dhcp_option_add_option_u64 (GHashTable *options, const NMDhcpOption *requests, guint option, guint64 value) +{ + if (options) + nm_dhcp_option_take_option (options, requests, option, g_strdup_printf ("%" G_GUINT64_FORMAT, value)); +} + +void +nm_dhcp_option_add_requests_to_options (GHashTable *options, const NMDhcpOption *requests) +{ + guint 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")); + } +} + +GHashTable * +nm_dhcp_option_create_options_dict (void) +{ + return g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_free); +} + diff --git a/src/dhcp/nm-dhcp-options.h b/src/dhcp/nm-dhcp-options.h new file mode 100644 index 00000000..0dc7a276 --- /dev/null +++ b/src/dhcp/nm-dhcp-options.h @@ -0,0 +1,204 @@ +/* + * 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. + * + * (C) Copyright 2019 Red Hat, Inc. + */ + +#ifndef __NM_DHCP_OPTIONS_H__ +#define __NM_DHCP_OPTIONS_H__ + +typedef enum { + NM_DHCP_OPTION_DHCP4_PAD = 0, + NM_DHCP_OPTION_DHCP4_SUBNET_MASK = 1, + NM_DHCP_OPTION_DHCP4_TIME_OFFSET = 2, + NM_DHCP_OPTION_DHCP4_ROUTER = 3, + NM_DHCP_OPTION_DHCP4_TIME_SERVERS = 4, + NM_DHCP_OPTION_DHCP4_IEN116_NAME_SERVERS = 5, + NM_DHCP_OPTION_DHCP4_DOMAIN_NAME_SERVER = 6, + NM_DHCP_OPTION_DHCP4_LOG_SERVERS = 7, + NM_DHCP_OPTION_DHCP4_COOKIE_SERVERS = 8, + NM_DHCP_OPTION_DHCP4_LPR_SERVERS = 9, + NM_DHCP_OPTION_DHCP4_IMPRESS_SERVERS = 10, + NM_DHCP_OPTION_DHCP4_RESOURCE_LOCATION_SERVERS = 11, + NM_DHCP_OPTION_DHCP4_HOST_NAME = 12, + NM_DHCP_OPTION_DHCP4_BOOT_FILE_SIZE = 13, + NM_DHCP_OPTION_DHCP4_MERIT_DUMP = 14, + NM_DHCP_OPTION_DHCP4_DOMAIN_NAME = 15, + NM_DHCP_OPTION_DHCP4_SWAP_SERVER = 16, + NM_DHCP_OPTION_DHCP4_ROOT_PATH = 17, + NM_DHCP_OPTION_DHCP4_EXTENSIONS_PATH = 18, + NM_DHCP_OPTION_DHCP4_ENABLE_IP_FORWARDING = 19, + NM_DHCP_OPTION_DHCP4_ENABLE_SRC_ROUTING = 20, + NM_DHCP_OPTION_DHCP4_POLICY_FILTER = 21, + NM_DHCP_OPTION_DHCP4_INTERFACE_MDR = 22, + NM_DHCP_OPTION_DHCP4_INTERFACE_TTL = 23, + NM_DHCP_OPTION_DHCP4_INTERFACE_MTU_AGING_TIMEOUT = 24, + NM_DHCP_OPTION_DHCP4_PATH_MTU_PLATEAU_TABLE = 25, + NM_DHCP_OPTION_DHCP4_INTERFACE_MTU = 26, + NM_DHCP_OPTION_DHCP4_ALL_SUBNETS_LOCAL = 27, + NM_DHCP_OPTION_DHCP4_BROADCAST = 28, + NM_DHCP_OPTION_DHCP4_PERFORM_MASK_DISCOVERY = 29, + NM_DHCP_OPTION_DHCP4_MASK_SUPPLIER = 30, + NM_DHCP_OPTION_DHCP4_ROUTER_DISCOVERY = 31, + NM_DHCP_OPTION_DHCP4_ROUTER_SOLICITATION_ADDR = 32, + NM_DHCP_OPTION_DHCP4_STATIC_ROUTE = 33, + NM_DHCP_OPTION_DHCP4_TRAILER_ENCAPSULATION = 34, + NM_DHCP_OPTION_DHCP4_ARP_CACHE_TIMEOUT = 35, + NM_DHCP_OPTION_DHCP4_IEEE802_3_ENCAPSULATION = 36, + NM_DHCP_OPTION_DHCP4_DEFAULT_TCP_TTL = 37, + NM_DHCP_OPTION_DHCP4_TCP_KEEPALIVE_INTERVAL = 38, + NM_DHCP_OPTION_DHCP4_TCP_KEEPALIVE_GARBAGE = 39, + NM_DHCP_OPTION_DHCP4_NIS_DOMAIN = 40, + NM_DHCP_OPTION_DHCP4_NIS_SERVERS = 41, + NM_DHCP_OPTION_DHCP4_NTP_SERVER = 42, + NM_DHCP_OPTION_DHCP4_VENDOR_SPECIFIC = 43, + NM_DHCP_OPTION_DHCP4_NETBIOS_NAMESERVER = 44, + NM_DHCP_OPTION_DHCP4_NETBIOS_DD_SERVER = 45, + NM_DHCP_OPTION_DHCP4_FONT_SERVERS = 48, + NM_DHCP_OPTION_DHCP4_X_DISPLAY_MANAGER = 49, + NM_DHCP_OPTION_DHCP4_IP_ADDRESS_LEASE_TIME = 51, + NM_DHCP_OPTION_DHCP4_SERVER_ID = 54, + NM_DHCP_OPTION_DHCP4_RENEWAL_T1_TIME = 58, + NM_DHCP_OPTION_DHCP4_REBINDING_T2_TIME = 59, + NM_DHCP_OPTION_DHCP4_CLIENT_ID = 61, + NM_DHCP_OPTION_DHCP4_NWIP_DOMAIN = 62, + NM_DHCP_OPTION_DHCP4_NWIP_SUBOPTIONS = 63, + NM_DHCP_OPTION_DHCP4_NISPLUS_DOMAIN = 64, + NM_DHCP_OPTION_DHCP4_NISPLUS_SERVERS = 65, + NM_DHCP_OPTION_DHCP4_TFTP_SERVER_NAME = 66, + NM_DHCP_OPTION_DHCP4_BOOTFILE_NAME = 67, + NM_DHCP_OPTION_DHCP4_MOBILE_IP_HOME_AGENT = 68, + NM_DHCP_OPTION_DHCP4_SMTP_SERVER = 69, + NM_DHCP_OPTION_DHCP4_POP_SERVER = 70, + NM_DHCP_OPTION_DHCP4_NNTP_SERVER = 71, + NM_DHCP_OPTION_DHCP4_WWW_SERVER = 72, + NM_DHCP_OPTION_DHCP4_FINGER_SERVER = 73, + NM_DHCP_OPTION_DHCP4_IRC_SERVER = 74, + NM_DHCP_OPTION_DHCP4_STREETTALK_SERVER = 75, + NM_DHCP_OPTION_DHCP4_STREETTALK_DIR_ASSIST_SERVER = 76, + NM_DHCP_OPTION_DHCP4_SLP_DIRECTORY_AGENT = 78, + NM_DHCP_OPTION_DHCP4_SLP_SERVICE_SCOPE = 79, + NM_DHCP_OPTION_DHCP4_CLIENT_FQDN = 81, + NM_DHCP_OPTION_DHCP4_RELAY_AGENT_INFORMATION = 82, + NM_DHCP_OPTION_DHCP4_NDS_SERVERS = 85, + NM_DHCP_OPTION_DHCP4_NDS_TREE_NAME = 86, + NM_DHCP_OPTION_DHCP4_NDS_CONTEXT = 87, + NM_DHCP_OPTION_DHCP4_BCMS_CONTROLLER_NAMES = 88, + NM_DHCP_OPTION_DHCP4_BCMS_CONTROLLER_ADDRESS = 89, + NM_DHCP_OPTION_DHCP4_CLIENT_LAST_TRANSACTION = 91, + NM_DHCP_OPTION_DHCP4_ASSOCIATED_IP = 92, + NM_DHCP_OPTION_DHCP4_PXE_SYSTEM_TYPE = 93, + NM_DHCP_OPTION_DHCP4_PXE_INTERFACE_ID = 94, + NM_DHCP_OPTION_DHCP4_PXE_CLIENT_ID = 97, + NM_DHCP_OPTION_DHCP4_UAP_SERVERS = 98, + NM_DHCP_OPTION_DHCP4_GEOCONF_CIVIC = 99, + NM_DHCP_OPTION_DHCP4_NEW_TZDB_TIMEZONE = 101, + NM_DHCP_OPTION_DHCP4_NETINFO_SERVER_ADDRESS = 112, + NM_DHCP_OPTION_DHCP4_NETINFO_SERVER_TAG = 113, + NM_DHCP_OPTION_DHCP4_DEFAULT_URL = 114, + NM_DHCP_OPTION_DHCP4_AUTO_CONFIG = 116, + NM_DHCP_OPTION_DHCP4_NAME_SERVICE_SEARCH = 117, + NM_DHCP_OPTION_DHCP4_SUBNET_SELECTION = 118, + NM_DHCP_OPTION_DHCP4_DOMAIN_SEARCH_LIST = 119, + NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE = 121, + NM_DHCP_OPTION_DHCP4_VIVCO = 124, + NM_DHCP_OPTION_DHCP4_VIVSO = 125, + NM_DHCP_OPTION_DHCP4_PANA_AGENT = 136, + NM_DHCP_OPTION_DHCP4_V4_LOST = 137, + NM_DHCP_OPTION_DHCP4_SIP_UA_CS_DOMAINS = 141, + NM_DHCP_OPTION_DHCP4_IPV4_ADDRESS_ANDSF = 142, + NM_DHCP_OPTION_DHCP4_RDNSS_SELECTION = 146, + NM_DHCP_OPTION_DHCP4_TFTP_SERVER_ADDRESS = 150, + NM_DHCP_OPTION_DHCP4_V4_PORTPARAMS = 159, + NM_DHCP_OPTION_DHCP4_V4_CAPTIVE_PORTAL = 160, + NM_DHCP_OPTION_DHCP4_LOADER_CONFIGFILE = 209, + NM_DHCP_OPTION_DHCP4_LOADER_PATHPREFIX = 210, + NM_DHCP_OPTION_DHCP4_LOADER_REBOOTTIME = 211, + NM_DHCP_OPTION_DHCP4_OPTION_6RD = 212, + NM_DHCP_OPTION_DHCP4_V4_ACCESS_DOMAIN = 213, + NM_DHCP_OPTION_DHCP4_PRIVATE_224 = 224, + NM_DHCP_OPTION_DHCP4_PRIVATE_225 = 225, + NM_DHCP_OPTION_DHCP4_PRIVATE_226 = 226, + NM_DHCP_OPTION_DHCP4_PRIVATE_227 = 227, + NM_DHCP_OPTION_DHCP4_PRIVATE_228 = 228, + NM_DHCP_OPTION_DHCP4_PRIVATE_229 = 229, + NM_DHCP_OPTION_DHCP4_PRIVATE_230 = 230, + NM_DHCP_OPTION_DHCP4_PRIVATE_231 = 231, + NM_DHCP_OPTION_DHCP4_PRIVATE_232 = 232, + NM_DHCP_OPTION_DHCP4_PRIVATE_233 = 233, + NM_DHCP_OPTION_DHCP4_PRIVATE_234 = 234, + NM_DHCP_OPTION_DHCP4_PRIVATE_235 = 235, + NM_DHCP_OPTION_DHCP4_PRIVATE_236 = 236, + NM_DHCP_OPTION_DHCP4_PRIVATE_237 = 237, + NM_DHCP_OPTION_DHCP4_PRIVATE_238 = 238, + NM_DHCP_OPTION_DHCP4_PRIVATE_239 = 239, + NM_DHCP_OPTION_DHCP4_PRIVATE_240 = 240, + NM_DHCP_OPTION_DHCP4_PRIVATE_241 = 241, + NM_DHCP_OPTION_DHCP4_PRIVATE_242 = 242, + NM_DHCP_OPTION_DHCP4_PRIVATE_243 = 243, + NM_DHCP_OPTION_DHCP4_PRIVATE_244 = 244, + NM_DHCP_OPTION_DHCP4_PRIVATE_245 = 245, + NM_DHCP_OPTION_DHCP4_PRIVATE_246 = 246, + NM_DHCP_OPTION_DHCP4_PRIVATE_247 = 247, + NM_DHCP_OPTION_DHCP4_PRIVATE_248 = 248, + NM_DHCP_OPTION_DHCP4_PRIVATE_CLASSLESS_STATIC_ROUTE = 249, + NM_DHCP_OPTION_DHCP4_PRIVATE_250 = 250, + NM_DHCP_OPTION_DHCP4_PRIVATE_251 = 251, + NM_DHCP_OPTION_DHCP4_PRIVATE_PROXY_AUTODISCOVERY = 252, + NM_DHCP_OPTION_DHCP4_PRIVATE_253 = 253, + NM_DHCP_OPTION_DHCP4_PRIVATE_254 = 254, + NM_DHCP_OPTION_DHCP4_END = 255, + /* Internal values */ + NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS = 1024, + NM_DHCP_OPTION_DHCP4_NM_EXPIRY = 1025, +} NMDhcpOptionDhcp4Options; + +typedef enum { + NM_DHCP_OPTION_DHCP6_CLIENTID = 1, + NM_DHCP_OPTION_DHCP6_SERVERID = 2, + NM_DHCP_OPTION_DHCP6_DNS_SERVERS = 23, + NM_DHCP_OPTION_DHCP6_DOMAIN_LIST = 24, + NM_DHCP_OPTION_DHCP6_SNTP_SERVERS = 31, + /* Internal values */ + NM_DHCP_OPTION_DHCP6_NM_IP_ADDRESS = 1026, + NM_DHCP_OPTION_DHCP6_NM_PREFIXLEN = 1027, + NM_DHCP_OPTION_DHCP6_NM_PREFERRED_LIFE = 1028, + NM_DHCP_OPTION_DHCP6_NM_MAX_LIFE = 1029, + NM_DHCP_OPTION_DHCP6_NM_STARTS = 1030, + NM_DHCP_OPTION_DHCP6_NM_LIFE_STARTS = 1031, + NM_DHCP_OPTION_DHCP6_NM_RENEW = 1032, + NM_DHCP_OPTION_DHCP6_NM_REBIND = 1033, + NM_DHCP_OPTION_DHCP6_NM_IAID = 1034, + +} NMDhcpOptionDhcp6Options; + +typedef struct { + const char *name; + uint16_t option_num; + bool include; +} NMDhcpOption; + +extern const NMDhcpOption _nm_dhcp_option_dhcp4_options[]; +extern const NMDhcpOption _nm_dhcp_option_dhcp6_options[]; + +const char *nm_dhcp_option_request_string (const NMDhcpOption *requests, guint option); +void nm_dhcp_option_take_option (GHashTable *options, const NMDhcpOption *requests, guint option, char *value); +void nm_dhcp_option_add_option (GHashTable *options, const NMDhcpOption *requests, guint option, const char *value); +void nm_dhcp_option_add_option_u64 (GHashTable *options, const NMDhcpOption *requests, guint option, guint64 value); +void nm_dhcp_option_add_requests_to_options (GHashTable *options, const NMDhcpOption *requests); +GHashTable *nm_dhcp_option_create_options_dict (void); + +#endif /* __NM_DHCP_OPTIONS_H__ */ diff --git a/src/dhcp/nm-dhcp-systemd.c b/src/dhcp/nm-dhcp-systemd.c index 1cd5ba27..073a6da0 100644 --- a/src/dhcp/nm-dhcp-systemd.c +++ b/src/dhcp/nm-dhcp-systemd.c @@ -1,8 +1,8 @@ -/* -*- 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 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 program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -32,11 +32,13 @@ #include "nm-utils.h" #include "nm-config.h" #include "nm-dhcp-utils.h" +#include "nm-dhcp-options.h" #include "nm-core-utils.h" #include "NetworkManagerUtils.h" #include "platform/nm-platform.h" #include "nm-dhcp-client-logging.h" #include "systemd/nm-sd.h" +#include "systemd/nm-sd-utils-dhcp.h" /*****************************************************************************/ @@ -79,160 +81,10 @@ G_DEFINE_TYPE (NMDhcpSystemd, nm_dhcp_systemd, NM_TYPE_DHCP_CLIENT) /*****************************************************************************/ -#define DHCP_OPTION_NIS_DOMAIN 40 -#define DHCP_OPTION_NIS_SERVERS 41 - -/* Internal values */ -#define DHCP_OPTION_IP_ADDRESS 1024 -#define DHCP_OPTION_EXPIRY 1025 -#define DHCP6_OPTION_IP_ADDRESS 1026 -#define DHCP6_OPTION_PREFIXLEN 1027 -#define DHCP6_OPTION_PREFERRED_LIFE 1028 -#define DHCP6_OPTION_MAX_LIFE 1029 -#define DHCP6_OPTION_STARTS 1030 -#define DHCP6_OPTION_LIFE_STARTS 1031 -#define DHCP6_OPTION_RENEW 1032 -#define DHCP6_OPTION_REBIND 1033 -#define DHCP6_OPTION_IAID 1034 - -typedef struct { - const char *name; - 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[] = { - 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 */ - 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[] = { - 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. - */ - REQ (SD_DHCP6_OPTION_SERVERID, "dhcp6_server_id", FALSE ), - - 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 */ - 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 -take_option (GHashTable *options, - const ReqOption *requests, - guint option, - char *value) -{ - guint i; - - nm_assert (options); - nm_assert (requests); - nm_assert (value); - - for (i = 0; requests[i].name; i++) { - 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); - return; - } - } - - /* Option should always be found */ - nm_assert_not_reached (); -} - -static void -add_option (GHashTable *options, const ReqOption *requests, guint option, const char *value) -{ - if (options) - take_option (options, requests, option, g_strdup (value)); -} - -static void -add_option_u64 (GHashTable *options, const ReqOption *requests, guint option, guint64 value) -{ - if (options) - take_option (options, requests, option, g_strdup_printf ("%" G_GUINT64_FORMAT, value)); -} - -static void -add_requests_to_options (GHashTable *options, const ReqOption *requests) -{ - guint 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) { \ - _LOG2I ((domain), (iface), " "__VA_ARGS__); \ + _LOG2D ((domain), (iface), " "__VA_ARGS__); \ } \ } G_STMT_END @@ -267,9 +119,14 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx, gint64 ts_time = time (NULL); struct in_addr a_address; struct in_addr a_netmask; + struct in_addr server_id; + struct in_addr broadcast; const struct in_addr *a_router; guint32 a_plen; guint32 a_lifetime; + guint32 renewal; + guint32 rebinding; + gs_free nm_sd_dhcp_option *private_options = NULL; g_return_val_if_fail (lease != NULL, NULL); @@ -290,26 +147,36 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx, ip4_config = nm_ip4_config_new (multi_idx, ifindex); - options = out_options ? create_options_dict () : NULL; + options = out_options ? nm_dhcp_option_create_options_dict () : 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); + LOG_LEASE (LOGD_DHCP4, "%s '%s'", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS), + addr_str); + nm_dhcp_option_add_option (options, _nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS, addr_str); 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 (a_netmask.s_addr, addr_str)); - - LOG_LEASE (LOGD_DHCP4, "expires in %u seconds (at %lld)", + LOG_LEASE (LOGD_DHCP4, "%s '%u'", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_SUBNET_MASK), + (guint) a_plen); + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_SUBNET_MASK, + nm_utils_inet4_ntop (a_netmask.s_addr, addr_str)); + + LOG_LEASE (LOGD_DHCP4, "%s '%u' seconds (at %lld)", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_IP_ADDRESS_LEASE_TIME), (guint) a_lifetime, (long long) (ts_time + a_lifetime)); - add_option_u64 (options, - dhcp4_requests, - SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME, - (guint64) (ts_time + a_lifetime)); + nm_dhcp_option_add_option_u64 (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_IP_ADDRESS_LEASE_TIME, + a_lifetime); + nm_dhcp_option_add_option_u64 (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_NM_EXPIRY, + (guint64) (ts_time + a_lifetime)); nm_ip4_config_add_address (ip4_config, &((const NMPlatformIP4Address) { @@ -322,6 +189,28 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx, .preferred = a_lifetime, })); + if (sd_dhcp_lease_get_server_identifier (lease, &server_id) >= 0) { + nm_utils_inet4_ntop (server_id.s_addr, addr_str); + LOG_LEASE (LOGD_DHCP4, "%s '%s'", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_SERVER_ID), + addr_str); + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_SERVER_ID, + addr_str); + } + + if (sd_dhcp_lease_get_broadcast (lease, &broadcast) >= 0) { + nm_utils_inet4_ntop (broadcast.s_addr, addr_str); + LOG_LEASE (LOGD_DHCP4, "%s '%s'", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_BROADCAST), + addr_str); + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_BROADCAST, + addr_str); + } + num = sd_dhcp_lease_get_dns (lease, &addr_list); if (num > 0) { nm_gstring_prepare (&str); @@ -337,8 +226,13 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx, } nm_ip4_config_add_nameserver (ip4_config, addr_list[i].s_addr); } - LOG_LEASE (LOGD_DHCP4, "nameserver '%s'", str->str); - add_option (options, dhcp4_requests, SD_DHCP_OPTION_DOMAIN_NAME_SERVER, str->str); + LOG_LEASE (LOGD_DHCP4, "%s '%s'", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_DOMAIN_NAME_SERVER), + str->str); + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_DOMAIN_NAME_SERVER, + str->str); } num = sd_dhcp_lease_get_search_domains (lease, (char ***) &search_domains); @@ -348,16 +242,23 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx, g_string_append (nm_gstring_add_space_delimiter (str), search_domains[i]); nm_ip4_config_add_search (ip4_config, search_domains[i]); } - LOG_LEASE (LOGD_DHCP4, "domain search '%s'", str->str); - add_option (options, dhcp4_requests, SD_DHCP_OPTION_DOMAIN_SEARCH_LIST, str->str); + LOG_LEASE (LOGD_DHCP4, "%s '%s'", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_DOMAIN_SEARCH_LIST), + str->str); + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_DOMAIN_SEARCH_LIST, + str->str); } if (sd_dhcp_lease_get_domainname (lease, &s) >= 0) { gs_strfreev char **domains = NULL; char **d; - LOG_LEASE (LOGD_DHCP4, "domain name '%s'", s); - add_option (options, dhcp4_requests, SD_DHCP_OPTION_DOMAIN_NAME, s); + LOG_LEASE (LOGD_DHCP4, "%s '%s'", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_DOMAIN_NAME), + s); + nm_dhcp_option_add_option (options, _nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_DOMAIN_NAME, s); /* Multiple domains sometimes stuffed into option 15 "Domain Name". * As systemd escapes such characters, split them at \\032. */ @@ -367,8 +268,10 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx, } 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); + LOG_LEASE (LOGD_DHCP4, "%s '%s'", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_HOST_NAME), + s); + nm_dhcp_option_add_option (options, _nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_HOST_NAME, s); } num = sd_dhcp_lease_get_routes (lease, &routes); @@ -379,10 +282,10 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx, for (i = 0; i < num; i++) { switch (sd_dhcp_route_get_option (routes[i])) { - case SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE: + case NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE: has_classless_route = TRUE; break; - case SD_DHCP_OPTION_STATIC_ROUTE: + case NM_DHCP_OPTION_DHCP4_STATIC_ROUTE: has_static_route = TRUE; break; } @@ -404,8 +307,8 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx, 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)) + if (!NM_IN_SET (option, NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE, + NM_DHCP_OPTION_DHCP4_STATIC_ROUTE)) continue; if (sd_dhcp_route_get_destination (routes[i], &r_network) < 0) @@ -422,14 +325,14 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx, 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 " + "%sstatic_route %s/%d gw %s", + option == NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE + ? "rfc3442_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 + g_string_append_printf (nm_gstring_add_space_delimiter ( option == NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE ? str_classless : str_static), "%s/%d %s", @@ -437,7 +340,7 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx, (int) r_plen, gateway_str); - if ( option == SD_DHCP_OPTION_STATIC_ROUTE + if ( option == NM_DHCP_OPTION_DHCP4_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 @@ -446,7 +349,7 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx, } if ( r_plen == 0 - && option == SD_DHCP_OPTION_STATIC_ROUTE) { + && option == NM_DHCP_OPTION_DHCP4_STATIC_ROUTE) { /* for option 33 (static route), RFC 2132 says: * * The default route (0.0.0.0) is an illegal destination for a static @@ -478,9 +381,15 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx, } if (str_classless && str_classless->len > 0) - add_option (options, dhcp4_requests, SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, str_classless->str); + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_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_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_STATIC_ROUTE, + str_static->str); } num = sd_dhcp_lease_get_router (lease, &a_router); @@ -524,14 +433,21 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx, }), NULL); } - LOG_LEASE (LOGD_DHCP4, "router %s", str->str); - add_option (options, dhcp4_requests, SD_DHCP_OPTION_ROUTER, str->str); + LOG_LEASE (LOGD_DHCP4, "%s '%s'", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_ROUTER), + str->str); + nm_dhcp_option_add_option (options, _nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_ROUTER, str->str); } if ( sd_dhcp_lease_get_mtu (lease, &mtu) >= 0 && mtu) { - LOG_LEASE (LOGD_DHCP4, "mtu %u", mtu); - add_option_u64 (options, dhcp4_requests, SD_DHCP_OPTION_INTERFACE_MTU, mtu); + LOG_LEASE (LOGD_DHCP4, "%s '%u'", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_INTERFACE_MTU), + mtu); + nm_dhcp_option_add_option_u64 (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_INTERFACE_MTU, + mtu); nm_ip4_config_set_mtu (ip4_config, mtu, NM_IP_CONFIG_SOURCE_DHCP); } @@ -542,19 +458,77 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx, nm_utils_inet4_ntop (addr_list[i].s_addr, addr_str); g_string_append (nm_gstring_add_space_delimiter (str), addr_str); } - LOG_LEASE (LOGD_DHCP4, "ntp server '%s'", str->str); - add_option (options, dhcp4_requests, SD_DHCP_OPTION_NTP_SERVER, str->str); + LOG_LEASE (LOGD_DHCP4, "%s '%s'", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_NTP_SERVER), + str->str); + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_NTP_SERVER, + str->str); } 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); + LOG_LEASE (LOGD_DHCP4, "%s '%s'", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_ROOT_PATH), + s); + nm_dhcp_option_add_option (options, _nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_ROOT_PATH, s); + } + + if (sd_dhcp_lease_get_t1 (lease, &renewal) >= 0) { + LOG_LEASE (LOGD_DHCP4, "%s '%u'", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_RENEWAL_T1_TIME), + renewal); + nm_dhcp_option_add_option_u64 (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_RENEWAL_T1_TIME, + renewal); + } + + if (sd_dhcp_lease_get_t2 (lease, &rebinding) >= 0) { + LOG_LEASE (LOGD_DHCP4, "%s '%u'", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_REBINDING_T2_TIME), + rebinding); + nm_dhcp_option_add_option_u64 (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_REBINDING_T2_TIME, + rebinding); + } + + if (sd_dhcp_lease_get_timezone (lease, &s) >= 0) { + LOG_LEASE (LOGD_DHCP4, "%s '%s'", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_NEW_TZDB_TIMEZONE), + s); + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp4_options, + NM_DHCP_OPTION_DHCP4_NEW_TZDB_TIMEZONE, + s); } 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); + num = nm_sd_dhcp_lease_get_private_options (lease, &private_options); + if (num > 0) { + for (i = 0; i < num; i++) { + char *option_string; + + option_string = nm_utils_bin2hexstr_full (private_options[i].data, + private_options[i].data_len, + ':', FALSE, NULL); + LOG_LEASE (LOGD_DHCP4, "%s '%s'", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, private_options[i].code), + option_string); + if (!options) { + g_free (option_string); + continue; + } + nm_dhcp_option_take_option (options, + _nm_dhcp_option_dhcp4_options, + private_options[i].code, + option_string); + } + } NM_SET_OUT (out_options, g_steal_pointer (&options)); return g_steal_pointer (&ip4_config); } @@ -627,7 +601,7 @@ bound4_handle (NMDhcpSystemd *self) return; } - add_requests_to_options (options, dhcp4_requests); + nm_dhcp_option_add_requests_to_options (options, _nm_dhcp_option_dhcp4_options); dhcp_lease_save (lease, priv->lease_file); nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), @@ -636,13 +610,13 @@ bound4_handle (NMDhcpSystemd *self) options); } -static void +static int dhcp_event_cb (sd_dhcp_client *client, int event, gpointer user_data) { NMDhcpSystemd *self = NM_DHCP_SYSTEMD (user_data); NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self); - g_assert (priv->client4 == client); + nm_assert (priv->client4 == client); _LOGD ("client event %d", event); @@ -658,10 +632,14 @@ dhcp_event_cb (sd_dhcp_client *client, int event, gpointer user_data) case SD_DHCP_CLIENT_EVENT_IP_ACQUIRE: bound4_handle (self); break; + case SD_DHCP_CLIENT_EVENT_SELECTING: + break; default: _LOGW ("unhandled DHCP event %d", event); break; } + + return 0; } static gboolean @@ -776,10 +754,10 @@ ip4_start (NMDhcpClient *client, } /* Add requested options */ - for (i = 0; dhcp4_requests[i].name; i++) { - 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); + for (i = 0; _nm_dhcp_option_dhcp4_options[i].name; i++) { + if (_nm_dhcp_option_dhcp4_options[i].include) { + nm_assert (_nm_dhcp_option_dhcp4_options[i].option_num <= 255); + r = sd_dhcp_client_set_request_option (sd_client, _nm_dhcp_option_dhcp4_options[i].option_num); nm_assert (r >= 0 || r == -EEXIST); } } @@ -846,7 +824,7 @@ lease_to_ip6_config (NMDedupMultiIndex *multi_idx, ip6_config = nm_ip6_config_new (multi_idx, ifindex); - options = out_options ? create_options_dict () : NULL; + options = out_options ? nm_dhcp_option_create_options_dict () : NULL; sd_dhcp6_lease_reset_address_iter (lease); nm_gstring_prepare (&str); @@ -866,12 +844,15 @@ lease_to_ip6_config (NMDedupMultiIndex *multi_idx, nm_utils_inet6_ntop (&tmp_addr, addr_str); g_string_append (nm_gstring_add_space_delimiter (str), addr_str); - LOG_LEASE (LOGD_DHCP6, - "address %s", + LOG_LEASE (LOGD_DHCP6, "%s '%s'", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp6_options, NM_DHCP_OPTION_DHCP6_NM_IP_ADDRESS), nm_platform_ip6_address_to_string (&address, sbuf, sizeof (sbuf))); }; if (str->len) - add_option (options, dhcp6_requests, DHCP6_OPTION_IP_ADDRESS, str->str); + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp6_options, + NM_DHCP_OPTION_DHCP6_NM_IP_ADDRESS, + str->str); if ( !info_only && nm_ip6_config_get_num_addresses (ip6_config) == 0) { @@ -890,8 +871,13 @@ lease_to_ip6_config (NMDedupMultiIndex *multi_idx, g_string_append (nm_gstring_add_space_delimiter (str), addr_str); nm_ip6_config_add_nameserver (ip6_config, &dns[i]); } - LOG_LEASE (LOGD_DHCP6, "nameserver %s", str->str); - add_option (options, dhcp6_requests, SD_DHCP6_OPTION_DNS_SERVERS, str->str); + LOG_LEASE (LOGD_DHCP6, "%s '%s'", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp6_options, NM_DHCP_OPTION_DHCP6_DNS_SERVERS), + str->str); + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp6_options, + NM_DHCP_OPTION_DHCP6_DNS_SERVERS, + str->str); } num = sd_dhcp6_lease_get_domains (lease, &domains); @@ -901,8 +887,13 @@ lease_to_ip6_config (NMDedupMultiIndex *multi_idx, g_string_append (nm_gstring_add_space_delimiter (str), domains[i]); nm_ip6_config_add_search (ip6_config, domains[i]); } - LOG_LEASE (LOGD_DHCP6, "domain name '%s'", str->str); - add_option (options, dhcp6_requests, SD_DHCP6_OPTION_DOMAIN_LIST, str->str); + LOG_LEASE (LOGD_DHCP6, "%s '%s'", + nm_dhcp_option_request_string (_nm_dhcp_option_dhcp6_options, NM_DHCP_OPTION_DHCP6_DOMAIN_LIST), + str->str); + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp6_options, + NM_DHCP_OPTION_DHCP6_DOMAIN_LIST, + str->str); } NM_SET_OUT (out_options, g_steal_pointer (&options)); @@ -955,7 +946,7 @@ dhcp6_event_cb (sd_dhcp6_client *client, int event, gpointer user_data) NMDhcpSystemd *self = NM_DHCP_SYSTEMD (user_data); NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self); - g_assert (priv->client6 == client); + nm_assert (priv->client6 == client); _LOGD ("client event %d", event); @@ -1075,9 +1066,9 @@ ip6_start (NMDhcpClient *client, } /* Add requested options */ - for (i = 0; dhcp6_requests[i].name; i++) { - if (dhcp6_requests[i].include) { - r = sd_dhcp6_client_set_request_option (sd_client, dhcp6_requests[i].option_num); + for (i = 0; _nm_dhcp_option_dhcp6_options[i].name; i++) { + if (_nm_dhcp_option_dhcp6_options[i].include) { + r = sd_dhcp6_client_set_request_option (sd_client, _nm_dhcp_option_dhcp6_options[i].option_num); nm_assert (r >= 0 || r == -EEXIST); } } diff --git a/src/dhcp/nm-dhcp-utils.c b/src/dhcp/nm-dhcp-utils.c index 7aa867c0..3f9368f2 100644 --- a/src/dhcp/nm-dhcp-utils.c +++ b/src/dhcp/nm-dhcp-utils.c @@ -1,4 +1,3 @@ -/* -*- 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) diff --git a/src/dhcp/nm-dhcp-utils.h b/src/dhcp/nm-dhcp-utils.h index 5c127bd1..39ae7693 100644 --- a/src/dhcp/nm-dhcp-utils.h +++ b/src/dhcp/nm-dhcp-utils.h @@ -1,4 +1,3 @@ -/* -*- 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) diff --git a/src/dhcp/tests/test-dhcp-dhclient.c b/src/dhcp/tests/test-dhcp-dhclient.c index 1eac3643..2cc7e5ca 100644 --- a/src/dhcp/tests/test-dhcp-dhclient.c +++ b/src/dhcp/tests/test-dhcp-dhclient.c @@ -1,4 +1,3 @@ -/* -*- 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 diff --git a/src/dhcp/tests/test-dhcp-utils.c b/src/dhcp/tests/test-dhcp-utils.c index 118082a5..e4d4c348 100644 --- a/src/dhcp/tests/test-dhcp-utils.c +++ b/src/dhcp/tests/test-dhcp-utils.c @@ -1,4 +1,3 @@ -/* -*- 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) diff --git a/src/dns/nm-dns-dnsmasq.c b/src/dns/nm-dns-dnsmasq.c index b54df730..13576c92 100644 --- a/src/dns/nm-dns-dnsmasq.c +++ b/src/dns/nm-dns-dnsmasq.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* * Copyright (C) 2010 Dan Williams <dcbw@redhat.com> * diff --git a/src/dns/nm-dns-dnsmasq.h b/src/dns/nm-dns-dnsmasq.h index cbba434b..7623d167 100644 --- a/src/dns/nm-dns-dnsmasq.h +++ b/src/dns/nm-dns-dnsmasq.h @@ -1,4 +1,3 @@ -/* -*- 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) diff --git a/src/dns/nm-dns-manager.c b/src/dns/nm-dns-manager.c index 27c3e710..2c55bee9 100644 --- a/src/dns/nm-dns-manager.c +++ b/src/dns/nm-dns-manager.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/dns/nm-dns-manager.h b/src/dns/nm-dns-manager.h index 7f6ed3ed..1cb2c669 100644 --- a/src/dns/nm-dns-manager.h +++ b/src/dns/nm-dns-manager.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/dns/nm-dns-plugin.c b/src/dns/nm-dns-plugin.c index ce814186..582abc62 100644 --- a/src/dns/nm-dns-plugin.c +++ b/src/dns/nm-dns-plugin.c @@ -1,4 +1,3 @@ -/* -*- 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) diff --git a/src/dns/nm-dns-plugin.h b/src/dns/nm-dns-plugin.h index 8a55921d..98bda7cf 100644 --- a/src/dns/nm-dns-plugin.h +++ b/src/dns/nm-dns-plugin.h @@ -1,4 +1,3 @@ -/* -*- 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) diff --git a/src/dns/nm-dns-systemd-resolved.c b/src/dns/nm-dns-systemd-resolved.c index 5d262ba3..69b3e45e 100644 --- a/src/dns/nm-dns-systemd-resolved.c +++ b/src/dns/nm-dns-systemd-resolved.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* * Copyright (C) 2010 Dan Williams <dcbw@redhat.com> * Copyright (C) 2016 Sjoerd Simons <sjoerd@luon.net> @@ -32,6 +31,7 @@ #include <linux/if.h> #include "nm-glib-aux/nm-c-list.h" +#include "nm-glib-aux/nm-dbus-aux.h" #include "nm-core-internal.h" #include "platform/nm-platform.h" #include "nm-utils.h" @@ -304,18 +304,12 @@ send_updates (NMDnsSystemdResolved *self) _LOGT ("send-updates: no name owner. Try start service..."); priv->try_start_blocked = TRUE; - g_dbus_connection_call (priv->dbus_connection, - DBUS_SERVICE_DBUS, - DBUS_PATH_DBUS, - DBUS_INTERFACE_DBUS, - "StartServiceByName", - g_variant_new ("(su)", SYSTEMD_RESOLVED_DBUS_SERVICE, 0u), - G_VARIANT_TYPE ("(u)"), - G_DBUS_CALL_FLAGS_NONE, - -1, - NULL, - NULL, - NULL); + nm_dbus_connection_call_start_service_by_name (priv->dbus_connection, + SYSTEMD_RESOLVED_DBUS_SERVICE, + -1, + NULL, + NULL, + NULL); return; } @@ -474,24 +468,17 @@ name_owner_changed_cb (GDBusConnection *connection, } static void -get_name_owner_cb (GObject *source, - GAsyncResult *res, +get_name_owner_cb (const char *name_owner, + GError *error, gpointer user_data) { NMDnsSystemdResolved *self; NMDnsSystemdResolvedPrivate *priv; - gs_unref_variant GVariant *ret = NULL; - gs_free_error GError *error = NULL; - const char *owner = NULL; - ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), res, &error); - if ( !ret + if ( !name_owner && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) return; - if (ret) - g_variant_get (ret, "(&s)", &owner); - self = user_data; priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE (self); @@ -499,7 +486,7 @@ get_name_owner_cb (GObject *source, priv->dbus_initied = TRUE; - name_owner_changed (self, owner); + name_owner_changed (self, name_owner); } /*****************************************************************************/ @@ -527,35 +514,24 @@ nm_dns_systemd_resolved_init (NMDnsSystemdResolved *self) c_list_init (&priv->request_queue_lst_head); - priv->dbus_connection = nm_g_object_ref (nm_dbus_manager_get_dbus_connection (nm_dbus_manager_get ())); + priv->dbus_connection = nm_g_object_ref (NM_MAIN_DBUS_CONNECTION_GET); if (!priv->dbus_connection) { _LOGD ("no D-Bus connection"); return; } - priv->name_owner_changed_id = g_dbus_connection_signal_subscribe (priv->dbus_connection, - DBUS_SERVICE_DBUS, - DBUS_INTERFACE_DBUS, - "NameOwnerChanged", - DBUS_PATH_DBUS, - SYSTEMD_RESOLVED_DBUS_SERVICE, - G_DBUS_SIGNAL_FLAGS_NONE, - name_owner_changed_cb, - self, - NULL); + priv->name_owner_changed_id = nm_dbus_connection_signal_subscribe_name_owner_changed (priv->dbus_connection, + SYSTEMD_RESOLVED_DBUS_SERVICE, + name_owner_changed_cb, + self, + NULL); priv->cancellable = g_cancellable_new (); - g_dbus_connection_call (priv->dbus_connection, - DBUS_SERVICE_DBUS, - DBUS_PATH_DBUS, - DBUS_INTERFACE_DBUS, - "GetNameOwner", - g_variant_new ("(s)", SYSTEMD_RESOLVED_DBUS_SERVICE), - G_VARIANT_TYPE ("(s)"), - G_DBUS_CALL_FLAGS_NONE, - -1, - priv->cancellable, - get_name_owner_cb, - self); + nm_dbus_connection_call_get_name_owner (priv->dbus_connection, + SYSTEMD_RESOLVED_DBUS_SERVICE, + -1, + priv->cancellable, + get_name_owner_cb, + self); } NMDnsPlugin * @@ -572,10 +548,8 @@ dispose (GObject *object) free_pending_updates (self); - if (priv->name_owner_changed_id != 0) { - g_dbus_connection_signal_unsubscribe (priv->dbus_connection, - nm_steal_int (&priv->name_owner_changed_id)); - } + nm_clear_g_dbus_connection_signal (priv->dbus_connection, + &priv->name_owner_changed_id); nm_clear_g_cancellable (&priv->cancellable); diff --git a/src/dns/nm-dns-systemd-resolved.h b/src/dns/nm-dns-systemd-resolved.h index b79ff5e4..14e31e2b 100644 --- a/src/dns/nm-dns-systemd-resolved.h +++ b/src/dns/nm-dns-systemd-resolved.h @@ -1,4 +1,3 @@ -/* -*- 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) diff --git a/src/dns/nm-dns-unbound.c b/src/dns/nm-dns-unbound.c index b900f29e..9e10950a 100644 --- a/src/dns/nm-dns-unbound.c +++ b/src/dns/nm-dns-unbound.c @@ -1,4 +1,3 @@ -/* -*- 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 diff --git a/src/dns/nm-dns-unbound.h b/src/dns/nm-dns-unbound.h index 100ff2c4..743699ea 100644 --- a/src/dns/nm-dns-unbound.h +++ b/src/dns/nm-dns-unbound.h @@ -1,4 +1,3 @@ -/* -*- 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) diff --git a/src/dnsmasq/nm-dnsmasq-manager.c b/src/dnsmasq/nm-dnsmasq-manager.c index 43bc66fc..b60b2952 100644 --- a/src/dnsmasq/nm-dnsmasq-manager.c +++ b/src/dnsmasq/nm-dnsmasq-manager.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/dnsmasq/nm-dnsmasq-manager.h b/src/dnsmasq/nm-dnsmasq-manager.h index dd4a9069..7d4d9c9b 100644 --- a/src/dnsmasq/nm-dnsmasq-manager.h +++ b/src/dnsmasq/nm-dnsmasq-manager.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/dnsmasq/nm-dnsmasq-utils.c b/src/dnsmasq/nm-dnsmasq-utils.c index ec5545d3..089b8ce0 100644 --- a/src/dnsmasq/nm-dnsmasq-utils.c +++ b/src/dnsmasq/nm-dnsmasq-utils.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/dnsmasq/nm-dnsmasq-utils.h b/src/dnsmasq/nm-dnsmasq-utils.h index 54b90309..393e7236 100644 --- a/src/dnsmasq/nm-dnsmasq-utils.h +++ b/src/dnsmasq/nm-dnsmasq-utils.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/dnsmasq/tests/test-dnsmasq-utils.c b/src/dnsmasq/tests/test-dnsmasq-utils.c index b311ccb4..6d8e5a1f 100644 --- a/src/dnsmasq/tests/test-dnsmasq-utils.c +++ b/src/dnsmasq/tests/test-dnsmasq-utils.c @@ -1,4 +1,3 @@ -/* -*- 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 diff --git a/src/initrd/nm-initrd-generator.c b/src/initrd/nm-initrd-generator.c index 4c1c4037..c916459d 100644 --- a/src/initrd/nm-initrd-generator.c +++ b/src/initrd/nm-initrd-generator.c @@ -66,7 +66,7 @@ output_conn (gpointer key, gpointer value, gpointer user_data) 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_print ("\n*** Connection '%s' ***\n\n%s", basename, data); return; err_out: diff --git a/src/initrd/nmi-cmdline-reader.c b/src/initrd/nmi-cmdline-reader.c index b9c75c1b..ccdd1f29 100644 --- a/src/initrd/nmi-cmdline-reader.c +++ b/src/initrd/nmi-cmdline-reader.c @@ -32,6 +32,20 @@ /*****************************************************************************/ +static gboolean +_connection_matches_type (gpointer key, gpointer value, gpointer user_data) +{ + NMConnection *connection = value; + const char *type_name = user_data; + NMSettingConnection *s_con; + + s_con = nm_connection_get_setting_connection (connection); + if (type_name == NULL) + return nm_setting_connection_get_master (s_con) == NULL; + else + return strcmp (nm_setting_connection_get_connection_type (s_con), type_name) == 0; +} + static NMConnection * get_conn (GHashTable *connections, const char *ifname, const char *type_name) { @@ -49,7 +63,18 @@ get_conn (GHashTable *connections, const char *ifname, const char *type_name) multi_connect = NM_CONNECTION_MULTI_CONNECT_MULTIPLE; } - connection = g_hash_table_lookup (connections, (gpointer)basename); + connection = g_hash_table_lookup (connections, (gpointer) basename); + if (!connection && !ifname) { + /* + * If ifname was not given, we'll match the connection by type. + * If the type was not given either, then we're happy with any connection but slaves. + * This is so that things like "bond=bond0:eth1,eth2 nameserver=1.3.3.7 end up + * slapping the nameserver to the most reasonable connection (bond0). + */ + connection = g_hash_table_find (connections, + _connection_matches_type, + (gpointer) type_name); + } if (connection) { setting = (NMSetting *)nm_connection_get_setting_connection (connection); @@ -270,11 +295,13 @@ parse_ip (GHashTable *connections, const char *sysfs_dir, char *argument) if (netmask && *netmask) { NMIPAddr addr; - if (nm_utils_parse_inaddr_bin (AF_INET, netmask, NULL, &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", client_ip); - } + else + client_ip_prefix = _nm_utils_ascii_str_to_int64 (netmask, 10, 0, 32, -1); + + if (client_ip_prefix == -1) + _LOGW (LOGD_CORE, "Invalid IP mask: %s", netmask); } /* Static IP configuration might be present. */ @@ -332,7 +359,7 @@ parse_ip (GHashTable *connections, const char *sysfs_dir, char *argument) if (g_strcmp0 (kind, "none") == 0 || (g_strcmp0 (kind, "off") == 0)) { if (nm_setting_ip_config_get_num_addresses (s_ip6) == 0) { g_object_set (s_ip6, - NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_DISABLED, NULL); } if (nm_setting_ip_config_get_num_addresses (s_ip4) == 0) { @@ -347,7 +374,7 @@ parse_ip (GHashTable *connections, const char *sysfs_dir, char *argument) NULL); if (nm_setting_ip_config_get_num_addresses (s_ip6) == 0) { g_object_set (s_ip6, - NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_DISABLED, NULL); } } else if (g_strcmp0 (kind, "dhcp6") == 0) { @@ -398,7 +425,7 @@ parse_ip (GHashTable *connections, const char *sysfs_dir, char *argument) } if (peer && *peer) - _LOGW (LOGD_CORE, "Ignoring peer: %s (not implemented)\b", peer); + _LOGW (LOGD_CORE, "Ignoring peer: %s (not implemented)\n", peer); if (gateway_ip && *gateway_ip) { int addr_family = guess_ip_address_family (gateway_ip); @@ -656,7 +683,6 @@ parse_rd_peerdns (GHashTable *connections, char *argument) NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, auto_dns, NULL); - s_ip = nm_connection_get_setting_ip6_config (connection); g_object_set (s_ip, NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, auto_dns, @@ -678,6 +704,7 @@ nmi_cmdline_reader_parse (const char *sysfs_dir, char **argv) const char *tag; char *argument; gboolean ignore_bootif = FALSE; + gboolean neednet = FALSE; char *bootif = NULL; int i; @@ -706,6 +733,8 @@ nmi_cmdline_reader_parse (const char *sysfs_dir, char **argv) parse_rd_peerdns (connections, argument); else if (strcmp (tag, "rd.bootif") == 0) ignore_bootif = !_nm_utils_ascii_str_to_bool (argument, TRUE); + else if (strcmp (tag, "rd.neednet") == 0) + neednet = _nm_utils_ascii_str_to_bool (argument, TRUE); else if (strcasecmp (tag, "BOOTIF") == 0) bootif = argument; } @@ -716,6 +745,16 @@ nmi_cmdline_reader_parse (const char *sysfs_dir, char **argv) NMConnection *connection; NMSettingWired *s_wired; + if ( !nm_utils_hwaddr_valid (bootif, ETH_ALEN) + && g_str_has_prefix (bootif, "01-") + && nm_utils_hwaddr_valid (&bootif[3], ETH_ALEN)) { + /* + * BOOTIF MAC address can be prefixed with a hardware type identifier. + * "01" stays for "wired", no other are known. + */ + bootif += 3; + } + connection = get_conn (connections, NULL, NM_SETTING_WIRED_SETTING_NAME); s_wired = nm_connection_get_setting_wired (connection); @@ -723,6 +762,10 @@ nmi_cmdline_reader_parse (const char *sysfs_dir, char **argv) NM_SETTING_WIRED_MAC_ADDRESS, bootif, NULL); } + if (neednet && g_hash_table_size (connections) == 0) { + /* Make sure there's some connection. */ + get_conn (connections, NULL, NM_SETTING_WIRED_SETTING_NAME); + } g_hash_table_foreach (connections, _normalize_conn, NULL); diff --git a/src/initrd/nmi-ibft-reader.c b/src/initrd/nmi-ibft-reader.c index efac5307..2db38e2c 100644 --- a/src/initrd/nmi-ibft-reader.c +++ b/src/initrd/nmi-ibft-reader.c @@ -182,7 +182,7 @@ ip_setting_add_from_block (GHashTable *nic, case AF_INET: s_ip = s_ip4; g_object_set (s_ip6, NM_SETTING_IP_CONFIG_METHOD, - NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); + NM_SETTING_IP6_CONFIG_METHOD_DISABLED, NULL); break; case AF_INET6: s_ip = s_ip6; diff --git a/src/initrd/tests/meson.build b/src/initrd/tests/meson.build index 0ef72fff..20cf6af2 100644 --- a/src/initrd/tests/meson.build +++ b/src/initrd/tests/meson.build @@ -18,6 +18,7 @@ foreach test_unit : test_units test( 'initrd/' + test_unit, test_script, + timeout: default_test_timeout, 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 1a87505a..9bae73a2 100644 --- a/src/initrd/tests/test-cmdline-reader.c +++ b/src/initrd/tests/test-cmdline-reader.c @@ -193,14 +193,16 @@ test_if_ip4_manual (void) gs_unref_hashtable GHashTable *connections = NULL; gs_strfreev char **argv = g_strdupv ((char *[]){ "ip=192.0.2.2::192.0.2.1:255.255.255.0:" - "hostname0.example.com:eth3::192.0.2.53", NULL }); + "hostname0.example.com:eth3::192.0.2.53", + "ip=203.0.113.2::203.0.113.1:26:" + "hostname1.example.com:eth4", NULL }); NMConnection *connection; NMSettingIPConfig *s_ip4; NMIPAddress *ip_addr; connections = nmi_cmdline_reader_parse (TEST_INITRD_DIR "/sysfs", argv); g_assert (connections); - g_assert_cmpint (g_hash_table_size (connections), ==, 1); + g_assert_cmpint (g_hash_table_size (connections), ==, 2); connection = g_hash_table_lookup (connections, "eth3"); g_assert (connection); @@ -221,8 +223,26 @@ test_if_ip4_manual (void) g_assert_cmpint (nm_ip_address_get_prefix (ip_addr), ==, 24); g_assert_cmpstr (nm_setting_ip_config_get_gateway (s_ip4), ==, "192.0.2.1"); g_assert_cmpstr (nm_setting_ip_config_get_dhcp_hostname (s_ip4), ==, "hostname0.example.com"); -} + connection = g_hash_table_lookup (connections, "eth4"); + g_assert (connection); + nmtst_assert_connection_verifies_without_normalization (connection); + g_assert_cmpstr (nm_connection_get_id (connection), ==, "eth4"); + + s_ip4 = nm_connection_get_setting_ip4_config (connection); + g_assert (s_ip4); + g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_MANUAL); + g_assert (!nm_setting_ip_config_get_ignore_auto_dns (s_ip4)); + g_assert_cmpint (nm_setting_ip_config_get_num_dns (s_ip4), ==, 0); + g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip4), ==, 0); + g_assert_cmpint (nm_setting_ip_config_get_num_addresses (s_ip4), ==, 1); + ip_addr = nm_setting_ip_config_get_address (s_ip4, 0); + g_assert (ip_addr); + g_assert_cmpstr (nm_ip_address_get_address (ip_addr), ==, "203.0.113.2"); + g_assert_cmpint (nm_ip_address_get_prefix (ip_addr), ==, 26); + g_assert_cmpstr (nm_setting_ip_config_get_gateway (s_ip4), ==, "203.0.113.1"); + g_assert_cmpstr (nm_setting_ip_config_get_dhcp_hostname (s_ip4), ==, "hostname1.example.com"); +} static void test_if_ip6_manual (void) @@ -266,8 +286,12 @@ static void test_multiple (void) { gs_unref_hashtable GHashTable *connections = NULL; - gs_strfreev char **argv = g_strdupv ((char *[]){ "ip=192.0.2.2:::::eth0", "ip=[2001:db8::2]:::::eth0", NULL }); + gs_strfreev char **argv = g_strdupv ((char *[]){ "ip=192.0.2.2:::::eth0", + "ip=[2001:db8::2]:::::eth0", + "BOOTIF=00:53:AB:cd:02:03", + NULL }); NMConnection *connection; + NMSettingWired *s_wired; NMSettingIPConfig *s_ip4; NMSettingIPConfig *s_ip6; NMIPAddress *ip_addr; @@ -281,6 +305,10 @@ test_multiple (void) nmtst_assert_connection_verifies_without_normalization (connection); g_assert_cmpstr (nm_connection_get_id (connection), ==, "eth0"); + s_wired = nm_connection_get_setting_wired (connection); + g_assert (s_wired); + g_assert_cmpstr (nm_setting_wired_get_mac_address (s_wired), ==, "00:53:AB:CD:02:03"); + s_ip4 = nm_connection_get_setting_ip4_config (connection); g_assert (s_ip4); g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_MANUAL); @@ -306,7 +334,7 @@ test_some_more (void) gs_unref_hashtable GHashTable *connections = NULL; gs_strfreev char **argv = g_strdupv ((char *[]){ "bootdev=eth1", "hail", "nameserver=[2001:DB8:3::53]", "satan", "nameserver=192.0.2.53", "worship", - "BOOTIF=00-53-AB-cd-02-03", "doom", "rd.peerdns=0", + "BOOTIF=01-00-53-AB-cd-02-03", "doom", "rd.peerdns=0", "rd.route=[2001:DB8:3::/48]:[2001:DB8:2::1]:ens10", NULL }); NMConnection *connection; @@ -387,7 +415,7 @@ static void test_no_bootif (void) { gs_unref_hashtable GHashTable *connections = NULL; - gs_strfreev char **argv = g_strdupv ((char *[]){ "BOOTIF=00-53-AB-cd-02-03", "rd.bootif=0", NULL }); + gs_strfreev char **argv = g_strdupv ((char *[]){ "BOOTIF=01-00-53-AB-cd-02-03", "rd.bootif=0", NULL }); connections = nmi_cmdline_reader_parse (TEST_INITRD_DIR "/sysfs", argv); g_assert (connections); @@ -398,7 +426,10 @@ static void test_bond (void) { gs_unref_hashtable GHashTable *connections = NULL; - gs_strfreev char **argv = g_strdupv ((char *[]){ "rd.route=192.0.2.53::bong0", "bond=bong0:eth0,eth1:mode=balance-rr", NULL }); + gs_strfreev char **argv = g_strdupv ((char *[]){ "rd.route=192.0.2.53::bong0", + "bond=bong0:eth0,eth1:mode=balance-rr", + "nameserver=203.0.113.53", + NULL }); NMConnection *connection; NMSettingConnection *s_con; NMSettingIPConfig *s_ip4; @@ -423,7 +454,8 @@ test_bond (void) g_assert (s_ip4); g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_AUTO); g_assert (!nm_setting_ip_config_get_ignore_auto_dns (s_ip4)); - g_assert_cmpint (nm_setting_ip_config_get_num_dns (s_ip4), ==, 0); + g_assert_cmpint (nm_setting_ip_config_get_num_dns (s_ip4), ==, 1); + g_assert_cmpstr (nm_setting_ip_config_get_dns (s_ip4, 0), ==, "203.0.113.53"); g_assert (!nm_setting_ip_config_get_gateway (s_ip4)); g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip4), ==, 1); ip_route = nm_setting_ip_config_get_route (s_ip4, 0); diff --git a/src/initrd/tests/test-ibft-reader.c b/src/initrd/tests/test-ibft-reader.c index 64362d18..a49d0abf 100644 --- a/src/initrd/tests/test-ibft-reader.c +++ b/src/initrd/tests/test-ibft-reader.c @@ -96,7 +96,7 @@ test_read_ibft_dhcp (void) s_ip6 = nm_connection_get_setting_ip6_config (connection); g_assert (s_ip6); - g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip6), ==, NM_SETTING_IP6_CONFIG_METHOD_IGNORE); + g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip6), ==, NM_SETTING_IP6_CONFIG_METHOD_DISABLED); g_object_unref (connection); } @@ -152,7 +152,7 @@ test_read_ibft_static (void) s_ip6 = nm_connection_get_setting_ip6_config (connection); g_assert (s_ip6); - g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip6), ==, NM_SETTING_IP6_CONFIG_METHOD_IGNORE); + g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip6), ==, NM_SETTING_IP6_CONFIG_METHOD_DISABLED); g_object_unref (connection); } @@ -263,7 +263,7 @@ test_read_ibft (void) s_ip6 = nm_connection_get_setting_ip6_config (connection); g_assert (nm_setting_ip_config_get_num_addresses (s_ip6) == 0); - g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip6), ==, NM_SETTING_IP6_CONFIG_METHOD_IGNORE); + g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip6), ==, NM_SETTING_IP6_CONFIG_METHOD_DISABLED); g_object_unref (connection); } diff --git a/src/main-utils.c b/src/main-utils.c index f3a2edf5..38fd519a 100644 --- a/src/main-utils.c +++ b/src/main-utils.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/main-utils.h b/src/main-utils.h index dbbdb85f..d4da6052 100644 --- a/src/main-utils.h +++ b/src/main-utils.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/main.c b/src/main.c index 9f979cf4..f1e9690f 100644 --- a/src/main.c +++ b/src/main.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -213,6 +212,35 @@ do_early_setup (int *argc, char **argv[], NMConfigCmdLineOptions *config_cli) global_opt.pidfile = global_opt.pidfile ?: g_strdup(NM_DEFAULT_PID_FILE); } +static gboolean +_dbus_manager_init (NMConfig *config) +{ + NMDBusManager *busmgr; + NMConfigConfigureAndQuitType c_a_q_type; + + busmgr = nm_dbus_manager_get (); + + c_a_q_type = nm_config_get_configure_and_quit (config); + + if (c_a_q_type == NM_CONFIG_CONFIGURE_AND_QUIT_DISABLED) + return nm_dbus_manager_acquire_bus (busmgr, TRUE); + + if (c_a_q_type == NM_CONFIG_CONFIGURE_AND_QUIT_ENABLED) { + /* D-Bus is useless in configure and quit mode -- we're eventually dropping + * off and potential clients would have no way of knowing whether we're + * finished already or didn't start yet. + * + * But we still create a nm_dbus_manager_get_dbus_connection() D-Bus connection + * so that we can talk to other services like firewalld. */ + return nm_dbus_manager_acquire_bus (busmgr, FALSE); + } + + nm_assert (c_a_q_type == NM_CONFIG_CONFIGURE_AND_QUIT_INITRD); + /* in initrd we don't have D-Bus at all. Don't even try to get the G_BUS_TYPE_SYSTEM + * connection. And of course don't claim the D-Bus name. */ + return TRUE; +} + /* * main * @@ -389,7 +417,9 @@ main (int argc, char *argv[]) #endif ); - /* Set up platform interaction layer */ + if (!_dbus_manager_init (config)) + goto done_no_manager; + nm_linux_platform_setup (); NM_UTILS_KEEP_ALIVE (config, nm_netns_get (), "NMConfig-depends-on-NMNetns"); @@ -399,21 +429,12 @@ main (int argc, char *argv[]) NM_CONFIG_KEYFILE_KEY_MAIN_AUTH_POLKIT, NM_CONFIG_DEFAULT_MAIN_AUTH_POLKIT_BOOL)); - if (!nm_config_get_configure_and_quit (config)) { - /* D-Bus is useless in configure and quit mode -- we're eventually dropping - * off and potential clients would have no way of knowing whether we're - * finished already or didn't start yet. */ - if (!nm_dbus_manager_acquire_bus (nm_dbus_manager_get ())) - goto done_no_manager; - } - manager = nm_manager_setup (); + nm_dbus_manager_start (nm_dbus_manager_get(), nm_manager_dbus_set_property_handle, manager); - nm_dispatcher_init (); - g_signal_connect (manager, NM_MANAGER_CONFIGURE_QUIT, G_CALLBACK (manager_configure_quit), config); if (!nm_manager_start (manager, &error)) { @@ -458,6 +479,8 @@ done: nm_dns_manager_stop (nm_dns_manager_get ()); + nm_settings_kf_db_write (NM_SETTINGS_GET); + done_no_manager: if (global_opt.pidfile && wrote_pidfile) unlink (global_opt.pidfile); diff --git a/src/meson.build b/src/meson.build index a3ac4625..f3f5ee58 100644 --- a/src/meson.build +++ b/src/meson.build @@ -22,8 +22,10 @@ cflags = nm_cflags sources = files( 'dhcp/nm-dhcp-client.c', 'dhcp/nm-dhcp-manager.c', + 'dhcp/nm-dhcp-nettools.c', 'dhcp/nm-dhcp-systemd.c', 'dhcp/nm-dhcp-utils.c', + 'dhcp/nm-dhcp-options.c', 'ndisc/nm-lndp-ndisc.c', 'ndisc/nm-ndisc.c', 'platform/nm-netlink.c', @@ -50,6 +52,7 @@ deps = [ libsystemd_dep, libudev_dep, libnm_core_dep, + shared_n_dhcp4_dep, ] if enable_wext @@ -100,7 +103,7 @@ sources = files( 'dnsmasq/nm-dnsmasq-manager.c', 'dnsmasq/nm-dnsmasq-utils.c', 'ppp/nm-ppp-manager-call.c', - 'settings/plugins/keyfile/nms-keyfile-connection.c', + 'settings/plugins/keyfile/nms-keyfile-storage.c', 'settings/plugins/keyfile/nms-keyfile-plugin.c', 'settings/plugins/keyfile/nms-keyfile-reader.c', 'settings/plugins/keyfile/nms-keyfile-utils.c', @@ -110,6 +113,8 @@ sources = files( 'settings/nm-settings.c', 'settings/nm-settings-connection.c', 'settings/nm-settings-plugin.c', + 'settings/nm-settings-storage.c', + 'settings/nm-settings-utils.c', 'supplicant/nm-supplicant-config.c', 'supplicant/nm-supplicant-interface.c', 'supplicant/nm-supplicant-manager.c', diff --git a/src/ndisc/nm-fake-ndisc.c b/src/ndisc/nm-fake-ndisc.c index f4719231..dadd1743 100644 --- a/src/ndisc/nm-fake-ndisc.c +++ b/src/ndisc/nm-fake-ndisc.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-fake-ndisc.c - Fake implementation of neighbor discovery * * This program is free software; you can redistribute it and/or modify diff --git a/src/ndisc/nm-fake-ndisc.h b/src/ndisc/nm-fake-ndisc.h index 3266dc89..5cf65bc1 100644 --- a/src/ndisc/nm-fake-ndisc.h +++ b/src/ndisc/nm-fake-ndisc.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-fake-ndisc.h - Fake implementation of neighbor discovery * * This program is free software; you can redistribute it and/or modify diff --git a/src/ndisc/nm-lndp-ndisc.c b/src/ndisc/nm-lndp-ndisc.c index 2dd7e7d8..5643c192 100644 --- a/src/ndisc/nm-lndp-ndisc.c +++ b/src/ndisc/nm-lndp-ndisc.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-lndp-ndisc.c - Router discovery implementation using libndp * * This program is free software; you can redistribute it and/or modify diff --git a/src/ndisc/nm-lndp-ndisc.h b/src/ndisc/nm-lndp-ndisc.h index f042fb74..f95d5d0c 100644 --- a/src/ndisc/nm-lndp-ndisc.h +++ b/src/ndisc/nm-lndp-ndisc.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-lndp-ndisc.h - Implementation of neighbor discovery using libndp * * This program is free software; you can redistribute it and/or modify diff --git a/src/ndisc/nm-ndisc-private.h b/src/ndisc/nm-ndisc-private.h index ae03c0ee..450def27 100644 --- a/src/ndisc/nm-ndisc-private.h +++ b/src/ndisc/nm-ndisc-private.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-ndisc.h - Perform IPv6 neighbor discovery * * This program is free software; you can redistribute it and/or modify diff --git a/src/ndisc/nm-ndisc.c b/src/ndisc/nm-ndisc.c index dd535630..1acf5ba9 100644 --- a/src/ndisc/nm-ndisc.c +++ b/src/ndisc/nm-ndisc.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-ndisc.c - Perform IPv6 neighbor discovery * * This program is free software; you can redistribute it and/or modify diff --git a/src/ndisc/nm-ndisc.h b/src/ndisc/nm-ndisc.h index 6c0c0264..766d4ab9 100644 --- a/src/ndisc/nm-ndisc.h +++ b/src/ndisc/nm-ndisc.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-ndisc.h - Perform IPv6 neighbor discovery * * This program is free software; you can redistribute it and/or modify diff --git a/src/ndisc/tests/test-ndisc-fake.c b/src/ndisc/tests/test-ndisc-fake.c index d2291d44..f752376d 100644 --- a/src/ndisc/tests/test-ndisc-fake.c +++ b/src/ndisc/tests/test-ndisc-fake.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* ndisc.c - test program * * This program is free software; you can redistribute it and/or modify diff --git a/src/ndisc/tests/test-ndisc-linux.c b/src/ndisc/tests/test-ndisc-linux.c index d47c0018..715d4037 100644 --- a/src/ndisc/tests/test-ndisc-linux.c +++ b/src/ndisc/tests/test-ndisc-linux.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* ndisc.c - test program * * This program is free software; you can redistribute it and/or modify diff --git a/src/nm-act-request.c b/src/nm-act-request.c index a79167f2..763b1503 100644 --- a/src/nm-act-request.c +++ b/src/nm-act-request.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/nm-act-request.h b/src/nm-act-request.h index e16e1ecf..055b2591 100644 --- a/src/nm-act-request.h +++ b/src/nm-act-request.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/nm-active-connection.c b/src/nm-active-connection.c index 17568c30..b937f732 100644 --- a/src/nm-active-connection.c +++ b/src/nm-active-connection.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -182,7 +181,7 @@ NM_UTILS_FLAGS2STR_DEFINE_STATIC (_state_flags_to_string, NMActivationStateFlags static void _settings_connection_updated (NMSettingsConnection *sett_conn, - gboolean by_user, + guint update_reason_u, gpointer user_data) { NMActiveConnection *self = user_data; @@ -285,7 +284,7 @@ nm_active_connection_set_state (NMActiveConnection *self, if ( new_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED || old_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { nm_settings_connection_update_timestamp (priv->settings_connection.obj, - (guint64) time (NULL), TRUE); + (guint64) time (NULL)); } if (priv->device) { @@ -523,8 +522,9 @@ nm_active_connection_clear_secrets (NMActiveConnection *self) if (nm_settings_connection_has_unmodified_applied_connection (priv->settings_connection.obj, priv->applied_connection, NM_SETTING_COMPARE_FLAG_NONE)) { - /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */ - nm_connection_clear_secrets (nm_settings_connection_get_connection (priv->settings_connection.obj)); + nm_settings_connection_clear_secrets (priv->settings_connection.obj, + FALSE, + FALSE); } nm_connection_clear_secrets (priv->applied_connection); } @@ -1534,8 +1534,7 @@ 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); + nm_clear_pointer (&priv->keep_alive, nm_keep_alive_destroy); G_OBJECT_CLASS (nm_active_connection_parent_class)->finalize (object); } diff --git a/src/nm-active-connection.h b/src/nm-active-connection.h index 1d4ce29e..d9d28378 100644 --- a/src/nm-active-connection.h +++ b/src/nm-active-connection.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/nm-audit-manager.c b/src/nm-audit-manager.c index 9f868508..fe7645f8 100644 --- a/src/nm-audit-manager.c +++ b/src/nm-audit-manager.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager audit support * * This program is free software; you can redistribute it and/or modify @@ -185,9 +184,15 @@ nm_audit_log (NMAuditManager *self, GPtrArray *fields, const char *file, } static void -_audit_log_helper (NMAuditManager *self, GPtrArray *fields, const char *file, - guint line, const char *func, const char *op, gboolean result, - gpointer subject_context, const char *reason) +_audit_log_helper (NMAuditManager *self, + GPtrArray *fields, + const char *file, + guint line, + const char *func, + const char *op, + gboolean result, + gpointer subject_context, + const char *reason) { AuditField op_field = { }, pid_field = { }, uid_field = { }; AuditField result_field = { }, reason_field = { }; @@ -247,9 +252,16 @@ nm_audit_manager_audit_enabled (NMAuditManager *self) } void -_nm_audit_manager_log_connection_op (NMAuditManager *self, const char *file, guint line, - const char *func, const char *op, NMSettingsConnection *connection, - gboolean result, const char *args, gpointer subject_context, const char *reason) +_nm_audit_manager_log_connection_op (NMAuditManager *self, + const char *file, + guint line, + const char *func, + const char *op, + NMSettingsConnection *connection, + gboolean result, + const char *args, + gpointer subject_context, + const char *reason) { gs_unref_ptrarray GPtrArray *fields = NULL; AuditField uuid_field = { }, name_field = { }, args_field = { }; diff --git a/src/nm-audit-manager.h b/src/nm-audit-manager.h index 59b8048f..b8678227 100644 --- a/src/nm-audit-manager.h +++ b/src/nm-audit-manager.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager audit support * * This program is free software; you can redistribute it and/or modify @@ -42,6 +41,9 @@ typedef struct _NMAuditManagerClass NMAuditManagerClass; #define NM_AUDIT_OP_CONN_DEACTIVATE "connection-deactivate" #define NM_AUDIT_OP_CONN_CLEAR_SECRETS "connection-clear-secrets" +#define NM_AUDIT_OP_CONNS_RELOAD "connections-reload" +#define NM_AUDIT_OP_CONNS_LOAD "connections-load" + #define NM_AUDIT_OP_RELOAD "reload" #define NM_AUDIT_OP_SLEEP_CONTROL "sleep-control" #define NM_AUDIT_OP_NET_CONTROL "networking-control" diff --git a/src/nm-auth-manager.c b/src/nm-auth-manager.c index 09a217ea..0663e207 100644 --- a/src/nm-auth-manager.c +++ b/src/nm-auth-manager.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -23,8 +22,10 @@ #include "nm-auth-manager.h" #include "c-list/src/c-list.h" +#include "nm-glib-aux/nm-dbus-aux.h" #include "nm-errors.h" #include "nm-core-internal.h" +#include "nm-dbus-manager.h" #include "NetworkManagerUtils.h" #define POLKIT_SERVICE "org.freedesktop.PolicyKit1" @@ -49,13 +50,13 @@ static guint signals[LAST_SIGNAL] = {0}; typedef struct { CList calls_lst_head; - GDBusProxy *proxy; - GCancellable *new_proxy_cancellable; - GCancellable *cancel_cancellable; + GDBusConnection *dbus_connection; + GCancellable *shutdown_cancellable; guint64 call_numid_counter; - bool polkit_enabled:1; + guint changed_signal_id; bool disposing:1; bool shutting_down:1; + bool polkit_enabled_construct_only:1; } NMAuthManagerPrivate; struct _NMAuthManager { @@ -113,7 +114,7 @@ nm_auth_manager_get_polkit_enabled (NMAuthManager *self) { g_return_val_if_fail (NM_IS_AUTH_MANAGER (self), FALSE); - return NM_AUTH_MANAGER_GET_PRIVATE (self)->polkit_enabled; + return NM_AUTH_MANAGER_GET_PRIVATE (self)->dbus_connection != NULL; } /*****************************************************************************/ @@ -131,7 +132,6 @@ typedef enum { struct _NMAuthManagerCallId { CList calls_lst; NMAuthManager *self; - GVariant *dbus_parameters; GCancellable *dbus_cancellable; NMAuthManagerCheckAuthorizationCallback callback; gpointer user_data; @@ -141,7 +141,7 @@ struct _NMAuthManagerCallId { }; #define cancellation_id_to_str_a(call_numid) \ - nm_sprintf_bufa (NM_STRLEN (CANCELLATION_ID_PREFIX) + 20, \ + nm_sprintf_bufa (NM_STRLEN (CANCELLATION_ID_PREFIX) + 60, \ CANCELLATION_ID_PREFIX"%"G_GUINT64_FORMAT, \ (call_numid)) @@ -150,8 +150,6 @@ _call_id_free (NMAuthManagerCallId *call_id) { c_list_unlink (&call_id->calls_lst); nm_clear_g_source (&call_id->idle_id); - if (call_id->dbus_parameters) - g_variant_unref (g_steal_pointer (&call_id->dbus_parameters)); if (call_id->dbus_cancellable) { /* we have a pending D-Bus call. We keep the call-id instance alive @@ -182,7 +180,7 @@ _call_id_invoke_callback (NMAuthManagerCallId *call_id, } static void -cancel_check_authorization_cb (GObject *proxy, +cancel_check_authorization_cb (GObject *source, GAsyncResult *res, gpointer user_data) { @@ -190,7 +188,7 @@ cancel_check_authorization_cb (GObject *proxy, gs_unref_variant GVariant *value = NULL; gs_free_error GError *error= NULL; - value = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, &error); + value = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), res, &error); if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) _LOG2T (call_id, "cancel request was cancelled"); else if (error) @@ -224,18 +222,18 @@ _call_check_authorize_cb (GObject *proxy, self = call_id->self; priv = NM_AUTH_MANAGER_GET_PRIVATE (self); - value = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, G_VARIANT_TYPE ("((bba{ss}))"), &error); + value = g_dbus_connection_call_finish (G_DBUS_CONNECTION (proxy), res, &error); if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { /* call_id was cancelled externally, but _call_id_free() kept call_id * alive (and it has still the reference on @self. */ - if (!priv->cancel_cancellable) { + if (!priv->shutdown_cancellable) { /* we do a forced shutdown. There is no more time for cancelling... */ _call_id_free (call_id); /* this shouldn't really happen, because: * _call_check_authorize() only scheduled the D-Bus request at a time when - * cancel_cancellable was still set. It means, somebody called force-shutdown + * shutdown_cancellable was still set. It means, somebody called force-shutdown * after call-id was schedule. * force-shutdown should only be called after: * - cancel all pending requests @@ -244,15 +242,19 @@ _call_check_authorize_cb (GObject *proxy, g_return_if_reached (); } - g_dbus_proxy_call (priv->proxy, - "CancelCheckAuthorization", - g_variant_new ("(s)", - cancellation_id_to_str_a (call_id->call_numid)), - G_DBUS_CALL_FLAGS_NONE, - CANCELLATION_TIMEOUT_MS, - priv->cancel_cancellable, - cancel_check_authorization_cb, - call_id); + g_dbus_connection_call (priv->dbus_connection, + POLKIT_SERVICE, + POLKIT_OBJECT_PATH, + POLKIT_INTERFACE, + "CancelCheckAuthorization", + g_variant_new ("(s)", + cancellation_id_to_str_a (call_id->call_numid)), + G_VARIANT_TYPE ("()"), + G_DBUS_CALL_FLAGS_NONE, + CANCELLATION_TIMEOUT_MS, + priv->shutdown_cancellable, + cancel_check_authorization_cb, + call_id); return; } @@ -270,30 +272,6 @@ _call_check_authorize_cb (GObject *proxy, _call_id_invoke_callback (call_id, is_authorized, is_challenge, error); } -static void -_call_check_authorize (NMAuthManagerCallId *call_id) -{ - NMAuthManager *self = call_id->self; - NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (self); - - nm_assert (call_id->dbus_parameters); - nm_assert (g_variant_is_floating (call_id->dbus_parameters)); - nm_assert (!call_id->dbus_cancellable); - - call_id->dbus_cancellable = g_cancellable_new (); - - nm_assert (priv->cancel_cancellable); - - g_dbus_proxy_call (priv->proxy, - "CheckAuthorization", - g_steal_pointer (&call_id->dbus_parameters), - G_DBUS_CALL_FLAGS_NONE, - G_MAXINT, /* no timeout */ - call_id->dbus_cancellable, - _call_check_authorize_cb, - call_id); -} - static gboolean _call_on_idle (gpointer user_data) { @@ -344,9 +322,6 @@ nm_auth_manager_check_authorization (NMAuthManager *self, NMAuthManagerPrivate *priv; PolkitCheckAuthorizationFlags flags; char subject_buf[64]; - GVariantBuilder builder; - GVariant *subject_value; - GVariant *details_value; NMAuthManagerCallId *call_id; g_return_val_if_fail (NM_IS_AUTH_MANAGER (self), NULL); @@ -365,14 +340,16 @@ nm_auth_manager_check_authorization (NMAuthManager *self, ? POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION : POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE; - call_id = g_slice_new0 (NMAuthManagerCallId); - call_id->self = g_object_ref (self); - call_id->callback = callback; - call_id->user_data = user_data; - call_id->call_numid = ++priv->call_numid_counter; + call_id = g_slice_new (NMAuthManagerCallId); + *call_id = (NMAuthManagerCallId) { + .self = g_object_ref (self), + .callback = callback, + .user_data = user_data, + .call_numid = ++priv->call_numid_counter, + }; c_list_link_tail (&priv->calls_lst_head, &call_id->calls_lst); - if (!priv->polkit_enabled) { + if (!priv->dbus_connection) { _LOG2T (call_id, "CheckAuthorization(%s), subject=%s (succeeding due to polkit authorization disabled)", action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf))); call_id->idle_reason = IDLE_REASON_AUTHORIZED; call_id->idle_id = g_idle_add (_call_on_idle, call_id); @@ -384,12 +361,12 @@ nm_auth_manager_check_authorization (NMAuthManager *self, _LOG2T (call_id, "CheckAuthorization(%s), subject=%s (succeeding for root)", action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf))); call_id->idle_reason = IDLE_REASON_AUTHORIZED; call_id->idle_id = g_idle_add (_call_on_idle, call_id); - } else if ( !priv->proxy - && !priv->new_proxy_cancellable) { - _LOG2T (call_id, "CheckAuthorization(%s), subject=%s (failing due to invalid DBUS proxy)", action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf))); - call_id->idle_reason = IDLE_REASON_NO_DBUS; - call_id->idle_id = g_idle_add (_call_on_idle, call_id); } else { + GVariant *parameters; + GVariantBuilder builder; + GVariant *subject_value; + GVariant *details_value; + subject_value = nm_auth_subject_unix_process_to_polkit_gvariant (subject); nm_assert (g_variant_is_floating (subject_value)); @@ -397,18 +374,31 @@ nm_auth_manager_check_authorization (NMAuthManager *self, g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{ss}")); details_value = g_variant_builder_end (&builder); - call_id->dbus_parameters = g_variant_new ("(@(sa{sv})s@a{ss}us)", - subject_value, - action_id, - details_value, - (guint32) flags, - cancellation_id_to_str_a (call_id->call_numid)); - if (!priv->proxy) { - _LOG2T (call_id, "CheckAuthorization(%s), subject=%s (wait for proxy)", action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf))); - } else { - _LOG2T (call_id, "CheckAuthorization(%s), subject=%s", action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf))); - _call_check_authorize (call_id); - } + parameters = g_variant_new ("(@(sa{sv})s@a{ss}us)", + subject_value, + action_id, + details_value, + (guint32) flags, + cancellation_id_to_str_a (call_id->call_numid)); + + _LOG2T (call_id, "CheckAuthorization(%s), subject=%s", action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf))); + + call_id->dbus_cancellable = g_cancellable_new (); + + nm_assert (priv->shutdown_cancellable); + + g_dbus_connection_call (priv->dbus_connection, + POLKIT_SERVICE, + POLKIT_OBJECT_PATH, + POLKIT_INTERFACE, + "CheckAuthorization", + parameters, + G_VARIANT_TYPE ("((bba{ss}))"), + G_DBUS_CALL_FLAGS_NONE, + G_MAXINT, /* no timeout */ + call_id->dbus_cancellable, + _call_check_authorize_cb, + call_id); } return call_id; @@ -440,113 +430,18 @@ nm_auth_manager_check_authorization_cancel (NMAuthManagerCallId *call_id) /*****************************************************************************/ static void -_emit_changed_signal (NMAuthManager *self) -{ - _LOGD ("emit changed signal"); - g_signal_emit (self, signals[CHANGED_SIGNAL], 0); -} - -static void -_log_name_owner (NMAuthManager *self, char **out_name_owner) -{ - NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (self); - gs_free char *name_owner = NULL; - - name_owner = g_dbus_proxy_get_name_owner (priv->proxy); - if (name_owner) - _LOGD ("dbus name owner: '%s'", name_owner); - else - _LOGD ("dbus name owner: none"); - - NM_SET_OUT (out_name_owner, g_steal_pointer (&name_owner)); -} - -static void -_dbus_on_name_owner_notify_cb (GObject *object, - GParamSpec *pspec, - gpointer user_data) -{ - NMAuthManager *self = user_data; - gs_free char *name_owner = NULL; - - nm_assert (NM_AUTH_MANAGER_GET_PRIVATE (self)->proxy == (GDBusProxy *) object); - - _log_name_owner (self, &name_owner); - if (!name_owner) { - /* when the name disappears, we also want to raise a emit signal. - * When it appears, we raise one already. */ - _emit_changed_signal (self); - } -} - -static void -_dbus_on_changed_signal_cb (GDBusProxy *proxy, - gpointer user_data) +changed_signal_cb (GDBusConnection *connection, + const char *sender_name, + const char *object_path, + const char *interface_name, + const char *signal_name, + GVariant *parameters, + gpointer user_data) { NMAuthManager *self = user_data; - nm_assert (NM_AUTH_MANAGER_GET_PRIVATE (self)->proxy == proxy); - _LOGD ("dbus signal: \"Changed\""); - _emit_changed_signal (self); -} - -static void -_dbus_new_proxy_cb (GObject *source_object, - GAsyncResult *res, - gpointer user_data) -{ - NMAuthManager *self; - NMAuthManagerPrivate *priv; - gs_free_error GError *error = NULL; - GDBusProxy *proxy; - NMAuthManagerCallId *call_id; - - proxy = g_dbus_proxy_new_for_bus_finish (res, &error); - - if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - return; - - self = user_data; - priv = NM_AUTH_MANAGER_GET_PRIVATE (self); - - priv->proxy = proxy; - g_clear_object (&priv->new_proxy_cancellable); - - if (!priv->proxy) { - _LOGE ("could not create polkit proxy: %s", error->message); - -again: - c_list_for_each_entry (call_id, &priv->calls_lst_head, calls_lst) { - if (call_id->dbus_parameters) { - _LOG2T (call_id, "completed: failed due to no D-Bus proxy after startup"); - _call_id_invoke_callback (call_id, FALSE, FALSE, error); - goto again; - } - } - return; - } - - priv->cancel_cancellable = g_cancellable_new (); - - g_signal_connect (priv->proxy, - "notify::g-name-owner", - G_CALLBACK (_dbus_on_name_owner_notify_cb), - self); - _nm_dbus_signal_connect (priv->proxy, "Changed", NULL, - G_CALLBACK (_dbus_on_changed_signal_cb), - self); - - _log_name_owner (self, NULL); - - c_list_for_each_entry (call_id, &priv->calls_lst_head, calls_lst) { - if (call_id->dbus_parameters) { - _LOG2T (call_id, "CheckAuthorization invoke now"); - _call_check_authorize (call_id); - } - } - - _emit_changed_signal (self); + g_signal_emit (self, signals[CHANGED_SIGNAL], 0); } /*****************************************************************************/ @@ -590,7 +485,7 @@ nm_auth_manager_force_shutdown (NMAuthManager *self) */ priv->shutting_down = TRUE; - nm_clear_g_cancellable (&priv->cancel_cancellable); + nm_clear_g_cancellable (&priv->shutdown_cancellable); } /*****************************************************************************/ @@ -603,7 +498,7 @@ set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *p switch (prop_id) { case PROP_POLKIT_ENABLED: /* construct-only */ - priv->polkit_enabled = !!g_value_get_boolean (value); + priv->polkit_enabled_construct_only = !!g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -626,23 +521,42 @@ constructed (GObject *object) { NMAuthManager *self = NM_AUTH_MANAGER (object); NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (self); + NMLogLevel logl = LOGL_DEBUG; + const char *create_message; G_OBJECT_CLASS (nm_auth_manager_parent_class)->constructed (object); - _LOGD ("create auth-manager: polkit %s", priv->polkit_enabled ? "enabled" : "disabled"); - - if (priv->polkit_enabled) { - priv->new_proxy_cancellable = g_cancellable_new (); - g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, - NULL, - POLKIT_SERVICE, - POLKIT_OBJECT_PATH, - POLKIT_INTERFACE, - priv->new_proxy_cancellable, - _dbus_new_proxy_cb, - self); + if (!priv->polkit_enabled_construct_only) { + create_message = "polkit disabled"; + goto out; } + + priv->dbus_connection = nm_g_object_ref (NM_MAIN_DBUS_CONNECTION_GET); + + if (!priv->dbus_connection) { + /* This warrants an info level message. */ + logl = LOGL_INFO; + create_message = "D-Bus connection not available. Polkit is disabled and all requests are authenticated."; + goto out; + } + + priv->shutdown_cancellable = g_cancellable_new (); + + priv->changed_signal_id = g_dbus_connection_signal_subscribe (priv->dbus_connection, + POLKIT_SERVICE, + POLKIT_INTERFACE, + "Changed", + POLKIT_OBJECT_PATH, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + changed_signal_cb, + self, + NULL); + + create_message = "polkit enabled"; + +out: + _NMLOG (logl, "create auth-manager: %s", create_message); } NMAuthManager * @@ -660,7 +574,8 @@ nm_auth_manager_setup (gboolean polkit_enabled) singleton_instance = self; nm_singleton_instance_register (); - nm_log_dbg (LOGD_CORE, "setup %s singleton (%p)", "NMAuthManager", singleton_instance); + nm_log_dbg (LOGD_CORE, "setup %s singleton ("NM_HASH_OBFUSCATE_PTR_FMT")", + "NMAuthManager", NM_HASH_OBFUSCATE_PTR (singleton_instance)); return self; } @@ -677,15 +592,14 @@ dispose (GObject *object) priv->disposing = TRUE; - nm_clear_g_cancellable (&priv->new_proxy_cancellable); - nm_clear_g_cancellable (&priv->cancel_cancellable); + nm_clear_g_cancellable (&priv->shutdown_cancellable); - if (priv->proxy) { - g_signal_handlers_disconnect_by_data (priv->proxy, self); - g_clear_object (&priv->proxy); - } + nm_clear_g_dbus_connection_signal (priv->dbus_connection, + &priv->changed_signal_id); G_OBJECT_CLASS (nm_auth_manager_parent_class)->dispose (object); + + g_clear_object (&priv->dbus_connection); } static void diff --git a/src/nm-auth-manager.h b/src/nm-auth-manager.h index fe7ee787..86746d09 100644 --- a/src/nm-auth-manager.h +++ b/src/nm-auth-manager.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/nm-auth-subject.c b/src/nm-auth-subject.c index 5599201e..fd39bff1 100644 --- a/src/nm-auth-subject.c +++ b/src/nm-auth-subject.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/nm-auth-subject.h b/src/nm-auth-subject.h index 9267dfd4..7af76018 100644 --- a/src/nm-auth-subject.h +++ b/src/nm-auth-subject.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/nm-auth-utils.c b/src/nm-auth-utils.c index a0ad84c1..7235cba1 100644 --- a/src/nm-auth-utils.c +++ b/src/nm-auth-utils.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -31,7 +30,10 @@ /*****************************************************************************/ struct NMAuthChain { - GHashTable *data_hash; + + CList parent_lst; + + CList data_lst_head; CList auth_call_lst_head; @@ -41,26 +43,49 @@ struct NMAuthChain { NMAuthChainResultFunc done_func; gpointer user_data; - guint32 refcount; + guint num_pending_auth_calls; - bool done:1; + bool is_destroyed:1; + bool is_finishing:1; }; +G_STATIC_ASSERT (G_STRUCT_OFFSET (NMAuthChain, parent_lst) == 0); + typedef struct { CList auth_call_lst; NMAuthChain *chain; NMAuthManagerCallId *call_id; - char *permission; + const char *permission; + NMAuthCallResult result; } AuthCall; /*****************************************************************************/ +static void _auth_chain_destroy (NMAuthChain *self); + +/*****************************************************************************/ + static void _ASSERT_call (AuthCall *call) { nm_assert (call); nm_assert (call->chain); + nm_assert (call->permission && strlen (call->permission) > 0); nm_assert (nm_c_list_contains_entry (&call->chain->auth_call_lst_head, call, auth_call_lst)); +#if NM_MORE_ASSERTS > 5 + { + AuthCall *auth_call; + guint n = 0; + + c_list_for_each_entry (auth_call, &call->chain->auth_call_lst_head, auth_call_lst) { + nm_assert ( auth_call->result == NM_AUTH_CALL_RESULT_UNKNOWN + || !auth_call->call_id); + if (auth_call->call_id) + n++; + } + nm_assert (n == call->chain->num_pending_auth_calls); + } +#endif } /*****************************************************************************/ @@ -68,67 +93,68 @@ _ASSERT_call (AuthCall *call) static void auth_call_free (AuthCall *call) { - if (call->call_id) - nm_auth_manager_check_authorization_cancel (call->call_id); + _ASSERT_call (call); + c_list_unlink_stale (&call->auth_call_lst); - g_free (call->permission); + if (call->call_id) { + call->chain->num_pending_auth_calls--; + nm_auth_manager_check_authorization_cancel (call->call_id); + } g_slice_free (AuthCall, call); } +static AuthCall * +_find_auth_call (NMAuthChain *self, const char *permission) +{ + AuthCall *auth_call; + + c_list_for_each_entry (auth_call, &self->auth_call_lst_head, auth_call_lst) { + if (nm_streq (auth_call->permission, permission)) + return auth_call; + } + return NULL; +} + /*****************************************************************************/ typedef struct { - - /* must be the first field. */ + CList data_lst; const char *tag; - gpointer data; GDestroyNotify destroy; - char tag_data[]; } ChainData; -static ChainData * -chain_data_new (const char *tag, gpointer data, GDestroyNotify destroy) -{ - ChainData *tmp; - gsize l = strlen (tag); - - tmp = g_malloc (sizeof (ChainData) + l + 1); - tmp->tag = &tmp->tag_data[0]; - tmp->data = data; - tmp->destroy = destroy; - memcpy (&tmp->tag_data[0], tag, l + 1); - return tmp; -} - static void -chain_data_free (gpointer data) +chain_data_free (ChainData *chain_data) { - ChainData *tmp = data; - - if (tmp->destroy) - tmp->destroy (tmp->data); - g_free (tmp); + c_list_unlink_stale (&chain_data->data_lst); + if (chain_data->destroy) + chain_data->destroy (chain_data->data); + g_slice_free (ChainData, chain_data); } -static gpointer +static ChainData * _get_data (NMAuthChain *self, const char *tag) { - ChainData *tmp; + ChainData *chain_data; - if (!self->data_hash) - return NULL; - tmp = g_hash_table_lookup (self->data_hash, &tag); - return tmp ? tmp->data : NULL; + c_list_for_each_entry (chain_data, &self->data_lst_head, data_lst) { + if (nm_streq (chain_data->tag, tag)) + return chain_data; + } + return NULL; } gpointer nm_auth_chain_get_data (NMAuthChain *self, const char *tag) { + ChainData *chain_data; + g_return_val_if_fail (self, NULL); g_return_val_if_fail (tag, NULL); - return _get_data (self, tag); + chain_data = _get_data (self, tag); + return chain_data ? chain_data->data : NULL; } /** @@ -145,47 +171,77 @@ nm_auth_chain_get_data (NMAuthChain *self, const char *tag) gpointer nm_auth_chain_steal_data (NMAuthChain *self, const char *tag) { - ChainData *tmp; - gpointer value = NULL; + ChainData *chain_data; + gpointer value; g_return_val_if_fail (self, NULL); g_return_val_if_fail (tag, NULL); - if (!self->data_hash) - return NULL; - - tmp = g_hash_table_lookup (self->data_hash, &tag); - if (!tmp) + chain_data = _get_data (self, tag); + if (!chain_data) return NULL; - value = tmp->data; + value = chain_data->data; /* Make sure the destroy handler isn't called when freeing */ - tmp->destroy = NULL; - g_hash_table_remove (self->data_hash, tmp); + chain_data->destroy = NULL; + chain_data_free (chain_data); return value; } +/** + * nm_auth_chain_set_data_unsafe: + * @self: the #NMAuthChain + * @tag: the tag for referencing the attached data. + * @data: the data to attach. If %NULL, this call has no effect + * and nothing is attached. + * @data_destroy: (allow-none): the destroy function for the data pointer. + * + * @tag string is not cloned and must outlife @self. That is why + * the function is "unsafe". Use nm_auth_chain_set_data() with a C literal + * instead. + * + * It is a bug to add the same tag more than once. + */ void -nm_auth_chain_set_data (NMAuthChain *self, - const char *tag, - gpointer data, - GDestroyNotify data_destroy) +nm_auth_chain_set_data_unsafe (NMAuthChain *self, + const char *tag, + gpointer data, + GDestroyNotify data_destroy) { + ChainData *chain_data; + g_return_if_fail (self); g_return_if_fail (tag); - if (data == NULL) { - if (self->data_hash) - g_hash_table_remove (self->data_hash, &tag); - } else { - if (!self->data_hash) { - self->data_hash = g_hash_table_new_full (nm_pstr_hash, nm_pstr_equal, - NULL, chain_data_free); - } - g_hash_table_add (self->data_hash, - chain_data_new (tag, data, data_destroy)); + /* we should not track a large number of elements via a linked list. If this becomes + * necessary, revert the code to use GHashTable again. */ + nm_assert (c_list_length (&self->data_lst_head) < 25); + + /* The tag must not yet exist. Otherwise we'd have to first search the linked + * list for an existing entry. */ + nm_assert (!_get_data (self, tag)); + + if (!data) { + /* we don't track user data of %NULL. + * + * In the past this had also the meaning of removing a user-data. But since + * nm_auth_chain_set_data() does not allow being called more than once + * for the same tag, we don't need to remove anything. */ + return; } + + chain_data = g_slice_new (ChainData); + *chain_data = (ChainData) { + .tag = tag, + .data = data, + .destroy = data_destroy, + }; + + /* we assert that no duplicate tags are added. But still, add the new + * element to the front, so that it would shadow the duplicate element + * in the list. */ + c_list_link_front (&self->data_lst_head, &chain_data->data_lst); } /*****************************************************************************/ @@ -193,13 +249,26 @@ nm_auth_chain_set_data (NMAuthChain *self, NMAuthCallResult nm_auth_chain_get_result (NMAuthChain *self, const char *permission) { - gpointer data; + AuthCall *auth_call; g_return_val_if_fail (self, NM_AUTH_CALL_RESULT_UNKNOWN); g_return_val_if_fail (permission, NM_AUTH_CALL_RESULT_UNKNOWN); - data = _get_data (self, permission); - return data ? GPOINTER_TO_UINT (data) : NM_AUTH_CALL_RESULT_UNKNOWN; + /* it is a bug to request the result other than from the done_func() + * callback. You are not supposed to poll for the result but request + * it upon notification. */ + nm_assert (self->is_finishing); + + auth_call = _find_auth_call (self, permission); + + /* it is a bug to request a permission result that was not + * previously requested or which did not complete yet. */ + if (!auth_call) + g_return_val_if_reached (NM_AUTH_CALL_RESULT_UNKNOWN); + + nm_assert (!auth_call->call_id); + + return auth_call->result; } NMAuthSubject * @@ -212,39 +281,6 @@ nm_auth_chain_get_subject (NMAuthChain *self) /*****************************************************************************/ -static gboolean -auth_chain_finish (NMAuthChain *self) -{ - self->done = TRUE; - - /* Ensure we stay alive across the callback */ - nm_assert (self->refcount == 1); - self->refcount++; - self->done_func (self, NULL, self->context, self->user_data); - nm_assert (NM_IN_SET (self->refcount, 1, 2)); - nm_auth_chain_destroy (self); - return FALSE; -} - -static void -auth_call_complete (AuthCall *call) -{ - NMAuthChain *self; - - _ASSERT_call (call); - - self = call->chain; - - nm_assert (!self->done); - - auth_call_free (call); - - if (c_list_is_empty (&self->auth_call_lst_head)) { - /* we are on an idle-handler or a clean call-stack (non-reentrant). */ - auth_chain_finish (self); - } -} - static void pk_call_cb (NMAuthManager *auth_manager, NMAuthManagerCallId *call_id, @@ -253,49 +289,110 @@ pk_call_cb (NMAuthManager *auth_manager, GError *error, gpointer user_data) { + NMAuthChain *self; AuthCall *call; - NMAuthCallResult call_result; + + nm_assert (call_id); if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) return; call = user_data; + _ASSERT_call (call); nm_assert (call->call_id == call_id); + nm_assert (call->result == NM_AUTH_CALL_RESULT_UNKNOWN); + + self = call->chain; + + nm_assert (!self->is_destroyed); + nm_assert (!self->is_finishing); call->call_id = NULL; - call_result = nm_auth_call_result_eval (is_authorized, is_challenge, error); + call->result = nm_auth_call_result_eval (is_authorized, is_challenge, error); + + call->chain->num_pending_auth_calls--; - nm_auth_chain_set_data (call->chain, call->permission, GUINT_TO_POINTER (call_result), NULL); + _ASSERT_call (call); - auth_call_complete (call); + if (call->chain->num_pending_auth_calls == 0) { + /* we are on an idle-handler or a clean call-stack (non-reentrant) so it's safe + * to invoke the callback right away. */ + self->is_finishing = TRUE; + self->done_func (self, self->context, self->user_data); + nm_assert (self->is_finishing); + _auth_chain_destroy (self); + } } +/** + * nm_auth_chain_add_call_unsafe: + * @self: the #NMAuthChain + * @permission: the permission string. This string is kept by reference + * and you must make sure that it's lifetime lasts until the NMAuthChain + * gets destroyed. That's why the function is "unsafe". Use + * nm_auth_chain_add_call() instead. + * @allow_interaction: flag + * + * It's "unsafe" because @permission is not copied. It's the callers responsibility + * that the permission string stays valid as long as NMAuthChain. + * + * If you can, use nm_auth_chain_add_call() instead! + * + * If you have a non-static string, you may attach the permission string as + * user-data via nm_auth_chain_set_data(). + */ void -nm_auth_chain_add_call (NMAuthChain *self, - const char *permission, - gboolean allow_interaction) +nm_auth_chain_add_call_unsafe (NMAuthChain *self, + const char *permission, + gboolean allow_interaction) { AuthCall *call; - NMAuthManager *auth_manager = nm_auth_manager_get (); g_return_if_fail (self); g_return_if_fail (self->subject); - g_return_if_fail (!self->done); + g_return_if_fail (!self->is_finishing); + g_return_if_fail (!self->is_destroyed); g_return_if_fail (permission && *permission); - g_return_if_fail (nm_auth_subject_is_unix_process (self->subject) || nm_auth_subject_is_internal (self->subject)); + nm_assert ( nm_auth_subject_is_unix_process (self->subject) + || nm_auth_subject_is_internal (self->subject)); + + /* duplicate permissions are not supported, also because nm_auth_chain_get_result() + * can only return one-permission. */ + nm_assert (!_find_auth_call (self, permission)); + + call = g_slice_new (AuthCall); - call = g_slice_new0 (AuthCall); - call->chain = self; - call->permission = g_strdup (permission); - c_list_link_tail (&self->auth_call_lst_head, &call->auth_call_lst); - call->call_id = nm_auth_manager_check_authorization (auth_manager, + *call = (AuthCall) { + .chain = self, + .call_id = NULL, + .result = NM_AUTH_CALL_RESULT_UNKNOWN, + + /* we don't clone the permission string. It's the callers responsiblity. */ + .permission = permission, + }; + + /* above we assert that no duplicate permissions are added. Still, track the + * new request to the front of the list so that it would shadow an earlier + * call. */ + c_list_link_front (&self->auth_call_lst_head, &call->auth_call_lst); + + call->call_id = nm_auth_manager_check_authorization (nm_auth_manager_get (), self->subject, permission, allow_interaction, pk_call_cb, call); + + self->num_pending_auth_calls++; + + _ASSERT_call (call); + + /* we track auth-calls in a linked list. If we end up requesting too many permissions this + * becomes inefficient. If that ever happens, consider a more efficient data structure for + * a large number of requests. */ + nm_assert (self->num_pending_auth_calls < 25); } /*****************************************************************************/ @@ -310,6 +407,7 @@ nm_auth_chain_new_context (GDBusMethodInvocation *context, NMAuthChain *chain; g_return_val_if_fail (context, NULL); + nm_assert (done_func); subject = nm_auth_subject_new_unix_process_from_context (context); if (!subject) @@ -323,7 +421,6 @@ nm_auth_chain_new_context (GDBusMethodInvocation *context, return chain; } -/* Requires an NMAuthSubject */ NMAuthChain * nm_auth_chain_new_subject (NMAuthSubject *subject, GDBusMethodInvocation *context, @@ -333,15 +430,20 @@ nm_auth_chain_new_subject (NMAuthSubject *subject, NMAuthChain *self; g_return_val_if_fail (NM_IS_AUTH_SUBJECT (subject), NULL); - nm_assert (nm_auth_subject_is_unix_process (subject) || nm_auth_subject_is_internal (subject)); - - self = g_slice_new0 (NMAuthChain); - c_list_init (&self->auth_call_lst_head); - self->refcount = 1; - self->done_func = done_func; - self->user_data = user_data; - self->context = context ? g_object_ref (context) : NULL; - self->subject = g_object_ref (subject); + nm_assert ( nm_auth_subject_is_unix_process (subject) + || nm_auth_subject_is_internal (subject)); + nm_assert (done_func); + + self = g_slice_new (NMAuthChain); + *self = (NMAuthChain) { + .done_func = done_func, + .user_data = user_data, + .context = nm_g_object_ref (context), + .subject = g_object_ref (subject), + .parent_lst = C_LIST_INIT (self->parent_lst), + .data_lst_head = C_LIST_INIT (self->data_lst_head), + .auth_call_lst_head = C_LIST_INIT (self->auth_call_lst_head), + }; return self; } @@ -352,30 +454,51 @@ nm_auth_chain_new_subject (NMAuthSubject *subject, * Destroys the auth-chain. By destroying the auth-chain, you also cancel * the receipt of the done-callback. IOW, the callback will not be invoked. * - * The only exception is, if may call nm_auth_chain_destroy() from inside - * the callback. In this case, @self stays alive until the callback returns. + * The only exception is, you may call nm_auth_chain_destroy() from inside + * the callback. In this case the call has no effect and @self stays alive + * until the callback returns. * * Note that you might only destroy an auth-chain exactly once, and never - * after the callback was handled. + * after the callback was handled. After the callback returns, the auth chain + * always gets automatically destroyed. So you only need to explicitly destroy + * it, if you want to abort it before the callback complets. */ void nm_auth_chain_destroy (NMAuthChain *self) { - AuthCall *call; - g_return_if_fail (self); - g_return_if_fail (NM_IN_SET (self->refcount, 1, 2)); + g_return_if_fail (!self->is_destroyed); - if (--self->refcount > 0) + self->is_destroyed = TRUE; + + if (self->is_finishing) { + /* we are called from inside the callback. Keep the instance alive for the moment. */ return; + } + + _auth_chain_destroy (self); +} + +static void +_auth_chain_destroy (NMAuthChain *self) +{ + AuthCall *call; + ChainData *chain_data; + + c_list_unlink (&self->parent_lst); nm_clear_g_object (&self->subject); nm_clear_g_object (&self->context); + /* we must first destry all AuthCall instances before ChainData. The reason is + * that AuthData.permission is not cloned and the lifetime of the string must + * be ensured by the caller. A sensible thing to do for the caller is attach the + * permission string via nm_auth_chain_set_data(). Hence, first free the AuthCall. */ while ((call = c_list_first_entry (&self->auth_call_lst_head, AuthCall, auth_call_lst))) auth_call_free (call); - nm_clear_pointer (&self->data_hash, g_hash_table_destroy); + while ((chain_data = c_list_first_entry (&self->data_lst_head, ChainData, data_lst))) + chain_data_free (chain_data); g_slice_free (NMAuthChain, self); } @@ -395,7 +518,8 @@ nm_auth_is_subject_in_acl (NMConnection *connection, g_return_val_if_fail (connection, FALSE); g_return_val_if_fail (NM_IS_AUTH_SUBJECT (subject), FALSE); - g_return_val_if_fail (nm_auth_subject_is_internal (subject) || nm_auth_subject_is_unix_process (subject), FALSE); + nm_assert ( nm_auth_subject_is_internal (subject) + || nm_auth_subject_is_unix_process (subject)); if (nm_auth_subject_is_internal (subject)) return TRUE; diff --git a/src/nm-auth-utils.h b/src/nm-auth-utils.h index 5f9823b6..5201d260 100644 --- a/src/nm-auth-utils.h +++ b/src/nm-auth-utils.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -25,10 +24,11 @@ #include "nm-auth-manager.h" +/*****************************************************************************/ + typedef struct NMAuthChain NMAuthChain; typedef void (*NMAuthChainResultFunc) (NMAuthChain *chain, - GError *error, GDBusMethodInvocation *context, gpointer user_data); @@ -45,22 +45,47 @@ gpointer nm_auth_chain_get_data (NMAuthChain *chain, const char *tag); gpointer nm_auth_chain_steal_data (NMAuthChain *chain, const char *tag); -void nm_auth_chain_set_data (NMAuthChain *chain, - const char *tag, - gpointer data, - GDestroyNotify data_destroy); +void nm_auth_chain_set_data_unsafe (NMAuthChain *chain, + const char *tag, + gpointer data, + GDestroyNotify data_destroy); + +#define nm_auth_chain_set_data(chain, tag, data, data_destroy) \ + nm_auth_chain_set_data_unsafe ((chain), ""tag"", (data), (data_destroy)) NMAuthCallResult nm_auth_chain_get_result (NMAuthChain *chain, const char *permission); -void nm_auth_chain_add_call (NMAuthChain *chain, - const char *permission, - gboolean allow_interaction); +void nm_auth_chain_add_call_unsafe (NMAuthChain *chain, + const char *permission, + gboolean allow_interaction); + +#define nm_auth_chain_add_call(chain, permission, allow_interaction) \ + nm_auth_chain_add_call_unsafe ((chain), ""permission"", (allow_interaction)) void nm_auth_chain_destroy (NMAuthChain *chain); NMAuthSubject *nm_auth_chain_get_subject (NMAuthChain *self); +/*****************************************************************************/ + +struct CList; + +static inline NMAuthChain * +nm_auth_chain_parent_lst_entry (struct CList *parent_lst_self) +{ + return (NMAuthChain *) ((void *) parent_lst_self); +} + +static inline struct CList * +nm_auth_chain_parent_lst_list (NMAuthChain *self) +{ + return (struct CList *) ((void *) self); +} + +/*****************************************************************************/ + + /* Caller must free returned error description */ gboolean nm_auth_is_subject_in_acl (NMConnection *connection, NMAuthSubject *subect, diff --git a/src/nm-checkpoint-manager.c b/src/nm-checkpoint-manager.c index 0c2af831..7c50d61f 100644 --- a/src/nm-checkpoint-manager.c +++ b/src/nm-checkpoint-manager.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/nm-checkpoint-manager.h b/src/nm-checkpoint-manager.h index ca66ef16..46590a0c 100644 --- a/src/nm-checkpoint-manager.h +++ b/src/nm-checkpoint-manager.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager * * This program is free software; you can redistribute it and/or modify diff --git a/src/nm-checkpoint.c b/src/nm-checkpoint.c index f715eea6..9bc979bb 100644 --- a/src/nm-checkpoint.c +++ b/src/nm-checkpoint.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -171,7 +170,7 @@ find_settings_connection (NMCheckpoint *self, *need_update = FALSE; uuid = nm_connection_get_uuid (dev_checkpoint->settings_connection); - sett_conn = nm_settings_get_connection_by_uuid (nm_settings_get (), uuid); + sett_conn = nm_settings_get_connection_by_uuid (NM_SETTINGS_GET, uuid); if (!sett_conn) return NULL; @@ -218,19 +217,35 @@ restore_and_activate_connection (NMCheckpoint *self, gs_unref_object NMAuthSubject *subject = NULL; GError *local_error = NULL; gboolean need_update, need_activation; + NMSettingsConnectionPersistMode persist_mode; + NMSettingsConnectionIntFlags sett_flags; + NMSettingsConnectionIntFlags sett_mask; connection = find_settings_connection (self, dev_checkpoint, &need_update, &need_activation); + + /* FIXME: we need to ensure to re-create/update the profile for the + * same settings plugin. E.g. if it was a keyfile in /run or /etc, + * it must be again. If it was previously handled by a certain settings plugin, + * so it must again. + * + * FIXME: preserve and restore the right settings flags (volatile, nm-generated). */ + sett_flags = NM_SETTINGS_CONNECTION_INT_FLAGS_NONE; + sett_mask = NM_SETTINGS_CONNECTION_INT_FLAGS_NONE; + if (connection) { if (need_update) { _LOGD ("rollback: updating connection %s", nm_settings_connection_get_uuid (connection)); + persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP; nm_settings_connection_update (connection, dev_checkpoint->settings_connection, - NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK, - NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, + persist_mode, + sett_flags, + sett_mask, + NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE, "checkpoint-rollback", NULL); } @@ -239,11 +254,14 @@ restore_and_activate_connection (NMCheckpoint *self, _LOGD ("rollback: adding connection %s again", nm_connection_get_uuid (dev_checkpoint->settings_connection)); - connection = nm_settings_add_connection (nm_settings_get (), - dev_checkpoint->settings_connection, - TRUE, - &local_error); - if (!connection) { + persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK; + if (!nm_settings_add_connection (NM_SETTINGS_GET, + dev_checkpoint->settings_connection, + persist_mode, + NM_SETTINGS_CONNECTION_ADD_REASON_NONE, + sett_flags, + &connection, + &local_error)) { _LOGD ("rollback: connection add failure: %s", local_error->message); g_clear_error (&local_error); return FALSE; @@ -419,7 +437,7 @@ next_dev: gs_free NMSettingsConnection **list = NULL; g_return_val_if_fail (priv->connection_uuids, NULL); - list = nm_settings_get_connections_clone (nm_settings_get (), NULL, + list = nm_settings_get_connections_clone (NM_SETTINGS_GET, NULL, NULL, NULL, nm_settings_connection_cmp_autoconnect_priority_p_with_data, NULL); @@ -429,7 +447,7 @@ next_dev: nm_settings_connection_get_uuid (con))) { _LOGD ("rollback: deleting new connection %s", nm_settings_connection_get_uuid (con)); - nm_settings_connection_delete (con, NULL); + nm_settings_connection_delete (con, FALSE); } } } @@ -687,7 +705,7 @@ nm_checkpoint_new (NMManager *manager, GPtrArray *devices, guint32 rollback_time if (NM_FLAGS_HAS (flags, NM_CHECKPOINT_CREATE_FLAG_DELETE_NEW_CONNECTIONS)) { priv->connection_uuids = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL); - for (con = nm_settings_get_connections (nm_settings_get (), NULL); *con; con++) { + for (con = nm_settings_get_connections (NM_SETTINGS_GET, NULL); *con; con++) { g_hash_table_add (priv->connection_uuids, g_strdup (nm_settings_connection_get_uuid (*con))); } diff --git a/src/nm-checkpoint.h b/src/nm-checkpoint.h index c8598f38..708d8dd6 100644 --- a/src/nm-checkpoint.h +++ b/src/nm-checkpoint.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/nm-config-data.c b/src/nm-config-data.c index 608b7e58..5655c8fd 100644 --- a/src/nm-config-data.c +++ b/src/nm-config-data.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -1504,16 +1503,6 @@ _match_section_infos_construct (GKeyFile *keyfile, const char *prefix) /*****************************************************************************/ -static gboolean -_slist_str_equals (GSList *a, GSList *b) -{ - while (a && b && g_strcmp0 (a->data, b->data) == 0) { - a = a->next; - b = b->next; - } - return !a && !b; -} - NMConfigChangeFlags nm_config_data_diff (NMConfigData *old_data, NMConfigData *new_data) { @@ -1542,8 +1531,8 @@ nm_config_data_diff (NMConfigData *old_data, NMConfigData *new_data) || g_strcmp0 (nm_config_data_get_connectivity_response (old_data), nm_config_data_get_connectivity_response (new_data))) changes |= NM_CONFIG_CHANGE_CONNECTIVITY; - if ( !_slist_str_equals (priv_old->no_auto_default.specs, priv_new->no_auto_default.specs) - || !_slist_str_equals (priv_old->no_auto_default.specs_config, priv_new->no_auto_default.specs_config)) + if ( nm_utils_g_slist_strlist_cmp (priv_old->no_auto_default.specs, priv_new->no_auto_default.specs) != 0 + || nm_utils_g_slist_strlist_cmp (priv_old->no_auto_default.specs_config, priv_new->no_auto_default.specs_config) != 0) changes |= NM_CONFIG_CHANGE_NO_AUTO_DEFAULT; if (g_strcmp0 (nm_config_data_get_dns_mode (old_data), nm_config_data_get_dns_mode (new_data))) @@ -1634,22 +1623,47 @@ set_property (GObject *object, case PROP_NO_AUTO_DEFAULT: /* construct-only */ { - char **value_arr = g_value_get_boxed (value); - guint i, j = 0; - - priv->no_auto_default.arr = g_new (char *, g_strv_length (value_arr) + 1); - priv->no_auto_default.specs = NULL; - - for (i = 0; value_arr && value_arr[i]; i++) { - if ( *value_arr[i] - && nm_utils_hwaddr_valid (value_arr[i], -1) - && nm_utils_strv_find_first (value_arr, i, value_arr[i]) < 0) { - priv->no_auto_default.arr[j++] = g_strdup (value_arr[i]); - priv->no_auto_default.specs = g_slist_prepend (priv->no_auto_default.specs, g_strdup_printf ("mac:%s", value_arr[i])); + const char *const*value_arr_orig = g_value_get_boxed (value); + gs_free const char **value_arr = NULL; + GSList *specs = NULL; + gsize i, j; + gsize len; + + len = NM_PTRARRAY_LEN (value_arr_orig); + + /* sort entries, remove duplicates and empty words. */ + value_arr = len == 0 + ? NULL + : nm_memdup (value_arr_orig, sizeof (const char *) * (len + 1)); + nm_utils_strv_sort (value_arr, len); + _nm_utils_strv_cleanup ((char **) value_arr, FALSE, TRUE, TRUE); + + len = NM_PTRARRAY_LEN (value_arr); + j = 0; + for (i = 0; i < len; i++) { + const char *s = value_arr[i]; + gboolean is_mac; + char *spec; + + if (NM_STR_HAS_PREFIX (s, NM_MATCH_SPEC_INTERFACE_NAME_TAG"=")) + is_mac = FALSE; + else if (nm_utils_hwaddr_valid (s, -1)) + is_mac = TRUE; + else { + /* we drop all lines that we don't understand. */ + continue; } + + value_arr[j++] = s; + + spec = is_mac + ? g_strdup_printf (NM_MATCH_SPEC_MAC_TAG"%s", s) + : g_strdup (s); + specs = g_slist_prepend (specs, spec); } - priv->no_auto_default.arr[j++] = NULL; - priv->no_auto_default.specs = g_slist_reverse (priv->no_auto_default.specs); + + priv->no_auto_default.arr = nm_utils_strv_dup (value_arr, j); + priv->no_auto_default.specs = g_slist_reverse (specs); } break; default: diff --git a/src/nm-config-data.h b/src/nm-config-data.h index c043aa35..8f925a6c 100644 --- a/src/nm-config-data.h +++ b/src/nm-config-data.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/nm-config.c b/src/nm-config.c index 3e82bdec..d1279814 100644 --- a/src/nm-config.c +++ b/src/nm-config.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -67,7 +66,7 @@ struct NMConfigCmdLineOptions { * * It is true, if NM is started the first time -- contrary to a restart * during the same boot up. That is determined by the content of the - * /var/run/NetworManager state directory. */ + * /run/NetworManager state directory. */ bool first_start; }; @@ -100,8 +99,6 @@ typedef struct { char *no_auto_default_file; char *intern_config_file; - gboolean monitor_connection_files; - char *log_level; char *log_domains; @@ -313,14 +310,6 @@ nm_config_get_data_orig (NMConfig *config) return NM_CONFIG_GET_PRIVATE (config)->config_data_orig; } -gboolean -nm_config_get_monitor_connection_files (NMConfig *config) -{ - g_return_val_if_fail (config != NULL, FALSE); - - return NM_CONFIG_GET_PRIVATE (config)->monitor_connection_files; -} - const char * nm_config_get_log_level (NMConfig *config) { @@ -360,47 +349,44 @@ nm_config_get_first_start (NMConfig *config) static char ** no_auto_default_from_file (const char *no_auto_default_file) { - GPtrArray *no_auto_default_new; - char **list; - guint i; - char *data; - - no_auto_default_new = g_ptr_array_new (); + gs_free char *data = NULL; + const char **list = NULL; + gsize i; if ( no_auto_default_file - && g_file_get_contents (no_auto_default_file, &data, NULL, NULL)) { - list = g_strsplit (data, "\n", -1); - for (i = 0; list[i]; i++) { - if ( *list[i] - && nm_utils_hwaddr_valid (list[i], -1) - && nm_utils_strv_find_first (list, i, list[i]) < 0) - g_ptr_array_add (no_auto_default_new, list[i]); - else - g_free (list[i]); - } - g_free (list); - g_free (data); + && g_file_get_contents (no_auto_default_file, &data, NULL, NULL)) + list = nm_utils_strsplit_set (data, "\n"); + + if (list) { + for (i = 0; list[i]; i++) + list[i] = nm_utils_str_utf8safe_unescape_cp (list[i]); } - g_ptr_array_add (no_auto_default_new, NULL); - return (char **) g_ptr_array_free (no_auto_default_new, FALSE); + /* The returned buffer here is not at all compact. That means, it has additional + * memory allocations and is larger than needed. That means, you should not keep + * this result around, only process it further and free it. */ + return (char **) list; } static gboolean no_auto_default_to_file (const char *no_auto_default_file, const char *const*no_auto_default, GError **error) { - GString *data; - gboolean success; - guint i; + nm_auto_free_gstring GString *data = NULL; + gsize i; data = g_string_new (""); for (i = 0; no_auto_default && no_auto_default[i]; i++) { - g_string_append (data, no_auto_default[i]); + gs_free char *s_to_free = NULL; + const char *s = no_auto_default[i]; + + s = nm_utils_str_utf8safe_escape (s, + NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL + | NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII, + &s_to_free); + g_string_append (data, s); g_string_append_c (data, '\n'); } - success = g_file_set_contents (no_auto_default_file, data->str, data->len, error); - g_string_free (data, TRUE); - return success; + return g_file_set_contents (no_auto_default_file, data->str, data->len, error); } gboolean @@ -424,44 +410,76 @@ nm_config_set_no_auto_default_for_device (NMConfig *self, NMDevice *device) NMConfigPrivate *priv; GError *error = NULL; NMConfigData *new_data = NULL; + gs_free char *spec_to_free = NULL; + const char *ifname; const char *hw_address; + const char *spec; const char *const*no_auto_default_current; - GPtrArray *no_auto_default_new = NULL; - guint i; + gs_free const char **no_auto_default_new = NULL; + gboolean is_fake; + gsize len; + gssize idx; g_return_if_fail (NM_IS_CONFIG (self)); g_return_if_fail (NM_IS_DEVICE (device)); priv = NM_CONFIG_GET_PRIVATE (self); - hw_address = nm_device_get_permanent_hw_address (device); - if (!hw_address) + hw_address = nm_device_get_permanent_hw_address_full (device, TRUE, &is_fake); + + if (!hw_address) { + /* No MAC address, not even a fake one. We don't do anything for this device. */ return; + } + + if (is_fake) { + /* A fake MAC address, no point in storing it to the file. + * Also, nm_match_spec_device() would ignore fake MAC addresses. + * + * Instead, try the interface-name... */ + ifname = nm_device_get_ip_iface (device); + if (!nm_utils_is_valid_iface_name (ifname, NULL)) + return; + + spec_to_free = g_strdup_printf (NM_MATCH_SPEC_INTERFACE_NAME_TAG"=%s", ifname); + spec = spec_to_free; + } else + spec = hw_address; no_auto_default_current = nm_config_data_get_no_auto_default (priv->config_data); - if (nm_utils_strv_find_first ((char **) no_auto_default_current, -1, hw_address) >= 0) { - /* @hw_address is already blocked. We don't have to update our in-memory representation. + len = NM_PTRARRAY_LEN (no_auto_default_current); + + idx = nm_utils_ptrarray_find_binary_search ((gconstpointer *) no_auto_default_current, + len, + spec, + nm_strcmp_with_data, + NULL, + NULL, + NULL); + if (idx >= 0) { + /* @spec is already blocked. We don't have to update our in-memory representation. * Maybe we should write to no_auto_default_file anew, but let's save that too. */ return; } - no_auto_default_new = g_ptr_array_new (); - for (i = 0; no_auto_default_current && no_auto_default_current[i]; i++) - g_ptr_array_add (no_auto_default_new, (char *) no_auto_default_current[i]); - g_ptr_array_add (no_auto_default_new, (char *) hw_address); - g_ptr_array_add (no_auto_default_new, NULL); + idx = ~idx; + + no_auto_default_new = g_new (const char *, len + 2); + if (idx > 0) + memcpy (no_auto_default_new, no_auto_default_current, sizeof (const char *) * idx); + no_auto_default_new[idx] = spec; + if (idx < len) + memcpy (&no_auto_default_new[idx + 1], &no_auto_default_current[idx], sizeof (const char *) * (len - idx)); + no_auto_default_new[len + 1] = NULL; - if (!no_auto_default_to_file (priv->no_auto_default_file, (const char *const*) no_auto_default_new->pdata, &error)) { + if (!no_auto_default_to_file (priv->no_auto_default_file, no_auto_default_new, &error)) { _LOGW ("Could not update no-auto-default.state file: %s", error->message); g_error_free (error); } - new_data = nm_config_data_new_update_no_auto_default (priv->config_data, (const char *const*) no_auto_default_new->pdata); - - /* unref no_auto_default_set here. Note that _set_config_data() probably invalidates the content of the array. */ - g_ptr_array_unref (no_auto_default_new); + new_data = nm_config_data_new_update_no_auto_default (priv->config_data, no_auto_default_new); _set_config_data (self, new_data, NM_CONFIG_CHANGE_CAUSE_NO_AUTO_DEFAULT); } @@ -2187,6 +2205,7 @@ _nm_config_state_set (NMConfig *self, #define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT_ASPIRED "route-metric-default-aspired" #define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT_EFFECTIVE "route-metric-default-effective" #define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROOT_PATH "root-path" +#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_NEXT_SERVER "next-server" NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_device_state_managed_type_to_str, NMConfigDeviceStateManagedType, NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT ("unknown"), @@ -2385,6 +2404,7 @@ nm_config_device_state_write (int ifindex, int nm_owned, guint32 route_metric_default_aspired, guint32 route_metric_default_effective, + const char *next_server, const char *root_path) { char path[NM_STRLEN (NM_CONFIG_DEVICE_STATE_DIR) + 60]; @@ -2439,6 +2459,12 @@ nm_config_device_state_write (int ifindex, route_metric_default_aspired); } } + if (next_server) { + g_key_file_set_string (kf, + DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, + DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_NEXT_SERVER, + next_server); + } if (root_path) { g_key_file_set_string (kf, DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, @@ -2451,13 +2477,14 @@ nm_config_device_state_write (int ifindex, g_error_free (local); return FALSE; } - _LOGT ("device-state: write #%d (%s); managed=%s%s%s%s%s%s%s, route-metric-default=%"G_GUINT32_FORMAT"-%"G_GUINT32_FORMAT"%s%s%s", + _LOGT ("device-state: write #%d (%s); managed=%s%s%s%s%s%s%s, route-metric-default=%"G_GUINT32_FORMAT"-%"G_GUINT32_FORMAT"%s%s%s%s%s%s", ifindex, path, _device_state_managed_type_to_str (managed), NM_PRINT_FMT_QUOTED (connection_uuid, ", connection-uuid=", connection_uuid, "", ""), NM_PRINT_FMT_QUOTED (perm_hw_addr_fake, ", perm-hw-addr-fake=", perm_hw_addr_fake, "", ""), route_metric_default_aspired, route_metric_default_effective, + NM_PRINT_FMT_QUOTED (next_server, ", next-server=", next_server, "", ""), NM_PRINT_FMT_QUOTED (root_path, ", root-path=", root_path, "", "")); return TRUE; } @@ -2709,7 +2736,8 @@ nm_config_setup (const NMConfigCmdLineOptions *cli, char **atomic_section_prefix /* usually, you would not see this logging line because when creating the * NMConfig instance, the logging is not yet set up to print debug message. */ - nm_log_dbg (LOGD_CORE, "setup %s singleton (%p)", "NMConfig", singleton_instance); + nm_log_dbg (LOGD_CORE, "setup %s singleton ("NM_HASH_OBFUSCATE_PTR_FMT")", + "NMConfig", NM_HASH_OBFUSCATE_PTR (singleton_instance)); } return singleton_instance; } @@ -2803,10 +2831,6 @@ init_sync (GInitable *initable, GCancellable *cancellable, GError **error) 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, - 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, diff --git a/src/nm-config.h b/src/nm-config.h index 66f1b69c..32ce236f 100644 --- a/src/nm-config.h +++ b/src/nm-config.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -157,7 +156,6 @@ NMConfigData *nm_config_get_data_orig (NMConfig *config); #define NM_CONFIG_GET_DATA (nm_config_get_data (nm_config_get ())) #define NM_CONFIG_GET_DATA_ORIG (nm_config_get_data_orig (nm_config_get ())) -gboolean nm_config_get_monitor_connection_files (NMConfig *config); const char *nm_config_get_log_level (NMConfig *config); const char *nm_config_get_log_domains (NMConfig *config); NMConfigConfigureAndQuitType nm_config_get_configure_and_quit (NMConfig *config); @@ -270,6 +268,7 @@ gboolean nm_config_device_state_write (int ifindex, int nm_owned, guint32 route_metric_default_aspired, guint32 route_metric_default_effective, + const char *next_server, const char *root_path); void nm_config_device_state_prune_unseen (GHashTable *seen_ifindexes); diff --git a/src/nm-connectivity.c b/src/nm-connectivity.c index 2816e76a..694733f6 100644 --- a/src/nm-connectivity.c +++ b/src/nm-connectivity.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -27,6 +26,7 @@ #if WITH_CONCHECK #include <curl/curl.h> #endif +#include <linux/rtnetlink.h> #include "c-list/src/c-list.h" #include "nm-core-internal.h" @@ -104,8 +104,7 @@ struct _NMConnectivityCheckHandle { guint timeout_id; NMConnectivityState completed_state; - - bool fail_reason_no_dbus_connection:1; + const char *completed_reason; }; enum { @@ -657,23 +656,10 @@ _idle_cb (gpointer user_data) nm_assert (NM_IS_CONNECTIVITY (cb_data->self)); nm_assert (c_list_contains (&NM_CONNECTIVITY_GET_PRIVATE (cb_data->self)->handles_lst_head, &cb_data->handles_lst)); + nm_assert (cb_data->completed_reason); cb_data->timeout_id = 0; - if (!cb_data->ifspec) { - gs_free_error GError *error = NULL; - - /* the invocation was with an invalid ifname. It is a fail. */ - 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"); + cb_data_complete (cb_data, cb_data->completed_state, cb_data->completed_reason); return G_SOURCE_REMOVE; } @@ -800,9 +786,78 @@ resolve_cb (GObject *object, GAsyncResult *res, gpointer user_data) #define SD_RESOLVED_DNS ((guint64) (1LL << 0)) +static NMConnectivityState +check_platform_config (NMConnectivity *self, + NMPlatform *platform, + int ifindex, + int addr_family, + const char **reason) +{ + const NMDedupMultiHeadEntry *addresses; + const NMDedupMultiHeadEntry *routes; + + if (!nm_platform_link_is_connected (platform, ifindex)) { + NM_SET_OUT (reason, "no carrier"); + return NM_CONNECTIVITY_NONE; + } + + addresses = nm_platform_lookup_object (platform, + addr_family == AF_INET + ? NMP_OBJECT_TYPE_IP4_ADDRESS + : NMP_OBJECT_TYPE_IP6_ADDRESS, + ifindex); + if (!addresses || addresses->len == 0) { + NM_SET_OUT (reason, "no IP address configured"); + return NM_CONNECTIVITY_NONE; + } + + routes = nm_platform_lookup_object (platform, + addr_family == AF_INET + ? NMP_OBJECT_TYPE_IP4_ROUTE + : NMP_OBJECT_TYPE_IP6_ROUTE, + ifindex); + if (!routes || routes->len == 0) { + NM_SET_OUT (reason, "no IP route configured"); + return NM_CONNECTIVITY_NONE; + } + + switch (addr_family) { + case AF_INET: { + const NMPlatformIP4Route *route; + gboolean found_global = FALSE; + NMDedupMultiIter iter; + const NMPObject *plobj; + + /* For IPv4 also require a route with global scope. */ + nmp_cache_iter_for_each (&iter, routes, &plobj) { + route = NMP_OBJECT_CAST_IP4_ROUTE (plobj); + if (nm_platform_route_scope_inv (route->scope_inv) == RT_SCOPE_UNIVERSE) { + found_global = TRUE; + break; + } + } + + if (!found_global) { + NM_SET_OUT (reason, "no global route configured"); + return NM_CONNECTIVITY_LIMITED; + } + break; + } + case AF_INET6: + /* Route scopes aren't meaningful for IPv6 so any route is fine. */ + break; + default: + g_return_val_if_reached (FALSE); + } + + NM_SET_OUT (reason, NULL); + return NM_CONNECTIVITY_UNKNOWN; +} + NMConnectivityCheckHandle * nm_connectivity_check_start (NMConnectivity *self, int addr_family, + NMPlatform *platform, int ifindex, const char *iface, NMConnectivityCheckCallback callback, @@ -814,6 +869,7 @@ nm_connectivity_check_start (NMConnectivity *self, g_return_val_if_fail (NM_IS_CONNECTIVITY (self), NULL); g_return_val_if_fail (callback, NULL); + nm_assert (!platform || NM_IS_PLATFORM (platform)); priv = NM_CONNECTIVITY_GET_PRIVATE (self); @@ -837,9 +893,27 @@ nm_connectivity_check_start (NMConnectivity *self, && priv->enabled && priv->uri_valid) { gboolean has_systemd_resolved; + NMConnectivityState state; + const char *reason; cb_data->concheck.ch_ifindex = ifindex; + if (platform) { + state = check_platform_config (self, + platform, + ifindex, + addr_family, + &reason); + nm_assert ((state == NM_CONNECTIVITY_UNKNOWN) == !reason); + if (state != NM_CONNECTIVITY_UNKNOWN) { + _LOG2D ("skip connectivity check due to %s", reason); + cb_data->completed_state = state; + cb_data->completed_reason = reason; + cb_data->timeout_id = g_idle_add (_idle_cb, cb_data); + return cb_data; + } + } + /* 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. @@ -863,20 +937,21 @@ nm_connectivity_check_start (NMConnectivity *self, if (has_systemd_resolved) { GDBusConnection *dbus_connection; - dbus_connection = nm_dbus_manager_get_dbus_connection (nm_dbus_manager_get ()); + dbus_connection = NM_MAIN_DBUS_CONNECTION_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->completed_state = NM_CONNECTIVITY_ERROR; + cb_data->completed_reason = "no D-Bus connection"; 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 ()), + g_dbus_connection_call (dbus_connection, "org.freedesktop.resolve1", "/org/freedesktop/resolve1", "org.freedesktop.resolve1.Manager", @@ -905,7 +980,14 @@ nm_connectivity_check_start (NMConnectivity *self, } #endif - _LOG2D ("start fake request"); + if (!cb_data->ifspec) { + cb_data->completed_state = NM_CONNECTIVITY_ERROR; + cb_data->completed_reason = "missing interface"; + } else { + cb_data->completed_state = NM_CONNECTIVITY_FAKE; + cb_data->completed_reason = "fake result"; + } + _LOG2D ("start fake request (%s)", cb_data->completed_reason); cb_data->timeout_id = g_idle_add (_idle_cb, cb_data); return cb_data; diff --git a/src/nm-connectivity.h b/src/nm-connectivity.h index f262298a..00d0e642 100644 --- a/src/nm-connectivity.h +++ b/src/nm-connectivity.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -74,6 +73,7 @@ typedef void (*NMConnectivityCheckCallback) (NMConnectivity *self, NMConnectivityCheckHandle *nm_connectivity_check_start (NMConnectivity *self, int family, + NMPlatform *platform, int ifindex, const char *iface, NMConnectivityCheckCallback callback, diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c index b0cc914e..d896d4d3 100644 --- a/src/nm-core-utils.c +++ b/src/nm-core-utils.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -41,6 +40,7 @@ #include "nm-glib-aux/nm-random-utils.h" #include "nm-glib-aux/nm-io-utils.h" #include "nm-glib-aux/nm-secret-utils.h" +#include "nm-glib-aux/nm-time-utils.h" #include "nm-utils.h" #include "nm-core-internal.h" #include "nm-setting-connection.h" @@ -127,6 +127,8 @@ static void _nm_singleton_instance_weak_cb (gpointer data, GObject *where_the_object_was) { + nm_assert (g_slist_find (_singletons, where_the_object_was)); + _singletons = g_slist_remove (_singletons, where_the_object_was); } @@ -142,8 +144,10 @@ _nm_singleton_instance_destroy (void) g_object_weak_unref (instance, _nm_singleton_instance_weak_cb, NULL); - if (instance->ref_count > 1) - nm_log_dbg (LOGD_CORE, "disown %s singleton (%p)", G_OBJECT_TYPE_NAME (instance), instance); + if (instance->ref_count > 1) { + nm_log_dbg (LOGD_CORE, "disown %s singleton ("NM_HASH_OBFUSCATE_PTR_FMT")", + G_OBJECT_TYPE_NAME (instance), NM_HASH_OBFUSCATE_PTR (instance)); + } g_object_unref (instance); } @@ -1154,13 +1158,10 @@ nm_utils_read_link_absolute (const char *link_file, GError **error) /*****************************************************************************/ -#define MAC_TAG "mac:" -#define INTERFACE_NAME_TAG "interface-name:" -#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 DEVICE_TYPE_TAG "type:" +#define DRIVER_TAG "driver:" +#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:" #define MATCH_TAG_CONFIG_NM_VERSION_MAX "nm-version-max:" @@ -1359,10 +1360,10 @@ match_device_eval (const char *spec_str, && nm_streq (spec_str, match_data->device_type); } - if (_MATCH_CHECK (spec_str, MAC_TAG)) + if (_MATCH_CHECK (spec_str, NM_MATCH_SPEC_MAC_TAG)) return match_device_hwaddr_eval (spec_str, match_data); - if (_MATCH_CHECK (spec_str, INTERFACE_NAME_TAG)) { + if (_MATCH_CHECK (spec_str, NM_MATCH_SPEC_INTERFACE_NAME_TAG)) { gboolean use_pattern = FALSE; if (spec_str[0] == '=') @@ -1414,7 +1415,7 @@ match_device_eval (const char *spec_str, match_data->driver_version ?: ""); } - if (_MATCH_CHECK (spec_str, SUBCHAN_TAG)) + if (_MATCH_CHECK (spec_str, NM_MATCH_SPEC_S390_SUBCHANNELS_TAG)) return match_data_s390_subchannels_eval (spec_str, match_data); if (_MATCH_CHECK (spec_str, DHCP_PLUGIN_TAG)) @@ -2543,7 +2544,7 @@ _host_id_read_timestamp (gboolean 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; + *out_timestamp_ns = nm_utils_timespec_to_ns (&st.st_mtim); return TRUE; } @@ -2697,7 +2698,7 @@ _host_id_read (guint8 **out_host_id, } else if (!nm_utils_file_set_contents (SECRET_KEY_FILE, (const char *) new_content, len, - 0077, + 0600, &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); @@ -3813,13 +3814,22 @@ nm_utils_parse_debug_string (const char *string, void nm_utils_ifname_cpy (char *dst, const char *name) { + int i; + g_return_if_fail (dst); g_return_if_fail (name && name[0]); nm_assert (nm_utils_is_valid_iface_name (name, NULL)); - if (g_strlcpy (dst, name, IFNAMSIZ) >= IFNAMSIZ) - g_return_if_reached (); + /* ensures NUL padding of the entire IFNAMSIZ buffer. */ + + for (i = 0; i < (int) IFNAMSIZ && name[i] != '\0'; i++) + dst[i] = name[i]; + + nm_assert (name[i] == '\0'); + + for (; i < (int) IFNAMSIZ; i++) + dst[i] = '\0'; } /*****************************************************************************/ diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h index 1f9996ca..855a5d08 100644 --- a/src/nm-core-utils.h +++ b/src/nm-core-utils.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -42,7 +41,9 @@ static void \ _singleton_instance_weak_ref_cb (gpointer data, \ GObject *where_the_object_was) \ { \ - nm_log_dbg (LOGD_CORE, "disposing %s singleton (%p)", G_STRINGIFY (TYPE), singleton_instance); \ + nm_log_dbg (LOGD_CORE, "disposing %s singleton ("NM_HASH_OBFUSCATE_PTR_FMT")", \ + G_STRINGIFY (TYPE), \ + NM_HASH_OBFUSCATE_PTR (singleton_instance)); \ singleton_instance = NULL; \ } \ static inline void \ @@ -73,7 +74,9 @@ GETTER (void) \ 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); \ + nm_log_dbg (LOGD_CORE, "create %s singleton ("NM_HASH_OBFUSCATE_PTR_FMT")", \ + G_STRINGIFY (TYPE), \ + NM_HASH_OBFUSCATE_PTR (singleton_instance)); \ } \ return singleton_instance; \ } \ @@ -213,6 +216,10 @@ const char *nm_utils_find_helper (const char *progname, char *nm_utils_read_link_absolute (const char *link_file, GError **error); +#define NM_MATCH_SPEC_MAC_TAG "mac:" +#define NM_MATCH_SPEC_S390_SUBCHANNELS_TAG "s390-subchannels:" +#define NM_MATCH_SPEC_INTERFACE_NAME_TAG "interface-name:" + typedef enum { NM_MATCH_SPEC_NO_MATCH = 0, NM_MATCH_SPEC_MATCH = 1, diff --git a/src/nm-dbus-manager.c b/src/nm-dbus-manager.c index 1a455c42..a14ea120 100644 --- a/src/nm-dbus-manager.c +++ b/src/nm-dbus-manager.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -471,6 +470,9 @@ _bus_get_unix_pid (NMDBusManager *self, guint32 unix_pid = G_MAXUINT32; gs_unref_variant GVariant *ret = NULL; + if (!priv->main_dbus_connection) + return FALSE; + ret = g_dbus_connection_call_sync (priv->main_dbus_connection, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, @@ -500,6 +502,9 @@ _bus_get_unix_user (NMDBusManager *self, guint32 unix_uid = G_MAXUINT32; gs_unref_variant GVariant *ret = NULL; + if (!priv->main_dbus_connection) + return FALSE; + ret = g_dbus_connection_call_sync (priv->main_dbus_connection, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, @@ -1024,6 +1029,7 @@ _obj_register (NMDBusManager *self, nm_assert (c_list_is_empty (&obj->internal.registration_lst_head)); nm_assert (priv->main_dbus_connection); + nm_assert (priv->objmgr_registration_id != 0); nm_assert (priv->started); n_klasses = 0; @@ -1119,15 +1125,10 @@ _obj_unregister (NMDBusManager *self, GVariantBuilder builder; nm_assert (NM_IS_DBUS_OBJECT (obj)); - - if (!priv->main_dbus_connection) { - /* nothing to do for the moment. */ - nm_assert (c_list_is_empty (&obj->internal.registration_lst_head)); - return; - } - + nm_assert (priv->main_dbus_connection); + nm_assert (priv->objmgr_registration_id != 0); + nm_assert (priv->started); nm_assert (!c_list_is_empty (&obj->internal.registration_lst_head)); - nm_assert (priv->objmgr_registration_id); g_variant_builder_init (&builder, G_VARIANT_TYPE ("as")); @@ -1202,7 +1203,7 @@ _nm_dbus_manager_obj_export (NMDBusObject *obj) nm_assert_not_reached (); c_list_link_tail (&priv->objects_lst_head, &obj->internal.objects_lst); - if (priv->main_dbus_connection && priv->started) + if (priv->started) _obj_register (self, obj); } @@ -1223,7 +1224,10 @@ _nm_dbus_manager_obj_unexport (NMDBusObject *obj) nm_assert (&obj->internal == g_hash_table_lookup (priv->objects_by_path, &obj->internal)); nm_assert (c_list_contains (&priv->objects_lst_head, &obj->internal.objects_lst)); - _obj_unregister (self, obj); + if (priv->started) + _obj_unregister (self, obj); + else + nm_assert (c_list_is_empty (&obj->internal.registration_lst_head)); if (!g_hash_table_remove (priv->objects_by_path, &obj->internal)) nm_assert_not_reached (); @@ -1249,6 +1253,16 @@ _nm_dbus_manager_obj_notify (NMDBusObject *obj, nm_assert (NM_IS_DBUS_MANAGER (obj->internal.bus_manager)); nm_assert (!c_list_is_empty (&obj->internal.objects_lst)); + self = obj->internal.bus_manager; + priv = NM_DBUS_MANAGER_GET_PRIVATE (self); + + nm_assert (!priv->started || priv->objmgr_registration_id != 0); + nm_assert (priv->objmgr_registration_id == 0 || priv->main_dbus_connection); + nm_assert (c_list_is_empty (&obj->internal.registration_lst_head) != priv->started); + + if (G_UNLIKELY (!priv->started)) + return; + c_list_for_each_entry (reg_data, &obj->internal.registration_lst_head, registration_lst) { if (_reg_data_get_interface_info (reg_data)->legacy_property_changed) { any_legacy_signals = TRUE; @@ -1256,9 +1270,6 @@ _nm_dbus_manager_obj_notify (NMDBusObject *obj, } } - self = obj->internal.bus_manager; - priv = NM_DBUS_MANAGER_GET_PRIVATE (self); - /* do a naive search for the matching NMDBusPropertyInfoExtended infos. Since the number of * (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. @@ -1401,7 +1412,7 @@ _nm_dbus_manager_obj_emit_signal (NMDBusObject *obj, self = obj->internal.bus_manager; priv = NM_DBUS_MANAGER_GET_PRIVATE (self); - if (!priv->main_dbus_connection || !priv->started) { + if (!priv->started) { nm_g_variant_unref_floating (args); return; } @@ -1565,9 +1576,12 @@ nm_dbus_manager_start (NMDBusManager *self, NMDBusObject *obj; g_return_if_fail (NM_IS_DBUS_MANAGER (self)); + priv = NM_DBUS_MANAGER_GET_PRIVATE (self); - if (!priv->main_dbus_connection) { + nm_assert (!priv->started); + + if (priv->objmgr_registration_id == 0) { /* Do nothing. We're presumably in the configure-and-quit mode. */ return; } @@ -1581,12 +1595,12 @@ nm_dbus_manager_start (NMDBusManager *self, } gboolean -nm_dbus_manager_acquire_bus (NMDBusManager *self) +nm_dbus_manager_acquire_bus (NMDBusManager *self, + gboolean request_name) { NMDBusManagerPrivate *priv; gs_free_error GError *error = NULL; gs_unref_variant GVariant *ret = NULL; - gs_unref_object GDBusConnection *connection = NULL; guint32 result; guint registration_id; @@ -1599,17 +1613,22 @@ nm_dbus_manager_acquire_bus (NMDBusManager *self) * acquire the name despite connecting to the bus successfully. * It means that something is gravely broken -- such as another NetworkManager * instance running. */ - connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, - NULL, - &error); - if (!connection) { - _LOGI ("cannot connect to D-Bus: %s", error->message); + priv->main_dbus_connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, + NULL, + &error); + if (!priv->main_dbus_connection) { + _LOGE ("cannot connect to D-Bus: %s", error->message); return FALSE; } - g_dbus_connection_set_exit_on_close (connection, FALSE); + g_dbus_connection_set_exit_on_close (priv->main_dbus_connection, FALSE); + + if (!request_name) { + _LOGD ("D-Bus connection created"); + return TRUE; + } - registration_id = g_dbus_connection_register_object (connection, + registration_id = g_dbus_connection_register_object (priv->main_dbus_connection, OBJECT_MANAGER_SERVER_BASE_PATH, NM_UNCONST_PTR (GDBusInterfaceInfo, &interface_info_objmgr), &dbus_vtable_objmgr, @@ -1621,7 +1640,7 @@ nm_dbus_manager_acquire_bus (NMDBusManager *self) return FALSE; } - ret = g_dbus_connection_call_sync (connection, + ret = g_dbus_connection_call_sync (priv->main_dbus_connection, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, @@ -1634,13 +1653,10 @@ nm_dbus_manager_acquire_bus (NMDBusManager *self) -1, NULL, &error); - if (!ret) - return FALSE; - if (!ret) { _LOGE ("fatal failure to acquire D-Bus service \"%s"": %s", NM_DBUS_SERVICE, error->message); - g_dbus_connection_unregister_object(connection, registration_id); + g_dbus_connection_unregister_object (priv->main_dbus_connection, registration_id); return FALSE; } @@ -1648,12 +1664,11 @@ nm_dbus_manager_acquire_bus (NMDBusManager *self) if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { _LOGE ("fatal failure to acquire D-Bus service \"%s\" (%u). Service already taken", NM_DBUS_SERVICE, (guint) result); - g_dbus_connection_unregister_object(connection, registration_id); + g_dbus_connection_unregister_object (priv->main_dbus_connection, registration_id); return FALSE; } priv->objmgr_registration_id = registration_id; - priv->main_dbus_connection = g_steal_pointer (&connection); _LOGI ("acquired D-Bus service \"%s\"", NM_DBUS_SERVICE); @@ -1690,6 +1705,7 @@ nm_dbus_manager_init (NMDBusManager *self) c_list_init (&priv->private_servers_lst_head); c_list_init (&priv->objects_lst_head); + priv->objects_by_path = g_hash_table_new ((GHashFunc) _objects_by_path_hash, (GEqualFunc) _objects_by_path_equal); c_list_init (&priv->caller_info_lst_head); diff --git a/src/nm-dbus-manager.h b/src/nm-dbus-manager.h index 2de6ff0b..c4a99563 100644 --- a/src/nm-dbus-manager.h +++ b/src/nm-dbus-manager.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -49,10 +48,13 @@ typedef void (*NMDBusManagerSetPropertyHandler) (NMDBusObject *obj, GVariant *value, gpointer user_data); -gboolean nm_dbus_manager_acquire_bus (NMDBusManager *self); +gboolean nm_dbus_manager_acquire_bus (NMDBusManager *self, + gboolean request_name); GDBusConnection *nm_dbus_manager_get_dbus_connection (NMDBusManager *self); +#define NM_MAIN_DBUS_CONNECTION_GET (nm_dbus_manager_get_dbus_connection (nm_dbus_manager_get ())) + void nm_dbus_manager_start (NMDBusManager *self, NMDBusManagerSetPropertyHandler set_property_handler, gpointer set_property_handler_data); diff --git a/src/nm-dbus-object.c b/src/nm-dbus-object.c index e92a8f5f..abf1aa57 100644 --- a/src/nm-dbus-object.c +++ b/src/nm-dbus-object.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/nm-dbus-object.h b/src/nm-dbus-object.h index 8b3ffdbd..574107a1 100644 --- a/src/nm-dbus-object.h +++ b/src/nm-dbus-object.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/nm-dbus-utils.c b/src/nm-dbus-utils.c index 7b9c56b6..fe3eff7a 100644 --- a/src/nm-dbus-utils.c +++ b/src/nm-dbus-utils.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/nm-dbus-utils.h b/src/nm-dbus-utils.h index 1f8e96c4..cf372e88 100644 --- a/src/nm-dbus-utils.h +++ b/src/nm-dbus-utils.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/nm-dcb.c b/src/nm-dcb.c index fdcdd8dc..d6a9946b 100644 --- a/src/nm-dcb.c +++ b/src/nm-dcb.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/nm-dcb.h b/src/nm-dcb.h index 8bc08470..425e3e23 100644 --- a/src/nm-dcb.h +++ b/src/nm-dcb.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/nm-dhcp4-config.c b/src/nm-dhcp4-config.c index fe0df3a7..8390a654 100644 --- a/src/nm-dhcp4-config.c +++ b/src/nm-dhcp4-config.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/nm-dhcp4-config.h b/src/nm-dhcp4-config.h index 7fdf0653..9853f4ff 100644 --- a/src/nm-dhcp4-config.h +++ b/src/nm-dhcp4-config.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/nm-dhcp6-config.c b/src/nm-dhcp6-config.c index 59266d55..5bbc20ef 100644 --- a/src/nm-dhcp6-config.c +++ b/src/nm-dhcp6-config.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/nm-dhcp6-config.h b/src/nm-dhcp6-config.h index 6241845a..ffe7b18d 100644 --- a/src/nm-dhcp6-config.h +++ b/src/nm-dhcp6-config.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/nm-dispatcher.c b/src/nm-dispatcher.c index d656edc8..652b9838 100644 --- a/src/nm-dispatcher.c +++ b/src/nm-dispatcher.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -15,7 +14,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2004 - 2017 Red Hat, Inc. + * Copyright (C) 2004 - 2018 Red Hat, Inc. * Copyright (C) 2005 - 2008 Novell, Inc. */ @@ -44,45 +43,122 @@ #define _NMLOG_DOMAIN LOGD_DISPATCH #define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "dispatcher", __VA_ARGS__) -static GDBusProxy *dispatcher_proxy; -static GHashTable *requests = NULL; - -typedef struct { - GFileMonitor *monitor; - const char *const description; - const char *const dir; - const guint16 dir_len; - char has_scripts; -} Monitor; - -enum { - MONITOR_INDEX_DEFAULT, - MONITOR_INDEX_PRE_UP, - MONITOR_INDEX_PRE_DOWN, +#define _NMLOG2_DOMAIN LOGD_DISPATCH +#define _NMLOG2(level, request_id, log_ifname, log_con_uuid, ...) \ + nm_log ((level), \ + _NMLOG2_DOMAIN, \ + (log_ifname), \ + (log_con_uuid), \ + "dispatcher: (%u) " \ + _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ + (request_id) \ + _NM_UTILS_MACRO_REST (__VA_ARGS__)) + +#define _NMLOG3_DOMAIN LOGD_DISPATCH +#define _NMLOG3(level, call_id, ...) \ + G_STMT_START { \ + const NMDispatcherCallId *const _call_id = (call_id); \ + \ + _NMLOG2 (level, _call_id->request_id, _call_id->log_ifname, _call_id->log_con_uuid, __VA_ARGS__); \ + } G_STMT_END + +/*****************************************************************************/ + +struct NMDispatcherCallId { + NMDispatcherFunc callback; + gpointer user_data; + const char *log_ifname; + const char *log_con_uuid; + NMDispatcherAction action; + guint idle_id; + guint32 request_id; + char extra_strings[]; }; -static Monitor monitors[3] = { -#define MONITORS_INIT_SET(INDEX, USE, SCRIPT_DIR) [INDEX] = { .dir_len = NM_STRLEN (SCRIPT_DIR), .dir = SCRIPT_DIR, .description = ("" USE), .has_scripts = TRUE } - MONITORS_INIT_SET (MONITOR_INDEX_DEFAULT, "default", NMD_SCRIPT_DIR_DEFAULT), - MONITORS_INIT_SET (MONITOR_INDEX_PRE_UP, "pre-up", NMD_SCRIPT_DIR_PRE_UP), - MONITORS_INIT_SET (MONITOR_INDEX_PRE_DOWN, "pre-down", NMD_SCRIPT_DIR_PRE_DOWN), -}; +/*****************************************************************************/ -static const Monitor* -_get_monitor_by_action (NMDispatcherAction action) +/* FIXME(shutdown): on shutdown, we should not run dispatcher scripts synchronously. + * Instead, we should of course still run them asynchronously. + * + * Also, we should wait for all pending requests to complete before exiting the main-loop + * (with a watchdog). If we hit a timeout, we log a warning and quit (but leave the scripts + * running). + * + * Finally, cleanup the global structures. */ +static struct { + GDBusConnection *dbus_connection; + GHashTable *requests; + guint request_id_counter; +} gl; + +/*****************************************************************************/ + +static NMDispatcherCallId * +dispatcher_call_id_new (guint32 request_id, + NMDispatcherAction action, + NMDispatcherFunc callback, + gpointer user_data, + const char *log_ifname, + const char *log_con_uuid) { - switch (action) { - case NM_DISPATCHER_ACTION_PRE_UP: - case NM_DISPATCHER_ACTION_VPN_PRE_UP: - return &monitors[MONITOR_INDEX_PRE_UP]; - case NM_DISPATCHER_ACTION_PRE_DOWN: - case NM_DISPATCHER_ACTION_VPN_PRE_DOWN: - return &monitors[MONITOR_INDEX_PRE_DOWN]; - default: - return &monitors[MONITOR_INDEX_DEFAULT]; + NMDispatcherCallId *call_id; + gsize l_log_ifname; + gsize l_log_con_uuid; + char *extra_strings; + + l_log_ifname = log_ifname ? (strlen (log_ifname) + 1) : 0u; + l_log_con_uuid = log_con_uuid ? (strlen (log_con_uuid) + 1) : 0u; + + call_id = g_malloc (sizeof (NMDispatcherCallId) + l_log_ifname + l_log_con_uuid); + + call_id->action = action; + call_id->request_id = request_id; + call_id->callback = callback; + call_id->user_data = user_data; + call_id->idle_id = 0; + + extra_strings = &call_id->extra_strings[0]; + + if (log_ifname) { + call_id->log_ifname = extra_strings; + memcpy (extra_strings, log_ifname, l_log_ifname); + extra_strings += l_log_ifname; + } else + call_id->log_ifname = NULL; + + if (log_con_uuid) { + call_id->log_con_uuid = extra_strings; + memcpy (extra_strings, log_con_uuid, l_log_con_uuid); + } else + call_id->log_con_uuid = NULL; + + return call_id; +} + +static void +dispatcher_call_id_free (NMDispatcherCallId *call_id) +{ + nm_clear_g_source (&call_id->idle_id); + g_free (call_id); +} + +/*****************************************************************************/ + + +static void +_init_dispatcher (void) +{ + if (G_UNLIKELY (gl.requests == NULL)) { + gl.requests = g_hash_table_new (nm_direct_hash, NULL); + gl.dbus_connection = nm_g_object_ref (NM_MAIN_DBUS_CONNECTION_GET); + + if (!gl.dbus_connection) + _LOGD ("No D-Bus connection to talk with NetworkManager-dispatcher service"); } } +/*****************************************************************************/ + static void dump_proxy_to_props (NMProxyConfig *proxy, GVariantBuilder *builder) { @@ -323,39 +399,6 @@ fill_vpn_props (NMProxyConfig *proxy_config, dump_ip6_to_props (ip6_config, ip6_builder); } -typedef struct { - NMDispatcherAction action; - guint request_id; - NMDispatcherFunc callback; - gpointer user_data; - guint idle_id; -} DispatchInfo; - -static void -dispatcher_info_free (DispatchInfo *info) -{ - if (info->idle_id) - g_source_remove (info->idle_id); - g_free (info); -} - -static void -_ensure_requests (void) -{ - if (G_UNLIKELY (requests == NULL)) { - requests = g_hash_table_new_full (nm_direct_hash, - NULL, - NULL, - (GDestroyNotify) dispatcher_info_free); - } -} - -static void -dispatcher_info_cleanup (DispatchInfo *info) -{ - g_hash_table_remove (requests, GUINT_TO_POINTER (info->request_id)); -} - static const char * dispatch_result_to_string (DispatchResult result) { @@ -375,81 +418,71 @@ dispatch_result_to_string (DispatchResult result) } static void -dispatcher_results_process (guint request_id, NMDispatcherAction action, GVariantIter *results) +dispatcher_results_process (guint32 request_id, + const char *log_ifname, + const char *log_con_uuid, + GVariant *v_results) { + nm_auto_free_variant_iter GVariantIter *results = NULL; const char *script, *err; guint32 result; - const Monitor *monitor = _get_monitor_by_action (action); - g_return_if_fail (results != NULL); + g_variant_get (v_results, "(a(sus))", &results); if (g_variant_iter_n_children (results) == 0) { - _LOGD ("(%u) succeeded but no scripts invoked", request_id); + _LOG2D (request_id, log_ifname, log_con_uuid, "succeeded but no scripts invoked"); return; } while (g_variant_iter_next (results, "(&su&s)", &script, &result, &err)) { - const char *script_validation_msg = ""; - - if (!*script) { - script_validation_msg = " (path is NULL)"; - script = "(unknown)"; - } else if (!strncmp (script, monitor->dir, monitor->dir_len) /* check: prefixed by script directory */ - && script[monitor->dir_len] == '/' && script[monitor->dir_len+1] /* check: with additional "/?" */ - && !strchr (&script[monitor->dir_len+1], '/')) { /* check: and no further '/' */ - /* we expect the script to lie inside monitor->dir. If it does, - * strip the directory name. Otherwise show the full path and a warning. */ - script += monitor->dir_len + 1; - } else - script_validation_msg = " (unexpected path)"; - if (result == DISPATCH_RESULT_SUCCESS) { - _LOGD ("(%u) %s succeeded%s", - request_id, - script, script_validation_msg); + _LOG2D (request_id, log_ifname, log_con_uuid, "%s succeeded", script); } else { - _LOGW ("(%u) %s failed (%s): %s%s", - request_id, - script, - dispatch_result_to_string (result), - err, - script_validation_msg); + _LOG2W (request_id, + log_ifname, + log_con_uuid, + "%s failed (%s): %s", + script, + dispatch_result_to_string (result), + err); } } } static void -dispatcher_done_cb (GObject *proxy, GAsyncResult *result, gpointer user_data) +dispatcher_done_cb (GObject *source, GAsyncResult *result, gpointer user_data) { - DispatchInfo *info = user_data; - GVariant *ret; - GVariantIter *results; - GError *error = NULL; - - ret = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), result, - G_VARIANT_TYPE ("(a(sus))"), - &error); - if (ret) { - g_variant_get (ret, "(a(sus))", &results); - dispatcher_results_process (info->request_id, info->action, results); - g_variant_iter_free (results); - g_variant_unref (ret); - } else { + gs_unref_variant GVariant *ret = NULL; + gs_free_error GError *error = NULL; + NMDispatcherCallId *call_id = user_data; + + nm_assert ((gpointer) source == gl.dbus_connection); + + ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), + result, + &error); + if (!ret) { if (_nm_dbus_error_has_name (error, "org.freedesktop.systemd1.LoadFailed")) { g_dbus_error_strip_remote_error (error); - _LOGW ("(%u) failed to call dispatcher scripts: %s", - info->request_id, error->message); + _LOG3W (call_id, "failed to call dispatcher scripts: %s", + error->message); } else { - _LOGD ("(%u) failed to call dispatcher scripts: %s", - info->request_id, error->message); + _LOG3D (call_id, "failed to call dispatcher scripts: %s", + error->message); } - g_clear_error (&error); + } else { + dispatcher_results_process (call_id->request_id, + call_id->log_ifname, + call_id->log_con_uuid, + ret); } - if (info->callback) - info->callback (info->request_id, info->user_data); + g_hash_table_remove (gl.requests, call_id); - dispatcher_info_cleanup (info); + if (call_id->callback) + call_id->callback (call_id, call_id->user_data); + + dispatcher_call_id_free (call_id); } static const char *action_table[] = { @@ -470,20 +503,9 @@ static const char *action_table[] = { static const char * action_to_string (NMDispatcherAction action) { - g_assert ((gsize) action < G_N_ELEMENTS (action_table)); - return action_table[action]; -} - -static gboolean -dispatcher_idle_cb (gpointer user_data) -{ - DispatchInfo *info = user_data; - - info->idle_id = 0; - if (info->callback) - info->callback (info->request_id, info->user_data); - dispatcher_info_cleanup (info); - return G_SOURCE_REMOVE; + if (G_UNLIKELY ((gsize) action >= G_N_ELEMENTS (action_table))) + g_return_val_if_reached (NULL); + return action_table[(gsize) action]; } static gboolean @@ -500,7 +522,7 @@ _dispatcher_call (NMDispatcherAction action, NMIP6Config *vpn_ip6_config, NMDispatcherFunc callback, gpointer user_data, - guint *out_call_id) + NMDispatcherCallId **out_call_id) { GVariant *connection_dict; GVariantBuilder connection_props; @@ -508,62 +530,57 @@ _dispatcher_call (NMDispatcherAction action, GVariantBuilder device_proxy_props; GVariantBuilder device_ip4_props; GVariantBuilder device_ip6_props; - GVariant *device_dhcp4_props = NULL; - GVariant *device_dhcp6_props = NULL; + gs_unref_variant GVariant *parameters_floating = NULL; + gs_unref_variant GVariant *device_dhcp4_props = NULL; + gs_unref_variant GVariant *device_dhcp6_props = NULL; GVariantBuilder vpn_proxy_props; GVariantBuilder vpn_ip4_props; GVariantBuilder vpn_ip6_props; - DispatchInfo *info = NULL; - gboolean success = FALSE; - GError *error = NULL; - static guint request_counter = 0; - guint reqid = ++request_counter; + NMDispatcherCallId *call_id; + guint request_id; const char *connectivity_state_string = "UNKNOWN"; + const char *log_ifname; + const char *log_con_uuid; - if (!dispatcher_proxy) - return FALSE; + g_return_val_if_fail (!blocking || (!callback && !user_data), FALSE); + + NM_SET_OUT (out_call_id, NULL); - /* Wrapping protection */ - if (G_UNLIKELY (!reqid)) - reqid = ++request_counter; + _init_dispatcher (); - g_assert (!blocking || (!callback && !user_data)); + if (!gl.dbus_connection) + return FALSE; + + log_ifname = device ? nm_device_get_iface (device) : NULL; + log_con_uuid = settings_connection ? nm_settings_connection_get_uuid (settings_connection) : NULL; - _ensure_requests (); + request_id = ++gl.request_id_counter; + if (G_UNLIKELY (!request_id)) + request_id = ++gl.request_id_counter; /* All actions except 'hostname' and 'connectivity-change' require a device */ if ( action == NM_DISPATCHER_ACTION_HOSTNAME || action == NM_DISPATCHER_ACTION_CONNECTIVITY_CHANGE) { - _LOGD ("(%u) dispatching action '%s'%s", - reqid, action_to_string (action), - blocking + _LOG2D (request_id, + log_ifname, + log_con_uuid, + "dispatching action '%s'%s", + action_to_string (action), + blocking ? " (blocking)" : (callback ? " (with callback)" : "")); } else { g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); - _LOGD ("(%u) (%s) dispatching action '%s'%s", - reqid, - vpn_iface ?: nm_device_get_iface(device), - action_to_string (action), - blocking - ? " (blocking)" - : (callback ? " (with callback)" : "")); - } - - if (!_get_monitor_by_action(action)->has_scripts) { - if (blocking == FALSE && (out_call_id || callback)) { - info = g_malloc0 (sizeof (*info)); - info->action = action; - info->request_id = reqid; - info->callback = callback; - info->user_data = user_data; - info->idle_id = g_idle_add (dispatcher_idle_cb, info); - _LOGD ("(%u) simulate request; no scripts in %s", reqid, _get_monitor_by_action(action)->dir); - } else - _LOGD ("(%u) ignoring request; no scripts in %s", reqid, _get_monitor_by_action(action)->dir); - success = TRUE; - goto done; + _LOG2D (request_id, + log_ifname, + log_con_uuid, + "(%s) dispatching action '%s'%s", + vpn_iface ?: nm_device_get_iface (device), + action_to_string (action), + blocking + ? " (blocking)" + : (callback ? " (with callback)" : "")); } if (applied_connection) @@ -623,91 +640,72 @@ _dispatcher_call (NMDispatcherAction action, } } - if (!device_dhcp4_props) - device_dhcp4_props = g_variant_ref_sink (g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0)); - if (!device_dhcp6_props) - device_dhcp6_props = g_variant_ref_sink (g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0)); - connectivity_state_string = nm_connectivity_state_to_string (connectivity_state); + parameters_floating = g_variant_new ("(s@a{sa{sv}}a{sv}a{sv}a{sv}a{sv}a{sv}@a{sv}@a{sv}ssa{sv}a{sv}a{sv}b)", + action_to_string (action), + connection_dict, + &connection_props, + &device_props, + &device_proxy_props, + &device_ip4_props, + &device_ip6_props, + device_dhcp4_props ?: g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0), + device_dhcp6_props ?: g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0), + connectivity_state_string, + vpn_iface ?: "", + &vpn_proxy_props, + &vpn_ip4_props, + &vpn_ip6_props, + nm_logging_enabled (LOGL_DEBUG, LOGD_DISPATCH)); + /* Send the action to the dispatcher */ if (blocking) { - GVariant *ret; - GVariantIter *results; - - ret = _nm_dbus_proxy_call_sync (dispatcher_proxy, "Action", - g_variant_new ("(s@a{sa{sv}}a{sv}a{sv}a{sv}a{sv}a{sv}@a{sv}@a{sv}ssa{sv}a{sv}a{sv}b)", - action_to_string (action), - connection_dict, - &connection_props, - &device_props, - &device_proxy_props, - &device_ip4_props, - &device_ip6_props, - device_dhcp4_props, - device_dhcp6_props, - connectivity_state_string, - vpn_iface ?: "", - &vpn_proxy_props, - &vpn_ip4_props, - &vpn_ip6_props, - nm_logging_enabled (LOGL_DEBUG, LOGD_DISPATCH)), - G_VARIANT_TYPE ("(a(sus))"), - G_DBUS_CALL_FLAGS_NONE, CALL_TIMEOUT, - NULL, &error); - if (ret) { - g_variant_get (ret, "(a(sus))", &results); - dispatcher_results_process (reqid, action, results); - g_variant_iter_free (results); - g_variant_unref (ret); - success = TRUE; - } else { + gs_unref_variant GVariant *ret = NULL; + gs_free_error GError *error = NULL; + + ret = g_dbus_connection_call_sync (gl.dbus_connection, + NM_DISPATCHER_DBUS_SERVICE, + NM_DISPATCHER_DBUS_PATH, + NM_DISPATCHER_DBUS_INTERFACE, + "Action", + g_steal_pointer (¶meters_floating), + G_VARIANT_TYPE ("(a(sus))"), + G_DBUS_CALL_FLAGS_NONE, + CALL_TIMEOUT, + NULL, + &error); + if (!ret) { g_dbus_error_strip_remote_error (error); - _LOGW ("(%u) failed: %s", reqid, error->message); - g_clear_error (&error); - success = FALSE; + _LOG2W (request_id, log_ifname, log_con_uuid, "failed: %s", error->message); + return FALSE; } - } else { - info = g_malloc0 (sizeof (*info)); - info->action = action; - info->request_id = reqid; - info->callback = callback; - info->user_data = user_data; - g_dbus_proxy_call (dispatcher_proxy, "Action", - g_variant_new ("(s@a{sa{sv}}a{sv}a{sv}a{sv}a{sv}a{sv}@a{sv}@a{sv}ssa{sv}a{sv}a{sv}b)", - action_to_string (action), - connection_dict, - &connection_props, - &device_props, - &device_proxy_props, - &device_ip4_props, - &device_ip6_props, - device_dhcp4_props, - device_dhcp6_props, - connectivity_state_string, - vpn_iface ?: "", - &vpn_proxy_props, - &vpn_ip4_props, - &vpn_ip6_props, - nm_logging_enabled (LOGL_DEBUG, LOGD_DISPATCH)), - G_DBUS_CALL_FLAGS_NONE, CALL_TIMEOUT, - NULL, dispatcher_done_cb, info); - success = TRUE; + dispatcher_results_process (request_id, log_ifname, log_con_uuid, ret); + return TRUE; } - g_variant_unref (device_dhcp4_props); - g_variant_unref (device_dhcp6_props); - -done: - if (success && info) { - /* Track the request in case of cancelation */ - g_hash_table_insert (requests, GUINT_TO_POINTER (info->request_id), info); - if (out_call_id) - *out_call_id = info->request_id; - } else if (out_call_id) - *out_call_id = 0; - - return success; + call_id = dispatcher_call_id_new (request_id, + action, + callback, + user_data, + log_ifname, + log_con_uuid); + + g_dbus_connection_call (gl.dbus_connection, + NM_DISPATCHER_DBUS_SERVICE, + NM_DISPATCHER_DBUS_PATH, + NM_DISPATCHER_DBUS_INTERFACE, + "Action", + g_steal_pointer (¶meters_floating), + G_VARIANT_TYPE ("(a(sus))"), + G_DBUS_CALL_FLAGS_NONE, + CALL_TIMEOUT, + NULL, + dispatcher_done_cb, + call_id); + g_hash_table_add (gl.requests, call_id); + NM_SET_OUT (out_call_id, call_id); + return TRUE; } /** @@ -724,7 +722,7 @@ done: gboolean nm_dispatcher_call_hostname (NMDispatcherFunc callback, gpointer user_data, - guint *out_call_id) + NMDispatcherCallId **out_call_id) { return _dispatcher_call (NM_DISPATCHER_ACTION_HOSTNAME, FALSE, NULL, NULL, NULL, FALSE, @@ -755,7 +753,7 @@ nm_dispatcher_call_device (NMDispatcherAction action, NMActRequest *act_request, NMDispatcherFunc callback, gpointer user_data, - guint *out_call_id) + NMDispatcherCallId **out_call_id) { nm_assert (NM_IS_DEVICE (device)); if (!act_request) { @@ -839,7 +837,7 @@ nm_dispatcher_call_vpn (NMDispatcherAction action, NMIP6Config *vpn_ip6_config, NMDispatcherFunc callback, gpointer user_data, - guint *out_call_id) + NMDispatcherCallId **out_call_id) { return _dispatcher_call (action, FALSE, parent_device, @@ -903,7 +901,7 @@ gboolean nm_dispatcher_call_connectivity (NMConnectivityState connectivity_state, NMDispatcherFunc callback, gpointer user_data, - guint *out_call_id) + NMDispatcherCallId **out_call_id) { return _dispatcher_call (NM_DISPATCHER_ACTION_CONNECTIVITY_CHANGE, FALSE, NULL, NULL, NULL, FALSE, @@ -913,99 +911,16 @@ nm_dispatcher_call_connectivity (NMConnectivityState connectivity_state, } void -nm_dispatcher_call_cancel (guint call_id) +nm_dispatcher_call_cancel (NMDispatcherCallId *call_id) { - DispatchInfo *info; - - _ensure_requests (); + if ( !call_id + || g_hash_table_lookup (gl.requests, call_id) != call_id + || !call_id->callback) + g_return_if_reached (); /* Canceling just means the callback doesn't get called, so set the * DispatcherInfo's callback to NULL. */ - info = g_hash_table_lookup (requests, GUINT_TO_POINTER (call_id)); - g_return_if_fail (info); - - if (info && info->callback) { - _LOGD ("(%u) cancelling dispatcher callback action", call_id); - info->callback = NULL; - } -} - -static void -dispatcher_dir_changed (GFileMonitor *monitor, - GFile *file, - GFile *other_file, - GFileMonitorEvent event_type, - Monitor *item) -{ - const char *name; - char *full_name; - GDir *dir; - GError *error = NULL; - - dir = g_dir_open (item->dir, 0, &error); - if (dir) { - int errsv = 0; - - item->has_scripts = FALSE; - errno = 0; - while (!item->has_scripts - && (name = g_dir_read_name (dir))) { - full_name = g_build_filename (item->dir, name, NULL); - item->has_scripts = g_file_test (full_name, G_FILE_TEST_IS_EXECUTABLE); - g_free (full_name); - } - errsv = errno; - g_dir_close (dir); - if (item->has_scripts) - _LOGD ("%s script directory '%s' has scripts", item->description, item->dir); - else if (errsv == 0) - _LOGD ("%s script directory '%s' has no scripts", item->description, item->dir); - else { - _LOGD ("%s script directory '%s' error reading (%s)", item->description, item->dir, nm_strerror_native (errsv)); - item->has_scripts = TRUE; - } - } else { - if (g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) { - _LOGD ("%s script directory '%s' does not exist", item->description, item->dir); - item->has_scripts = FALSE; - } else { - _LOGD ("%s script directory '%s' error (%s)", item->description, item->dir, error->message); - item->has_scripts = TRUE; - } - g_error_free (error); - } - + _LOG3D (call_id, "cancelling dispatcher callback action"); + call_id->callback = NULL; } - -void -nm_dispatcher_init (void) -{ - GFile *file; - guint i; - GError *error = NULL; - - for (i = 0; i < G_N_ELEMENTS (monitors); i++) { - file = g_file_new_for_path (monitors[i].dir); - monitors[i].monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL); - if (monitors[i].monitor) { - g_signal_connect (monitors[i].monitor, "changed", G_CALLBACK (dispatcher_dir_changed), &monitors[i]); - dispatcher_dir_changed (monitors[i].monitor, file, NULL, 0, &monitors[i]); - } - g_object_unref (file); - } - - dispatcher_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | - G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, - NULL, - NM_DISPATCHER_DBUS_SERVICE, - NM_DISPATCHER_DBUS_PATH, - NM_DISPATCHER_DBUS_INTERFACE, - NULL, &error); - if (!dispatcher_proxy) { - _LOGE ("could not get dispatcher proxy! %s", error->message); - g_clear_error (&error); - } -} - diff --git a/src/nm-dispatcher.h b/src/nm-dispatcher.h index 1cdeeb88..5c6c5805 100644 --- a/src/nm-dispatcher.h +++ b/src/nm-dispatcher.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -39,18 +38,20 @@ typedef enum { NM_DISPATCHER_ACTION_CONNECTIVITY_CHANGE } NMDispatcherAction; -typedef void (*NMDispatcherFunc) (guint call_id, gpointer user_data); +typedef struct NMDispatcherCallId NMDispatcherCallId; + +typedef void (*NMDispatcherFunc) (NMDispatcherCallId *call_id, gpointer user_data); gboolean nm_dispatcher_call_hostname (NMDispatcherFunc callback, gpointer user_data, - guint *out_call_id); + NMDispatcherCallId **out_call_id); gboolean nm_dispatcher_call_device (NMDispatcherAction action, NMDevice *device, NMActRequest *act_request, NMDispatcherFunc callback, gpointer user_data, - guint *out_call_id); + NMDispatcherCallId **out_call_id); gboolean nm_dispatcher_call_device_sync (NMDispatcherAction action, NMDevice *device, @@ -66,7 +67,7 @@ gboolean nm_dispatcher_call_vpn (NMDispatcherAction action, NMIP6Config *vpn_ip6_config, NMDispatcherFunc callback, gpointer user_data, - guint *out_call_id); + NMDispatcherCallId **out_call_id); gboolean nm_dispatcher_call_vpn_sync (NMDispatcherAction action, NMSettingsConnection *settings_connection, @@ -80,10 +81,8 @@ gboolean nm_dispatcher_call_vpn_sync (NMDispatcherAction action, gboolean nm_dispatcher_call_connectivity (NMConnectivityState state, NMDispatcherFunc callback, gpointer user_data, - guint *out_call_id); - -void nm_dispatcher_call_cancel (guint call_id); + NMDispatcherCallId **out_call_id); -void nm_dispatcher_init (void); +void nm_dispatcher_call_cancel (NMDispatcherCallId *call_id); #endif /* __NM_DISPATCHER_H__ */ diff --git a/src/nm-firewall-manager.c b/src/nm-firewall-manager.c index a4b079b4..9b0d49c9 100644 --- a/src/nm-firewall-manager.c +++ b/src/nm-firewall-manager.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/nm-firewall-manager.h b/src/nm-firewall-manager.h index 8bbf82a7..ceca58be 100644 --- a/src/nm-firewall-manager.h +++ b/src/nm-firewall-manager.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/nm-hostname-manager.c b/src/nm-hostname-manager.c index 3fcd8482..86da0168 100644 --- a/src/nm-hostname-manager.c +++ b/src/nm-hostname-manager.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager * * This program is free software; you can redistribute it and/or modify @@ -131,8 +130,7 @@ read_hostname_slackware (const char *path) { gs_free char *contents = NULL; gs_strfreev char **all_lines = NULL; - char *tmp; - guint i, j = 0; + guint i = 0; if (!g_file_get_contents (path, &contents, NULL, NULL)) return NULL; @@ -142,17 +140,7 @@ read_hostname_slackware (const char *path) g_strstrip (all_lines[i]); if (all_lines[i][0] == '#' || all_lines[i][0] == '\0') continue; - tmp = &all_lines[i][0]; - /* We only want up to the first '.' -- the rest of the */ - /* fqdn is defined in /etc/hosts */ - while (tmp[j] != '\0') { - if (tmp[j] == '.') { - tmp[j] = '\0'; - break; - } - j++; - } - return g_shell_unquote (tmp, NULL); + return g_shell_unquote (&all_lines[i][0], NULL); } return NULL; } diff --git a/src/nm-hostname-manager.h b/src/nm-hostname-manager.h index a837e9b9..84ee7cb2 100644 --- a/src/nm-hostname-manager.h +++ b/src/nm-hostname-manager.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager * * Søren Sandmann <sandmann@daimi.au.dk> diff --git a/src/nm-iface-helper.c b/src/nm-iface-helper.c index 812ab83e..dd5bb327 100644 --- a/src/nm-iface-helper.c +++ b/src/nm-iface-helper.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -382,8 +381,10 @@ main (int argc, char *argv[]) gs_unref_object NMDhcpClient *dhcp4_client = NULL; gs_unref_object NMNDisc *ndisc = NULL; gs_unref_bytes GBytes *hwaddr = NULL; + gs_unref_bytes GBytes *bcast_hwaddr = NULL; gs_unref_bytes GBytes *client_id = NULL; gs_free NMUtilsIPv6IfaceId *iid = NULL; + const NMPlatformLink *pllink; guint sd_id; int errsv; @@ -470,7 +471,11 @@ main (int argc, char *argv[]) /* Set up platform interaction layer */ nm_linux_platform_setup (); - hwaddr = nm_platform_link_get_address_as_bytes (NM_PLATFORM_GET, gl.ifindex); + pllink = nm_platform_link_get (NM_PLATFORM_GET, gl.ifindex); + if (pllink) { + hwaddr = nmp_link_address_get_as_bytes (&pllink->l_address); + bcast_hwaddr = nmp_link_address_get_as_bytes (&pllink->l_broadcast); + } if (global_opt.iid_str) { GBytes *bytes; @@ -506,6 +511,7 @@ main (int argc, char *argv[]) global_opt.ifname, gl.ifindex, hwaddr, + bcast_hwaddr, global_opt.uuid, RT_TABLE_MAIN, global_opt.priority_v4, @@ -590,8 +596,9 @@ main (int argc, char *argv[]) /*****************************************************************************/ -const NMDhcpClientFactory *const _nm_dhcp_manager_factories[4] = { +const NMDhcpClientFactory *const _nm_dhcp_manager_factories[5] = { &_nm_dhcp_client_factory_internal, + &_nm_dhcp_client_factory_nettools, }; /*****************************************************************************/ diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c index 15088c60..86c82925 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager * * This program is free software; you can redistribute it and/or modify diff --git a/src/nm-ip4-config.h b/src/nm-ip4-config.h index 6d89903f..fe0a085b 100644 --- a/src/nm-ip4-config.h +++ b/src/nm-ip4-config.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager * * This program is free software; you can redistribute it and/or modify diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c index baef60bd..1096fb42 100644 --- a/src/nm-ip6-config.c +++ b/src/nm-ip6-config.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -26,6 +25,7 @@ #include <arpa/inet.h> #include <resolv.h> #include <linux/rtnetlink.h> +#include <linux/if.h> #include "nm-glib-aux/nm-dedup-multi.h" @@ -77,6 +77,7 @@ typedef struct { NMIPConfigDedupMultiIdxType idx_ip6_routes_; NMDedupMultiIdxType idx_ip6_routes; }; + bool ipv6_disabled; } NMIP6ConfigPrivate; struct _NMIP6Config { @@ -382,6 +383,8 @@ nm_ip6_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int i const NMDedupMultiHeadEntry *head_entry; NMDedupMultiIter iter; const NMPObject *plobj = NULL; + char ifname[IFNAMSIZ]; + char *path; nm_assert (ifindex > 0); @@ -423,6 +426,12 @@ nm_ip6_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int i nmp_cache_iter_for_each (&iter, head_entry, &plobj) _add_route (self, plobj, NULL, NULL); + if (nm_platform_if_indextoname (platform, ifindex, ifname)) { + path = nm_sprintf_bufa (128, "/proc/sys/net/ipv6/conf/%s/disable_ipv6", ifname); + if (nm_platform_sysctl_get_int32 (platform, NMP_SYSCTL_PATHID_ABSOLUTE (path), 0) != 0) + priv->ipv6_disabled = TRUE; + } + return self; } @@ -672,7 +681,7 @@ nm_ip6_config_merge_setting (NMIP6Config *self, nm_utils_ip6_address_clear_host_address (&route.network, &route.network, route.plen); - _nm_ip_config_merge_route_attributes (AF_INET, + _nm_ip_config_merge_route_attributes (AF_INET6, s_route, NM_PLATFORM_IP_ROUTE_CAST (&route), route_table); @@ -772,8 +781,11 @@ nm_ip6_config_create_setting (const NMIP6Config *self) } /* Use 'ignore' if the method wasn't previously set */ - if (!method) - method = NM_SETTING_IP6_CONFIG_METHOD_IGNORE; + if (!method) { + method = priv->ipv6_disabled + ? NM_SETTING_IP6_CONFIG_METHOD_DISABLED + : NM_SETTING_IP6_CONFIG_METHOD_IGNORE; + } g_object_set (s_ip6, NM_SETTING_IP_CONFIG_METHOD, method, @@ -838,10 +850,15 @@ nm_ip6_config_merge (NMIP6Config *dst, guint32 i; NMDedupMultiIter ipconf_iter; const NMPlatformIP6Address *address = NULL; + const NMIP6ConfigPrivate *src_priv; + NMIP6ConfigPrivate *dst_priv; g_return_if_fail (src != NULL); g_return_if_fail (dst != NULL); + src_priv = NM_IP6_CONFIG_GET_PRIVATE (src); + dst_priv = NM_IP6_CONFIG_GET_PRIVATE (dst); + g_object_freeze_notify (G_OBJECT (dst)); /* addresses */ @@ -896,6 +913,9 @@ nm_ip6_config_merge (NMIP6Config *dst, if (nm_ip6_config_get_dns_priority (src)) nm_ip6_config_set_dns_priority (dst, nm_ip6_config_get_dns_priority (src)); + if (src_priv->ipv6_disabled) + dst_priv->ipv6_disabled = src_priv->ipv6_disabled; + g_object_thaw_notify (G_OBJECT (dst)); } @@ -1501,6 +1521,11 @@ nm_ip6_config_replace (NMIP6Config *dst, const NMIP6Config *src, gboolean *relev has_minor_changes = TRUE; } + if (src_priv->ipv6_disabled != dst_priv->ipv6_disabled) { + dst_priv->ipv6_disabled = src_priv->ipv6_disabled; + has_minor_changes = TRUE; + } + #if NM_MORE_ASSERTS /* config_equal does not compare *all* the fields, therefore, we might have has_minor_changes * regardless of config_equal. But config_equal must correspond to has_relevant_changes. */ diff --git a/src/nm-ip6-config.h b/src/nm-ip6-config.h index 86786ea8..6c7b4cf4 100644 --- a/src/nm-ip6-config.h +++ b/src/nm-ip6-config.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager * * This program is free software; you can redistribute it and/or modify diff --git a/src/nm-keep-alive.c b/src/nm-keep-alive.c index cfec138a..ba080a81 100644 --- a/src/nm-keep-alive.c +++ b/src/nm-keep-alive.c @@ -24,6 +24,7 @@ #include "nm-keep-alive.h" #include "settings/nm-settings-connection.h" +#include "nm-glib-aux/nm-dbus-aux.h" /*****************************************************************************/ @@ -211,32 +212,24 @@ nm_keep_alive_set_settings_connection_watch_visible (NMKeepAlive *self, /*****************************************************************************/ static void -get_name_owner_cb (GObject *source_object, - GAsyncResult *res, +get_name_owner_cb (const char *name_owner, + GError *error, gpointer user_data) { - NMKeepAlive *self = user_data; + NMKeepAlive *self; 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 + + if ( !name_owner && 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); + self = user_data; + priv = NM_KEEP_ALIVE_GET_PRIVATE (self); - if (nm_streq (name_owner, priv->dbus_client)) { - /* all good, the name is confirmed. */ - return; - } + if ( name_owner + && 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"); @@ -259,18 +252,12 @@ _is_alive_dbus_client (NMKeepAlive *self) 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); + nm_dbus_connection_call_get_name_owner (priv->dbus_connection, + priv->dbus_client, + -1, + priv->dbus_client_confirm_cancellable, + get_name_owner_cb, + self); } return TRUE; } @@ -289,7 +276,7 @@ cleanup_dbus_watch (NMKeepAlive *self) nm_clear_g_free (&priv->dbus_client); if (priv->dbus_connection) { g_dbus_connection_signal_unsubscribe (priv->dbus_connection, - priv->subscription_id); + nm_steal_int (&priv->subscription_id)); g_clear_object (&priv->dbus_connection); } } @@ -336,16 +323,11 @@ nm_keep_alive_set_dbus_client_watch (NMKeepAlive *self, 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); + priv->subscription_id = nm_dbus_connection_signal_subscribe_name_owner_changed (priv->dbus_connection, + priv->dbus_client, + name_owner_changed_cb, + self, + NULL); } else priv->dbus_client_watching = FALSE; @@ -403,6 +385,26 @@ nm_keep_alive_disarm (NMKeepAlive *self) cleanup_dbus_watch (self); } +/** + * nm_keep_alive_destroy: + * @self: (allow-none): the #NMKeepAlive instance to destroy. + * + * This does 3 things in one: + * + * - set owner to %NULL + * - disarm the instance. + * - unref @self. + */ +void +nm_keep_alive_destroy (NMKeepAlive *self) +{ + if (!self) + return; + _nm_keep_alive_set_owner (self, NULL); + nm_keep_alive_disarm (self); + g_object_unref (self); +} + /*****************************************************************************/ static void diff --git a/src/nm-keep-alive.h b/src/nm-keep-alive.h index 160b2adb..fcee9d6d 100644 --- a/src/nm-keep-alive.h +++ b/src/nm-keep-alive.h @@ -43,6 +43,8 @@ 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_destroy (NMKeepAlive *self); + void nm_keep_alive_set_settings_connection_watch_visible (NMKeepAlive *self, NMSettingsConnection *connection); diff --git a/src/nm-logging.c b/src/nm-logging.c index 48e51421..ea505a81 100644 --- a/src/nm-logging.c +++ b/src/nm-logging.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -68,6 +67,15 @@ /*****************************************************************************/ +G_STATIC_ASSERT (LOG_EMERG == 0); +G_STATIC_ASSERT (LOG_ALERT == 1); +G_STATIC_ASSERT (LOG_CRIT == 2); +G_STATIC_ASSERT (LOG_ERR == 3); +G_STATIC_ASSERT (LOG_WARNING == 4); +G_STATIC_ASSERT (LOG_NOTICE == 5); +G_STATIC_ASSERT (LOG_INFO == 6); +G_STATIC_ASSERT (LOG_DEBUG == 7); + /* We have more then 32 logging domains. Assert that it compiles to a 64 bit sized enum */ G_STATIC_ASSERT (sizeof (NMLogDomain) >= sizeof (guint64)); @@ -540,27 +548,50 @@ _domains_to_string (gboolean include_level_override, return g_string_free (str, FALSE); } +static char _all_logging_domains_to_str[273]; + const char * nm_logging_all_domains_to_string (void) { - static GString *str; + static const char *volatile str = NULL; + const char *s; - if (G_UNLIKELY (!str)) { +again: + s = g_atomic_pointer_get (&str); + if (G_UNLIKELY (!s)) { + static gsize once = 0; const LogDesc *diter; + gsize buf_l; + char *buf_p; - str = g_string_new (LOGD_DEFAULT_STRING); + if (!g_once_init_enter (&once)) + goto again; + + buf_p = _all_logging_domains_to_str; + buf_l = sizeof (_all_logging_domains_to_str); + + nm_utils_strbuf_append_str (&buf_p, &buf_l, LOGD_DEFAULT_STRING); for (diter = &domain_desc[0]; diter->name; diter++) { - g_string_append_c (str, ','); - g_string_append (str, diter->name); + nm_utils_strbuf_append_c (&buf_p, &buf_l, ','); + nm_utils_strbuf_append_str (&buf_p, &buf_l, diter->name); if (diter->num == LOGD_DHCP6) - g_string_append (str, "," LOGD_DHCP_STRING); + nm_utils_strbuf_append_str (&buf_p, &buf_l, ","LOGD_DHCP_STRING); else if (diter->num == LOGD_IP6) - g_string_append (str, "," LOGD_IP_STRING); + nm_utils_strbuf_append_str (&buf_p, &buf_l, ","LOGD_IP_STRING); } - g_string_append (str, "," LOGD_ALL_STRING); + nm_utils_strbuf_append_str (&buf_p, &buf_l, LOGD_ALL_STRING); + + /* Did you modify the logging domains (or their names)? Adjust the size of + * _all_logging_domains_to_str buffer above to have the exact size. */ + nm_assert (strlen (_all_logging_domains_to_str) == sizeof (_all_logging_domains_to_str) - 1); + nm_assert (buf_l == 1); + + s = _all_logging_domains_to_str; + g_atomic_pointer_set (&str, s); + g_once_init_leave (&once, 1); } - return str->str; + return s; } /** @@ -618,9 +649,11 @@ _iovec_set_string (struct iovec *iov, const char *str) _iovec_set (iov, str, strlen (str)); } +#define _iovec_set_string_literal(iov, str) _iovec_set ((iov), ""str"", NM_STRLEN (str)) + _nm_printf (3, 4) static void -_iovec_set_format (struct iovec *iov, gpointer *iov_free, const char *format, ...) +_iovec_set_format (struct iovec *iov, char **iov_free, const char *format, ...) { va_list ap; char *str; @@ -737,12 +770,10 @@ _nm_log_impl (const char *file, case LOG_BACKEND_JOURNAL: { gint64 now, boottime; -#define _NUM_MAX_FIELDS_SYSLOG_FACILITY 10 - struct iovec iov_data[12 + _NUM_MAX_FIELDS_SYSLOG_FACILITY]; + struct iovec iov_data[15]; struct iovec *iov = iov_data; - gpointer iov_free_data[5]; - gpointer *iov_free = iov_free_data; - nm_auto_free_gstring GString *s_domain_all = NULL; + char *iov_free_data[5]; + char **iov_free = iov_free_data; now = nm_utils_get_monotonic_timestamp_ns (); boottime = nm_utils_monotonic_timestamp_as_boottime (now, 1); @@ -753,46 +784,25 @@ _nm_log_impl (const char *file, _iovec_set_format_a (iov++, 30, "SYSLOG_PID=%ld", (long) getpid ()); { const LogDesc *diter; - int i_domain = _NUM_MAX_FIELDS_SYSLOG_FACILITY; - const char *s_domain_1 = NULL; NMLogDomain dom_all = domain; - NMLogDomain dom = dom_all & cur_log_state[level]; + char s_log_domains_buf[NM_STRLEN ("NM_LOG_DOMAINS=") + sizeof (_all_logging_domains_to_str)]; + char *s_log_domains = s_log_domains_buf; + gsize l_log_domains = sizeof (s_log_domains_buf); - for (diter = &domain_desc[0]; diter->name; diter++) { + nm_utils_strbuf_append_str (&s_log_domains, &l_log_domains, "NM_LOG_DOMAINS="); + for (diter = &domain_desc[0]; dom_all != 0 && diter->name; diter++) { if (!NM_FLAGS_ANY (dom_all, diter->num)) continue; - - /* construct a list of all domains (not only the enabled ones). - * Note that in by far most cases, there is only one domain present. - * Hence, save the construction of the GString. */ + if (dom_all != domain) + nm_utils_strbuf_append_c (&s_log_domains, &l_log_domains, ','); + nm_utils_strbuf_append_str (&s_log_domains, &l_log_domains, diter->name); dom_all &= ~diter->num; - if (!s_domain_1) - s_domain_1 = diter->name; - else { - if (!s_domain_all) { - s_domain_all = g_string_new ("NM_LOG_DOMAINS="); - g_string_append (s_domain_all, s_domain_1); - } - g_string_append_c (s_domain_all, ','); - g_string_append (s_domain_all, diter->name); - } - - 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_str_a (iov++, 30, "SYSLOG_FACILITY=%s", diter->name); - i_domain--; - } - dom &= ~diter->num; - } - if (!dom && !dom_all) - break; } - if (s_domain_all) - _iovec_set (iov++, s_domain_all->str, s_domain_all->len); - else - _iovec_set_format_str_a (iov++, 30, "NM_LOG_DOMAINS=%s", s_domain_1); + nm_assert (l_log_domains > 0); + _iovec_set (iov++, s_log_domains_buf, s_log_domains - s_log_domains_buf); } + G_STATIC_ASSERT_EXPR (LOG_FAC (LOG_DAEMON) == 3); + _iovec_set_string_literal (iov++, "SYSLOG_FACILITY=3"); _iovec_set_format_str_a (iov++, 15, "NM_LOG_LEVEL=%s", level_desc[level].name); if (func) _iovec_set_format (iov++, iov_free++, "CODE_FUNC=%s", func); @@ -907,7 +917,7 @@ nm_log_handler (const char *log_domain, "MESSAGE=%s%s", gl.imm.prefix, message ?: "", syslog_identifier_full (gl.imm.syslog_identifier), "SYSLOG_PID=%ld", (long) getpid (), - "SYSLOG_FACILITY=GLIB", + "SYSLOG_FACILITY=3", "GLIB_DOMAIN=%s", log_domain ?: "", "GLIB_LEVEL=%d", (int) (level & G_LOG_LEVEL_MASK), "TIMESTAMP_MONOTONIC=%lld.%06lld", (long long) (now / NM_UTILS_NS_PER_SECOND), (long long) ((now % NM_UTILS_NS_PER_SECOND) / 1000), diff --git a/src/nm-logging.h b/src/nm-logging.h index a0f4ad73..c604f493 100644 --- a/src/nm-logging.h +++ b/src/nm-logging.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -310,9 +309,9 @@ gboolean nm_logging_syslog_enabled (void); #define __NMLOG_DEFAULT_WITH_ADDR(level, domain, prefix, ...) \ G_STMT_START { \ nm_log ((level), (domain), NULL, NULL, \ - "%s[%p]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + "%s["NM_HASH_OBFUSCATE_PTR_FMT"]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ (prefix), \ - (self) \ + NM_HASH_OBFUSCATE_PTR (self) \ _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ } G_STMT_END diff --git a/src/nm-manager.c b/src/nm-manager.c index 40e068ac..6ea53502 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -95,7 +94,8 @@ typedef struct { struct { GDBusMethodInvocation *invocation; NMConnection *connection; - NMSettingsConnectionPersistMode persist; + NMSettingsConnectionPersistMode persist_mode; + bool is_volatile:1; } add_and_activate; }; } ac_auth; @@ -132,6 +132,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMManager, PROP_CONNECTIVITY, PROP_CONNECTIVITY_CHECK_AVAILABLE, PROP_CONNECTIVITY_CHECK_ENABLED, + PROP_CONNECTIVITY_CHECK_URI, PROP_PRIMARY_CONNECTION, PROP_PRIMARY_CONNECTION_TYPE, PROP_ACTIVATING_CONNECTION, @@ -177,6 +178,9 @@ typedef struct { NMSettings *settings; + CList connection_changed_on_idle_lst; + guint connection_changed_on_idle_id; + RadioState radio_states[RFKILL_TYPE_MAX]; NMVpnManager *vpn_manager; @@ -186,7 +190,8 @@ typedef struct { GHashTable *device_route_metrics; - GSList *auth_chains; + CList auth_lst_head; + GHashTable *sleep_devices; /* Firmware dir monitor */ @@ -375,7 +380,8 @@ static void _add_and_activate_auth_done (NMManager *self, NMActiveConnection *active, NMConnection *connection, GDBusMethodInvocation *invocation, - NMSettingsConnectionPersistMode persist, + NMSettingsConnectionPersistMode persist_mode, + gboolean is_volatile, gboolean success, const char *error_desc); static void _activation_auth_done (NMManager *self, @@ -492,7 +498,8 @@ _async_op_data_new_ac_auth_add_and_activate (NMManager *self, NMActiveConnection *active_take, GDBusMethodInvocation *invocation_take, NMConnection *connection_take, - NMSettingsConnectionPersistMode persist) + NMSettingsConnectionPersistMode persist_mode, + gboolean is_volatile) { AsyncOpData *async_op_data; @@ -505,7 +512,8 @@ _async_op_data_new_ac_auth_add_and_activate (NMManager *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; + async_op_data->ac_auth.add_and_activate.persist_mode = persist_mode; + async_op_data->ac_auth.add_and_activate.is_volatile = is_volatile; c_list_link_tail (&NM_MANAGER_GET_PRIVATE (self)->async_op_lst_head, &async_op_data->async_op_lst); return async_op_data; } @@ -547,7 +555,8 @@ _async_op_complete_ac_auth_cb (NMActiveConnection *active, 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, + async_op_data->ac_auth.add_and_activate.persist_mode, + async_op_data->ac_auth.add_and_activate.is_volatile, success, error_desc); g_object_unref (async_op_data->ac_auth.add_and_activate.connection); @@ -817,18 +826,18 @@ _delete_volatile_connection_do (NMManager *self, if (!NM_FLAGS_HAS (nm_settings_connection_get_flags (connection), NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE)) return; + if (!nm_settings_has_connection (priv->settings, connection)) + return; if (active_connection_find (self, connection, NULL, NM_ACTIVE_CONNECTION_STATE_DEACTIVATED, NULL)) return; - if (!nm_settings_has_connection (priv->settings, connection)) - return; _LOGD (LOGD_DEVICE, "volatile connection disconnected. Deleting connection '%s' (%s)", nm_settings_connection_get_id (connection), nm_settings_connection_get_uuid (connection)); - nm_settings_connection_delete (connection, NULL); + nm_settings_connection_delete (connection, FALSE); } /* Returns: whether to notify D-Bus of the removal or not */ @@ -1121,15 +1130,19 @@ _config_changed_cb (NMConfig *config, NMConfigData *config_data, NMConfigChangeF if (NM_FLAGS_HAS (changes, NM_CONFIG_CHANGE_GLOBAL_DNS_CONFIG)) _notify (self, PROP_GLOBAL_DNS_CONFIGURATION); - if ((!nm_config_data_get_connectivity_uri (config_data)) != (!nm_config_data_get_connectivity_uri (old_data))) - _notify (self, PROP_CONNECTIVITY_CHECK_AVAILABLE); + + if (!nm_streq0 (nm_config_data_get_connectivity_uri (config_data), + nm_config_data_get_connectivity_uri (old_data))) { + if ((!nm_config_data_get_connectivity_uri (config_data)) != (!nm_config_data_get_connectivity_uri (old_data))) + _notify (self, PROP_CONNECTIVITY_CHECK_AVAILABLE); + _notify (self, PROP_CONNECTIVITY_CHECK_URI); + } g_object_thaw_notify (G_OBJECT (self)); } static void _reload_auth_cb (NMAuthChain *chain, - GError *error, GDBusMethodInvocation *context, gpointer user_data) { @@ -1142,21 +1155,15 @@ _reload_auth_cb (NMAuthChain *chain, char s_buf[60]; NMConfigChangeFlags reload_type = NM_CONFIG_CHANGE_NONE; - g_assert (context); + nm_assert (G_IS_DBUS_METHOD_INVOCATION (context)); - priv->auth_chains = g_slist_remove (priv->auth_chains, chain); + c_list_unlink (nm_auth_chain_parent_lst_list (chain)); flags = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "flags")); subject = nm_auth_chain_get_subject (chain); result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_RELOAD); - if (error) { - _LOGD (LOGD_CORE, "Reload request failed: %s", error->message); - ret_error = g_error_new (NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - "Reload request failed: %s", - error->message); - } else if (result != NM_AUTH_CALL_RESULT_YES) { + if (result != NM_AUTH_CALL_RESULT_YES) { ret_error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, "Not authorized to reload configuration"); @@ -1188,14 +1195,11 @@ _reload_auth_cb (NMAuthChain *chain, if (ret_error) { g_dbus_method_invocation_take_error (context, ret_error); - goto out; + return; } nm_config_reload (priv->config, reload_type, TRUE); g_dbus_method_invocation_return_value (context, NULL); - -out: - nm_auth_chain_destroy (chain); } static void @@ -1223,7 +1227,7 @@ impl_manager_reload (NMDBusObject *obj, return; } - priv->auth_chains = g_slist_append (priv->auth_chains, chain); + c_list_link_tail (&priv->auth_lst_head, nm_auth_chain_parent_lst_list (chain)); nm_auth_chain_set_data (chain, "flags", GUINT_TO_POINTER (flags), NULL); nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_RELOAD, TRUE); } @@ -1635,17 +1639,16 @@ device_is_wake_on_lan (NMPlatform *platform, NMDevice *device) static void remove_device (NMManager *self, NMDevice *device, - gboolean quitting, - gboolean allow_unmanage) + gboolean quitting) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); gboolean unmanage = FALSE; - _LOG2D (LOGD_DEVICE, device, "removing device (allow_unmanage %d, managed %d, wol %d)", - allow_unmanage, nm_device_get_managed (device, FALSE), + _LOG2D (LOGD_DEVICE, device, "removing device (managed %d, wol %d)", + nm_device_get_managed (device, FALSE), device_is_wake_on_lan (priv->platform, device)); - if (allow_unmanage && nm_device_get_managed (device, FALSE)) { + if (nm_device_get_managed (device, FALSE)) { if (quitting) { /* Leave configured if wo(w)lan and quitting */ @@ -1712,7 +1715,7 @@ remove_device (NMManager *self, static void device_removed_cb (NMDevice *device, gpointer user_data) { - remove_device (NM_MANAGER (user_data), device, FALSE, TRUE); + remove_device (NM_MANAGER (user_data), device, FALSE); } NMState @@ -1907,7 +1910,7 @@ nm_manager_remove_device (NMManager *self, const char *ifname, NMDeviceType devi if (!d) return FALSE; - remove_device (self, d, FALSE, FALSE); + remove_device (self, d, FALSE); return TRUE; } @@ -2029,7 +2032,7 @@ system_create_virtual_device (NMManager *self, NMConnection *connection) "couldn't create the device: %s", error->message); g_error_free (error); - remove_device (self, device, FALSE, TRUE); + remove_device (self, device, FALSE); return NULL; } @@ -2075,8 +2078,20 @@ static void connection_changed (NMManager *self, NMSettingsConnection *sett_conn) { + NMManagerPrivate *priv; + NMConnection *connection; NMDevice *device; - NMConnection *connection = nm_settings_connection_get_connection (sett_conn); + + if (NM_FLAGS_HAS (nm_settings_connection_get_flags (sett_conn), + NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE)) + return; + + priv = NM_MANAGER_GET_PRIVATE (self); + + if (!nm_settings_has_connection (priv->settings, sett_conn)) + return; + + connection = nm_settings_connection_get_connection (sett_conn); if (!nm_connection_is_virtual (connection)) return; @@ -2091,48 +2106,69 @@ connection_changed (NMManager *self, retry_connections_for_parent_device (self, device); } +static gboolean +connection_changed_on_idle_cb (gpointer user_data) +{ + NMManager *self = user_data; + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMCListElem *elem; + + priv->connection_changed_on_idle_id = 0; + + while ((elem = c_list_first_entry (&priv->connection_changed_on_idle_lst, NMCListElem, lst))) { + gs_unref_object NMSettingsConnection *sett_conn = NULL; + + sett_conn = nm_c_list_elem_free_steal (elem); + connection_changed (self, sett_conn); + } + + return G_SOURCE_REMOVE; +} + +static void +connection_changed_on_idle (NMManager *self, + NMSettingsConnection *sett_conn) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + + if (priv->connection_changed_on_idle_id == 0) + priv->connection_changed_on_idle_id = g_idle_add (connection_changed_on_idle_cb, self); + + if (!nm_c_list_elem_find_first (&priv->connection_changed_on_idle_lst, sett_conn)) { + c_list_link_tail (&priv->connection_changed_on_idle_lst, + &nm_c_list_elem_new_stale (g_object_ref (sett_conn))->lst); + } +} + static void connection_added_cb (NMSettings *settings, NMSettingsConnection *sett_conn, NMManager *self) { - connection_changed (self, sett_conn); + connection_changed_on_idle (self, sett_conn); } static void connection_updated_cb (NMSettings *settings, NMSettingsConnection *sett_conn, - gboolean by_user, + guint update_reason_u, NMManager *self) { - if (by_user) - connection_changed (self, sett_conn); + connection_changed_on_idle (self, sett_conn); } /*****************************************************************************/ -typedef struct { - CList delete_volatile_connection_lst; - NMSettingsConnection *connection; -} DeleteVolatileConnectionData; - static void _delete_volatile_connection_all (NMManager *self, gboolean do_delete) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - CList *lst; - DeleteVolatileConnectionData *data; + NMCListElem *elem; - while ((lst = c_list_first (&priv->delete_volatile_connection_lst_head))) { + while ((elem = c_list_first_entry (&priv->delete_volatile_connection_lst_head, NMCListElem, lst))) { gs_unref_object NMSettingsConnection *connection = NULL; - data = c_list_entry (lst, - DeleteVolatileConnectionData, - delete_volatile_connection_lst); - connection = data->connection; - c_list_unlink_stale (&data->delete_volatile_connection_lst); - g_slice_free (DeleteVolatileConnectionData, data); - + connection = nm_c_list_elem_free_steal (elem); if (do_delete) _delete_volatile_connection_do (self, connection); } @@ -2156,7 +2192,6 @@ connection_flags_changed (NMSettings *settings, { NMManager *self = user_data; NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - DeleteVolatileConnectionData *data; if (!NM_FLAGS_HAS (nm_settings_connection_get_flags (connection), NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE)) @@ -2168,9 +2203,8 @@ connection_flags_changed (NMSettings *settings, return; } - data = g_slice_new (DeleteVolatileConnectionData); - data->connection = g_object_ref (connection); - c_list_link_tail (&priv->delete_volatile_connection_lst_head, &data->delete_volatile_connection_lst); + c_list_link_tail (&priv->delete_volatile_connection_lst_head, + &nm_c_list_elem_new_stale (g_object_ref (connection))->lst); if (!priv->delete_volatile_connection_idle_id) priv->delete_volatile_connection_idle_id = g_idle_add (_delete_volatile_connection_cb, self); } @@ -2338,41 +2372,32 @@ nm_manager_rfkill_update (NMManager *self, RfKillType rtype) static void device_auth_done_cb (NMAuthChain *chain, - GError *auth_error, GDBusMethodInvocation *context, gpointer user_data) { NMManager *self = NM_MANAGER (user_data); - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - GError *error = NULL; + gs_free_error GError *error = NULL; NMAuthCallResult result; NMDevice *device; const char *permission; NMDeviceAuthRequestFunc callback; NMAuthSubject *subject; - g_assert (context); + nm_assert (G_IS_DBUS_METHOD_INVOCATION (context)); - priv->auth_chains = g_slist_remove (priv->auth_chains, chain); + c_list_unlink (nm_auth_chain_parent_lst_list (chain)); - permission = nm_auth_chain_get_data (chain, "requested-permission"); - g_assert (permission); + permission = nm_auth_chain_get_data (chain, "perm"); + nm_assert (permission); callback = nm_auth_chain_get_data (chain, "callback"); - g_assert (callback); + nm_assert (callback); device = nm_auth_chain_get_data (chain, "device"); - g_assert (device); + nm_assert (NM_IS_DEVICE (device)); result = nm_auth_chain_get_result (chain, permission); subject = nm_auth_chain_get_subject (chain); - if (auth_error) { - /* translate the auth error into a manager permission denied error */ - _LOGD (LOGD_CORE, "%s request failed: %s", permission, auth_error->message); - error = g_error_new (NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - "%s request failed: %s", - permission, auth_error->message); - } else if (result != NM_AUTH_CALL_RESULT_YES) { + if (result != NM_AUTH_CALL_RESULT_YES) { _LOGD (LOGD_CORE, "%s request failed: not authorized", permission); error = g_error_new (NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, @@ -2380,16 +2405,13 @@ device_auth_done_cb (NMAuthChain *chain, permission); } - g_assert (error || (result == NM_AUTH_CALL_RESULT_YES)); + nm_assert (error || (result == NM_AUTH_CALL_RESULT_YES)); callback (device, context, subject, error, nm_auth_chain_get_data (chain, "user-data")); - - g_clear_error (&error); - nm_auth_chain_destroy (chain); } static void @@ -2406,6 +2428,7 @@ device_auth_request_cb (NMDevice *device, GError *error = NULL; NMAuthSubject *subject = NULL; NMAuthChain *chain; + char *permission_dup; /* Validate the caller */ subject = nm_auth_subject_new_unix_process_from_context (context); @@ -2434,12 +2457,14 @@ device_auth_request_cb (NMDevice *device, goto done; } - priv->auth_chains = g_slist_append (priv->auth_chains, chain); + permission_dup = g_strdup (permission); + + c_list_link_tail (&priv->auth_lst_head, nm_auth_chain_parent_lst_list (chain)); nm_auth_chain_set_data (chain, "device", g_object_ref (device), g_object_unref); - nm_auth_chain_set_data (chain, "requested-permission", g_strdup (permission), g_free); nm_auth_chain_set_data (chain, "callback", callback, NULL); nm_auth_chain_set_data (chain, "user-data", user_data, NULL); - nm_auth_chain_add_call (chain, permission, allow_interaction); + nm_auth_chain_set_data (chain, "perm", permission_dup /* transfer ownership */, g_free); + nm_auth_chain_add_call_unsafe (chain, permission_dup, allow_interaction); done: if (error) @@ -2646,8 +2671,14 @@ get_existing_connection (NMManager *self, nm_device_assume_state_reset (device); - added = nm_settings_add_connection (priv->settings, connection, FALSE, &error); - if (!added) { + if (!nm_settings_add_connection (priv->settings, + connection, + NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY, + NM_SETTINGS_CONNECTION_ADD_REASON_NONE, + NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE + | NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED, + &added, + &error)) { _LOG2W (LOGD_SETTINGS, device, "assume: failure to save generated connection '%s': %s", nm_connection_get_id (connection), error->message); @@ -2655,10 +2686,6 @@ get_existing_connection (NMManager *self, return NULL; } - nm_settings_connection_set_flags (added, - NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED | - NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE, - TRUE); NM_SET_OUT (out_generated, TRUE); return added; } @@ -2760,7 +2787,7 @@ recheck_assume_connection (NMManager *self, if (generated) { _LOG2D (LOGD_DEVICE, device, "assume: deleting generated connection after assuming failed"); - nm_settings_connection_delete (sett_conn, NULL); + nm_settings_connection_delete (sett_conn, FALSE); } else { if (nm_device_sys_iface_state_get (device) == NM_DEVICE_SYS_IFACE_STATE_ASSUME) nm_device_sys_iface_state_set (device, NM_DEVICE_SYS_IFACE_STATE_EXTERNAL); @@ -2821,7 +2848,7 @@ device_ip_iface_changed (NMDevice *device, && g_strcmp0 (nm_device_get_iface (candidate), ip_iface) == 0 && nm_device_get_device_type (candidate) == device_type && nm_device_is_real (candidate)) { - remove_device (self, candidate, FALSE, FALSE); + remove_device (self, candidate, FALSE); break; } } @@ -3026,7 +3053,7 @@ add_device (NMManager *self, NMDevice *device, GError **error) remove = g_slist_prepend (remove, candidate); } for (iter = remove; iter; iter = iter->next) - remove_device (self, NM_DEVICE (iter->data), FALSE, FALSE); + remove_device (self, NM_DEVICE (iter->data), FALSE); g_slist_free (remove); g_object_ref (device); @@ -3215,7 +3242,7 @@ platform_link_added (NMManager *self, */ _LOGD (LOGD_DEVICE, "(%s): removing old device %p after ifindex change from %d to %d", plink->name, candidate, nm_device_get_ifindex (candidate), ifindex); - remove_device (self, candidate, FALSE, TRUE); + remove_device (self, candidate, FALSE); goto add; } return; @@ -3348,13 +3375,13 @@ _platform_link_cb_idle (PlatformLinkCbData *data) if (!nm_device_unrealize (device, FALSE, &error)) { _LOG2W (LOGD_DEVICE, device, "failed to unrealize: %s", error->message); g_clear_error (&error); - remove_device (self, device, FALSE, TRUE); + remove_device (self, device, FALSE); } else { nm_device_update_from_platform_link (device, NULL); } } else { /* Hardware and external devices always get removed when their kernel link is gone */ - remove_device (self, device, FALSE, TRUE); + remove_device (self, device, FALSE); } } } @@ -4153,7 +4180,8 @@ static SlaveConnectionInfo * find_slaves (NMManager *manager, NMSettingsConnection *sett_conn, NMDevice *device, - guint *out_n_slaves) + guint *out_n_slaves, + gboolean for_user_request) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); gs_free NMSettingsConnection **all_connections = NULL; @@ -4195,7 +4223,7 @@ find_slaves (NMManager *manager, slave_device = nm_manager_get_best_device_for_connection (manager, candidate, NULL, - FALSE, + for_user_request, devices, NULL); @@ -4271,7 +4299,8 @@ static void autoconnect_slaves (NMManager *self, NMSettingsConnection *master_connection, NMDevice *master_device, - NMAuthSubject *subject) + NMAuthSubject *subject, + gboolean for_user_request) { GError *local_err = NULL; @@ -4281,7 +4310,7 @@ autoconnect_slaves (NMManager *self, guint i, n_slaves = 0; gboolean bind_lifetime_to_profile_visibility; - slaves = find_slaves (self, master_connection, master_device, &n_slaves); + slaves = find_slaves (self, master_connection, master_device, &n_slaves, for_user_request); if (n_slaves > 1) { gs_free char *value = NULL; @@ -4681,7 +4710,8 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError * } /* Check slaves for master connection and possibly activate them */ - autoconnect_slaves (self, sett_conn, device, nm_active_connection_get_subject (active)); + autoconnect_slaves (self, sett_conn, device, nm_active_connection_get_subject (active), + nm_active_connection_get_activation_reason (active) == NM_ACTIVATION_REASON_USER_REQUEST); multi_connect = _nm_connection_get_multi_connect (nm_settings_connection_get_connection (sett_conn)); if ( multi_connect == NM_CONNECTION_MULTI_CONNECT_MULTIPLE @@ -4892,9 +4922,7 @@ _internal_activation_auth_done (NMManager *self, c_list_for_each_entry (ac, &priv->active_connections_lst_head, active_connections_lst) { if ( nm_active_connection_get_device (ac) == nm_active_connection_get_device (active) && nm_active_connection_get_settings_connection (ac) == nm_active_connection_get_settings_connection (active) - && NM_IN_SET (nm_active_connection_get_state (ac), - NM_ACTIVE_CONNECTION_STATE_ACTIVATING, - NM_ACTIVE_CONNECTION_STATE_ACTIVATED)) { + && nm_active_connection_get_state (ac) <= NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { g_set_error (&error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_ALREADY_ACTIVE, @@ -5300,14 +5328,11 @@ 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, &persist_ptr, &async_op_type_ptr); - persist = GPOINTER_TO_INT (persist_ptr); + nm_utils_user_data_unpack (user_data, &self, &active, &async_op_type_ptr); async_op_type = GPOINTER_TO_INT (async_op_type_ptr); if (error) @@ -5315,17 +5340,8 @@ activation_add_done (NMSettings *settings, nm_active_connection_set_settings_connection (active, new_connection); - if (!_internal_activate_generic (self, active, &local)) { - error = local; + if (!_internal_activate_generic (self, active, &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)", @@ -5348,13 +5364,17 @@ activation_add_done (NMSettings *settings, return; fail: - nm_assert (error); + if (local) { + nm_assert (!error); + error = local; + } else + nm_assert (error); nm_active_connection_set_state_fail (active, NM_ACTIVE_CONNECTION_STATE_REASON_UNKNOWN, error->message); if (new_connection) - nm_settings_connection_delete (new_connection, NULL); + nm_settings_connection_delete (new_connection, FALSE); g_dbus_method_invocation_return_gerror (context, error); nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD_ACTIVATE, NULL, @@ -5370,7 +5390,8 @@ _add_and_activate_auth_done (NMManager *self, NMActiveConnection *active, NMConnection *connection, GDBusMethodInvocation *invocation, - NMSettingsConnectionPersistMode persist, + NMSettingsConnectionPersistMode persist_mode, + gboolean is_volatile, gboolean success, const char *error_desc) { @@ -5398,13 +5419,16 @@ _add_and_activate_auth_done (NMManager *self, * shutdown. */ nm_settings_add_connection_dbus (priv->settings, connection, - FALSE, + persist_mode, + NM_SETTINGS_CONNECTION_ADD_REASON_NONE, + ( is_volatile + ? NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE + : NM_SETTINGS_CONNECTION_INT_FLAGS_NONE), nm_active_connection_get_subject (active), invocation, activation_add_done, nm_utils_user_data_pack (self, g_object_ref (active), - GINT_TO_POINTER (persist), GINT_TO_POINTER (async_op_type))); } @@ -5430,7 +5454,8 @@ impl_manager_add_and_activate_connection (NMDBusObject *obj, const char *device_path; const char *specific_object_path; gs_free NMConnection **conns = NULL; - NMSettingsConnectionPersistMode persist = NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK; + NMSettingsConnectionPersistMode persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK; + gboolean is_volatile = FALSE; gboolean bind_dbus_client = FALSE; AsyncOpType async_op_type; @@ -5459,13 +5484,17 @@ impl_manager_add_and_activate_connection (NMDBusObject *obj, && 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 { + is_volatile = FALSE; + persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK; + + if (nm_streq (s, "volatile")) { + persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY; + is_volatile = TRUE; + } else if (nm_streq (s, "memory")) + persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY; + else if (nm_streq (s, "disk")) { + /* pass */ + } else { error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_INVALID_ARGUMENTS, "Option \"persist\" must be one of \"volatile\", \"memory\" or \"disk\""); @@ -5538,6 +5567,7 @@ impl_manager_add_and_activate_connection (NMDBusObject *obj, NULL, _("VPN connection"), NULL, + NULL, FALSE); /* No IPv6 by default for now */ } else { conns = nm_settings_connections_array_to_connections (nm_settings_get_connections (priv->settings, NULL), -1); @@ -5550,6 +5580,8 @@ impl_manager_add_and_activate_connection (NMDBusObject *obj, goto error; } + nm_assert (_nm_connection_verify (incompl_conn, NULL) == NM_SETTING_VERIFY_SUCCESS); + active = _new_active_connection (self, is_vpn, NULL, @@ -5581,7 +5613,8 @@ impl_manager_add_and_activate_connection (NMDBusObject *obj, active, invocation, incompl_conn, - persist)); + persist_mode, + is_volatile)); /* we passed the pointers on to _async_op_data_new_ac_auth_add_and_activate() */ g_steal_pointer (&incompl_conn); @@ -5625,32 +5658,24 @@ nm_manager_deactivate_connection (NMManager *manager, static void deactivate_net_auth_done_cb (NMAuthChain *chain, - GError *auth_error, GDBusMethodInvocation *context, gpointer user_data) { NMManager *self = NM_MANAGER (user_data); - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); GError *error = NULL; NMAuthCallResult result; NMActiveConnection *active; char *path; - g_assert (context); + nm_assert (G_IS_DBUS_METHOD_INVOCATION (context)); - priv->auth_chains = g_slist_remove (priv->auth_chains, chain); + c_list_unlink (nm_auth_chain_parent_lst_list (chain)); path = nm_auth_chain_get_data (chain, "path"); result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL); active = active_connection_get_by_path (self, path); - if (auth_error) { - _LOGD (LOGD_CORE, "Disconnect request failed: %s", auth_error->message); - error = g_error_new (NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - "Deactivate request failed: %s", - auth_error->message); - } else if (result != NM_AUTH_CALL_RESULT_YES) { + if (result != NM_AUTH_CALL_RESULT_YES) { error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, "Not authorized to deactivate connections"); @@ -5680,8 +5705,6 @@ deactivate_net_auth_done_cb (NMAuthChain *chain, g_dbus_method_invocation_take_error (context, error); else g_dbus_method_invocation_return_value (context, NULL); - - nm_auth_chain_destroy (chain); } static void @@ -5741,7 +5764,7 @@ impl_manager_deactivate_connection (NMDBusObject *obj, goto done; } - priv->auth_chains = g_slist_append (priv->auth_chains, chain); + c_list_link_tail (&priv->auth_lst_head, nm_auth_chain_parent_lst_list (chain)); nm_auth_chain_set_data (chain, "path", g_strdup (active_path), g_free); nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE); @@ -5866,9 +5889,15 @@ do_sleep_wake (NMManager *self, gboolean sleeping_changed) * suspend/resume? */ c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { - /* FIXME: shouldn't we be unmanaging software devices if !suspending? */ - if (nm_device_is_software (device)) - continue; + if (nm_device_is_software (device)) { + /* If a user disables networking we consider that as an + * indication that also software devices must be disconnected. + * But we don't want to destroy them for external events as + * a system suspend. + */ + if (suspending) + continue; + } /* Wake-on-LAN devices will be taken down post-suspend rather than pre- */ if ( suspending && device_is_wake_on_lan (priv->platform, device)) { @@ -5923,10 +5952,10 @@ do_sleep_wake (NMManager *self, gboolean sleeping_changed) c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { guint i; - if (nm_device_is_software (device)) { - /* We do not manage/unmanage software devices but - * their dhcp leases could have gone stale so we need - * to renew them */ + if ( nm_device_is_software (device) + && !nm_device_get_unmanaged_flags (device, NM_UNMANAGED_SLEEPING)) { + /* DHCP leases of software devices could have gone stale + * so we need to renew them. */ nm_device_update_dynamic_ip_setup (device); continue; } @@ -5978,45 +6007,6 @@ _internal_sleep (NMManager *self, gboolean do_sleep) _notify (self, PROP_SLEEPING); } -#if 0 -static void -sleep_auth_done_cb (NMAuthChain *chain, - GError *error, - GDBusMethodInvocation *context, - gpointer user_data) -{ - NMManager *self = NM_MANAGER (user_data); - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - GError *ret_error; - NMAuthCallResult result; - gboolean do_sleep; - - priv->auth_chains = g_slist_remove (priv->auth_chains, chain); - - result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_SLEEP_WAKE); - if (error) { - _LOGD (LOGD_SUSPEND, "Sleep/wake request failed: %s", error->message); - ret_error = g_error_new (NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - "Sleep/wake request failed: %s", - error->message); - g_dbus_method_invocation_take_error (context, ret_error); - } else if (result != NM_AUTH_CALL_RESULT_YES) { - ret_error = g_error_new_literal (NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - "Not authorized to sleep/wake"); - g_dbus_method_invocation_take_error (context, ret_error); - } else { - /* Auth success */ - do_sleep = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "sleep")); - _internal_sleep (self, do_sleep); - g_dbus_method_invocation_return_value (context, NULL); - } - - nm_auth_chain_destroy (chain); -} -#endif - static void impl_manager_sleep (NMDBusObject *obj, const NMDBusInterfaceInfoExtended *interface_info, @@ -6091,49 +6081,37 @@ _internal_enable (NMManager *self, gboolean enable) static void enable_net_done_cb (NMAuthChain *chain, - GError *error, GDBusMethodInvocation *context, gpointer user_data) { NMManager *self = NM_MANAGER (user_data); - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - GError *ret_error = NULL; NMAuthCallResult result; gboolean enable; NMAuthSubject *subject; - g_assert (context); + nm_assert (G_IS_DBUS_METHOD_INVOCATION (context)); - priv->auth_chains = g_slist_remove (priv->auth_chains, chain); + c_list_unlink (nm_auth_chain_parent_lst_list (chain)); enable = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "enable")); subject = nm_auth_chain_get_subject (chain); result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK); - if (error) { - _LOGD (LOGD_CORE, "Enable request failed: %s", error->message); - ret_error = g_error_new (NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - "Enable request failed: %s", - error->message); - } else if (result != NM_AUTH_CALL_RESULT_YES) { + if (result != NM_AUTH_CALL_RESULT_YES) { + GError *ret_error; + ret_error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, "Not authorized to enable/disable networking"); - } else { - /* Auth success */ - _internal_enable (self, enable); - g_dbus_method_invocation_return_value (context, NULL); - nm_audit_log_control_op (NM_AUDIT_OP_NET_CONTROL, enable ? "on" : "off", TRUE, - subject, NULL); - } - - if (ret_error) { nm_audit_log_control_op (NM_AUDIT_OP_NET_CONTROL, enable ? "on" : "off", FALSE, subject, ret_error->message); g_dbus_method_invocation_take_error (context, ret_error); + return; } - nm_auth_chain_destroy (chain); + _internal_enable (self, enable); + g_dbus_method_invocation_return_value (context, NULL); + nm_audit_log_control_op (NM_AUDIT_OP_NET_CONTROL, enable ? "on" : "off", TRUE, + subject, NULL); } static void @@ -6168,7 +6146,7 @@ impl_manager_enable (NMDBusObject *obj, goto done; } - priv->auth_chains = g_slist_append (priv->auth_chains, chain); + c_list_link_tail (&priv->auth_lst_head, nm_auth_chain_parent_lst_list (chain)); nm_auth_chain_set_data (chain, "enable", GUINT_TO_POINTER (enable), NULL); nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK, TRUE); @@ -6198,50 +6176,37 @@ get_perm_add_result (NMManager *self, NMAuthChain *chain, GVariantBuilder *resul static void get_permissions_done_cb (NMAuthChain *chain, - GError *error, GDBusMethodInvocation *context, gpointer user_data) { NMManager *self = NM_MANAGER (user_data); - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - GError *ret_error; GVariantBuilder results; - g_assert (context); + nm_assert (G_IS_DBUS_METHOD_INVOCATION (context)); - priv->auth_chains = g_slist_remove (priv->auth_chains, chain); - if (error) { - _LOGD (LOGD_CORE, "Permissions request failed: %s", error->message); - ret_error = g_error_new (NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - "Permissions request failed: %s", - error->message); - g_dbus_method_invocation_take_error (context, ret_error); - } else { - g_variant_builder_init (&results, G_VARIANT_TYPE ("a{ss}")); + c_list_unlink (nm_auth_chain_parent_lst_list (chain)); - get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK); - get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_SLEEP_WAKE); - get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI); - get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN); - get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_ENABLE_DISABLE_WIMAX); - get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_NETWORK_CONTROL); - get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED); - get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN); - get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM); - get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN); - get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME); - get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_SETTINGS_MODIFY_GLOBAL_DNS); - get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_RELOAD); - get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK); - get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_ENABLE_DISABLE_STATISTICS); - get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_ENABLE_DISABLE_CONNECTIVITY_CHECK); + g_variant_builder_init (&results, G_VARIANT_TYPE ("a{ss}")); - g_dbus_method_invocation_return_value (context, - g_variant_new ("(a{ss})", &results)); - } + get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK); + get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_SLEEP_WAKE); + get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI); + get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN); + get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_ENABLE_DISABLE_WIMAX); + get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_NETWORK_CONTROL); + get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED); + get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN); + get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM); + get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN); + get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME); + get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_SETTINGS_MODIFY_GLOBAL_DNS); + get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_RELOAD); + get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK); + get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_ENABLE_DISABLE_STATISTICS); + get_perm_add_result (self, chain, &results, NM_AUTH_PERMISSION_ENABLE_DISABLE_CONNECTIVITY_CHECK); - nm_auth_chain_destroy (chain); + g_dbus_method_invocation_return_value (context, + g_variant_new ("(a{ss})", &results)); } static void @@ -6266,7 +6231,7 @@ impl_manager_get_permissions (NMDBusObject *obj, return; } - priv->auth_chains = g_slist_append (priv->auth_chains, chain); + c_list_link_tail (&priv->auth_lst_head, nm_auth_chain_parent_lst_list (chain)); nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK, FALSE); nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SLEEP_WAKE, FALSE); nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI, FALSE); @@ -6407,7 +6372,6 @@ device_connectivity_done (NMDevice *device, static void check_connectivity_auth_done_cb (NMAuthChain *chain, - GError *auth_error, GDBusMethodInvocation *context, gpointer user_data) { @@ -6418,25 +6382,18 @@ check_connectivity_auth_done_cb (NMAuthChain *chain, ConnectivityCheckData *data; NMDevice *device; - priv->auth_chains = g_slist_remove (priv->auth_chains, chain); + c_list_unlink (nm_auth_chain_parent_lst_list (chain)); result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL); - if (auth_error) { - _LOGD (LOGD_CORE, "CheckConnectivity request failed: %s", auth_error->message); - error = g_error_new (NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - "Connectivity check request failed: %s", - auth_error->message); - } else if (result != NM_AUTH_CALL_RESULT_YES) { + if (result != NM_AUTH_CALL_RESULT_YES) { error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, "Not authorized to recheck connectivity"); } - if (error) { g_dbus_method_invocation_take_error (context, error); - goto out; + return; } data = g_slice_new (ConnectivityCheckData); @@ -6467,9 +6424,6 @@ check_connectivity_auth_done_cb (NMAuthChain *chain, data); /* @data got destroyed. */ } - -out: - nm_auth_chain_destroy (chain); } static void @@ -6494,7 +6448,7 @@ impl_manager_check_connectivity (NMDBusObject *obj, return; } - priv->auth_chains = g_slist_append (priv->auth_chains, chain); + c_list_link_tail (&priv->auth_lst_head, nm_auth_chain_parent_lst_list (chain)); nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE); } @@ -6518,6 +6472,7 @@ nm_manager_write_device_state (NMManager *self, NMDevice *device) guint32 route_metric_default_effective; int nm_owned; NMDhcp4Config *dhcp4_config; + const char *next_server = NULL; const char *root_path = NULL; ifindex = nm_device_get_ip_ifindex (device); @@ -6555,8 +6510,10 @@ nm_manager_write_device_state (NMManager *self, NMDevice *device) TRUE, &route_metric_default_aspired); dhcp4_config = nm_device_get_dhcp4_config (device); - if (dhcp4_config) + if (dhcp4_config) { root_path = nm_dhcp4_config_get_option (dhcp4_config, "root_path"); + next_server = nm_dhcp4_config_get_option (dhcp4_config, "next_server"); + } return nm_config_device_state_write (ifindex, managed_type, @@ -6565,6 +6522,7 @@ nm_manager_write_device_state (NMManager *self, NMDevice *device) nm_owned, route_metric_default_aspired, route_metric_default_effective, + next_server, root_path); } @@ -6606,8 +6564,9 @@ nm_manager_start (NMManager *self, GError **error) gs_free NMSettingsConnection **connections = NULL; guint i; - if (!nm_settings_start (priv->settings, error)) - return FALSE; + nm_device_factory_manager_load_factories (_register_device_factory, self); + + nm_device_factory_manager_for_each_factory (start_factory, NULL); /* Set initial radio enabled/disabled state */ for (i = 0; i < RFKILL_TYPE_MAX; i++) { @@ -6630,16 +6589,15 @@ nm_manager_start (NMManager *self, GError **error) manager_update_radio_enabled (self, rstate, enabled); } - /* Log overall networking status - enabled/disabled */ _LOGI (LOGD_CORE, "Networking is %s by state file", priv->net_enabled ? "enabled" : "disabled"); system_unmanaged_devices_changed_cb (priv->settings, NULL, self); + hostname_changed_cb (priv->hostname_manager, NULL, self); - /* Start device factories */ - nm_device_factory_manager_load_factories (_register_device_factory, self); - nm_device_factory_manager_for_each_factory (start_factory, NULL); + if (!nm_settings_start (priv->settings, error)) + return FALSE; nm_platform_process_events (priv->platform); @@ -6653,10 +6611,11 @@ nm_manager_start (NMManager *self, GError **error) /* Load VPN plugins */ priv->vpn_manager = g_object_ref (nm_vpn_manager_get ()); - /* Connections added before the manager is started do not emit - * connection-added signals thus devices have to be created manually. - */ _LOGD (LOGD_CORE, "creating virtual devices..."); + g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_ADDED, + G_CALLBACK (connection_added_cb), self); + g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_UPDATED, + G_CALLBACK (connection_updated_cb), self); connections = nm_settings_get_connections_clone (priv->settings, NULL, NULL, NULL, nm_settings_connection_cmp_autoconnect_priority_p_with_data, NULL); @@ -6691,7 +6650,7 @@ nm_manager_stop (NMManager *self) nm_dbus_manager_stop (nm_dbus_object_get_manager (NM_DBUS_OBJECT (self))); while ((device = c_list_first_entry (&priv->devices_lst_head, NMDevice, devices_lst))) - remove_device (self, device, TRUE, TRUE); + remove_device (self, device, TRUE); _active_connection_cleanup (self); @@ -6841,7 +6800,6 @@ typedef struct { static void _dbus_set_property_auth_cb (NMAuthChain *chain, - GError *error, GDBusMethodInvocation *invocation, gpointer user_data) { @@ -6861,13 +6819,12 @@ _dbus_set_property_auth_cb (NMAuthChain *chain, g_slice_free (DBusSetPropertyHandle, handle_data); - priv->auth_chains = g_slist_remove (priv->auth_chains, chain); + c_list_unlink (nm_auth_chain_parent_lst_list (chain)); result = nm_auth_chain_get_result (chain, property_info->writable.permission); - if ( error - || result != NM_AUTH_CALL_RESULT_YES) { + if (result != NM_AUTH_CALL_RESULT_YES) { error_name = NM_PERM_DENIED_ERROR; - error_message = error ? error->message : "Not authorized to perform this operation"; + error_message = "Not authorized to perform this operation"; goto out; } @@ -6908,7 +6865,6 @@ out: g_dbus_method_invocation_return_dbus_error (invocation, error_name, error_message); else g_dbus_method_invocation_return_value (invocation, NULL); - nm_auth_chain_destroy (chain); } void @@ -6943,8 +6899,8 @@ nm_manager_dbus_set_property_handle (NMDBusObject *obj, handle_data->export_version_id = nm_dbus_object_get_export_version_id (obj); chain = nm_auth_chain_new_subject (subject, invocation, _dbus_set_property_auth_cb, handle_data); - priv->auth_chains = g_slist_append (priv->auth_chains, chain); - nm_auth_chain_add_call (chain, property_info->writable.permission, TRUE); + c_list_link_tail (&priv->auth_lst_head, nm_auth_chain_parent_lst_list (chain)); + nm_auth_chain_add_call_unsafe (chain, property_info->writable.permission, TRUE); return; err: @@ -6973,13 +6929,13 @@ _checkpoint_mgr_get (NMManager *self, gboolean create_as_needed) static void checkpoint_auth_done_cb (NMAuthChain *chain, - GError *auth_error, GDBusMethodInvocation *context, gpointer user_data) { NMManager *self = NM_MANAGER (user_data); - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - char *op, *checkpoint_path = NULL, **devices; + char *op; + char *checkpoint_path = NULL; + char **devices; NMCheckpoint *checkpoint; NMAuthCallResult result; guint32 timeout, flags; @@ -6989,7 +6945,7 @@ checkpoint_auth_done_cb (NMAuthChain *chain, guint32 add_timeout; op = nm_auth_chain_get_data (chain, "audit-op"); - priv->auth_chains = g_slist_remove (priv->auth_chains, chain); + c_list_unlink (nm_auth_chain_parent_lst_list (chain)); result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK); if (NM_IN_STRSET (op, NM_AUDIT_OP_CHECKPOINT_DESTROY, @@ -6997,12 +6953,7 @@ checkpoint_auth_done_cb (NMAuthChain *chain, NM_AUDIT_OP_CHECKPOINT_ADJUST_ROLLBACK_TIMEOUT)) arg = checkpoint_path = nm_auth_chain_get_data (chain, "checkpoint_path"); - if (auth_error) { - error = g_error_new (NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - "checkpoint check request failed: %s", - auth_error->message); - } else if (result != NM_AUTH_CALL_RESULT_YES) { + if (result != NM_AUTH_CALL_RESULT_YES) { error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, "Not authorized to checkpoint/rollback"); @@ -7042,8 +6993,6 @@ checkpoint_auth_done_cb (NMAuthChain *chain, g_dbus_method_invocation_take_error (context, error); else g_dbus_method_invocation_return_value (context, variant); - - nm_auth_chain_destroy (chain); } static void @@ -7075,7 +7024,7 @@ impl_manager_checkpoint_create (NMDBusObject *obj, g_variant_get (parameters, "(^aouu)", &devices, &rollback_timeout, &flags); - priv->auth_chains = g_slist_append (priv->auth_chains, chain); + c_list_link_tail (&priv->auth_lst_head, nm_auth_chain_parent_lst_list (chain)); nm_auth_chain_set_data (chain, "audit-op", NM_AUDIT_OP_CHECKPOINT_CREATE, NULL); nm_auth_chain_set_data (chain, "devices", devices, (GDestroyNotify) g_strfreev); nm_auth_chain_set_data (chain, "flags", GUINT_TO_POINTER (flags), NULL); @@ -7108,7 +7057,7 @@ impl_manager_checkpoint_destroy (NMDBusObject *obj, g_variant_get (parameters, "(&o)", &checkpoint_path); - priv->auth_chains = g_slist_append (priv->auth_chains, chain); + c_list_link_tail (&priv->auth_lst_head, nm_auth_chain_parent_lst_list (chain)); nm_auth_chain_set_data (chain, "audit-op", NM_AUDIT_OP_CHECKPOINT_DESTROY, NULL); nm_auth_chain_set_data (chain, "checkpoint_path", g_strdup (checkpoint_path), g_free); nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK, TRUE); @@ -7139,7 +7088,7 @@ impl_manager_checkpoint_rollback (NMDBusObject *obj, g_variant_get (parameters, "(&o)", &checkpoint_path); - priv->auth_chains = g_slist_append (priv->auth_chains, chain); + c_list_link_tail (&priv->auth_lst_head, nm_auth_chain_parent_lst_list (chain)); nm_auth_chain_set_data (chain, "audit-op", NM_AUDIT_OP_CHECKPOINT_ROLLBACK, NULL); nm_auth_chain_set_data (chain, "checkpoint_path", g_strdup (checkpoint_path), g_free); nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK, TRUE); @@ -7171,7 +7120,7 @@ impl_manager_checkpoint_adjust_rollback_timeout (NMDBusObject *obj, g_variant_get (parameters, "(&ou)", &checkpoint_path, &add_timeout); - priv->auth_chains = g_slist_append (priv->auth_chains, chain); + c_list_link_tail (&priv->auth_lst_head, nm_auth_chain_parent_lst_list (chain)); nm_auth_chain_set_data (chain, "audit-op", NM_AUDIT_OP_CHECKPOINT_ADJUST_ROLLBACK_TIMEOUT, NULL); nm_auth_chain_set_data (chain, "checkpoint_path", g_strdup (checkpoint_path), g_free); nm_auth_chain_set_data (chain, "add_timeout", GUINT_TO_POINTER (add_timeout), NULL); @@ -7307,7 +7256,7 @@ periodic_update_active_connection_timestamps (gpointer user_data) c_list_for_each_entry (ac, &priv->active_connections_lst_head, active_connections_lst) { if (nm_active_connection_get_state (ac) == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { nm_settings_connection_update_timestamp (nm_active_connection_get_settings_connection (ac), - (guint64) time (NULL), FALSE); + (guint64) time (NULL)); } } return G_SOURCE_CONTINUE; @@ -7377,7 +7326,8 @@ nm_manager_setup (void) singleton_instance = self; nm_singleton_instance_register (); - _LOGD (LOGD_CORE, "setup %s singleton (%p)", "NMManager", singleton_instance); + nm_log_dbg (LOGD_CORE, "setup %s singleton ("NM_HASH_OBFUSCATE_PTR_FMT")", + "NMManager", NM_HASH_OBFUSCATE_PTR (singleton_instance)); nm_dbus_object_export (NM_DBUS_OBJECT (self)); return self; @@ -7400,10 +7350,6 @@ constructed (GObject *object) G_CALLBACK (settings_startup_complete_changed), self); g_signal_connect (priv->settings, "notify::" NM_SETTINGS_UNMANAGED_SPECS, G_CALLBACK (system_unmanaged_devices_changed_cb), self); - g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_ADDED, - G_CALLBACK (connection_added_cb), self); - g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_UPDATED, - G_CALLBACK (connection_updated_cb), self); g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_FLAGS_CHANGED, G_CALLBACK (connection_flags_changed), self); priv->hostname_manager = g_object_ref (nm_hostname_manager_get ()); @@ -7461,11 +7407,13 @@ nm_manager_init (NMManager *self) guint i; GFile *file; + c_list_init (&priv->auth_lst_head); c_list_init (&priv->link_cb_lst); c_list_init (&priv->devices_lst_head); c_list_init (&priv->active_connections_lst_head); c_list_init (&priv->async_op_lst_head); c_list_init (&priv->delete_volatile_connection_lst_head); + c_list_init (&priv->connection_changed_on_idle_lst); priv->platform = g_object_ref (NM_PLATFORM_GET); @@ -7602,6 +7550,10 @@ get_property (GObject *object, guint prop_id, case PROP_CONNECTIVITY_CHECK_ENABLED: g_value_set_boolean (value, concheck_enabled (self, NULL)); break; + case PROP_CONNECTIVITY_CHECK_URI: + config_data = nm_config_get_data (priv->config); + g_value_set_string (value, nm_config_data_get_connectivity_uri (config_data)); + break; case PROP_PRIMARY_CONNECTION: nm_dbus_utils_g_value_set_object_path (value, priv->primary_connection); break; @@ -7709,24 +7661,23 @@ dispose (GObject *object) { NMManager *self = NM_MANAGER (object); NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - CList *iter, *iter_safe; - NMActiveConnection *ac, *ac_safe; + CList *iter; nm_assert (c_list_is_empty (&priv->async_op_lst_head)); g_signal_handlers_disconnect_by_func (priv->platform, G_CALLBACK (platform_link_cb), self); - c_list_for_each_safe (iter, iter_safe, &priv->link_cb_lst) { + while ((iter = c_list_first (&priv->link_cb_lst))) { PlatformLinkCbData *data = c_list_entry (iter, PlatformLinkCbData, lst); g_source_remove (data->idle_id); - c_list_unlink_stale (iter); + c_list_unlink_stale (&data->lst); g_slice_free (PlatformLinkCbData, data); } - g_slist_free_full (priv->auth_chains, (GDestroyNotify) nm_auth_chain_destroy); - priv->auth_chains = NULL; + while ((iter = c_list_first (&priv->auth_lst_head))) + nm_auth_chain_destroy (nm_auth_chain_parent_lst_entry (iter)); nm_clear_g_source (&priv->devices_inited_id); @@ -7750,8 +7701,9 @@ dispose (GObject *object) nm_clear_g_source (&priv->ac_cleanup_id); - c_list_for_each_entry_safe (ac, ac_safe, &priv->active_connections_lst_head, active_connections_lst) - active_connection_remove (self, ac); + while ((iter = c_list_first (&priv->active_connections_lst_head))) + active_connection_remove (self, c_list_entry (iter, NMActiveConnection, active_connections_lst)); + nm_assert (c_list_is_empty (&priv->active_connections_lst_head)); g_clear_object (&priv->primary_connection); g_clear_object (&priv->activating_connection); @@ -7767,6 +7719,9 @@ dispose (GObject *object) g_clear_object (&priv->policy); } + nm_clear_g_source (&priv->connection_changed_on_idle_id); + nm_c_list_elem_free_all (&priv->connection_changed_on_idle_lst, g_object_unref); + if (priv->settings) { g_signal_handlers_disconnect_by_func (priv->settings, settings_startup_complete_changed, self); g_signal_handlers_disconnect_by_func (priv->settings, system_unmanaged_devices_changed_cb, self); @@ -8095,6 +8050,7 @@ static const NMDBusInterfaceInfoExtended interface_info_manager = { NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Connectivity", "u", NM_MANAGER_CONNECTIVITY), NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("ConnectivityCheckAvailable", "b", NM_MANAGER_CONNECTIVITY_CHECK_AVAILABLE), NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_L ("ConnectivityCheckEnabled", "b", NM_MANAGER_CONNECTIVITY_CHECK_ENABLED, NM_AUTH_PERMISSION_ENABLE_DISABLE_CONNECTIVITY_CHECK, NM_AUDIT_OP_NET_CONTROL), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("ConnectivityCheckUri", "s", NM_MANAGER_CONNECTIVITY_CHECK_URI), NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_L ("GlobalDnsConfiguration", "a{sv}", NM_MANAGER_GLOBAL_DNS_CONFIGURATION, NM_AUTH_PERMISSION_SETTINGS_MODIFY_GLOBAL_DNS, NM_AUDIT_OP_NET_CONTROL), ), ), @@ -8207,6 +8163,12 @@ nm_manager_class_init (NMManagerClass *manager_class) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_CONNECTIVITY_CHECK_URI] = + g_param_spec_string (NM_MANAGER_CONNECTIVITY_CHECK_URI, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + obj_properties[PROP_PRIMARY_CONNECTION] = g_param_spec_string (NM_MANAGER_PRIMARY_CONNECTION, "", "", NULL, diff --git a/src/nm-manager.h b/src/nm-manager.h index ecb4b017..966abce0 100644 --- a/src/nm-manager.h +++ b/src/nm-manager.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -48,6 +47,7 @@ #define NM_MANAGER_CONNECTIVITY "connectivity" #define NM_MANAGER_CONNECTIVITY_CHECK_AVAILABLE "connectivity-check-available" #define NM_MANAGER_CONNECTIVITY_CHECK_ENABLED "connectivity-check-enabled" +#define NM_MANAGER_CONNECTIVITY_CHECK_URI "connectivity-check-uri" #define NM_MANAGER_PRIMARY_CONNECTION "primary-connection" #define NM_MANAGER_PRIMARY_CONNECTION_TYPE "primary-connection-type" #define NM_MANAGER_ACTIVATING_CONNECTION "activating-connection" diff --git a/src/nm-netns.c b/src/nm-netns.c index 650578af..c1ced153 100644 --- a/src/nm-netns.c +++ b/src/nm-netns.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -128,17 +127,27 @@ constructed (GObject *object) priv->rules_manager = nmp_rules_manager_new (priv->platform); - /* Weakly track the default rules and rules that were added - * outside of NetworkManager. */ + /* Weakly track the default rules with a dummy user-tag. These + * rules are always weekly tracked... */ nmp_rules_manager_track_default (priv->rules_manager, AF_UNSPEC, 0, nm_netns_parent_class /* static dummy user-tag */); + + /* Also weakly track all existing rules. These were added before NetworkManager + * starts, so they are probably none of NetworkManager's business. + * + * However note that during service restart, devices may stay up and rules kept. + * That means, after restart such rules may have been added by a previous run + * of NetworkManager, we just don't know. + * + * For that reason, whenever we will touch such rules later one, we make them + * fully owned and no longer weekly tracked. See %NMP_RULES_MANAGER_EXTERN_WEAKLY_TRACKED_USER_TAG. */ nmp_rules_manager_track_from_platform (priv->rules_manager, NULL, AF_UNSPEC, 0, - nm_netns_parent_class /* static dummy user-tag */); + NMP_RULES_MANAGER_EXTERN_WEAKLY_TRACKED_USER_TAG); G_OBJECT_CLASS (nm_netns_parent_class)->constructed (object); } diff --git a/src/nm-netns.h b/src/nm-netns.h index 250d1149..126ef375 100644 --- a/src/nm-netns.h +++ b/src/nm-netns.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/nm-pacrunner-manager.c b/src/nm-pacrunner-manager.c index b9881f76..38b53eab 100644 --- a/src/nm-pacrunner-manager.c +++ b/src/nm-pacrunner-manager.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager * * This program is free software; you can redistribute it and/or modify @@ -23,11 +22,14 @@ #include "nm-pacrunner-manager.h" #include "nm-utils.h" +#include "NetworkManagerUtils.h" #include "platform/nm-platform.h" +#include "nm-dbus-manager.h" #include "nm-proxy-config.h" #include "nm-ip4-config.h" #include "nm-ip6-config.h" #include "c-list/src/c-list.h" +#include "nm-glib-aux/nm-dbus-aux.h" #define PACRUNNER_DBUS_SERVICE "org.pacrunner" #define PACRUNNER_DBUS_INTERFACE "org.pacrunner.Manager" @@ -35,25 +37,27 @@ /*****************************************************************************/ -struct _NMPacrunnerCallId { - CList lst; +struct _NMPacrunnerConfId { + CList conf_id_lst; - /* this might be a dangling pointer after the async operation - * is cancelled. */ - NMPacrunnerManager *manager_maybe_dangling; + NMPacrunnerManager *self; + + GVariant *parameters; - GVariant *args; char *path; + guint64 log_id; guint refcount; }; -typedef struct _NMPacrunnerCallId Config; - typedef struct { - char *iface; - GDBusProxy *pacrunner; + GDBusConnection *dbus_connection; GCancellable *cancellable; - CList configs; + CList conf_id_lst_head; + guint64 log_id_counter; + guint name_owner_changed_id; + bool dbus_initied:1; + bool has_name_owner:1; + bool try_start_blocked:1; } NMPacrunnerManagerPrivate; struct _NMPacrunnerManager { @@ -79,469 +83,511 @@ NM_DEFINE_SINGLETON_GETTER (NMPacrunnerManager, nm_pacrunner_manager_get, NM_TYP #define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "pacrunner", __VA_ARGS__) #define _NMLOG2_PREFIX_NAME "pacrunner" -#define _NMLOG2(level, config, ...) \ +#define _NMLOG2(level, conf_id, ...) \ G_STMT_START { \ nm_log ((level), _NMLOG_DOMAIN, NULL, NULL, \ - "%s%p]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + "%s%"G_GUINT64_FORMAT"]: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ _NMLOG2_PREFIX_NAME": call[", \ - (config) \ - _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + (conf_id)->log_id \ + _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ } G_STMT_END /*****************************************************************************/ -static void pacrunner_remove_done (GObject *source, GAsyncResult *res, gpointer user_data); +static void _call_destroy_proxy_configuration (NMPacrunnerManager *self, + NMPacrunnerConfId *conf_id, + const char *path, + gboolean verbose_log); /*****************************************************************************/ -static Config * -config_new (NMPacrunnerManager *manager, GVariant *args) +static NMPacrunnerConfId * +conf_id_ref (NMPacrunnerConfId *conf_id) { - Config *config; - - config = g_slice_new0 (Config); - config->manager_maybe_dangling = manager; - config->args = g_variant_ref_sink (args); - config->refcount = 1; - c_list_link_tail (&NM_PACRUNNER_MANAGER_GET_PRIVATE (manager)->configs, - &config->lst); + nm_assert (conf_id); + nm_assert (conf_id->refcount > 0); - return config; -} - -static Config * -config_ref (Config *config) -{ - nm_assert (config); - nm_assert (config->refcount > 0); - - config->refcount++; - return config; + conf_id->refcount++; + return conf_id; } static void -config_unref (Config *config) +conf_id_unref (NMPacrunnerConfId *conf_id) { - nm_assert (config); - nm_assert (config->refcount > 0); - - if (config->refcount == 1) { - g_variant_unref (config->args); - g_free (config->path); - c_list_unlink_stale (&config->lst); - g_slice_free (Config, config); + nm_assert (conf_id); + nm_assert (conf_id->refcount > 0); + + if (conf_id->refcount == 1) { + g_variant_unref (conf_id->parameters); + g_free (conf_id->path); + c_list_unlink_stale (&conf_id->conf_id_lst); + g_object_unref (conf_id->self); + g_slice_free (NMPacrunnerConfId, conf_id); } else - config->refcount--; + conf_id->refcount--; } +NM_AUTO_DEFINE_FCN0 (NMPacrunnerConfId *, _nm_auto_unref_conf_id, conf_id_unref); +#define nm_auto_unref_conf_id nm_auto (_nm_auto_unref_conf_id) + /*****************************************************************************/ static void -add_proxy_config (GVariantBuilder *proxy_data, const NMProxyConfig *proxy_config) +get_ip_domains (GPtrArray *domains, NMIPConfig *ip_config) { - const char *pac_url, *pac_script; + NMDedupMultiIter ipconf_iter; + char *cidr; + guint i, num; + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + int addr_family; + + if (!ip_config) + return; + + addr_family = nm_ip_config_get_addr_family (ip_config); + + num = nm_ip_config_get_num_searches (ip_config); + for (i = 0; i < num; i++) + g_ptr_array_add (domains, g_strdup (nm_ip_config_get_search (ip_config, i))); + + num = nm_ip_config_get_num_domains (ip_config); + for (i = 0; i < num; i++) + g_ptr_array_add (domains, g_strdup (nm_ip_config_get_domain (ip_config, i))); + + if (addr_family == AF_INET) { + const NMPlatformIP4Address *address; + + nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, (NMIP4Config *) ip_config, &address) { + cidr = g_strdup_printf ("%s/%u", + nm_utils_inet4_ntop (address->address, sbuf), + address->plen); + g_ptr_array_add (domains, cidr); + } + } else { + const NMPlatformIP6Address *address; + + nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, (NMIP6Config *) ip_config, &address) { + cidr = g_strdup_printf ("%s/%u", + nm_utils_inet6_ntop (&address->address, sbuf), + address->plen); + g_ptr_array_add (domains, cidr); + } + } + + if (addr_family == AF_INET) { + const NMPlatformIP4Route *routes; + + nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, (NMIP4Config *) ip_config, &routes) { + if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (routes)) + continue; + cidr = g_strdup_printf ("%s/%u", + nm_utils_inet4_ntop (routes->network, sbuf), + routes->plen); + g_ptr_array_add (domains, cidr); + } + } else { + const NMPlatformIP6Route *routes; + + nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, (NMIP6Config *) ip_config, &routes) { + if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (routes)) + continue; + cidr = g_strdup_printf ("%s/%u", + nm_utils_inet6_ntop (&routes->network, sbuf), + routes->plen); + g_ptr_array_add (domains, cidr); + } + } +} + +static GVariant * +_make_request_create_proxy_configuration (NMProxyConfig *proxy_config, + const char *iface, + NMIP4Config *ip4_config, + NMIP6Config *ip6_config) +{ + GVariantBuilder builder; NMProxyConfigMethod method; + const char *pac_url; + const char *pac_script; + + nm_assert (NM_IS_PROXY_CONFIG (proxy_config)); + + g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); + + if (iface) { + g_variant_builder_add (&builder, "{sv}", + "Interface", + g_variant_new_string (iface)); + } method = nm_proxy_config_get_method (proxy_config); + switch (method) { + case NM_PROXY_CONFIG_METHOD_AUTO: + g_variant_builder_add (&builder, "{sv}", + "Method", + g_variant_new_string ("auto")); - if (method == NM_PROXY_CONFIG_METHOD_AUTO) { pac_url = nm_proxy_config_get_pac_url (proxy_config); if (pac_url) { - g_variant_builder_add (proxy_data, "{sv}", + g_variant_builder_add (&builder, "{sv}", "URL", g_variant_new_string (pac_url)); } pac_script = nm_proxy_config_get_pac_script (proxy_config); if (pac_script) { - g_variant_builder_add (proxy_data, "{sv}", + g_variant_builder_add (&builder, "{sv}", "Script", g_variant_new_string (pac_script)); } + break; + case NM_PROXY_CONFIG_METHOD_NONE: + g_variant_builder_add (&builder, "{sv}", + "Method", + g_variant_new_string ("direct")); + break; } - g_variant_builder_add (proxy_data, "{sv}", + g_variant_builder_add (&builder, "{sv}", "BrowserOnly", g_variant_new_boolean (nm_proxy_config_get_browser_only (proxy_config))); -} - -static void -get_ip4_domains (GPtrArray *domains, NMIP4Config *ip4) -{ - NMDedupMultiIter ipconf_iter; - char *cidr; - 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++) - g_ptr_array_add (domains, g_strdup (nm_ip4_config_get_search (ip4, i))); + if (ip4_config || ip6_config) { + gs_unref_ptrarray GPtrArray *domains = NULL; - /* Extract domains */ - for (i = 0; i < nm_ip4_config_get_num_domains (ip4); i++) - g_ptr_array_add (domains, g_strdup (nm_ip4_config_get_domain (ip4, i))); + domains = g_ptr_array_new_with_free_func (g_free); - /* Add addresses and routes in CIDR form */ + get_ip_domains (domains, NM_IP_CONFIG_CAST (ip4_config)); + get_ip_domains (domains, NM_IP_CONFIG_CAST (ip6_config)); - nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, ip4, &address) { - cidr = g_strdup_printf ("%s/%u", - nm_utils_inet4_ntop (address->address, sbuf), - address->plen); - g_ptr_array_add (domains, cidr); + if (domains->len > 0) { + g_variant_builder_add (&builder, "{sv}", + "Domains", + g_variant_new_strv ((const char *const*) domains->pdata, + domains->len)); + } } - nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, ip4, &routes) { - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (routes)) - continue; - cidr = g_strdup_printf ("%s/%u", - nm_utils_inet4_ntop (routes->network, sbuf), - routes->plen); - g_ptr_array_add (domains, cidr); - } + return g_variant_new ("(a{sv})", &builder); } +/*****************************************************************************/ + static void -get_ip6_domains (GPtrArray *domains, NMIP6Config *ip6) +_call_destroy_proxy_configuration_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) { - NMDedupMultiIter ipconf_iter; - char *cidr; - 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++) - g_ptr_array_add (domains, g_strdup (nm_ip6_config_get_search (ip6, i))); - - /* Extract domains */ - for (i = 0; i < nm_ip6_config_get_num_domains (ip6); i++) - g_ptr_array_add (domains, g_strdup (nm_ip6_config_get_domain (ip6, i))); - - /* 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, sbuf), - address->plen); - g_ptr_array_add (domains, cidr); - } + nm_auto_unref_conf_id NMPacrunnerConfId *conf_id = user_data; + gs_free_error GError *error = NULL; + gs_unref_variant GVariant *ret = NULL; - nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, ip6, &routes) { - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (routes)) - continue; - cidr = g_strdup_printf ("%s/%u", - nm_utils_inet6_ntop (&routes->network, sbuf), - routes->plen); - g_ptr_array_add (domains, cidr); + ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), res, &error); + if (!ret) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + _LOG2T (conf_id, "destroy proxy configuration: failed with %s", error->message); + else + _LOG2T (conf_id, "destroy proxy configuration: cancelled"); + return; } -} - -/*****************************************************************************/ - -static GCancellable * -_ensure_cancellable (NMPacrunnerManagerPrivate *priv) -{ - if (G_UNLIKELY (!priv->cancellable)) - priv->cancellable = g_cancellable_new (); - return priv->cancellable; + _LOG2T (conf_id, "destroy proxy configuration: success"); } static void -pacrunner_send_done (GObject *source, GAsyncResult *res, gpointer user_data) +_call_create_proxy_configuration_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) { - Config *config = user_data; - NMPacrunnerManager *self; - NMPacrunnerManagerPrivate *priv; + nm_auto_unref_conf_id NMPacrunnerConfId *conf_id = user_data; + NMPacrunnerManager *self = NM_PACRUNNER_MANAGER (conf_id->self); gs_free_error GError *error = NULL; gs_unref_variant GVariant *variant = NULL; const char *path = NULL; - nm_assert (!config->path); + nm_assert (!conf_id->path); - variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); - if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - goto out; + variant = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), res, &error); - self = NM_PACRUNNER_MANAGER (config->manager_maybe_dangling); - priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self); - - if (!variant) - _LOG2D (config, "sending failed: %s", error->message); - else { - g_variant_get (variant, "(&o)", &path); - - if (c_list_is_empty (&config->lst)) { - _LOG2D (config, "sent (%s), but destroy it right away", path); - g_dbus_proxy_call (priv->pacrunner, - "DestroyProxyConfiguration", - g_variant_new ("(o)", path), - G_DBUS_CALL_FLAGS_NO_AUTO_START, - -1, - _ensure_cancellable (priv), - pacrunner_remove_done, - config_ref (config)); - } else { - _LOG2D (config, "sent (%s)", path); - config->path = g_strdup (path); - } + if (!variant) { + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + _LOG2T (conf_id, "create proxy configuration failed: %s", error->message); + else + _LOG2T (conf_id, "create proxy configuration cancelled"); + return; } -out: - config_unref (config); + g_variant_get (variant, "(&o)", &path); + + if (c_list_is_empty (&conf_id->conf_id_lst)) { + _LOG2T (conf_id, "create proxy configuration succeeded (%s), but destroy it right away", path); + _call_destroy_proxy_configuration (self, + conf_id, + path, + FALSE); + } else { + _LOG2T (conf_id, "create proxy configuration succeeded (%s)", path); + conf_id->path = g_strdup (path); + } } static void -pacrunner_send_config (NMPacrunnerManager *self, Config *config) +_call_destroy_proxy_configuration (NMPacrunnerManager *self, + NMPacrunnerConfId *conf_id, + const char *path, + gboolean verbose_log) { NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self); - if (priv->pacrunner) { - _LOG2T (config, "sending..."); - - nm_assert (!config->path); - g_dbus_proxy_call (priv->pacrunner, - "CreateProxyConfiguration", - config->args, - G_DBUS_CALL_FLAGS_NO_AUTO_START, - -1, - _ensure_cancellable (priv), - pacrunner_send_done, - config_ref (config)); - } + if (verbose_log) + _LOG2T (conf_id, "destroy proxy configuration %s...", path); + + g_dbus_connection_call (priv->dbus_connection, + PACRUNNER_DBUS_SERVICE, + PACRUNNER_DBUS_PATH, + PACRUNNER_DBUS_INTERFACE, + "DestroyProxyConfiguration", + g_variant_new ("(o)", path), + G_VARIANT_TYPE ("()"), + G_DBUS_CALL_FLAGS_NO_AUTO_START, + NM_SHUTDOWN_TIMEOUT_MS, + priv->cancellable, + _call_destroy_proxy_configuration_cb, + conf_id_ref (conf_id)); } static void -name_owner_changed (NMPacrunnerManager *self) +_call_create_proxy_configuration (NMPacrunnerManager *self, + NMPacrunnerConfId *conf_id, + gboolean verbose_log) { NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self); - gs_free char *owner = NULL; - CList *iter; - - owner = g_dbus_proxy_get_name_owner (priv->pacrunner); - if (owner) { - _LOGD ("name owner appeared (%s)", owner); - c_list_for_each (iter, &priv->configs) - pacrunner_send_config (self, c_list_entry (iter, Config, lst)); - } else { - _LOGD ("name owner disappeared"); - nm_clear_g_cancellable (&priv->cancellable); - c_list_for_each (iter, &priv->configs) - nm_clear_g_free (&c_list_entry (iter, Config, lst)->path); - } -} -static void -name_owner_changed_cb (GObject *object, - GParamSpec *pspec, - gpointer user_data) -{ - name_owner_changed (user_data); + if (verbose_log) + _LOG2T (conf_id, "create proxy configuration..."); + + g_dbus_connection_call (priv->dbus_connection, + PACRUNNER_DBUS_SERVICE, + PACRUNNER_DBUS_PATH, + PACRUNNER_DBUS_INTERFACE, + "CreateProxyConfiguration", + conf_id->parameters, + G_VARIANT_TYPE ("(o)"), + G_DBUS_CALL_FLAGS_NO_AUTO_START, + NM_SHUTDOWN_TIMEOUT_MS, + priv->cancellable, + _call_create_proxy_configuration_cb, + conf_id_ref (conf_id)); } -static void -pacrunner_proxy_cb (GObject *source, GAsyncResult *res, gpointer user_data) +static gboolean +_try_start_service_by_name (NMPacrunnerManager *self) { - NMPacrunnerManager *self = user_data; - NMPacrunnerManagerPrivate *priv; - gs_free_error GError *error = NULL; - GDBusProxy *proxy; - - proxy = g_dbus_proxy_new_for_bus_finish (res, &error); - if (!proxy) { - if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - _LOGE ("failed to create D-Bus proxy for pacrunner: %s", error->message); - return; - } + NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self); - priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self); + if ( priv->try_start_blocked + || !priv->dbus_initied) + return FALSE; - priv->pacrunner = proxy; - g_signal_connect (priv->pacrunner, "notify::g-name-owner", - G_CALLBACK (name_owner_changed_cb), self); - name_owner_changed (self); + _LOGD ("try D-Bus activating pacrunner..."); + priv->try_start_blocked = TRUE; + nm_dbus_connection_call_start_service_by_name (priv->dbus_connection, + PACRUNNER_DBUS_SERVICE, + -1, + NULL, + NULL, + NULL); + return TRUE; } +/*****************************************************************************/ + /** - * nm_pacrunner_manager_send: + * nm_pacrunner_manager_add: * @self: the #NMPacrunnerManager - * @iface: the iface for the connection or %NULL * @proxy_config: proxy config of the connection + * @iface: the iface for the connection or %NULL * @ip4_config: IP4 config of the connection to extract domain info from * @ip6_config: IP6 config of the connection to extract domain info from * - * Returns: a #NMPacrunnerCallId call id. The function cannot - * fail and always returns a non NULL pointer. The call-id may + * Returns: a #NMPacrunnerConfId id. The function cannot + * fail and always returns a non NULL pointer. The conf-id may * be used to remove the configuration later via nm_pacrunner_manager_remove(). - * Note that the call-id does not keep the @self instance alive. - * If you plan to remove the configuration later, you must keep - * the instance alive long enough. You can remove the configuration - * at most once using this call call-id. + * Note that the conf-id keeps the @self instance alive. */ -NMPacrunnerCallId * -nm_pacrunner_manager_send (NMPacrunnerManager *self, - const char *iface, - NMProxyConfig *proxy_config, - NMIP4Config *ip4_config, - NMIP6Config *ip6_config) +NMPacrunnerConfId * +nm_pacrunner_manager_add (NMPacrunnerManager *self, + NMProxyConfig *proxy_config, + const char *iface, + NMIP4Config *ip4_config, + NMIP6Config *ip6_config) { - char **strv = NULL; - NMProxyConfigMethod method; NMPacrunnerManagerPrivate *priv; - GVariantBuilder proxy_data; - GPtrArray *domains; - Config *config; + NMPacrunnerConfId *conf_id; + gs_free char *log_msg = NULL; g_return_val_if_fail (NM_IS_PACRUNNER_MANAGER (self), NULL); g_return_val_if_fail (proxy_config, NULL); priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self); - g_free (priv->iface); - priv->iface = g_strdup (iface); - - g_variant_builder_init (&proxy_data, G_VARIANT_TYPE_VARDICT); - - if (iface) { - g_variant_builder_add (&proxy_data, "{sv}", - "Interface", - g_variant_new_string (iface)); + conf_id = g_slice_new (NMPacrunnerConfId); + *conf_id = (NMPacrunnerConfId) { + .log_id = ++priv->log_id_counter, + .refcount = 1, + .self = g_object_ref (self), + .parameters = g_variant_ref_sink (_make_request_create_proxy_configuration (proxy_config, + iface, + ip4_config, + ip6_config)), + }; + c_list_link_tail (&priv->conf_id_lst_head, + &conf_id->conf_id_lst); + + if (!priv->has_name_owner) { + _LOG2T (conf_id, "add config: %s (%s)", + (log_msg = g_variant_print (conf_id->parameters, FALSE)), + "pacrunner D-Bus service not running"); + _try_start_service_by_name (self); + } else { + _LOG2T (conf_id, "add config: %s (%s)", + (log_msg = g_variant_print (conf_id->parameters, FALSE)), + "create proxy configuration"); + _call_create_proxy_configuration (self, conf_id, FALSE); } - method = nm_proxy_config_get_method (proxy_config); - switch (method) { - case NM_PROXY_CONFIG_METHOD_AUTO: - g_variant_builder_add (&proxy_data, "{sv}", - "Method", - g_variant_new_string ("auto")); + return conf_id; +} - break; - case NM_PROXY_CONFIG_METHOD_NONE: - g_variant_builder_add (&proxy_data, "{sv}", - "Method", - g_variant_new_string ("direct")); - } +/** + * nm_pacrunner_manager_remove: + * @conf_id: the conf id obtained from nm_pacrunner_manager_add() + */ +void +nm_pacrunner_manager_remove (NMPacrunnerConfId *conf_id) +{ + _nm_unused nm_auto_unref_conf_id NMPacrunnerConfId *conf_id_free = conf_id; + NMPacrunnerManager *self; + NMPacrunnerManagerPrivate *priv; - /* Extract stuff from configs */ - add_proxy_config (&proxy_data, proxy_config); + g_return_if_fail (conf_id); - if (ip4_config || ip6_config) { - domains = g_ptr_array_new_with_free_func (g_free); + self = conf_id->self; - if (ip4_config) - get_ip4_domains (domains, ip4_config); - if (ip6_config) - get_ip6_domains (domains, ip6_config); + g_return_if_fail (NM_IS_PACRUNNER_MANAGER (self)); - g_ptr_array_add (domains, NULL); - strv = (char **) g_ptr_array_free (domains, (domains->len == 1)); + priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self); - if (strv) { - g_variant_builder_add (&proxy_data, "{sv}", - "Domains", - g_variant_new_strv ((const char *const *) strv, -1)); - g_strfreev (strv); - } + _LOG2T (conf_id, "removing..."); + + nm_assert (c_list_contains (&priv->conf_id_lst_head, &conf_id->conf_id_lst)); + + c_list_unlink (&conf_id->conf_id_lst); + + if (!conf_id->path) { + /* There is no ID to destroy the configuration. + * + * That can happen because: + * + * - pacrunner D-Bus service is not running (no name owner) and we didn't call CreateProxyConfiguration. + * - CreateProxyConfiguration failed. + * - CreateProxyConfiguration is in progress. + * + * In all cases there is nothing to do. Note that if CreateProxyConfiguration is in progress + * it has a reference on the conf-id and it will automatically destroy the configuration + * when it completes. + */ + return; } - config = config_new (self, g_variant_new ("(a{sv})", &proxy_data)); - - { - gs_free char *args_str = NULL; - - _LOG2D (config, "send: new config %s", - (args_str = g_variant_print (config->args, FALSE))); - } + _call_destroy_proxy_configuration (self, conf_id, conf_id->path, TRUE); +} - /* Send if pacrunner is available on bus, otherwise - * config has already been appended above to be - * sent when pacrunner appears. - */ - pacrunner_send_config (self, config); +gboolean +nm_pacrunner_manager_remove_clear (NMPacrunnerConfId **p_conf_id) +{ + g_return_val_if_fail (p_conf_id, FALSE); - return config; + if (!*p_conf_id) + return FALSE; + nm_pacrunner_manager_remove (g_steal_pointer (p_conf_id)); + return TRUE; } +/*****************************************************************************/ + static void -pacrunner_remove_done (GObject *source, GAsyncResult *res, gpointer user_data) +name_owner_changed (NMPacrunnerManager *self, + const char *name_owner) { - Config *config = user_data; - gs_free_error GError *error = NULL; - gs_unref_variant GVariant *ret = NULL; + NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self); + NMPacrunnerConfId *conf_id; + gboolean has_name_owner; - ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); - if (!ret) { - if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - goto out; - _LOG2D (config, "remove failed: %s", error->message); - goto out; - } + has_name_owner = (name_owner && name_owner[0]); + + if ( priv->dbus_initied + && priv->has_name_owner == has_name_owner) + return; + + priv->has_name_owner = has_name_owner; - _LOG2D (config, "removed"); + nm_clear_g_cancellable (&priv->cancellable); -out: - config_unref (config); + if (has_name_owner) { + priv->dbus_initied = TRUE; + priv->try_start_blocked = FALSE; + _LOGD ("pacrunner appeared on D-Bus (%s)", name_owner); + priv->cancellable = g_cancellable_new (); + c_list_for_each_entry (conf_id, &priv->conf_id_lst_head, conf_id_lst) + _call_create_proxy_configuration (self, conf_id, TRUE); + } else { + if (!priv->dbus_initied) { + priv->dbus_initied = TRUE; + nm_assert (!priv->try_start_blocked); + _LOGD ("pacrunner not on D-Bus"); + } else + _LOGD ("pacrunner disappeared from D-Bus"); + if (!c_list_is_empty (&priv->conf_id_lst_head)) { + c_list_for_each_entry (conf_id, &priv->conf_id_lst_head, conf_id_lst) + nm_clear_g_free (&conf_id->path); + _try_start_service_by_name (self); + } + } } -/** - * nm_pacrunner_manager_remove: - * @self: the #NMPacrunnerManager - * @call_id: the call-id obtained from nm_pacrunner_manager_send() - */ -void -nm_pacrunner_manager_remove (NMPacrunnerManager *self, NMPacrunnerCallId *call_id) +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) { - NMPacrunnerManagerPrivate *priv; - Config *config; + const char *new_owner; - g_return_if_fail (NM_IS_PACRUNNER_MANAGER (self)); - g_return_if_fail (call_id); - - config = call_id; - priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self); + if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sss)"))) + return; - _LOG2T (config, "removing..."); - - nm_assert (c_list_contains (&priv->configs, &config->lst)); - - if (priv->pacrunner) { - if (!config->path) { - /* send() failed or is still pending. The item is unlinked from - * priv->configs, so pacrunner_send_done() knows to call - * DestroyProxyConfiguration right away. - */ - } else { - g_dbus_proxy_call (priv->pacrunner, - "DestroyProxyConfiguration", - g_variant_new ("(o)", config->path), - G_DBUS_CALL_FLAGS_NO_AUTO_START, - -1, - _ensure_cancellable (priv), - pacrunner_remove_done, - config_ref (config)); - nm_clear_g_free (&config->path); - } - } + g_variant_get (parameters, + "(&s&s&s)", + NULL, + NULL, + &new_owner); - c_list_unlink (&config->lst); - config_unref (config); + name_owner_changed (user_data, new_owner); } -gboolean -nm_pacrunner_manager_remove_clear (NMPacrunnerManager *self, - NMPacrunnerCallId **p_call_id) +static void +get_name_owner_cb (const char *name_owner, + GError *error, + gpointer user_data) { - g_return_val_if_fail (p_call_id, FALSE); - - /* if we have no call-id, allow for %NULL */ - g_return_val_if_fail ((!self && !*p_call_id) || NM_IS_PACRUNNER_MANAGER (self), FALSE); + if ( !name_owner + && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; - if (!*p_call_id) - return FALSE; - nm_pacrunner_manager_remove (self, - g_steal_pointer (p_call_id)); - return TRUE; + name_owner_changed (user_data, name_owner); } /*****************************************************************************/ @@ -551,28 +597,36 @@ nm_pacrunner_manager_init (NMPacrunnerManager *self) { NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self); - c_list_init (&priv->configs); - g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_NONE, - NULL, - PACRUNNER_DBUS_SERVICE, - PACRUNNER_DBUS_PATH, - PACRUNNER_DBUS_INTERFACE, - _ensure_cancellable (priv), - pacrunner_proxy_cb, - self); + c_list_init (&priv->conf_id_lst_head); + + priv->dbus_connection = nm_g_object_ref (NM_MAIN_DBUS_CONNECTION_GET); + + if (!priv->dbus_connection) { + _LOGD ("no D-Bus connection to talk to pacrunner"); + return; + } + + priv->name_owner_changed_id = nm_dbus_connection_signal_subscribe_name_owner_changed (priv->dbus_connection, + PACRUNNER_DBUS_SERVICE, + name_owner_changed_cb, + self, + NULL); + priv->cancellable = g_cancellable_new (); + + nm_dbus_connection_call_get_name_owner (priv->dbus_connection, + PACRUNNER_DBUS_SERVICE, + -1, + priv->cancellable, + get_name_owner_cb, + self); } static void dispose (GObject *object) { NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE ((NMPacrunnerManager *) object); - CList *iter, *safe; - c_list_for_each_safe (iter, safe, &priv->configs) { - c_list_unlink (iter); - config_unref (c_list_entry (iter, Config, lst)); - } + nm_assert (c_list_is_empty (&priv->conf_id_lst_head)); /* we cancel all pending operations. Note that pacrunner automatically * removes all configuration once NetworkManager disconnects from @@ -580,8 +634,9 @@ dispose (GObject *object) */ nm_clear_g_cancellable (&priv->cancellable); - g_clear_pointer (&priv->iface, g_free); - g_clear_object (&priv->pacrunner); + nm_clear_g_dbus_connection_signal (priv->dbus_connection, + &priv->name_owner_changed_id); + g_clear_object (&priv->dbus_connection); G_OBJECT_CLASS (nm_pacrunner_manager_parent_class)->dispose (object); } diff --git a/src/nm-pacrunner-manager.h b/src/nm-pacrunner-manager.h index 3080c4f5..f3f41e9c 100644 --- a/src/nm-pacrunner-manager.h +++ b/src/nm-pacrunner-manager.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager * * This program is free software; you can redistribute it and/or modify @@ -31,22 +30,20 @@ typedef struct _NMPacrunnerManagerClass NMPacrunnerManagerClass; -typedef struct _NMPacrunnerCallId NMPacrunnerCallId; +typedef struct _NMPacrunnerConfId NMPacrunnerConfId; GType nm_pacrunner_manager_get_type (void); NMPacrunnerManager *nm_pacrunner_manager_get (void); -NMPacrunnerCallId *nm_pacrunner_manager_send (NMPacrunnerManager *self, - const char *iface, - NMProxyConfig *proxy_config, - NMIP4Config *ip4_config, - NMIP6Config *ip6_config); +NMPacrunnerConfId *nm_pacrunner_manager_add (NMPacrunnerManager *self, + NMProxyConfig *proxy_config, + const char *iface, + NMIP4Config *ip4_config, + NMIP6Config *ip6_config); -void nm_pacrunner_manager_remove (NMPacrunnerManager *self, - NMPacrunnerCallId *call_id); +void nm_pacrunner_manager_remove (NMPacrunnerConfId *conf_id); -gboolean nm_pacrunner_manager_remove_clear (NMPacrunnerManager *self, - NMPacrunnerCallId **p_call_id); +gboolean nm_pacrunner_manager_remove_clear (NMPacrunnerConfId **p_conf_id); #endif /* __NETWORKMANAGER_PACRUNNER_MANAGER_H__ */ diff --git a/src/nm-policy.c b/src/nm-policy.c index 1faba5c7..559babed 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -1847,8 +1846,7 @@ device_state_changed (NMDevice *device, } } - /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */ - nm_connection_clear_secrets (nm_settings_connection_get_connection (sett_conn)); + nm_settings_connection_clear_secrets (sett_conn, FALSE, FALSE); } break; case NM_DEVICE_STATE_ACTIVATED: @@ -1859,9 +1857,7 @@ device_state_changed (NMDevice *device, /* And clear secrets so they will always be requested from the * settings service when the next connection is made. */ - - /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */ - nm_connection_clear_secrets (nm_settings_connection_get_connection (sett_conn)); + nm_settings_connection_clear_secrets (sett_conn, FALSE, FALSE); } /* Add device's new IPv4 and IPv6 configs to DNS */ @@ -2400,29 +2396,22 @@ dns_config_changed (NMDnsManager *dns_manager, gpointer user_data) static void connection_updated (NMSettings *settings, NMSettingsConnection *connection, - gboolean by_user, + guint update_reason_u, gpointer user_data) { NMPolicyPrivate *priv = user_data; NMPolicy *self = _PRIV_TO_SELF (priv); - const CList *tmp_lst; - NMDevice *device = NULL; - NMDevice *dev; + NMSettingsConnectionUpdateReason update_reason = update_reason_u; + + if (NM_FLAGS_HAS (update_reason, NM_SETTINGS_CONNECTION_UPDATE_REASON_REAPPLY_PARTIAL)) { + const CList *tmp_lst; + NMDevice *device; - if (by_user) { /* find device with given connection */ - nm_manager_for_each_device (priv->manager, dev, tmp_lst) { - if (nm_device_get_settings_connection (dev) == connection) { - device = dev; - break; - } + nm_manager_for_each_device (priv->manager, device, tmp_lst) { + if (nm_device_get_settings_connection (device) == connection) + nm_device_reapply_settings_immediately (device); } - - if (device) - nm_device_reapply_settings_immediately (device); - - /* Reset auto retries back to default since connection was updated */ - nm_settings_connection_autoconnect_retries_reset (connection); } schedule_activate_all (self); diff --git a/src/nm-policy.h b/src/nm-policy.h index 916ede00..dea500bf 100644 --- a/src/nm-policy.h +++ b/src/nm-policy.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/nm-proxy-config.c b/src/nm-proxy-config.c index e051367a..5a7a85c0 100644 --- a/src/nm-proxy-config.c +++ b/src/nm-proxy-config.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager * * This program is free software; you can redistribute it and/or modify diff --git a/src/nm-proxy-config.h b/src/nm-proxy-config.h index 2712e43f..050e0d98 100644 --- a/src/nm-proxy-config.h +++ b/src/nm-proxy-config.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager * * This program is free software; you can redistribute it and/or modify diff --git a/src/nm-rfkill-manager.c b/src/nm-rfkill-manager.c index cf2e7fe9..c276fc77 100644 --- a/src/nm-rfkill-manager.c +++ b/src/nm-rfkill-manager.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/nm-rfkill-manager.h b/src/nm-rfkill-manager.h index 490c1ca2..5f2d3adb 100644 --- a/src/nm-rfkill-manager.h +++ b/src/nm-rfkill-manager.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/nm-session-monitor.c b/src/nm-session-monitor.c index e8c25fd6..8cb1b88d 100644 --- a/src/nm-session-monitor.c +++ b/src/nm-session-monitor.c @@ -1,4 +1,3 @@ -/* -*- 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 of the License, or @@ -44,7 +43,7 @@ #define SESSION_TRACKING_XLOGIND (SESSION_TRACKING_SYSTEMD || SESSION_TRACKING_ELOGIND) -#define CKDB_PATH "/var/run/ConsoleKit/database" +#define CKDB_PATH "/run/ConsoleKit/database" /*****************************************************************************/ diff --git a/src/nm-session-monitor.h b/src/nm-session-monitor.h index d3d10e98..43f9eef9 100644 --- a/src/nm-session-monitor.h +++ b/src/nm-session-monitor.h @@ -1,4 +1,3 @@ -/* -*- 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 of the License, or diff --git a/src/nm-sleep-monitor.c b/src/nm-sleep-monitor.c index 7e0ebe6c..c97f6f7d 100644 --- a/src/nm-sleep-monitor.c +++ b/src/nm-sleep-monitor.c @@ -1,4 +1,3 @@ -/* -*- 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 of the License, or diff --git a/src/nm-sleep-monitor.h b/src/nm-sleep-monitor.h index 350f1eec..da1fd8cf 100644 --- a/src/nm-sleep-monitor.h +++ b/src/nm-sleep-monitor.h @@ -1,4 +1,3 @@ -/* -*- 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 of the License, or diff --git a/src/nm-test-utils-core.h b/src/nm-test-utils-core.h index 7f8b3327..1f4b87ef 100644 --- a/src/nm-test-utils-core.h +++ b/src/nm-test-utils-core.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/src/nm-types.h b/src/nm-types.h index 03ee9929..fae344f2 100644 --- a/src/nm-types.h +++ b/src/nm-types.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index 40a85390..967aaee8 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-platform-fake.c - Fake platform interaction code for testing NetworkManager * * This program is free software; you can redistribute it and/or modify @@ -276,9 +275,9 @@ link_add_pre (NMPlatform *platform, o->_link.netlink.is_in_netlink = TRUE; if (address) { - g_assert (address_len > 0 && address_len <= sizeof (link->addr.data)); - memcpy (link->addr.data, address, address_len); - link->addr.len = address_len; + g_assert (address_len > 0 && address_len <= sizeof (link->l_address.data)); + memcpy (link->l_address.data, address, address_len); + link->l_address.len = address_len; } else g_assert (address_len == 0); @@ -584,9 +583,9 @@ link_set_address (NMPlatform *platform, int ifindex, gconstpointer addr, size_t return -NME_PL_EXISTS; obj_tmp = nmp_object_clone (device->obj, FALSE); - obj_tmp->link.addr.len = len; - memset (obj_tmp->link.addr.data, 0, sizeof (obj_tmp->link.addr.data)); - memcpy (obj_tmp->link.addr.data, addr, len); + obj_tmp->link.l_address.len = len; + memset (obj_tmp->link.l_address.data, 0, sizeof (obj_tmp->link.l_address.data)); + memcpy (obj_tmp->link.l_address.data, addr, len); link_set_obj (platform, device, obj_tmp); return 0; diff --git a/src/platform/nm-fake-platform.h b/src/platform/nm-fake-platform.h index 8f42d11a..d5be8c65 100644 --- a/src/platform/nm-fake-platform.h +++ b/src/platform/nm-fake-platform.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-fake-platform.h - Fake platform interaction code for testing NetworkManager * * This program is free software; you can redistribute it and/or modify diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index d4b01152..e2e1e581 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-linux-platform.c - Linux kernel & udev network configuration layer * * This program is free software; you can redistribute it and/or modify @@ -34,6 +33,7 @@ #include <linux/if_tun.h> #include <linux/if_tunnel.h> #include <linux/ip6_tunnel.h> +#include <linux/tc_act/tc_mirred.h> #include <netinet/icmp6.h> #include <netinet/in.h> #include <poll.h> @@ -84,6 +84,13 @@ enum { /*****************************************************************************/ +/* Compat with older kernels. */ + +#define TCA_FQ_CODEL_CE_THRESHOLD 7 +#define TCA_FQ_CODEL_MEMORY_LIMIT 9 + +/*****************************************************************************/ + #define VLAN_FLAG_MVRP 0x8 /*****************************************************************************/ @@ -732,29 +739,27 @@ _timestamp_nl_to_ms (guint32 timestamp_nl, gint64 monotonic_ms) static guint32 _addrtime_timestamp_to_nm (guint32 timestamp, gint32 *out_now_nm) { - struct timespec tp; - gint64 now_nl, now_nm, result; - int err; + gint64 now_nl; + gint64 now_nm; + gint64 result; /* timestamp is unset. Default to 1. */ if (!timestamp) { - if (out_now_nm) - *out_now_nm = 0; + NM_SET_OUT (out_now_nm, 0); return 1; } /* do all the calculations in milliseconds scale */ - err = clock_gettime (CLOCK_MONOTONIC, &tp); - g_assert (err == 0); now_nm = nm_utils_get_monotonic_timestamp_ms (); - now_nl = (((gint64) tp.tv_sec) * ((gint64) 1000)) + - (tp.tv_nsec / (NM_UTILS_NS_PER_SECOND/1000)); + now_nl = nm_utils_clock_gettime_ms (CLOCK_MONOTONIC); + + nm_assert (now_nm >= 1000); + nm_assert (now_nl >= 0); result = now_nm - (now_nl - _timestamp_nl_to_ms (timestamp, now_nl)); - if (out_now_nm) - *out_now_nm = now_nm / 1000; + NM_SET_OUT (out_now_nm, now_nm / 1000); /* converting the timestamp into nm_utils_get_monotonic_timestamp_ms() scale is * a good guess but fails in the following situations: @@ -2579,6 +2584,25 @@ link_wireguard_change (NMPlatform *platform, /*****************************************************************************/ +static void +_nmp_link_address_set (NMPLinkAddress *dst, + const struct nlattr *nla) +{ + *dst = (NMPLinkAddress) { + .len = 0, + }; + if (nla) { + int l = nla_len (nla); + + if ( l > 0 + && l <= NM_UTILS_HWADDR_LEN_MAX) { + G_STATIC_ASSERT_EXPR (sizeof (dst->data) == NM_UTILS_HWADDR_LEN_MAX); + memcpy (dst->data, nla_data (nla), l); + dst->len = l; + } + } +} + /* Copied and heavily modified from libnl3's link_msg_parser(). */ static NMPObject * _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr *nlh, gboolean id_only) @@ -2622,6 +2646,7 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr const NMPObject *link_cached = NULL; const NMPObject *lnk_data = NULL; gboolean address_complete_from_cache = TRUE; + gboolean broadcast_complete_from_cache = TRUE; gboolean lnk_data_complete_from_cache = TRUE; gboolean need_ext_data = FALSE; gboolean af_inet6_token_valid = FALSE; @@ -2720,16 +2745,15 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr } if (tb[IFLA_ADDRESS]) { - int l = nla_len (tb[IFLA_ADDRESS]); - - if (l > 0 && l <= NM_UTILS_HWADDR_LEN_MAX) { - G_STATIC_ASSERT (NM_UTILS_HWADDR_LEN_MAX == sizeof (obj->link.addr.data)); - memcpy (obj->link.addr.data, nla_data (tb[IFLA_ADDRESS]), l); - obj->link.addr.len = l; - } + _nmp_link_address_set (&obj->link.l_address, tb[IFLA_ADDRESS]); address_complete_from_cache = FALSE; } + if (tb[IFLA_BROADCAST]) { + _nmp_link_address_set (&obj->link.l_broadcast, tb[IFLA_BROADCAST]); + broadcast_complete_from_cache = FALSE; + } + if (tb[IFLA_AF_SPEC]) { struct nlattr *af_attr; int remaining; @@ -2803,6 +2827,7 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr && ( lnk_data_complete_from_cache || need_ext_data || address_complete_from_cache + || broadcast_complete_from_cache || !af_inet6_token_valid || !af_inet6_addr_gen_mode_valid || !tb[IFLA_STATS64])) { @@ -2832,7 +2857,9 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr } if (address_complete_from_cache) - obj->link.addr = link_cached->link.addr; + obj->link.l_address = link_cached->link.l_address; + if (broadcast_complete_from_cache) + obj->link.l_broadcast = link_cached->link.l_broadcast; if (!af_inet6_token_valid) obj->link.inet6_token = link_cached->link.inet6_token; if (!af_inet6_addr_gen_mode_valid) @@ -3481,6 +3508,7 @@ _new_from_nl_qdisc (struct nlmsghdr *nlh, gboolean id_only) { static const struct nla_policy policy[] = { [TCA_KIND] = { .type = NLA_STRING }, + [TCA_OPTIONS] = { .type = NLA_NESTED }, }; struct nlattr *tb[G_N_ELEMENTS (policy)]; const struct tcmsg *tcm; @@ -3506,6 +3534,50 @@ _new_from_nl_qdisc (struct nlmsghdr *nlh, gboolean id_only) obj->qdisc.parent = tcm->tcm_parent; obj->qdisc.info = tcm->tcm_info; + if (nm_streq0 (obj->qdisc.kind, "fq_codel")) { + obj->qdisc.fq_codel.memory_limit = NM_PLATFORM_FQ_CODEL_MEMORY_LIMIT_UNSET; + obj->qdisc.fq_codel.ce_threshold = NM_PLATFORM_FQ_CODEL_CE_THRESHOLD_DISABLED; + } + + if (tb[TCA_OPTIONS]) { + struct nlattr *options_attr; + int remaining; + + nla_for_each_nested (options_attr, tb[TCA_OPTIONS], remaining) { + if (nla_len (options_attr) < sizeof (uint32_t)) + continue; + + if (nm_streq0 (obj->qdisc.kind, "fq_codel")) { + switch (nla_type (options_attr)) { + case TCA_FQ_CODEL_LIMIT: + obj->qdisc.fq_codel.limit = nla_get_u32 (options_attr); + break; + case TCA_FQ_CODEL_FLOWS: + obj->qdisc.fq_codel.flows = nla_get_u32 (options_attr); + break; + case TCA_FQ_CODEL_TARGET: + obj->qdisc.fq_codel.target = nla_get_u32 (options_attr); + break; + case TCA_FQ_CODEL_INTERVAL: + obj->qdisc.fq_codel.interval = nla_get_u32 (options_attr); + break; + case TCA_FQ_CODEL_QUANTUM: + obj->qdisc.fq_codel.quantum = nla_get_u32 (options_attr); + break; + case TCA_FQ_CODEL_CE_THRESHOLD: + obj->qdisc.fq_codel.ce_threshold = nla_get_u32 (options_attr); + break; + case TCA_FQ_CODEL_MEMORY_LIMIT: + obj->qdisc.fq_codel.memory_limit = nla_get_u32 (options_attr); + break; + case TCA_FQ_CODEL_ECN: + obj->qdisc.fq_codel.ecn = !!nla_get_u32 (options_attr); + break; + } + } + } + } + return obj; } @@ -3628,7 +3700,7 @@ _nl_msg_new_link_set_afspec (struct nl_msg *msg, return TRUE; nla_put_failure: - return FALSE; + g_return_val_if_reached (FALSE); } static gboolean @@ -3779,7 +3851,7 @@ _nl_msg_new_link_set_linkinfo_vlan (struct nl_msg *msg, return TRUE; nla_put_failure: - return FALSE; + g_return_val_if_reached (FALSE); } static struct nl_msg * @@ -4161,6 +4233,7 @@ _nl_msg_new_qdisc (int nlmsg_type, const NMPlatformQdisc *qdisc) { nm_auto_nlmsg struct nl_msg *msg = NULL; + struct nlattr *tc_options; const struct tcmsg tcm = { .tcm_family = qdisc->addr_family, .tcm_ifindex = qdisc->ifindex, @@ -4176,55 +4249,34 @@ _nl_msg_new_qdisc (int nlmsg_type, NLA_PUT_STRING (msg, TCA_KIND, qdisc->kind); - return g_steal_pointer (&msg); - -nla_put_failure: - g_return_val_if_reached (NULL); -} - -static gboolean -_add_action_simple (struct nl_msg *msg, - const NMPlatformActionSimple *simple) -{ - struct nlattr *act_options; - struct tc_defact sel = { 0, }; - - if (!(act_options = nla_nest_start (msg, TCA_ACT_OPTIONS))) - goto nla_put_failure; - - NLA_PUT (msg, TCA_DEF_PARMS, sizeof (sel), &sel); - NLA_PUT (msg, TCA_DEF_DATA, sizeof (simple->sdata), simple->sdata); - - nla_nest_end (msg, act_options); - - return TRUE; - -nla_put_failure: - return FALSE; -} - -static gboolean -_add_action (struct nl_msg *msg, - const NMPlatformAction *action) -{ - struct nlattr *prio; - - nm_assert (action || action->kind); - - if (!(prio = nla_nest_start (msg, 1 /* priority */))) + if (!(tc_options = nla_nest_start (msg, TCA_OPTIONS))) goto nla_put_failure; - NLA_PUT_STRING (msg, TCA_ACT_KIND, action->kind); - - if (nm_streq (action->kind, NM_PLATFORM_ACTION_KIND_SIMPLE)) - _add_action_simple (msg, &action->simple); + if (strcmp (qdisc->kind, "fq_codel") == 0) { + if (qdisc->fq_codel.limit) + NLA_PUT_U32 (msg, TCA_FQ_CODEL_LIMIT, qdisc->fq_codel.limit); + if (qdisc->fq_codel.flows) + NLA_PUT_U32 (msg, TCA_FQ_CODEL_FLOWS, qdisc->fq_codel.flows); + if (qdisc->fq_codel.target) + NLA_PUT_U32 (msg, TCA_FQ_CODEL_TARGET, qdisc->fq_codel.target); + if (qdisc->fq_codel.interval) + NLA_PUT_U32 (msg, TCA_FQ_CODEL_INTERVAL, qdisc->fq_codel.interval); + if (qdisc->fq_codel.quantum) + NLA_PUT_U32 (msg, TCA_FQ_CODEL_QUANTUM, qdisc->fq_codel.quantum); + if (qdisc->fq_codel.ce_threshold != NM_PLATFORM_FQ_CODEL_CE_THRESHOLD_DISABLED) + NLA_PUT_U32 (msg, TCA_FQ_CODEL_CE_THRESHOLD, qdisc->fq_codel.ce_threshold); + if (qdisc->fq_codel.memory_limit != NM_PLATFORM_FQ_CODEL_MEMORY_LIMIT_UNSET) + NLA_PUT_U32 (msg, TCA_FQ_CODEL_MEMORY_LIMIT, qdisc->fq_codel.memory_limit); + if (qdisc->fq_codel.ecn) + NLA_PUT_U32 (msg, TCA_FQ_CODEL_ECN, qdisc->fq_codel.ecn); + } - nla_nest_end (msg, prio); + nla_nest_end (msg, tc_options); - return TRUE; + return g_steal_pointer (&msg); nla_put_failure: - return FALSE; + g_return_val_if_reached (NULL); } static struct nl_msg * @@ -4256,8 +4308,52 @@ _nl_msg_new_tfilter (int nlmsg_type, if (!(act_tab = nla_nest_start (msg, TCA_OPTIONS))) // 3 TCA_ACT_KIND TCA_ACT_KIND goto nla_put_failure; - if (tfilter->action.kind) - _add_action (msg, &tfilter->action); + if (tfilter->action.kind) { + const NMPlatformAction *action = &tfilter->action; + struct nlattr *prio; + struct nlattr *act_options; + + if (!(prio = nla_nest_start (msg, 1 /* priority */))) + goto nla_put_failure; + + NLA_PUT_STRING (msg, TCA_ACT_KIND, action->kind); + + if (nm_streq (action->kind, NM_PLATFORM_ACTION_KIND_SIMPLE)) { + const NMPlatformActionSimple *simple = &action->simple; + struct tc_defact sel = { 0, }; + + if (!(act_options = nla_nest_start (msg, TCA_ACT_OPTIONS))) + goto nla_put_failure; + + NLA_PUT (msg, TCA_DEF_PARMS, sizeof (sel), &sel); + NLA_PUT (msg, TCA_DEF_DATA, sizeof (simple->sdata), simple->sdata); + + nla_nest_end (msg, act_options); + + } else if (nm_streq (action->kind, NM_PLATFORM_ACTION_KIND_MIRRED)) { + const NMPlatformActionMirred *mirred = &action->mirred; + struct tc_mirred sel = { 0, }; + + if (!(act_options = nla_nest_start (msg, TCA_ACT_OPTIONS))) + goto nla_put_failure; + + if (mirred->egress && mirred->redirect) + sel.eaction = TCA_EGRESS_REDIR; + else if (mirred->egress && mirred->mirror) + sel.eaction = TCA_EGRESS_MIRROR; + else if (mirred->ingress && mirred->redirect) + sel.eaction = TCA_INGRESS_REDIR; + else if (mirred->ingress && mirred->mirror) + sel.eaction = TCA_INGRESS_MIRROR; + sel.ifindex = mirred->ifindex; + + NLA_PUT (msg, TCA_MIRRED_PARMS, sizeof (sel), &sel); + + nla_nest_end (msg, act_options); + } + + nla_nest_end (msg, prio); + } nla_nest_end (msg, tc_options); @@ -4298,6 +4394,14 @@ _genl_sock (NMLinuxPlatform *platform) } \ } G_STMT_END +/*****************************************************************************/ + +/* core sysctl-set functions can be called from a non-main thread. + * Hence, we require locking from nm-logging. Indicate that by + * setting NM_THREAD_SAFE_ON_MAIN_THREAD to zero. */ +#undef NM_THREAD_SAFE_ON_MAIN_THREAD +#define NM_THREAD_SAFE_ON_MAIN_THREAD 0 + static void _log_dbg_sysctl_set_impl (NMPlatform *platform, const char *pathid, int dirfd, const char *path, const char *value) { @@ -4308,18 +4412,18 @@ _log_dbg_sysctl_set_impl (NMPlatform *platform, const char *pathid, int dirfd, c if (nm_utils_file_get_contents (dirfd, path, 1*1024*1024, NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE, &contents, NULL, &error) < 0) { - _LOGD ("sysctl: setting '%s' to '%s' (current value cannot be read: %s)", pathid, value_escaped, error->message); + _LOGD ("sysctl: setting '%s' to '%s' (current value cannot be read: %s)", pathid ?: path, value_escaped, error->message); g_clear_error (&error); return; } g_strstrip (contents); if (nm_streq (contents, value)) - _LOGD ("sysctl: setting '%s' to '%s' (current value is identical)", pathid, value_escaped); + _LOGD ("sysctl: setting '%s' to '%s' (current value is identical)", pathid ?: path, value_escaped); else { gs_free char *contents_escaped = g_strescape (contents, NULL); - _LOGD ("sysctl: setting '%s' to '%s' (current value is '%s')", pathid, value_escaped, contents_escaped); + _LOGD ("sysctl: setting '%s' to '%s' (current value is '%s')", pathid ?: path, value_escaped, contents_escaped); } g_free (contents); } @@ -4332,9 +4436,12 @@ _log_dbg_sysctl_set_impl (NMPlatform *platform, const char *pathid, int dirfd, c } G_STMT_END static gboolean -sysctl_set (NMPlatform *platform, const char *pathid, int dirfd, const char *path, const char *value) +sysctl_set_internal (NMPlatform *platform, + const char *pathid, + int dirfd, + const char *path, + const char *value) { - nm_auto_pop_netns NMPNetns *netns = NULL; int fd, tries; gssize nwrote; gssize len; @@ -4342,17 +4449,7 @@ sysctl_set (NMPlatform *platform, const char *pathid, int dirfd, const char *pat gs_free char *actual_free = NULL; int errsv; - g_return_val_if_fail (path != NULL, FALSE); - g_return_val_if_fail (value != NULL, FALSE); - - ASSERT_SYSCTL_ARGS (pathid, dirfd, path); - if (dirfd < 0) { - if (!nm_platform_netns_push (platform, &netns)) { - errno = ENETDOWN; - return FALSE; - } - pathid = path; fd = open (path, O_WRONLY | O_TRUNC | O_CLOEXEC); @@ -4452,6 +4549,207 @@ sysctl_set (NMPlatform *platform, const char *pathid, int dirfd, const char *pat return TRUE; } +#undef NM_THREAD_SAFE_ON_MAIN_THREAD +#define NM_THREAD_SAFE_ON_MAIN_THREAD 1 + +/*****************************************************************************/ + +static gboolean +sysctl_set (NMPlatform *platform, + const char *pathid, + int dirfd, + const char *path, + const char *value) +{ + nm_auto_pop_netns NMPNetns *netns = NULL; + + g_return_val_if_fail (path, FALSE); + g_return_val_if_fail (value, FALSE); + + ASSERT_SYSCTL_ARGS (pathid, dirfd, path); + + if ( dirfd < 0 + && !nm_platform_netns_push (platform, &netns)) { + errno = ENETDOWN; + return FALSE; + } + + return sysctl_set_internal (platform, pathid, dirfd, path, value); +} + +typedef struct { + NMPlatform *platform; + char *pathid; + int dirfd; + char *path; + char **values; + GCancellable *cancellable; + NMPlatformAsyncCallback callback; + gpointer callback_data; +} SysctlAsyncInfo; + +static void +sysctl_async_info_free (SysctlAsyncInfo *info) +{ + g_object_unref (info->platform); + g_free (info->pathid); + if (info->dirfd >= 0) + nm_close (info->dirfd); + g_free (info->path); + g_strfreev (info->values); + g_object_unref (info->cancellable); + g_slice_free (SysctlAsyncInfo, info); +} + +static void +sysctl_async_cb (GObject *object, + GAsyncResult *res, + gpointer user_data) +{ + NMPlatform *platform; + GTask *task = G_TASK (res); + SysctlAsyncInfo *info; + gs_free_error GError *error = NULL; + gs_free char *values_str = NULL; + + info = g_task_get_task_data (task); + + if (g_task_propagate_boolean (task, &error)) { + platform = info->platform; + _LOGD ("sysctl: successfully set-async '%s' to values '%s'", + info->pathid ?: info->path, + (values_str = g_strjoinv (", ", info->values))); + } + + if (info->callback) + info->callback (error, info->callback_data); +} + +static void +sysctl_async_thread_fn (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + nm_auto_pop_netns NMPNetns *netns = NULL; + SysctlAsyncInfo *info = task_data; + GError *error = NULL; + char **value; + + if (g_task_return_error_if_cancelled (task)) + return; + + if ( info->dirfd < 0 + && !nm_platform_netns_push (info->platform, &netns)) { + g_set_error_literal (&error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + "sysctl: failed changing namespace"); + g_task_return_error (task, error); + return; + } + + for (value = info->values; *value; value++) { + if (!sysctl_set_internal (info->platform, + info->pathid, + info->dirfd, + info->path, + *value)) { + g_set_error (&error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + "sysctl: failed setting '%s' to value '%s': %s", + info->pathid ?: info->path, + *value, + nm_strerror_native (errno)); + g_task_return_error (task, error); + return; + } + if (g_task_return_error_if_cancelled (task)) + return; + } + g_task_return_boolean (task, TRUE); +} + +static void +sysctl_set_async_return_idle (gpointer user_data, + GCancellable *cancellable) +{ + gs_unref_object NMPlatform *platform = NULL; + gs_free_error GError *cancelled_error = NULL; + gs_free_error GError *error = NULL; + NMPlatformAsyncCallback callback; + gpointer callback_data; + + nm_utils_user_data_unpack (user_data, &platform, &callback, &callback_data, &error); + g_cancellable_set_error_if_cancelled (cancellable, &cancelled_error); + callback (cancelled_error ?: error, callback_data); +} + +static void +sysctl_set_async (NMPlatform *platform, + const char *pathid, + int dirfd, + const char *path, + const char *const *values, + NMPlatformAsyncCallback callback, + gpointer data, + GCancellable *cancellable) +{ + SysctlAsyncInfo *info; + GTask *task; + int dirfd_dup, errsv; + gpointer packed; + GError *error = NULL; + + g_return_if_fail (platform); + g_return_if_fail (path); + g_return_if_fail (values && values[0]); + g_return_if_fail (cancellable); + g_return_if_fail (!data || callback); + + ASSERT_SYSCTL_ARGS (pathid, dirfd, path); + + if (dirfd >= 0) { + dirfd_dup = fcntl (dirfd, F_DUPFD_CLOEXEC, 0); + if (dirfd_dup < 0) { + if (!callback) + return; + errsv = errno; + g_set_error (&error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + "sysctl: failure duplicating directory fd: %s", + nm_strerror_native (errsv)); + packed = nm_utils_user_data_pack (g_object_ref (platform), + callback, + data, + error); + nm_utils_invoke_on_idle (sysctl_set_async_return_idle, + packed, + cancellable); + return; + } + } else + dirfd_dup = -1; + + info = g_slice_new0 (SysctlAsyncInfo); + info->platform = g_object_ref (platform); + info->pathid = g_strdup (pathid); + info->dirfd = dirfd_dup; + info->path = g_strdup (path); + info->values = g_strdupv ((char **) values); + info->callback = callback; + info->callback_data = data; + info->cancellable = g_object_ref (cancellable); + + task = g_task_new (platform, cancellable, sysctl_async_cb, NULL); + g_task_set_task_data (task, info, (GDestroyNotify) sysctl_async_info_free); + g_task_set_return_on_cancel (task, FALSE); + g_task_run_in_thread (task, sysctl_async_thread_fn); + g_object_unref (task); +} + static GSList *sysctl_clear_cache_list; void @@ -5310,7 +5608,7 @@ cache_on_change (NMPlatform *platform, * Request it again. */ re_request_link = TRUE; } else if ( obj_new->link.type == NM_LINK_TYPE_ETHERNET - && obj_new->link.addr.len == 0) { + && obj_new->link.l_address.len == 0) { /* Due to a kernel bug, we sometimes receive spurious NEWLINK * messages after a wifi interface has disappeared. Since the * link is not present anymore we can't determine its type and @@ -6170,8 +6468,8 @@ retry: } 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)) - && obj_cache->link.addr.len == data->set_address.length - && memcmp (obj_cache->link.addr.data, data->set_address.address, data->set_address.length) == 0) { + && obj_cache->link.l_address.len == data->set_address.length + && memcmp (obj_cache->link.l_address.data, data->set_address.address, data->set_address.length) == 0) { /* workaround ENFILE which may be wrongly returned (bgo #770456). * If the MAC address is as expected, assume success? */ log_result = "success"; @@ -6409,7 +6707,8 @@ link_supports_carrier_detect (NMPlatform *platform, int ifindex) * us whether the device actually supports carrier detection in the first * place. We assume any device that does implements one of these two APIs. */ - return nmp_utils_ethtool_supports_carrier_detect (ifindex) || nmp_utils_mii_supports_carrier_detect (ifindex); + return nmp_utils_ethtool_supports_carrier_detect (ifindex) + || nmp_utils_mii_supports_carrier_detect (ifindex); } static gboolean @@ -6534,35 +6833,74 @@ nla_put_failure: g_return_val_if_reached (FALSE); } -static gboolean -link_set_sriov_params (NMPlatform *platform, - int ifindex, - guint num_vfs, - NMTernary autoprobe) +static void +sriov_idle_cb (gpointer user_data, + GCancellable *cancellable) +{ + gs_unref_object NMPlatform *platform = NULL; + gs_free_error GError *cancelled_error = NULL; + gs_free_error GError *error = NULL; + NMPlatformAsyncCallback callback; + gpointer callback_data; + + g_cancellable_set_error_if_cancelled (cancellable, &cancelled_error); + nm_utils_user_data_unpack (user_data, &platform, &error, &callback, &callback_data); + callback (cancelled_error ?: error, callback_data); +} + +static void +link_set_sriov_params_async (NMPlatform *platform, + int ifindex, + guint num_vfs, + NMTernary autoprobe, + NMPlatformAsyncCallback callback, + gpointer data, + GCancellable *cancellable) { nm_auto_pop_netns NMPNetns *netns = NULL; + gs_free_error GError *error = NULL; nm_auto_close int dirfd = -1; int current_autoprobe; - guint total; + guint i, total; gint64 current_num; char ifname[IFNAMSIZ]; + gpointer packed; + const char *values[3]; char buf[64]; - int errsv; - if (!nm_platform_netns_push (platform, &netns)) - return FALSE; + g_return_if_fail (callback || !data); + g_return_if_fail (cancellable); + + if (!nm_platform_netns_push (platform, &netns)) { + g_set_error_literal (&error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + "couldn't change namespace"); + goto out_idle; + } dirfd = nm_platform_sysctl_open_netdir (platform, ifindex, ifname); - if (!dirfd) - return FALSE; + if (!dirfd) { + g_set_error_literal (&error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + "couldn't open netdir"); + goto out_idle; + } total = nm_platform_sysctl_get_int_checked (platform, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname, "device/sriov_totalvfs"), 10, 0, G_MAXUINT, 0); - if (errno) - return FALSE; + if (errno) { + g_set_error (&error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + "failed reading sriov_totalvfs value: %s", + nm_strerror_native (errno)); + goto out_idle; + } if (num_vfs > total) { _LOGW ("link: %d only supports %u VFs (requested %u)", ifindex, total, num_vfs); num_vfs = total; @@ -6594,23 +6932,7 @@ link_set_sriov_params (NMPlatform *platform, if ( current_num == num_vfs && (autoprobe == NM_TERNARY_DEFAULT || current_autoprobe == autoprobe)) - return TRUE; - - if (current_num != 0) { - /* We need to destroy all other VFs before changing any value */ - if (!nm_platform_sysctl_set (NM_PLATFORM_GET, - NMP_SYSCTL_PATHID_NETDIR (dirfd, - ifname, - "device/sriov_numvfs"), - "0")) { - errsv = errno; - _LOGW ("link: couldn't reset SR-IOV num_vfs: %s", nm_strerror_native (errsv)); - return FALSE; - } - } - - if (num_vfs == 0) - return TRUE; + goto out_idle; if ( NM_IN_SET (autoprobe, NM_TERNARY_TRUE, NM_TERNARY_FALSE) && current_autoprobe != autoprobe @@ -6619,22 +6941,40 @@ link_set_sriov_params (NMPlatform *platform, ifname, "device/sriov_drivers_autoprobe"), nm_sprintf_buf (buf, "%d", (int) autoprobe))) { - errsv = errno; - _LOGW ("link: couldn't set SR-IOV drivers-autoprobe to %d: %s", (int) autoprobe, nm_strerror_native (errsv)); - return FALSE; + g_set_error (&error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + "couldn't set SR-IOV drivers-autoprobe to %d: %s", + (int) autoprobe, nm_strerror_native (errno)); + goto out_idle; } - if (!nm_platform_sysctl_set (NM_PLATFORM_GET, - NMP_SYSCTL_PATHID_NETDIR (dirfd, - ifname, - "device/sriov_numvfs"), - nm_sprintf_buf (buf, "%u", num_vfs))) { - errsv = errno; - _LOGW ("link: couldn't set SR-IOV num_vfs to %d: %s", num_vfs, nm_strerror_native (errsv)); - return FALSE; - } + if (current_num == 0 && num_vfs == 0) + goto out_idle; - return TRUE; + i = 0; + if (current_num != 0) + values[i++] = "0"; + if (num_vfs != 0) + values[i++] = nm_sprintf_bufa (32, "%u", num_vfs); + values[i++] = NULL; + + sysctl_set_async (platform, + NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname, "device/sriov_numvfs"), + values, + callback, + data, + cancellable); + return; + +out_idle: + if (callback) { + packed = nm_utils_user_data_pack (g_object_ref (platform), + g_steal_pointer (&error), + callback, + data); + nm_utils_invoke_on_idle (sriov_idle_cb, packed, cancellable); + } } static gboolean @@ -8167,6 +8507,9 @@ qdisc_add (NMPlatform *platform, char s_buf[256]; nm_auto_nlmsg struct nl_msg *msg = NULL; + /* Note: @qdisc must not be copied or kept alive because the lifetime of qdisc.kind + * is undefined. */ + msg = _nl_msg_new_qdisc (RTM_NEWQDISC, flags, qdisc); event_handler_read_netlink (platform, FALSE); @@ -8208,6 +8551,9 @@ tfilter_add (NMPlatform *platform, char s_buf[256]; nm_auto_nlmsg struct nl_msg *msg = NULL; + /* Note: @tfilter must not be copied or kept alive because the lifetime of tfilter.kind + * and tfilter.action.kind is undefined. */ + msg = _nl_msg_new_tfilter (RTM_NEWTFILTER, flags, tfilter); event_handler_read_netlink (platform, FALSE); @@ -8913,6 +9259,7 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) object_class->finalize = finalize; platform_class->sysctl_set = sysctl_set; + platform_class->sysctl_set_async = sysctl_set_async; platform_class->sysctl_get = sysctl_get; platform_class->link_add = link_add; @@ -8936,7 +9283,7 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->link_get_permanent_address = link_get_permanent_address; platform_class->link_set_mtu = link_set_mtu; platform_class->link_set_name = link_set_name; - platform_class->link_set_sriov_params = link_set_sriov_params; + platform_class->link_set_sriov_params_async = link_set_sriov_params_async; platform_class->link_set_sriov_vfs = link_set_sriov_vfs; platform_class->link_set_bridge_vlans = link_set_bridge_vlans; diff --git a/src/platform/nm-linux-platform.h b/src/platform/nm-linux-platform.h index bff6c00c..f08f91aa 100644 --- a/src/platform/nm-linux-platform.h +++ b/src/platform/nm-linux-platform.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-linux-platform.h - Linux kernel & udev network configuration layer * * This program is free software; you can redistribute it and/or modify diff --git a/src/platform/nm-netlink.c b/src/platform/nm-netlink.c index 71506a2c..da009d01 100644 --- a/src/platform/nm-netlink.c +++ b/src/platform/nm-netlink.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-platform.c - Handle runtime kernel networking configuration * * This program is free software; you can redistribute it and/or modify @@ -510,7 +509,7 @@ nla_nest_start (struct nl_msg *msg, int attrtype) { struct nlattr *start = (struct nlattr *) nlmsg_tail (msg->nm_nlh); - if (nla_put (msg, attrtype, 0, NULL) < 0) + if (nla_put (msg, NLA_F_NESTED | attrtype, 0, NULL) < 0) return NULL; return start; diff --git a/src/platform/nm-netlink.h b/src/platform/nm-netlink.h index b12fd243..c793f04a 100644 --- a/src/platform/nm-netlink.h +++ b/src/platform/nm-netlink.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-platform.c - Handle runtime kernel networking configuration * * This program is free software; you can redistribute it and/or modify diff --git a/src/platform/nm-platform-private.h b/src/platform/nm-platform-private.h index b6c94baa..5a24eda0 100644 --- a/src/platform/nm-platform-private.h +++ b/src/platform/nm-platform-private.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-platform.c - Handle runtime kernel networking configuration * * This program is free software; you can redistribute it and/or modify diff --git a/src/platform/nm-platform-utils.c b/src/platform/nm-platform-utils.c index 456871a7..bc9ec11d 100644 --- a/src/platform/nm-platform-utils.c +++ b/src/platform/nm-platform-utils.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-platform.c - Handle runtime kernel networking configuration * * This program is free software; you can redistribute it and/or modify @@ -69,37 +68,209 @@ nmp_utils_if_nametoindex (const char *ifname) typedef struct { int fd; - int ifindex; + const int ifindex; char ifname[IFNAMSIZ]; } SocketHandle; +#define SOCKET_HANDLE_INIT(_ifindex) \ + { \ + .fd = -1, \ + .ifindex = (_ifindex), \ + } + +static void +_nm_auto_socket_handle (SocketHandle *shandle) +{ + if (shandle->fd >= 0) + nm_close (shandle->fd); +} + +#define nm_auto_socket_handle nm_auto(_nm_auto_socket_handle) + +/*****************************************************************************/ + +typedef enum { + IOCTL_CALL_DATA_TYPE_NONE, + IOCTL_CALL_DATA_TYPE_IFRDATA, + IOCTL_CALL_DATA_TYPE_IFRU, +} IoctlCallDataType; + static int -socket_handle_init (SocketHandle *shandle, int ifindex) +_ioctl_call (const char *log_ioctl_type, + const char *log_subtype, + unsigned long int ioctl_request, + int ifindex, + int *inout_fd, + char *inout_ifname, + IoctlCallDataType edata_type, + gpointer edata, + gsize edata_size, + struct ifreq *out_ifreq) { - if (!nmp_utils_if_indextoname (ifindex, shandle->ifname)) { - shandle->ifindex = 0; - return -ENODEV; + nm_auto_close int fd_close = -1; + int fd; + int r; + gpointer edata_backup = NULL; + gs_free gpointer edata_backup_free = NULL; + guint try_count; + char known_ifnames[2][IFNAMSIZ]; + const char *failure_reason = NULL; + struct ifreq ifr; + + nm_assert (ifindex > 0); + nm_assert (NM_IN_SET (edata_type, IOCTL_CALL_DATA_TYPE_NONE, + IOCTL_CALL_DATA_TYPE_IFRDATA, + IOCTL_CALL_DATA_TYPE_IFRU)); + nm_assert (edata_type != IOCTL_CALL_DATA_TYPE_NONE || edata_size == 0); + nm_assert (edata_type != IOCTL_CALL_DATA_TYPE_IFRDATA || edata_size > 0); + nm_assert (edata_type != IOCTL_CALL_DATA_TYPE_IFRU || (edata_size > 0 && edata_size <= sizeof (ifr.ifr_ifru))); + nm_assert (edata_size == 0 || edata); + + /* open a file descriptor (or use the one provided). */ + if ( inout_fd + && *inout_fd >= 0) + fd = *inout_fd; + else { + fd = socket (PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (fd < 0) { + r = -NM_ERRNO_NATIVE (errno); + failure_reason = "failed creating socket or ioctl"; + goto out; + } + if (inout_fd) + *inout_fd = fd; + else + fd_close = fd; } - shandle->fd = socket (PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); - if (shandle->fd < 0) { - shandle->ifindex = 0; - return -NM_ERRNO_NATIVE (errno); + /* resolve the ifindex to name (or use the one provided). */ + if ( inout_ifname + && inout_ifname[0]) + nm_utils_ifname_cpy (known_ifnames[0], inout_ifname); + else { + if (!nmp_utils_if_indextoname (ifindex, known_ifnames[0])) { + failure_reason = "cannot resolve ifindex"; + r = -ENODEV; + goto out; + } + if (inout_ifname) + nm_utils_ifname_cpy (inout_ifname, known_ifnames[0]); } - shandle->ifindex = ifindex; - return 0; -} + /* we might need to retry the request. Backup edata so that we can + * restore it on retry. */ + if (edata_size > 0) + edata_backup = nm_memdup_maybe_a (500, edata, edata_size, &edata_backup_free); -static void -socket_handle_destroy (SocketHandle *shandle) -{ - if (shandle->ifindex) { - shandle->ifindex = 0; - nm_close (shandle->fd); + try_count = 0; + +again: + { + const char *ifname = known_ifnames[try_count % 2]; + + nm_assert (ifindex > 0); + nm_assert (ifname && nm_utils_is_valid_iface_name (ifname, NULL)); + nm_assert (fd >= 0); + + memset (&ifr, 0, sizeof (ifr)); + nm_utils_ifname_cpy (ifr.ifr_name, ifname); + if (edata_type == IOCTL_CALL_DATA_TYPE_IFRDATA) + ifr.ifr_data = edata; + else if (edata_type == IOCTL_CALL_DATA_TYPE_IFRU) + memcpy (&ifr.ifr_ifru, edata, NM_MIN (edata_size, sizeof (ifr.ifr_ifru))); + + if (ioctl (fd, ioctl_request, &ifr) < 0) { + r = -NM_ERRNO_NATIVE (errno); + nm_log_trace (LOGD_PLATFORM, "%s[%d]: %s, %s: failed: %s", + log_ioctl_type, + ifindex, + log_subtype, + ifname, + nm_strerror_native (-r)); + } else { + r = 0; + nm_log_trace (LOGD_PLATFORM, "%s[%d]: %s, %s: success", + log_ioctl_type, + ifindex, + log_subtype, + ifname); + } + } + + try_count++; + + /* resolve the name again to see whether the ifindex still has the same name. */ + if (!nmp_utils_if_indextoname (ifindex, known_ifnames[try_count % 2])) { + /* we could not find the ifindex again. Probably the device just got + * removed. + * + * In both cases we return the error code we got from ioctl above. + * Either it failed because the device was gone already or it still + * managed to complete the call. In both cases, the error code is good. */ + failure_reason = "cannot resolve ifindex after ioctl call. Probably the device was just removed"; + goto out; } + + /* check whether the ifname changed in the meantime. If yes, would render the result + * invalid. Note that this cannot detect every race regarding renames, for example: + * + * - if_indextoname(#10) gives eth0 + * - rename(#10) => eth0_tmp + * - rename(#11) => eth0 + * - ioctl(eth0) (wrongly fetching #11, formerly eth1) + * - rename(#11) => eth_something + * - rename(#10) => eth0 + * - if_indextoname(#10) gives eth0 + */ + if (!nm_streq (known_ifnames[0], known_ifnames[1])) { + gboolean retry; + + /* we detected a possible(!) rename. + * + * For getters it's straight forward to just retry the call. + * + * For setters we also always retry. If our previous call operated on the right device, + * calling it again should have no bad effect (just setting the same thing more than once). + * + * The only potential bad thing is if there was a race involving swapping names, and we just + * set the ioctl option on the wrong device. But then the bad thing already happend and + * we cannot detect it (nor do anything about it). At least, we can retry and set the + * option on the right interface. */ + retry = (try_count < 5); + + nm_log_trace (LOGD_PLATFORM, "%s[%d]: %s: rename detected from \"%s\" to \"%s\". %s", + log_ioctl_type, + ifindex, + log_subtype, + known_ifnames[(try_count - 1) % 2], + known_ifnames[ try_count % 2], + retry + ? "Retry" + : "No retry"); + if (inout_ifname) + nm_utils_ifname_cpy (inout_ifname, known_ifnames[try_count % 2]); + if (retry) { + if (edata_size > 0) + memcpy (edata, edata_backup, edata_size); + goto again; + } + } + +out: + if (failure_reason) { + nm_log_trace (LOGD_PLATFORM, "%s[%d]: %s: %s: %s", + log_ioctl_type, + ifindex, + log_subtype, + failure_reason, + r < 0 + ? nm_strerror_native (-r) + : "assume success"); + } + if (r >= 0) + NM_SET_OUT (out_ifreq, ifr); + return r; } -#define nm_auto_socket_handle nm_auto(socket_handle_destroy) /****************************************************************************** * ethtool @@ -121,9 +292,13 @@ NM_UTILS_ENUM2STR_DEFINE_STATIC (_ethtool_cmd_to_string, guint32, ); static const char * -_ethtool_data_to_string (gconstpointer edata, char *buf, gsize len) +_ethtool_edata_to_string (gpointer edata, gsize edata_size, char *sbuf, gsize sbuf_len) { - return _ethtool_cmd_to_string (*((guint32 *) edata), buf, len); + nm_assert (edata); + nm_assert (edata_size >= sizeof (guint32)); + nm_assert ((((intptr_t) edata) % _nm_alignof (guint32)) == 0); + + return _ethtool_cmd_to_string (*((guint32 *) edata), sbuf, sbuf_len); } /*****************************************************************************/ @@ -136,56 +311,37 @@ _ethtool_data_to_string (gconstpointer edata, char *buf, gsize len) #endif static int -ethtool_call_handle (SocketHandle *shandle, gpointer edata) +_ethtool_call_handle (SocketHandle *shandle, gpointer edata, gsize edata_size) { - struct ifreq ifr = { - .ifr_data = edata, - }; char sbuf[50]; - int errsv; - - nm_assert (shandle); - nm_assert (shandle->ifindex); - nm_assert (shandle->ifname[0]); - nm_assert (strlen (shandle->ifname) < IFNAMSIZ); - nm_assert (edata); - memcpy (ifr.ifr_name, shandle->ifname, IFNAMSIZ); - if (ioctl (shandle->fd, SIOCETHTOOL, &ifr) < 0) { - errsv = errno; - nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s, %s: failed: %s", - shandle->ifindex, - _ethtool_data_to_string (edata, sbuf, sizeof (sbuf)), - shandle->ifname, - nm_strerror_native (errsv)); - return -NM_ERRNO_NATIVE (errsv); - } - - nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s, %s: success", - shandle->ifindex, - _ethtool_data_to_string (edata, sbuf, sizeof (sbuf)), - shandle->ifname); - return 0; + return _ioctl_call ("ethtool", + _ethtool_edata_to_string (edata, edata_size, sbuf, sizeof (sbuf)), + SIOCETHTOOL, + shandle->ifindex, + &shandle->fd, + shandle->ifname, + IOCTL_CALL_DATA_TYPE_IFRDATA, + edata, + edata_size, + NULL); } static int -ethtool_call_ifindex (int ifindex, gpointer edata) +_ethtool_call_once (int ifindex, gpointer edata, gsize edata_size) { - nm_auto_socket_handle SocketHandle shandle = { }; - int r; char sbuf[50]; - nm_assert (edata); - - if ((r = socket_handle_init (&shandle, ifindex)) < 0) { - nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: failed creating ethtool socket: %s", - ifindex, - _ethtool_data_to_string (edata, sbuf, sizeof (sbuf)), - nm_strerror_native (-r)); - return r; - } - - return ethtool_call_handle (&shandle, edata); + return _ioctl_call ("ethtool", + _ethtool_edata_to_string (edata, edata_size, sbuf, sizeof (sbuf)), + SIOCETHTOOL, + ifindex, + NULL, + NULL, + IOCTL_CALL_DATA_TYPE_IFRDATA, + edata, + edata_size, + NULL); } /*****************************************************************************/ @@ -196,27 +352,29 @@ ethtool_get_stringset (SocketHandle *shandle, int stringset_id) struct { struct ethtool_sset_info info; guint32 sentinel; - } sset_info = { }; + } sset_info = { + .info.cmd = ETHTOOL_GSSET_INFO, + .info.reserved = 0, + .info.sset_mask = (1ULL << stringset_id), + }; gs_free struct ethtool_gstrings *gstrings = NULL; + gsize gstrings_len; guint32 i, len; - sset_info.info.cmd = ETHTOOL_GSSET_INFO; - sset_info.info.reserved = 0; - sset_info.info.sset_mask = (1ULL << stringset_id); - - if (ethtool_call_handle (shandle, &sset_info) < 0) + if (_ethtool_call_handle (shandle, &sset_info, sizeof (sset_info)) < 0) return NULL; if (!sset_info.info.sset_mask) return NULL; len = sset_info.info.data[0]; - gstrings = g_malloc0 (sizeof (*gstrings) + (len * ETH_GSTRING_LEN)); + gstrings_len = sizeof (*gstrings) + (len * ETH_GSTRING_LEN); + gstrings = g_malloc0 (gstrings_len); gstrings->cmd = ETHTOOL_GSTRINGS; gstrings->string_set = stringset_id; gstrings->len = len; if (gstrings->len > 0) { - if (ethtool_call_handle (shandle, gstrings) < 0) + if (_ethtool_call_handle (shandle, gstrings, gstrings_len) < 0) return NULL; for (i = 0; i < gstrings->len; i++) { /* ensure NUL terminated */ @@ -402,18 +560,20 @@ ethtool_get_features (SocketHandle *shandle) return NULL; if (ss_features->len > 0) { - gs_free struct ethtool_gfeatures *gfeatures = NULL; + gs_free struct ethtool_gfeatures *gfeatures_free = NULL; + struct ethtool_gfeatures *gfeatures; + gsize gfeatures_len; guint idx; const NMEthtoolFeatureState *states_list0 = NULL; const NMEthtoolFeatureState *const*states_plist0 = NULL; guint states_plist_n = 0; - gfeatures = g_malloc0 ( sizeof (struct ethtool_gfeatures) - + (NM_DIV_ROUND_UP (ss_features->len, 32u) * sizeof(gfeatures->features[0]))); - + gfeatures_len = sizeof (struct ethtool_gfeatures) + + (NM_DIV_ROUND_UP (ss_features->len, 32u) * sizeof(gfeatures->features[0])); + gfeatures = nm_malloc0_maybe_a (300, gfeatures_len, &gfeatures_free); gfeatures->cmd = ETHTOOL_GFEATURES; gfeatures->size = NM_DIV_ROUND_UP (ss_features->len, 32u); - if (ethtool_call_handle (shandle, gfeatures) < 0) + if (_ethtool_call_handle (shandle, gfeatures, gfeatures_len) < 0) return NULL; for (idx = 0; idx < G_N_ELEMENTS (_ethtool_feature_infos); idx++) { @@ -477,20 +637,11 @@ ethtool_get_features (SocketHandle *shandle) NMEthtoolFeatureStates * nmp_utils_ethtool_get_features (int ifindex) { - nm_auto_socket_handle SocketHandle shandle = { }; + nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT (ifindex); NMEthtoolFeatureStates *features; - int r; g_return_val_if_fail (ifindex > 0, 0); - if ((r = socket_handle_init (&shandle, ifindex)) < 0) { - nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: failed creating ethtool socket: %s", - ifindex, - "get-features", - nm_strerror_native (-r)); - return FALSE; - } - features = ethtool_get_features (&shandle); if (!features) { @@ -530,8 +681,10 @@ nmp_utils_ethtool_set_features (int ifindex, const NMTernary *requested /* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */, gboolean do_set /* or reset */) { - nm_auto_socket_handle SocketHandle shandle = { }; - gs_free struct ethtool_sfeatures *sfeatures = NULL; + nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT (ifindex); + gs_free struct ethtool_sfeatures *sfeatures_free = NULL; + struct ethtool_sfeatures *sfeatures; + gsize sfeatures_len; int r; guint i, j; struct { @@ -614,16 +767,9 @@ nmp_utils_ethtool_set_features (int ifindex, return TRUE; } - if ((r = socket_handle_init (&shandle, ifindex)) < 0) { - nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: failed creating ethtool socket: %s", - ifindex, - "set-features", - nm_strerror_native (-r)); - return FALSE; - } - - sfeatures = g_malloc0 (sizeof (struct ethtool_sfeatures) - + (NM_DIV_ROUND_UP (features->n_ss_features, 32U) * sizeof(sfeatures->features[0]))); + sfeatures_len = sizeof (struct ethtool_sfeatures) + + (NM_DIV_ROUND_UP (features->n_ss_features, 32U) * sizeof(sfeatures->features[0])); + sfeatures = nm_malloc0_maybe_a (300, sfeatures_len, &sfeatures_free); sfeatures->cmd = ETHTOOL_SFEATURES; sfeatures->size = NM_DIV_ROUND_UP (features->n_ss_features, 32U); @@ -649,7 +795,8 @@ nmp_utils_ethtool_set_features (int ifindex, sfeatures->features[i_block].requested &= ~i_flag; } - if ((r = ethtool_call_handle (&shandle, sfeatures)) < 0) { + r = _ethtool_call_handle (&shandle, sfeatures, sfeatures_len); + if (r < 0) { success = FALSE; nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: failure setting features (%s)", ifindex, @@ -687,10 +834,10 @@ nmp_utils_ethtool_get_driver_info (int ifindex, g_return_val_if_fail (data, FALSE); drvinfo = (struct ethtool_drvinfo *) data; - - memset (drvinfo, 0, sizeof (*drvinfo)); - drvinfo->cmd = ETHTOOL_GDRVINFO; - return ethtool_call_ifindex (ifindex, drvinfo) >= 0; + *drvinfo = (struct ethtool_drvinfo) { + .cmd = ETHTOOL_GDRVINFO, + }; + return _ethtool_call_once (ifindex, drvinfo, sizeof (*drvinfo)) >= 0; } gboolean @@ -701,16 +848,16 @@ nmp_utils_ethtool_get_permanent_address (int ifindex, struct { struct ethtool_perm_addr e; guint8 _extra_data[NM_UTILS_HWADDR_LEN_MAX + 1]; - } edata; + } edata = { + .e.cmd = ETHTOOL_GPERMADDR, + .e.size = NM_UTILS_HWADDR_LEN_MAX, + }; + guint i; g_return_val_if_fail (ifindex > 0, FALSE); - memset (&edata, 0, sizeof (edata)); - edata.e.cmd = ETHTOOL_GPERMADDR; - edata.e.size = NM_UTILS_HWADDR_LEN_MAX; - - if (ethtool_call_ifindex (ifindex, &edata.e) < 0) + if (_ethtool_call_once (ifindex, &edata, sizeof (edata)) < 0) return FALSE; if (edata.e.size > NM_UTILS_HWADDR_LEN_MAX) @@ -747,27 +894,20 @@ nmp_utils_ethtool_supports_carrier_detect (int ifindex) * assume the device supports carrier-detect, otherwise we assume it * doesn't. */ - return ethtool_call_ifindex (ifindex, &edata) >= 0; + return _ethtool_call_once (ifindex, &edata, sizeof (edata)) >= 0; } gboolean nmp_utils_ethtool_supports_vlans (int ifindex) { - nm_auto_socket_handle SocketHandle shandle = { }; - int r; - gs_free struct ethtool_gfeatures *features = NULL; + nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT (ifindex); + gs_free struct ethtool_gfeatures *features_free = NULL; + struct ethtool_gfeatures *features; + gsize features_len; int idx, block, bit, size; g_return_val_if_fail (ifindex > 0, FALSE); - if ((r = socket_handle_init (&shandle, ifindex)) < 0) { - nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: failed creating ethtool socket: %s", - ifindex, - "support-vlans", - nm_strerror_native (-r)); - return FALSE; - } - idx = ethtool_get_stringset_index (&shandle, ETH_SS_FEATURES, "vlan-challenged"); if (idx < 0) { nm_log_dbg (LOGD_PLATFORM, "ethtool[%d]: vlan-challenged ethtool feature does not exist?", ifindex); @@ -778,11 +918,13 @@ nmp_utils_ethtool_supports_vlans (int ifindex) bit = idx % 32; size = block + 1; - features = g_malloc0 (sizeof (*features) + size * sizeof (struct ethtool_get_features_block)); + features_len = sizeof (*features) + + (size * sizeof (struct ethtool_get_features_block)); + features = nm_malloc0_maybe_a (300, features_len, &features_free); features->cmd = ETHTOOL_GFEATURES; features->size = size; - if (ethtool_call_handle (&shandle, features) < 0) + if (_ethtool_call_handle (&shandle, features, features_len) < 0) return FALSE; return !(features->features[block].active & (1 << bit)); @@ -791,32 +933,25 @@ nmp_utils_ethtool_supports_vlans (int ifindex) int nmp_utils_ethtool_get_peer_ifindex (int ifindex) { - nm_auto_socket_handle SocketHandle shandle = { }; - int r; - - gs_free struct ethtool_stats *stats = NULL; + nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT (ifindex); + gsize stats_len; + gs_free struct ethtool_stats *stats_free = NULL; + struct ethtool_stats *stats; int peer_ifindex_stat; g_return_val_if_fail (ifindex > 0, 0); - if ((r = socket_handle_init (&shandle, ifindex)) < 0) { - nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: failed creating ethtool socket: %s", - ifindex, - "get-peer-ifindex", - nm_strerror_native (-r)); - return FALSE; - } - peer_ifindex_stat = ethtool_get_stringset_index (&shandle, ETH_SS_STATS, "peer_ifindex"); if (peer_ifindex_stat < 0) { nm_log_dbg (LOGD_PLATFORM, "ethtool[%d]: peer_ifindex stat does not exist?", ifindex); return FALSE; } - stats = g_malloc0 (sizeof (*stats) + (peer_ifindex_stat + 1) * sizeof (guint64)); + stats_len = sizeof (*stats) + (peer_ifindex_stat + 1) * sizeof (guint64); + stats = nm_malloc0_maybe_a (300, stats_len, &stats_free); stats->cmd = ETHTOOL_GSTATS; stats->n_stats = peer_ifindex_stat + 1; - if (ethtool_call_ifindex (ifindex, stats) < 0) + if (_ethtool_call_handle (&shandle, stats, stats_len) < 0) return 0; return stats->data[peer_ifindex_stat]; @@ -825,13 +960,13 @@ nmp_utils_ethtool_get_peer_ifindex (int ifindex) gboolean nmp_utils_ethtool_get_wake_on_lan (int ifindex) { - struct ethtool_wolinfo wol; + struct ethtool_wolinfo wol = { + .cmd = ETHTOOL_GWOL, + }; g_return_val_if_fail (ifindex > 0, FALSE); - memset (&wol, 0, sizeof (wol)); - wol.cmd = ETHTOOL_GWOL; - if (ethtool_call_ifindex (ifindex, &wol) < 0) + if (_ethtool_call_once (ifindex, &wol, sizeof (wol)) < 0) return FALSE; return wol.wolopts != 0; @@ -849,11 +984,10 @@ nmp_utils_ethtool_get_link_settings (int ifindex, g_return_val_if_fail (ifindex > 0, FALSE); - if (ethtool_call_ifindex (ifindex, &edata) < 0) + if (_ethtool_call_once (ifindex, &edata, sizeof (edata)) < 0) return FALSE; - if (out_autoneg) - *out_autoneg = (edata.autoneg == AUTONEG_ENABLE); + NM_SET_OUT (out_autoneg, (edata.autoneg == AUTONEG_ENABLE)); if (out_speed) { guint32 speed; @@ -922,6 +1056,7 @@ nmp_utils_ethtool_set_link_settings (int ifindex, guint32 speed, NMPlatformLinkDuplexType duplex) { + nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT (ifindex); struct ethtool_cmd edata = { .cmd = ETHTOOL_GSET, }; @@ -931,7 +1066,7 @@ nmp_utils_ethtool_set_link_settings (int ifindex, || (!speed && duplex == NM_PLATFORM_LINK_DUPLEX_UNKNOWN), FALSE); /* retrieve first current settings */ - if (ethtool_call_ifindex (ifindex, &edata) < 0) + if (_ethtool_call_handle (&shandle, &edata, sizeof (edata)) < 0) return FALSE; /* FIXME: try first new ETHTOOL_GLINKSETTINGS/SLINKSETTINGS API @@ -987,7 +1122,7 @@ nmp_utils_ethtool_set_link_settings (int ifindex, } } - return ethtool_call_ifindex (ifindex, &edata) >= 0; + return _ethtool_call_handle (&shandle, &edata, sizeof (edata)) >= 0; } gboolean @@ -995,7 +1130,10 @@ nmp_utils_ethtool_set_wake_on_lan (int ifindex, NMSettingWiredWakeOnLan wol, const char *wol_password) { - struct ethtool_wolinfo wol_info = { }; + struct ethtool_wolinfo wol_info = { + .cmd = ETHTOOL_SWOL, + .wolopts = 0, + }; g_return_val_if_fail (ifindex > 0, FALSE); @@ -1005,9 +1143,6 @@ nmp_utils_ethtool_set_wake_on_lan (int ifindex, nm_log_dbg (LOGD_PLATFORM, "ethtool[%d]: setting Wake-on-LAN options 0x%x, password '%s'", ifindex, (unsigned) wol, wol_password); - wol_info.cmd = ETHTOOL_SWOL; - wol_info.wolopts = 0; - if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_PHY)) wol_info.wolopts |= WAKE_PHY; if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_UNICAST)) @@ -1029,7 +1164,7 @@ nmp_utils_ethtool_set_wake_on_lan (int ifindex, wol_info.wolopts |= WAKE_MAGICSECURE; } - return ethtool_call_ifindex (ifindex, &wol_info) >= 0; + return _ethtool_call_once (ifindex, &wol_info, sizeof (wol_info)) >= 0; } /****************************************************************************** @@ -1039,40 +1174,44 @@ nmp_utils_ethtool_set_wake_on_lan (int ifindex, gboolean nmp_utils_mii_supports_carrier_detect (int ifindex) { - nm_auto_socket_handle SocketHandle shandle = { }; + nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT (ifindex); int r; struct ifreq ifr; struct mii_ioctl_data *mii; - int errsv; g_return_val_if_fail (ifindex > 0, FALSE); - if ((r = socket_handle_init (&shandle, ifindex)) < 0) { - nm_log_trace (LOGD_PLATFORM, "mii[%d]: carrier-detect no: failed creating ethtool socket: %s", - ifindex, - nm_strerror_native (-r)); + r = _ioctl_call ("mii", + "SIOCGMIIPHY", + SIOCGMIIPHY, + shandle.ifindex, + &shandle.fd, + shandle.ifname, + IOCTL_CALL_DATA_TYPE_NONE, + NULL, + 0, + &ifr); + if (r < 0) return FALSE; - } - - memset (&ifr, 0, sizeof (struct ifreq)); - memcpy (ifr.ifr_name, shandle.ifname, IFNAMSIZ); - - if (ioctl (shandle.fd, SIOCGMIIPHY, &ifr) < 0) { - errsv = errno; - nm_log_trace (LOGD_PLATFORM, "mii[%d,%s]: carrier-detect no: SIOCGMIIPHY failed: %s", ifindex, shandle.ifname, nm_strerror_native (errsv)); - return FALSE; - } /* If we can read the BMSR register, we assume that the card supports MII link detection */ mii = (struct mii_ioctl_data *) &ifr.ifr_ifru; mii->reg_num = MII_BMSR; - if (ioctl (shandle.fd, SIOCGMIIREG, &ifr) != 0) { - errsv = errno; - nm_log_trace (LOGD_PLATFORM, "mii[%d,%s]: carrier-detect no: SIOCGMIIREG failed: %s", ifindex, shandle.ifname, nm_strerror_native (errsv)); + r = _ioctl_call ("mii", + "SIOCGMIIREG", + SIOCGMIIREG, + shandle.ifindex, + &shandle.fd, + shandle.ifname, + IOCTL_CALL_DATA_TYPE_IFRU, + mii, + sizeof (*mii), + &ifr); + if (r < 0) return FALSE; - } + mii = (struct mii_ioctl_data *) &ifr.ifr_ifru; nm_log_trace (LOGD_PLATFORM, "mii[%d,%s]: carrier-detect yes: SIOCGMIIREG result 0x%X", ifindex, shandle.ifname, mii->val_out); return TRUE; } diff --git a/src/platform/nm-platform-utils.h b/src/platform/nm-platform-utils.h index f5491679..ae3f51e2 100644 --- a/src/platform/nm-platform-utils.h +++ b/src/platform/nm-platform-utils.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-platform.c - Handle runtime kernel networking configuration * * This program is free software; you can redistribute it and/or modify diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 1fc0ccb7..6ead15ed 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-platform.c - Handle runtime kernel networking configuration * * This program is free software; you can redistribute it and/or modify @@ -34,6 +33,7 @@ #include <linux/if_tun.h> #include <linux/if_tunnel.h> #include <linux/rtnetlink.h> +#include <linux/tc_act/tc_mirred.h> #include <libudev.h> #include "nm-utils.h" @@ -50,12 +50,71 @@ /*****************************************************************************/ -G_STATIC_ASSERT (sizeof ( ((NMPlatformLink *) NULL)->addr.data ) == NM_UTILS_HWADDR_LEN_MAX); G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPAddress, address_ptr) == G_STRUCT_OFFSET (NMPlatformIP4Address, address)); G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPAddress, address_ptr) == G_STRUCT_OFFSET (NMPlatformIP6Address, address)); G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPRoute, network_ptr) == G_STRUCT_OFFSET (NMPlatformIP4Route, network)); G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPRoute, network_ptr) == G_STRUCT_OFFSET (NMPlatformIP6Route, network)); +/*****************************************************************************/ + +G_STATIC_ASSERT (sizeof ( ((NMPLinkAddress *) NULL)->data ) == NM_UTILS_HWADDR_LEN_MAX); +G_STATIC_ASSERT (sizeof ( ((NMPlatformLink *) NULL)->l_address.data ) == NM_UTILS_HWADDR_LEN_MAX); +G_STATIC_ASSERT (sizeof ( ((NMPlatformLink *) NULL)->l_broadcast.data ) == NM_UTILS_HWADDR_LEN_MAX); + +static const char * +_nmp_link_address_to_string (const NMPLinkAddress *addr, + char buf[static (NM_UTILS_HWADDR_LEN_MAX * 3)]) +{ + nm_assert (addr); + + if (addr->len > 0) { + if (!nm_utils_hwaddr_ntoa_buf (addr->data, + addr->len, + TRUE, + buf, + NM_UTILS_HWADDR_LEN_MAX * 3)) { + buf[0] = '\0'; + g_return_val_if_reached (buf); + } + } else + buf[0] = '\0'; + + return buf; +} + +gconstpointer +nmp_link_address_get (const NMPLinkAddress *addr, size_t *length) +{ + if ( !addr + || addr->len <= 0) { + NM_SET_OUT (length, 0); + return NULL; + } + + if (addr->len > NM_UTILS_HWADDR_LEN_MAX) { + NM_SET_OUT (length, 0); + g_return_val_if_reached (NULL); + } + + NM_SET_OUT (length, addr->len); + return addr->data; +} + +GBytes * +nmp_link_address_get_as_bytes (const NMPLinkAddress *addr) +{ + gconstpointer data; + size_t length; + + data = nmp_link_address_get (addr, &length); + + return length > 0 + ? g_bytes_new (data, length) + : NULL; +} + +/*****************************************************************************/ + #define _NMLOG_DOMAIN LOGD_PLATFORM #define _NMLOG_PREFIX_NAME "platform" @@ -220,7 +279,8 @@ nm_platform_setup (NMPlatform *instance) nm_singleton_instance_register (); - nm_log_dbg (LOGD_CORE, "setup %s singleton (%p, %s)", "NMPlatform", singleton_instance, G_OBJECT_TYPE_NAME (instance)); + nm_log_dbg (LOGD_CORE, "setup %s singleton ("NM_HASH_OBFUSCATE_PTR_FMT")", + "NMPlatform", NM_HASH_OBFUSCATE_PTR (instance)); } /** @@ -479,6 +539,40 @@ nm_platform_sysctl_set (NMPlatform *self, const char *pathid, int dirfd, const c return klass->sysctl_set (self, pathid, dirfd, path, value); } +/** + * nm_platform_sysctl_set_async: + * @self: platform instance + * @pathid: if @dirfd is present, this must be the full path that is looked up + * @dirfd: optional file descriptor for parent directory for openat() + * @path: absolute option path + * @values: NULL-terminated array of strings to be written + * @callback: function called on termination + * @data: data passed to callback function + * @cancellable: to cancel the operation + * + * This function is intended to be used for writing values to sysctl-style + * virtual runtime configuration files. This includes not only /proc/sys + * but also for example /sys/class. The function does not block and returns + * immediately. The callback is always invoked, and asynchronously. The file + * is closed after writing each value and reopened to write the next one so + * that the function can be used safely on all /proc and /sys files, + * independently of how /proc/sys/kernel/sysctl_writes_strict is configured. + */ +void nm_platform_sysctl_set_async (NMPlatform *self, + const char *pathid, + int dirfd, + const char *path, + const char *const *values, + NMPlatformAsyncCallback callback, + gpointer data, + GCancellable *cancellable) +{ + _CHECK_SELF_VOID (self, klass); + + klass->sysctl_set_async (self, pathid, dirfd, path, values, callback, data, cancellable); +} + + gboolean nm_platform_sysctl_ip_conf_set_ipv6_hop_limit_safe (NMPlatform *self, const char *iface, @@ -621,14 +715,14 @@ nm_platform_sysctl_get_int_checked (NMPlatform *self, /*****************************************************************************/ char * -nm_platform_sysctl_ip_conf_get (NMPlatform *platform, +nm_platform_sysctl_ip_conf_get (NMPlatform *self, int addr_family, const char *ifname, const char *property) { char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; - return nm_platform_sysctl_get (platform, + return nm_platform_sysctl_get (self, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (addr_family, buf, ifname, @@ -636,7 +730,7 @@ nm_platform_sysctl_ip_conf_get (NMPlatform *platform, } gint64 -nm_platform_sysctl_ip_conf_get_int_checked (NMPlatform *platform, +nm_platform_sysctl_ip_conf_get_int_checked (NMPlatform *self, int addr_family, const char *ifname, const char *property, @@ -647,7 +741,7 @@ nm_platform_sysctl_ip_conf_get_int_checked (NMPlatform *platform, { char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; - return nm_platform_sysctl_get_int_checked (platform, + return nm_platform_sysctl_get_int_checked (self, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (addr_family, buf, ifname, @@ -659,7 +753,7 @@ nm_platform_sysctl_ip_conf_get_int_checked (NMPlatform *platform, } gboolean -nm_platform_sysctl_ip_conf_set (NMPlatform *platform, +nm_platform_sysctl_ip_conf_set (NMPlatform *self, int addr_family, const char *ifname, const char *property, @@ -667,7 +761,7 @@ nm_platform_sysctl_ip_conf_set (NMPlatform *platform, { char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; - return nm_platform_sysctl_set (platform, + return nm_platform_sysctl_set (self, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (addr_family, buf, ifname, @@ -676,7 +770,7 @@ nm_platform_sysctl_ip_conf_set (NMPlatform *platform, } gboolean -nm_platform_sysctl_ip_conf_set_int64 (NMPlatform *platform, +nm_platform_sysctl_ip_conf_set_int64 (NMPlatform *self, int addr_family, const char *ifname, const char *property, @@ -685,7 +779,7 @@ nm_platform_sysctl_ip_conf_set_int64 (NMPlatform *platform, char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; char s[64]; - return nm_platform_sysctl_set (platform, + return nm_platform_sysctl_set (self, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (addr_family, buf, ifname, @@ -693,6 +787,47 @@ nm_platform_sysctl_ip_conf_set_int64 (NMPlatform *platform, nm_sprintf_buf (s, "%"G_GINT64_FORMAT, value)); } +int +nm_platform_sysctl_ip_conf_get_rp_filter_ipv4 (NMPlatform *self, + const char *ifname, + gboolean consider_all, + gboolean *out_due_to_all) +{ + int val, val_all; + + NM_SET_OUT (out_due_to_all, FALSE); + + if (!ifname) + return -1; + + val = nm_platform_sysctl_ip_conf_get_int_checked (self, + AF_INET, + ifname, + "rp_filter", + 10, 0, 2, -1); + if (val == -1) + return -1; + + /* the effectively used value is the rp_filter sysctl value of MAX(all,ifname). + * Note that this is the numerical MAX(), despite rp_filter "1" being more strict + * than "2". */ + if ( val < 2 + && consider_all + && !nm_streq (ifname, "all")) { + val_all = nm_platform_sysctl_ip_conf_get_int_checked (self, + AF_INET, + "all", + "rp_filter", + 10, 0, 2, val); + if (val_all > val) { + val = val_all; + NM_SET_OUT (out_due_to_all, TRUE); + } + } + + return val; +} + /*****************************************************************************/ static int @@ -905,14 +1040,15 @@ nm_platform_link_get_by_ifname (NMPlatform *self, const char *ifname) } struct _nm_platform_link_get_by_address_data { - gconstpointer address; - guint8 length; + gconstpointer data; + guint8 len; }; static gboolean _nm_platform_link_get_by_address_match_link (const NMPObject *obj, struct _nm_platform_link_get_by_address_data *d) { - return obj->link.addr.len == d->length && !memcmp (obj->link.addr.data, d->address, d->length); + return obj->link.l_address.len == d->len + && !memcmp (obj->link.l_address.data, d->data, d->len); } /** @@ -932,8 +1068,8 @@ nm_platform_link_get_by_address (NMPlatform *self, { const NMPObject *obj; struct _nm_platform_link_get_by_address_data d = { - .address = address, - .length = length, + .data = address, + .len = length, }; _CHECK_SELF (self, klass, NULL); @@ -1471,19 +1607,7 @@ nm_platform_link_get_address (NMPlatform *self, int ifindex, size_t *length) const NMPlatformLink *pllink; pllink = nm_platform_link_get (self, ifindex); - if ( !pllink - || pllink->addr.len <= 0) { - NM_SET_OUT (length, 0); - return NULL; - } - - if (pllink->addr.len > NM_UTILS_HWADDR_LEN_MAX) { - NM_SET_OUT (length, 0); - g_return_val_if_reached (NULL); - } - - NM_SET_OUT (length, pllink->addr.len); - return pllink->addr.data; + return nmp_link_address_get (pllink ? &pllink->l_address : NULL, length); } /** @@ -1551,19 +1675,35 @@ nm_platform_link_supports_sriov (NMPlatform *self, int ifindex) * @num_vfs: the number of VFs to create * @autoprobe: the new autoprobe-drivers value (pass * %NM_TERNARY_DEFAULT to keep current value) + * @callback: called when the operation finishes + * @callback_data: data passed to @callback + * @cancellable: cancellable to abort the operation + * + * Sets SR-IOV parameters asynchronously without + * blocking the main thread. The callback function is + * always invoked, and asynchronously. */ -gboolean -nm_platform_link_set_sriov_params (NMPlatform *self, - int ifindex, - guint num_vfs, - NMTernary autoprobe) +void +nm_platform_link_set_sriov_params_async (NMPlatform *self, + int ifindex, + guint num_vfs, + NMTernary autoprobe, + NMPlatformAsyncCallback callback, + gpointer callback_data, + GCancellable *cancellable) { - _CHECK_SELF (self, klass, FALSE); + _CHECK_SELF_VOID (self, klass); - g_return_val_if_fail (ifindex > 0, FALSE); + g_return_if_fail (ifindex > 0); _LOG3D ("link: setting %u total VFs and autoprobe %d", num_vfs, (int) autoprobe); - return klass->link_set_sriov_params (self, ifindex, num_vfs, autoprobe); + klass->link_set_sriov_params_async (self, + ifindex, + num_vfs, + autoprobe, + callback, + callback_data, + cancellable); } gboolean @@ -2369,9 +2509,10 @@ nm_platform_link_6lowpan_get_properties (NMPlatform *self, int ifindex, int *out if (out_parent) { const NMPlatformLink *parent_plink; - parent_plink = nm_platform_link_get_by_address (self, NM_LINK_TYPE_WPAN, - plink->addr.data, - plink->addr.len); + parent_plink = nm_platform_link_get_by_address (self, + NM_LINK_TYPE_WPAN, + plink->l_address.data, + plink->l_address.len); NM_SET_OUT (out_parent, parent_plink ? parent_plink->ifindex : -1); } @@ -3400,21 +3541,21 @@ nm_platform_ethtool_set_features (NMPlatform *self, /*****************************************************************************/ const NMDedupMultiHeadEntry * -nm_platform_lookup_all (NMPlatform *platform, +nm_platform_lookup_all (NMPlatform *self, NMPCacheIdType cache_id_type, const NMPObject *obj) { - return nmp_cache_lookup_all (nm_platform_get_cache (platform), + return nmp_cache_lookup_all (nm_platform_get_cache (self), cache_id_type, obj); } const NMDedupMultiEntry * -nm_platform_lookup_entry (NMPlatform *platform, +nm_platform_lookup_entry (NMPlatform *self, NMPCacheIdType cache_id_type, const NMPObject *obj) { - return nmp_cache_lookup_entry_with_idx_type (nm_platform_get_cache (platform), + return nmp_cache_lookup_entry_with_idx_type (nm_platform_get_cache (self), cache_id_type, obj); } @@ -5076,10 +5217,27 @@ nm_platform_qdisc_add (NMPlatform *self, int ifindex = qdisc->ifindex; _CHECK_SELF (self, klass, -NME_BUG); + /* Note: @qdisc must not be copied or kept alive because the lifetime of qdisc.kind + * is undefined. */ + _LOG3D ("adding or updating a qdisc: %s", nm_platform_qdisc_to_string (qdisc, NULL, 0)); return klass->qdisc_add (self, flags, qdisc); } +/** + * nm_platform_qdisc_sync: + * @self: the #NMPlatform instance + * @ifindex: the ifindex where to configure the qdiscs. + * @known_qdiscs: the list of qdiscs (#NMPObject). + * + * The function promises not to take any reference to the qdisc + * instances from @known_qdiscs, nor to keep them around after + * the function returns. This is important, because it allows the + * caller to pass NMPlatformQdisc instances which "kind" string + * have a limited lifetime. + * + * Returns: %TRUE on success. + */ gboolean nm_platform_qdisc_sync (NMPlatform *self, int ifindex, @@ -5142,10 +5300,27 @@ nm_platform_tfilter_add (NMPlatform *self, int ifindex = tfilter->ifindex; _CHECK_SELF (self, klass, -NME_BUG); + /* Note: @tfilter must not be copied or kept alive because the lifetime of tfilter.kind + * and tfilter.action.kind is undefined. */ + _LOG3D ("adding or updating a tfilter: %s", nm_platform_tfilter_to_string (tfilter, NULL, 0)); return klass->tfilter_add (self, flags, tfilter); } +/** + * nm_platform_qdisc_sync: + * @self: the #NMPlatform instance + * @ifindex: the ifindex where to configure the qdiscs. + * @known_tfilters: the list of tfilters (#NMPObject). + * + * The function promises not to take any reference to the tfilter + * instances from @known_tfilters, nor to keep them around after + * the function returns. This is important, because it allows the + * caller to pass NMPlatformTfilter instances which "kind" string + * have a limited lifetime. + * + * Returns: %TRUE on success. + */ gboolean nm_platform_tfilter_sync (NMPlatform *self, int ifindex, @@ -5268,31 +5443,36 @@ nm_platform_link_to_string (const NMPlatformLink *link, char *buf, gsize len) { char master[20]; char parent[20]; - GString *str_flags; + char str_flags[1 + NM_PLATFORM_LINK_FLAGS2STR_MAX_LEN + 1]; + char str_highlighted_flags[50]; + char *s; + gsize l; char str_addrmode[30]; - gs_free char *str_addr = NULL; + char str_address[NM_UTILS_HWADDR_LEN_MAX * 3]; + char str_broadcast[NM_UTILS_HWADDR_LEN_MAX * 3]; char str_inet6_token[NM_UTILS_INET_ADDRSTRLEN]; const char *str_link_type; if (!nm_utils_to_string_buffer_init_null (link, &buf, &len)) return buf; - str_flags = g_string_new (NULL); + s = str_highlighted_flags; + l = sizeof (str_highlighted_flags); if (NM_FLAGS_HAS (link->n_ifi_flags, IFF_NOARP)) - g_string_append (str_flags, "NOARP,"); + nm_utils_strbuf_append_str (&s, &l, "NOARP,"); if (NM_FLAGS_HAS (link->n_ifi_flags, IFF_UP)) - g_string_append (str_flags, "UP"); + nm_utils_strbuf_append_str (&s, &l, "UP"); else - g_string_append (str_flags, "DOWN"); + nm_utils_strbuf_append_str (&s, &l, "DOWN"); if (link->connected) - g_string_append (str_flags, ",LOWER_UP"); + nm_utils_strbuf_append_str (&s, &l, ",LOWER_UP"); + nm_assert (s > str_highlighted_flags && l > 0); if (link->n_ifi_flags) { - char str_flags_buf[64]; - - nm_platform_link_flags2str (link->n_ifi_flags, str_flags_buf, sizeof (str_flags_buf)); - g_string_append_printf (str_flags, ";%s", str_flags_buf); - } + str_flags[0] = ';'; + nm_platform_link_flags2str (link->n_ifi_flags, &str_flags[1], sizeof (str_flags) - 1); + } else + str_flags[0] = '\0'; if (link->master) g_snprintf (master, sizeof (master), " master %d", link->master); @@ -5306,8 +5486,8 @@ nm_platform_link_to_string (const NMPlatformLink *link, char *buf, gsize len) else parent[0] = 0; - if (link->addr.len) - str_addr = nm_utils_hwaddr_ntoa (link->addr.data, MIN (link->addr.len, sizeof (link->addr.data))); + _nmp_link_address_to_string (&link->l_address, str_address); + _nmp_link_address_to_string (&link->l_broadcast, str_broadcast); str_link_type = nm_link_type_to_string (link->type); @@ -5315,7 +5495,7 @@ nm_platform_link_to_string (const NMPlatformLink *link, char *buf, gsize len) "%d: " /* ifindex */ "%s" /* name */ "%s" /* parent */ - " <%s>" /* flags */ + " <%s%s>" /* flags */ " mtu %d" "%s" /* master */ " arp %u" /* arptype */ @@ -5323,7 +5503,8 @@ nm_platform_link_to_string (const NMPlatformLink *link, char *buf, gsize len) "%s%s" /* kind */ "%s" /* is-in-udev */ "%s%s" /* addr-gen-mode */ - "%s%s" /* addr */ + "%s%s" /* l_address */ + "%s%s" /* l_broadcast */ "%s%s" /* inet6_token */ "%s%s" /* driver */ " rx:%"G_GUINT64_FORMAT",%"G_GUINT64_FORMAT @@ -5332,7 +5513,8 @@ nm_platform_link_to_string (const NMPlatformLink *link, char *buf, gsize len) link->ifindex, link->name, parent, - str_flags->str, + str_highlighted_flags, + str_flags, link->mtu, master, link->arptype, str_link_type ?: "???", @@ -5341,15 +5523,16 @@ nm_platform_link_to_string (const NMPlatformLink *link, char *buf, gsize len) link->initialized ? " init" : " not-init", link->inet6_addr_gen_mode_inv ? " addrgenmode " : "", link->inet6_addr_gen_mode_inv ? nm_platform_link_inet6_addrgenmode2str (_nm_platform_uint8_inv (link->inet6_addr_gen_mode_inv), str_addrmode, sizeof (str_addrmode)) : "", - str_addr ? " addr " : "", - str_addr ?: "", + str_address[0] ? " addr " : "", + str_address[0] ? str_address : "", + str_broadcast[0] ? " brd " : "", + str_broadcast[0] ? str_broadcast : "", link->inet6_token.id ? " inet6token " : "", link->inet6_token.id ? nm_utils_inet6_interface_identifier_to_token (link->inet6_token, str_inet6_token) : "", link->driver ? " driver " : "", link->driver ?: "", link->rx_packets, link->rx_bytes, link->tx_packets, link->tx_bytes); - g_string_free (str_flags, TRUE); return buf; } @@ -6426,19 +6609,41 @@ const char * nm_platform_qdisc_to_string (const NMPlatformQdisc *qdisc, char *buf, gsize len) { char str_dev[TO_STRING_DEV_BUF_SIZE]; + const char *buf0; if (!nm_utils_to_string_buffer_init_null (qdisc, &buf, &len)) return buf; - g_snprintf (buf, len, "%s%s family %d handle %x parent %x info %x", - qdisc->kind, - _to_string_dev (NULL, qdisc->ifindex, str_dev, sizeof (str_dev)), - qdisc->addr_family, - qdisc->handle, - qdisc->parent, - qdisc->info); + buf0 = buf; - return buf; + nm_utils_strbuf_append (&buf, &len, "%s%s family %u handle %x parent %x info %x", + qdisc->kind, + _to_string_dev (NULL, qdisc->ifindex, str_dev, sizeof (str_dev)), + qdisc->addr_family, + qdisc->handle, + qdisc->parent, + qdisc->info); + + if (nm_streq0 (qdisc->kind, "fq_codel")) { + if (qdisc->fq_codel.limit) + nm_utils_strbuf_append (&buf, &len, " limit %u", qdisc->fq_codel.limit); + if (qdisc->fq_codel.flows) + nm_utils_strbuf_append (&buf, &len, " flows %u", qdisc->fq_codel.flows); + if (qdisc->fq_codel.target) + nm_utils_strbuf_append (&buf, &len, " target %u", qdisc->fq_codel.target); + if (qdisc->fq_codel.interval) + nm_utils_strbuf_append (&buf, &len, " interval %u", qdisc->fq_codel.interval); + if (qdisc->fq_codel.quantum) + nm_utils_strbuf_append (&buf, &len, " quantum %u", qdisc->fq_codel.quantum); + if (qdisc->fq_codel.ce_threshold != NM_PLATFORM_FQ_CODEL_CE_THRESHOLD_DISABLED) + nm_utils_strbuf_append (&buf, &len, " ce_threshold %u", qdisc->fq_codel.ce_threshold); + if (qdisc->fq_codel.memory_limit != NM_PLATFORM_FQ_CODEL_MEMORY_LIMIT_UNSET) + nm_utils_strbuf_append (&buf, &len, " memory_limit %u", qdisc->fq_codel.memory_limit); + if (qdisc->fq_codel.ecn) + nm_utils_strbuf_append (&buf, &len, " ecn"); + } + + return buf0; } void @@ -6451,6 +6656,18 @@ nm_platform_qdisc_hash_update (const NMPlatformQdisc *obj, NMHashState *h) obj->handle, obj->parent, obj->info); + if (nm_streq0 (obj->kind, "fq_codel")) { + nm_hash_update_vals (h, + obj->fq_codel.limit, + obj->fq_codel.flows, + obj->fq_codel.target, + obj->fq_codel.interval, + obj->fq_codel.quantum, + obj->fq_codel.ce_threshold, + obj->fq_codel.memory_limit, + NM_HASH_COMBINE_BOOLS (guint8, + obj->fq_codel.ecn)); + } } int @@ -6464,6 +6681,17 @@ nm_platform_qdisc_cmp (const NMPlatformQdisc *a, const NMPlatformQdisc *b) NM_CMP_FIELD (a, b, handle); NM_CMP_FIELD (a, b, info); + if (nm_streq0 (a->kind, "fq_codel")) { + NM_CMP_FIELD (a, b, fq_codel.limit); + NM_CMP_FIELD (a, b, fq_codel.flows); + NM_CMP_FIELD (a, b, fq_codel.target); + NM_CMP_FIELD (a, b, fq_codel.interval); + NM_CMP_FIELD (a, b, fq_codel.quantum); + NM_CMP_FIELD (a, b, fq_codel.ce_threshold); + NM_CMP_FIELD (a, b, fq_codel.memory_limit); + NM_CMP_FIELD_UNSAFE (a, b, fq_codel.ecn); + } + return 0; } @@ -6492,11 +6720,18 @@ nm_platform_tfilter_to_string (const NMPlatformTfilter *tfilter, char *buf, gsiz NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL | NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII, &t)); + } else if (nm_streq (tfilter->action.kind, NM_PLATFORM_ACTION_KIND_MIRRED)) { + nm_utils_strbuf_append (&p, &l, "%s%s%s%s dev %d", + tfilter->action.mirred.ingress ? " ingress" : "", + tfilter->action.mirred.egress ? " egress" : "", + tfilter->action.mirred.mirror ? " mirror" : "", + tfilter->action.mirred.redirect ? " redirect" : "", + tfilter->action.mirred.ifindex); } } else act_buf[0] = '\0'; - g_snprintf (buf, len, "%s%s family %d handle %x parent %x info %x%s", + g_snprintf (buf, len, "%s%s family %u handle %x parent %x info %x%s", tfilter->kind, _to_string_dev (NULL, tfilter->ifindex, str_dev, sizeof (str_dev)), tfilter->addr_family, @@ -6520,8 +6755,17 @@ nm_platform_tfilter_hash_update (const NMPlatformTfilter *obj, NMHashState *h) obj->info); if (obj->action.kind) { nm_hash_update_str (h, obj->action.kind); - if (nm_streq (obj->action.kind, NM_PLATFORM_ACTION_KIND_SIMPLE)) + if (nm_streq (obj->action.kind, NM_PLATFORM_ACTION_KIND_SIMPLE)) { nm_hash_update_strarr (h, obj->action.simple.sdata); + } else if (nm_streq (obj->action.kind, NM_PLATFORM_ACTION_KIND_MIRRED)) { + nm_hash_update_vals (h, + obj->action.mirred.ifindex, + NM_HASH_COMBINE_BOOLS (guint8, + obj->action.mirred.ingress, + obj->action.mirred.egress, + obj->action.mirred.mirror, + obj->action.mirred.redirect)); + } } } @@ -6538,8 +6782,15 @@ nm_platform_tfilter_cmp (const NMPlatformTfilter *a, const NMPlatformTfilter *b) NM_CMP_FIELD_STR_INTERNED (a, b, action.kind); if (a->action.kind) { - if (nm_streq (a->action.kind, NM_PLATFORM_ACTION_KIND_SIMPLE)) + if (nm_streq (a->action.kind, NM_PLATFORM_ACTION_KIND_SIMPLE)) { NM_CMP_FIELD_STR (a, b, action.simple.sdata); + } else if (nm_streq (a->action.kind, NM_PLATFORM_ACTION_KIND_MIRRED)) { + NM_CMP_FIELD (a, b, action.mirred.ifindex); + NM_CMP_FIELD_UNSAFE (a, b, action.mirred.ingress); + NM_CMP_FIELD_UNSAFE (a, b, action.mirred.egress); + NM_CMP_FIELD_UNSAFE (a, b, action.mirred.mirror); + NM_CMP_FIELD_UNSAFE (a, b, action.mirred.redirect); + } } return 0; @@ -6641,7 +6892,8 @@ nm_platform_link_hash_update (const NMPlatformLink *obj, NMHashState *h) nm_hash_update_str0 (h, obj->kind); nm_hash_update_str0 (h, obj->driver); /* nm_hash_update_mem() also hashes the length obj->addr.len */ - nm_hash_update_mem (h, obj->addr.data, obj->addr.len); + nm_hash_update_mem (h, obj->l_address.data, NM_MIN (obj->l_address.len, sizeof (obj->l_address.data))); + nm_hash_update_mem (h, obj->l_broadcast.data, NM_MIN (obj->l_broadcast.len, sizeof (obj->l_broadcast.data))); } int @@ -6658,12 +6910,15 @@ nm_platform_link_cmp (const NMPlatformLink *a, const NMPlatformLink *b) NM_CMP_FIELD (a, b, mtu); NM_CMP_FIELD_BOOL (a, b, initialized); NM_CMP_FIELD (a, b, arptype); - NM_CMP_FIELD (a, b, addr.len); + NM_CMP_FIELD (a, b, l_address.len); + NM_CMP_FIELD (a, b, l_broadcast.len); NM_CMP_FIELD (a, b, inet6_addr_gen_mode_inv); NM_CMP_FIELD_STR_INTERNED (a, b, kind); NM_CMP_FIELD_STR_INTERNED (a, b, driver); - if (a->addr.len) - NM_CMP_FIELD_MEMCMP_LEN (a, b, addr.data, a->addr.len); + if (a->l_address.len) + NM_CMP_FIELD_MEMCMP_LEN (a, b, l_address.data, a->l_address.len); + if (a->l_broadcast.len) + NM_CMP_FIELD_MEMCMP_LEN (a, b, l_broadcast.data, a->l_broadcast.len); NM_CMP_FIELD_MEMCMP (a, b, inet6_token); NM_CMP_FIELD (a, b, rx_packets); NM_CMP_FIELD (a, b, rx_bytes); diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 97be8832..248eca44 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-platform.c - Handle runtime kernel networking configuration * * This program is free software; you can redistribute it and/or modify @@ -159,6 +158,14 @@ typedef enum { NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL, } NMPlatformRoutingRuleCmpType; +typedef struct { + guint8 data[20 /* NM_UTILS_HWADDR_LEN_MAX */ ]; + guint8 len; +} NMPLinkAddress; + +gconstpointer nmp_link_address_get (const NMPLinkAddress *addr, size_t *length); +GBytes *nmp_link_address_get_as_bytes (const NMPLinkAddress *addr); + typedef enum { /* match-flags are strictly inclusive. That means, @@ -230,11 +237,11 @@ struct _NMPlatformLink { /* rtnl_link_get_arptype(), ifinfomsg.ifi_type. */ guint32 arptype; - /* rtnl_link_get_addr(), IFLA_ADDRESS */ - struct { - guint8 data[20]; /* NM_UTILS_HWADDR_LEN_MAX */ - guint8 len; - } addr; + /* IFLA_ADDRESS */ + NMPLinkAddress l_address; + + /* IFLA_BROADCAST */ + NMPLinkAddress l_broadcast; /* rtnl_link_inet6_get_token(), IFLA_INET6_TOKEN */ NMUtilsIPv6IfaceId inet6_token; @@ -596,13 +603,48 @@ typedef struct { bool uid_range_has:1; /* has(FRA_UID_RANGE) */ } NMPlatformRoutingRule; +#define NM_PLATFORM_FQ_CODEL_MEMORY_LIMIT_UNSET (~((guint32) 0)) + +#define NM_PLATFORM_FQ_CODEL_CE_THRESHOLD_DISABLED ((guint32) 0x83126E97u) + +G_STATIC_ASSERT (((((guint64) NM_PLATFORM_FQ_CODEL_CE_THRESHOLD_DISABLED) * 1000u) >> 10) == (guint64) INT_MAX); + +typedef struct { + guint32 limit; + guint32 flows; + guint32 target; + guint32 interval; + guint32 quantum; + guint32 ce_threshold; /* TCA_FQ_CODEL_CE_THRESHOLD: kernel internally stores this value as + * ((val64 * NSEC_PER_USEC) >> CODEL_SHIFT). The default value (in + * the domain with this coersion) is CODEL_DISABLED_THRESHOLD (INT_MAX). + * That means, "disabled" is expressed on RTM_NEWQDISC netlink API by absence of the + * netlink attribute but also as the special value 0x83126E97u + * (NM_PLATFORM_FQ_CODEL_CE_THRESHOLD_DISABLED). + * Beware: zero is not the default you must always explicitly set this value. */ + guint32 memory_limit; /* TCA_FQ_CODEL_MEMORY_LIMIT: note that only values <= 2^31 are accepted by kernel + * and kernel defaults to 32MB. + * Note that we use the special value NM_PLATFORM_FQ_CODEL_MEMORY_LIMIT_UNSET + * to indicate that no explicit limit is set (when we send a RTM_NEWQDISC request). + * This will cause kernel to choose the default (32MB). + * Beware: zero is not the default you must always explicitly set this value. */ + bool ecn:1; +} NMPlatformQdiscFqCodel; + typedef struct { __NMPlatformObjWithIfindex_COMMON; + + /* beware, kind is embedded in an NMPObject, hence you must + * take care of the lifetime of the string. */ const char *kind; + int addr_family; guint32 handle; guint32 parent; guint32 info; + union { + NMPlatformQdiscFqCodel fq_codel; + }; } NMPlatformQdisc; typedef struct { @@ -610,17 +652,35 @@ typedef struct { } NMPlatformActionSimple; typedef struct { + int ifindex; + bool egress:1; + bool ingress:1; + bool mirror:1; + bool redirect:1; +} NMPlatformActionMirred; + +typedef struct { + + /* beware, kind is embedded in an NMPObject, hence you must + * take care of the lifetime of the string. */ const char *kind; + union { NMPlatformActionSimple simple; + NMPlatformActionMirred mirred; }; } NMPlatformAction; #define NM_PLATFORM_ACTION_KIND_SIMPLE "simple" +#define NM_PLATFORM_ACTION_KIND_MIRRED "mirred" typedef struct { __NMPlatformObjWithIfindex_COMMON; + + /* beware, kind is embedded in an NMPObject, hence you must + * take care of the lifetime of the string. */ const char *kind; + int addr_family; guint32 handle; guint32 parent; @@ -842,6 +902,8 @@ typedef enum { } NMPlatformWireGuardChangePeerFlags; +typedef void (*NMPlatformAsyncCallback) (GError *error, gpointer user_data); + /*****************************************************************************/ typedef enum { @@ -864,28 +926,28 @@ extern volatile int _nm_platform_kernel_support_state[_NM_PLATFORM_KERNEL_SUPPOR int _nm_platform_kernel_support_init (NMPlatformKernelSupportType type, int value); -#define _nm_platform_kernel_support_detected(type) \ - G_LIKELY (({ \ - const NMPlatformKernelSupportType _type = (type); \ - \ - nm_assert (_NM_INT_NOT_NEGATIVE (_type) && _type < G_N_ELEMENTS (_nm_platform_kernel_support_state)); \ - \ - (_nm_platform_kernel_support_state[_type] != 0); \ - })) - -#define nm_platform_kernel_support_get(type) \ - ({ \ - const NMPlatformKernelSupportType _type = (type); \ - int _v; \ - \ - nm_assert (_NM_INT_NOT_NEGATIVE (_type) && _type < G_N_ELEMENTS (_nm_platform_kernel_support_state)); \ - \ - _v = _nm_platform_kernel_support_state[_type]; \ - if (G_UNLIKELY (_v == 0)) \ - _v = _nm_platform_kernel_support_init (_type, 0); \ - \ - (_v >= 0); \ - }) +static inline gboolean +_nm_platform_kernel_support_detected (NMPlatformKernelSupportType type) +{ + nm_assert ( _NM_INT_NOT_NEGATIVE (type) + && type < G_N_ELEMENTS (_nm_platform_kernel_support_state)); + + return G_LIKELY (_nm_platform_kernel_support_state[type] != 0); +} + +static inline gboolean +nm_platform_kernel_support_get (NMPlatformKernelSupportType type) +{ + int v; + + nm_assert (_NM_INT_NOT_NEGATIVE (type) + && type < G_N_ELEMENTS (_nm_platform_kernel_support_state)); + + v = _nm_platform_kernel_support_state[type]; + if (G_UNLIKELY (v == 0)) + v = _nm_platform_kernel_support_init (type, 0); + return (v >= 0); +} /*****************************************************************************/ @@ -900,64 +962,75 @@ struct _NMPlatform { typedef struct { GObjectClass parent; - gboolean (*sysctl_set) (NMPlatform *, const char *pathid, int dirfd, const char *path, const char *value); - char * (*sysctl_get) (NMPlatform *, const char *pathid, int dirfd, const char *path); + gboolean (*sysctl_set) (NMPlatform *self, const char *pathid, int dirfd, const char *path, const char *value); + void (*sysctl_set_async) (NMPlatform *self, + const char *pathid, + int dirfd, + const char *path, + const char *const *values, + NMPlatformAsyncCallback callback, + gpointer data, + GCancellable *cancellable); + char * (*sysctl_get) (NMPlatform *self, const char *pathid, int dirfd, const char *path); + + void (*refresh_all) (NMPlatform *self, NMPObjectType obj_type); + void (*process_events) (NMPlatform *self); - int (*link_add) (NMPlatform *, + int (*link_add) (NMPlatform *self, 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); - - gboolean (*link_set_netns) (NMPlatform *, int ifindex, int netns_fd); - - void (*process_events) (NMPlatform *self); - - gboolean (*link_set_up) (NMPlatform *, int ifindex, gboolean *out_no_firmware); - gboolean (*link_set_down) (NMPlatform *, int ifindex); - gboolean (*link_set_arp) (NMPlatform *, int ifindex); - gboolean (*link_set_noarp) (NMPlatform *, int ifindex); + gboolean (*link_delete) (NMPlatform *self, int ifindex); + gboolean (*link_refresh) (NMPlatform *self, int ifindex); + gboolean (*link_set_netns) (NMPlatform *self, int ifindex, int netns_fd); + gboolean (*link_set_up) (NMPlatform *self, int ifindex, gboolean *out_no_firmware); + gboolean (*link_set_down) (NMPlatform *self, int ifindex); + gboolean (*link_set_arp) (NMPlatform *self, int ifindex); + gboolean (*link_set_noarp) (NMPlatform *self, int ifindex); const char *(*link_get_udi) (NMPlatform *self, int ifindex); struct udev_device *(*link_get_udev_device) (NMPlatform *self, int ifindex); - int (*link_set_user_ipv6ll_enabled) (NMPlatform *, int ifindex, gboolean enabled); - gboolean (*link_set_token) (NMPlatform *, int ifindex, NMUtilsIPv6IfaceId iid); + int (*link_set_user_ipv6ll_enabled) (NMPlatform *self, int ifindex, gboolean enabled); + gboolean (*link_set_token) (NMPlatform *self, int ifindex, NMUtilsIPv6IfaceId iid); - gboolean (*link_get_permanent_address) (NMPlatform *, + gboolean (*link_get_permanent_address) (NMPlatform *self, int ifindex, guint8 *buf, size_t *length); - 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); + int (*link_set_address) (NMPlatform *self, int ifindex, gconstpointer address, size_t length); + int (*link_set_mtu) (NMPlatform *self, int ifindex, guint32 mtu); + gboolean (*link_set_name) (NMPlatform *self, int ifindex, const char *name); + void (*link_set_sriov_params_async) (NMPlatform *self, + int ifindex, + guint num_vfs, + int autoprobe, + NMPlatformAsyncCallback callback, + gpointer callback_data, + GCancellable *cancellable); gboolean (*link_set_sriov_vfs) (NMPlatform *self, int ifindex, const NMPlatformVF *const *vfs); gboolean (*link_set_bridge_vlans) (NMPlatform *self, int ifindex, gboolean on_master, const NMPlatformBridgeVlan *const *vlans); - char * (*link_get_physical_port_id) (NMPlatform *, int ifindex); - guint (*link_get_dev_id) (NMPlatform *, int ifindex); - gboolean (*link_get_wake_on_lan) (NMPlatform *, int ifindex); - gboolean (*link_get_driver_info) (NMPlatform *, + char * (*link_get_physical_port_id) (NMPlatform *self, int ifindex); + guint (*link_get_dev_id) (NMPlatform *self, int ifindex); + gboolean (*link_get_wake_on_lan) (NMPlatform *self, int ifindex); + gboolean (*link_get_driver_info) (NMPlatform *self, int ifindex, char **out_driver_name, char **out_driver_version, char **out_fw_version); - gboolean (*link_supports_carrier_detect) (NMPlatform *, int ifindex); - gboolean (*link_supports_vlans) (NMPlatform *, int ifindex); - gboolean (*link_supports_sriov) (NMPlatform *, int ifindex); + gboolean (*link_supports_carrier_detect) (NMPlatform *self, int ifindex); + gboolean (*link_supports_vlans) (NMPlatform *self, int ifindex); + gboolean (*link_supports_sriov) (NMPlatform *self, int ifindex); - gboolean (*link_enslave) (NMPlatform *, int master, int slave); - gboolean (*link_release) (NMPlatform *, int master, int slave); + gboolean (*link_enslave) (NMPlatform *self, int master, int slave); + gboolean (*link_release) (NMPlatform *self, int master, int slave); - gboolean (*link_can_assume) (NMPlatform *, int ifindex); + gboolean (*link_can_assume) (NMPlatform *self, int ifindex); int (*link_wireguard_change) (NMPlatform *self, int ifindex, @@ -967,7 +1040,7 @@ typedef struct { guint peers_len, NMPlatformWireGuardChangeFlags change_flags); - gboolean (*vlan_add) (NMPlatform *, const char *name, int parent, int vlanid, guint32 vlanflags, const NMPlatformLink **out_link); + gboolean (*vlan_add) (NMPlatform *self, const char *name, int parent, int vlanid, guint32 vlanflags, const NMPlatformLink **out_link); gboolean (*link_vlan_change) (NMPlatform *self, int ifindex, NMVlanFlags flags_mask, @@ -978,81 +1051,79 @@ typedef struct { gboolean egress_reset_all, const NMVlanQosMapping *egress_map, gsize n_egress_map); - gboolean (*link_vxlan_add) (NMPlatform *, + gboolean (*link_vxlan_add) (NMPlatform *self, const char *name, const NMPlatformLnkVxlan *props, const NMPlatformLink **out_link); - gboolean (*link_gre_add) (NMPlatform *, + gboolean (*link_gre_add) (NMPlatform *self, const char *name, const NMPlatformLnkGre *props, const NMPlatformLink **out_link); - gboolean (*link_ip6tnl_add) (NMPlatform *, + gboolean (*link_ip6tnl_add) (NMPlatform *self, const char *name, const NMPlatformLnkIp6Tnl *props, const NMPlatformLink **out_link); - gboolean (*link_ip6gre_add) (NMPlatform *, + gboolean (*link_ip6gre_add) (NMPlatform *self, const char *name, const NMPlatformLnkIp6Tnl *props, const NMPlatformLink **out_link); - gboolean (*link_ipip_add) (NMPlatform *, + gboolean (*link_ipip_add) (NMPlatform *self, const char *name, const NMPlatformLnkIpIp *props, const NMPlatformLink **out_link); - gboolean (*link_macsec_add) (NMPlatform *, + gboolean (*link_macsec_add) (NMPlatform *self, const char *name, int parent, const NMPlatformLnkMacsec *props, const NMPlatformLink **out_link); - gboolean (*link_macvlan_add) (NMPlatform *, + gboolean (*link_macvlan_add) (NMPlatform *self, const char *name, int parent, const NMPlatformLnkMacvlan *props, const NMPlatformLink **out_link); - gboolean (*link_sit_add) (NMPlatform *, + gboolean (*link_sit_add) (NMPlatform *self, const char *name, const NMPlatformLnkSit *props, const NMPlatformLink **out_link); - - gboolean (*link_tun_add) (NMPlatform *platform, + gboolean (*link_tun_add) (NMPlatform *self, const char *name, const NMPlatformLnkTun *props, const NMPlatformLink **out_link, int *out_fd); - - gboolean (*link_6lowpan_add) (NMPlatform *platform, + gboolean (*link_6lowpan_add) (NMPlatform *self, const char *name, int parent, const NMPlatformLink **out_link); - gboolean (*infiniband_partition_add) (NMPlatform *, int parent, int p_key, const NMPlatformLink **out_link); - gboolean (*infiniband_partition_delete) (NMPlatform *, int parent, int p_key); - - gboolean (*wifi_get_capabilities) (NMPlatform *, int ifindex, NMDeviceWifiCapabilities *caps); - gboolean (*wifi_get_bssid) (NMPlatform *, int ifindex, guint8 *bssid); - guint32 (*wifi_get_frequency) (NMPlatform *, int ifindex); - int (*wifi_get_quality) (NMPlatform *, int ifindex); - guint32 (*wifi_get_rate) (NMPlatform *, int ifindex); - NM80211Mode (*wifi_get_mode) (NMPlatform *, int ifindex); - void (*wifi_set_mode) (NMPlatform *, int ifindex, NM80211Mode mode); - void (*wifi_set_powersave) (NMPlatform *, int ifindex, guint32 powersave); - guint32 (*wifi_find_frequency) (NMPlatform *, int ifindex, const guint32 *freqs); - void (*wifi_indicate_addressing_running) (NMPlatform *, int ifindex, gboolean running); - NMSettingWirelessWakeOnWLan (*wifi_get_wake_on_wlan) (NMPlatform *, int ifindex); - gboolean (*wifi_set_wake_on_wlan) (NMPlatform *, int ifindex, NMSettingWirelessWakeOnWLan wowl); - - guint32 (*mesh_get_channel) (NMPlatform *, int ifindex); - gboolean (*mesh_set_channel) (NMPlatform *, int ifindex, guint32 channel); - gboolean (*mesh_set_ssid) (NMPlatform *, int ifindex, const guint8 *ssid, gsize len); - - guint16 (*wpan_get_pan_id) (NMPlatform *, int ifindex); - 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); - - gboolean (*ip4_address_add) (NMPlatform *, + gboolean (*infiniband_partition_add) (NMPlatform *self, int parent, int p_key, const NMPlatformLink **out_link); + gboolean (*infiniband_partition_delete) (NMPlatform *self, int parent, int p_key); + + gboolean (*wifi_get_capabilities) (NMPlatform *self, int ifindex, NMDeviceWifiCapabilities *caps); + gboolean (*wifi_get_bssid) (NMPlatform *self, int ifindex, guint8 *bssid); + guint32 (*wifi_get_frequency) (NMPlatform *self, int ifindex); + int (*wifi_get_quality) (NMPlatform *self, int ifindex); + guint32 (*wifi_get_rate) (NMPlatform *self, int ifindex); + NM80211Mode (*wifi_get_mode) (NMPlatform *self, int ifindex); + void (*wifi_set_mode) (NMPlatform *self, int ifindex, NM80211Mode mode); + void (*wifi_set_powersave) (NMPlatform *self, int ifindex, guint32 powersave); + guint32 (*wifi_find_frequency) (NMPlatform *self, int ifindex, const guint32 *freqs); + void (*wifi_indicate_addressing_running) (NMPlatform *self, int ifindex, gboolean running); + NMSettingWirelessWakeOnWLan (*wifi_get_wake_on_wlan) (NMPlatform *self, int ifindex); + gboolean (*wifi_set_wake_on_wlan) (NMPlatform *self, int ifindex, NMSettingWirelessWakeOnWLan wowl); + + guint32 (*mesh_get_channel) (NMPlatform *self, int ifindex); + gboolean (*mesh_set_channel) (NMPlatform *self, int ifindex, guint32 channel); + gboolean (*mesh_set_ssid) (NMPlatform *self, int ifindex, const guint8 *ssid, gsize len); + + guint16 (*wpan_get_pan_id) (NMPlatform *self, int ifindex); + gboolean (*wpan_set_pan_id) (NMPlatform *self, int ifindex, guint16 pan_id); + guint16 (*wpan_get_short_addr) (NMPlatform *self, int ifindex); + gboolean (*wpan_set_short_addr) (NMPlatform *self, int ifindex, guint16 short_addr); + gboolean (*wpan_set_channel) (NMPlatform *self, int ifindex, guint8 page, guint8 channel); + + gboolean (*object_delete) (NMPlatform *self, const NMPObject *obj); + + gboolean (*ip4_address_add) (NMPlatform *self, int ifindex, in_addr_t address, guint8 plen, @@ -1061,7 +1132,7 @@ typedef struct { guint32 preferred_lft, guint32 flags, const char *label); - gboolean (*ip6_address_add) (NMPlatform *, + gboolean (*ip6_address_add) (NMPlatform *self, int ifindex, struct in6_addr address, guint8 plen, @@ -1069,10 +1140,10 @@ typedef struct { guint32 lifetime, guint32 preferred_lft, guint32 flags); - 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); + gboolean (*ip4_address_delete) (NMPlatform *self, int ifindex, in_addr_t address, guint8 plen, in_addr_t peer_address); + gboolean (*ip6_address_delete) (NMPlatform *self, int ifindex, struct in6_addr address, guint8 plen); - int (*ip_route_add) (NMPlatform *, + int (*ip_route_add) (NMPlatform *self, NMPNlmFlags flags, int addr_family, const NMPlatformIPRoute *route); @@ -1215,7 +1286,7 @@ gboolean nm_platform_get_use_udev (NMPlatform *self); gboolean nm_platform_get_log_with_ptr (NMPlatform *self); NMPNetns *nm_platform_netns_get (NMPlatform *self); -gboolean nm_platform_netns_push (NMPlatform *platform, NMPNetns **netns); +gboolean nm_platform_netns_push (NMPlatform *self, NMPNetns **netns); const char *nm_link_type_to_string (NMLinkType link_type); @@ -1241,16 +1312,24 @@ const char *nm_link_type_to_string (NMLinkType link_type); int nm_platform_sysctl_open_netdir (NMPlatform *self, int ifindex, char *out_ifname); gboolean nm_platform_sysctl_set (NMPlatform *self, const char *pathid, int dirfd, const char *path, const char *value); +void nm_platform_sysctl_set_async (NMPlatform *self, + const char *pathid, + int dirfd, + const char *path, + const char *const *values, + NMPlatformAsyncCallback callback, + gpointer data, + GCancellable *cancellable); char *nm_platform_sysctl_get (NMPlatform *self, const char *pathid, int dirfd, const char *path); 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); -char *nm_platform_sysctl_ip_conf_get (NMPlatform *platform, +char *nm_platform_sysctl_ip_conf_get (NMPlatform *self, int addr_family, const char *ifname, const char *property); -gint64 nm_platform_sysctl_ip_conf_get_int_checked (NMPlatform *platform, +gint64 nm_platform_sysctl_ip_conf_get_int_checked (NMPlatform *self, int addr_family, const char *ifname, const char *property, @@ -1259,13 +1338,13 @@ gint64 nm_platform_sysctl_ip_conf_get_int_checked (NMPlatform *platform, gint64 max, gint64 fallback); -gboolean nm_platform_sysctl_ip_conf_set (NMPlatform *platform, +gboolean nm_platform_sysctl_ip_conf_set (NMPlatform *self, int addr_family, const char *ifname, const char *property, const char *value); -gboolean nm_platform_sysctl_ip_conf_set_int64 (NMPlatform *platform, +gboolean nm_platform_sysctl_ip_conf_set_int64 (NMPlatform *self, int addr_family, const char *ifname, const char *property, @@ -1274,6 +1353,10 @@ gboolean nm_platform_sysctl_ip_conf_set_int64 (NMPlatform *platform, gboolean nm_platform_sysctl_ip_conf_set_ipv6_hop_limit_safe (NMPlatform *self, const char *iface, int value); +int nm_platform_sysctl_ip_conf_get_rp_filter_ipv4 (NMPlatform *platform, + const char *iface, + gboolean consider_all, + gboolean *out_due_to_all); 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); @@ -1298,7 +1381,7 @@ gboolean nm_platform_link_set_netns (NMPlatform *self, int ifindex, int netns_fd struct _NMDedupMultiHeadEntry; struct _NMPLookup; -const struct _NMDedupMultiHeadEntry *nm_platform_lookup (NMPlatform *platform, +const struct _NMDedupMultiHeadEntry *nm_platform_lookup (NMPlatform *self, const struct _NMPLookup *lookup); gboolean nm_platform_lookup_predicate_routes_main (const NMPObject *obj, @@ -1306,7 +1389,7 @@ gboolean nm_platform_lookup_predicate_routes_main (const NMPObject *obj, gboolean nm_platform_lookup_predicate_routes_main_skip_rtprot_kernel (const NMPObject *obj, gpointer user_data); -GPtrArray *nm_platform_lookup_clone (NMPlatform *platform, +GPtrArray *nm_platform_lookup_clone (NMPlatform *self, const struct _NMPLookup *lookup, NMPObjectPredicateFunc predicate, gpointer user_data); @@ -1325,18 +1408,6 @@ gboolean nm_platform_link_get_user_ipv6ll_enabled (NMPlatform *self, int ifindex gconstpointer nm_platform_link_get_address (NMPlatform *self, int ifindex, size_t *length); -static inline GBytes * -nm_platform_link_get_address_as_bytes (NMPlatform *self, int ifindex) -{ - gconstpointer p; - gsize l; - - p = nm_platform_link_get_address (self, ifindex, &l); - return p - ? g_bytes_new (p, l) - : NULL; -} - int nm_platform_link_get_master (NMPlatform *self, int slave); gboolean nm_platform_link_can_assume (NMPlatform *self, int ifindex); @@ -1368,7 +1439,15 @@ gboolean nm_platform_link_get_permanent_address (NMPlatform *self, int ifindex, 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); + +void nm_platform_link_set_sriov_params_async (NMPlatform *self, + int ifindex, + guint num_vfs, + int autoprobe, + NMPlatformAsyncCallback callback, + gpointer callback_data, + GCancellable *cancellable); + gboolean nm_platform_link_set_sriov_vfs (NMPlatform *self, int ifindex, const NMPlatformVF *const *vfs); gboolean nm_platform_link_set_bridge_vlans (NMPlatform *self, int ifindex, gboolean on_master, const NMPlatformBridgeVlan *const *vlans); @@ -1466,11 +1545,11 @@ guint32 nm_platform_mesh_get_channel (NMPlatform *self, int ifindex); gboolean nm_platform_mesh_set_channel (NMPlatform *self, int ifindex, guint32 channel); gboolean nm_platform_mesh_set_ssid (NMPlatform *self, int ifindex, const guint8 *ssid, gsize len); -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); +guint16 nm_platform_wpan_get_pan_id (NMPlatform *self, int ifindex); +gboolean nm_platform_wpan_set_pan_id (NMPlatform *self, int ifindex, guint16 pan_id); +guint16 nm_platform_wpan_get_short_addr (NMPlatform *self, int ifindex); +gboolean nm_platform_wpan_set_short_addr (NMPlatform *self, int ifindex, guint16 short_addr); +gboolean nm_platform_wpan_set_channel (NMPlatform *self, 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); @@ -1704,6 +1783,8 @@ void nm_platform_lnk_wireguard_hash_update (const NMPlatformLnkWireGuard *obj, N void nm_platform_qdisc_hash_update (const NMPlatformQdisc *obj, NMHashState *h); void nm_platform_tfilter_hash_update (const NMPlatformTfilter *obj, NMHashState *h); +#define NM_PLATFORM_LINK_FLAGS2STR_MAX_LEN ((gsize) 162) + const char *nm_platform_link_flags2str (unsigned flags, char *buf, gsize len); const char *nm_platform_link_inet6_addrgenmode2str (guint8 mode, char *buf, gsize len); const char *nm_platform_addr_flags2str (unsigned flags, char *buf, gsize len); diff --git a/src/platform/nmp-netns.c b/src/platform/nmp-netns.c index f34dc83d..b33d86fb 100644 --- a/src/platform/nmp-netns.c +++ b/src/platform/nmp-netns.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-platform.c - Handle runtime kernel networking configuration * * This program is free software; you can redistribute it and/or modify diff --git a/src/platform/nmp-netns.h b/src/platform/nmp-netns.h index 55a4b95f..3c0ca74b 100644 --- a/src/platform/nmp-netns.h +++ b/src/platform/nmp-netns.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-platform.c - Handle runtime kernel networking configuration * * This program is free software; you can redistribute it and/or modify diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c index 0d20100c..5cbf9428 100644 --- a/src/platform/nmp-object.c +++ b/src/platform/nmp-object.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-platform.c - Handle runtime kernel networking configuration * * This program is free software; you can redistribute it and/or modify @@ -398,7 +397,7 @@ _idx_obj_part (const DedupMultiIdxType *idx_type, if (h) { nm_hash_update_vals (h, idx_type->cache_id_type, - NMP_OBJECT_CAST_OBJ_WITH_IFINDEX (obj_a)->ifindex); + obj_a->obj_with_ifindex.ifindex); } return 1; @@ -787,7 +786,7 @@ _nmp_object_new_from_class (const NMPClass *klass) } NMPObject * -nmp_object_new (NMPObjectType obj_type, const NMPlatformObject *plobj) +nmp_object_new (NMPObjectType obj_type, gconstpointer plobj) { const NMPClass *klass = nmp_class_from_type (obj_type); NMPObject *obj; @@ -2147,9 +2146,12 @@ nmp_lookup_init_object_by_addr_family (NMPLookup *lookup, NMPObject *o; nm_assert (lookup); - nm_assert_addr_family (addr_family); nm_assert (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_ROUTING_RULE)); + if (addr_family == AF_UNSPEC) + return nmp_lookup_init_obj_type (lookup, obj_type); + + nm_assert_addr_family (addr_family); o = _nmp_object_stackinit_from_type (&lookup->selector_obj, obj_type); NMP_OBJECT_CAST_ROUTING_RULE (o)->addr_family = addr_family; lookup->cache_id_type = NMP_CACHE_ID_TYPE_OBJECT_BY_ADDR_FAMILY; diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h index 3ce9db36..cea5f958 100644 --- a/src/platform/nmp-object.h +++ b/src/platform/nmp-object.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-platform.c - Handle runtime kernel networking configuration * * This program is free software; you can redistribute it and/or modify @@ -535,101 +534,27 @@ _NMP_OBJECT_TYPE_IS_OBJ_WITH_IFINDEX (NMPObjectType obj_type) _obj ? &NM_CONSTCAST (NMPObject, _obj)->obj_with_ifindex : NULL; \ }) -#define NMP_OBJECT_CAST_LINK(obj) \ +#define _NMP_OBJECT_CAST(obj, field, ...) \ ({ \ typeof (obj) _obj = (obj); \ \ - nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_LINK); \ - _obj ? &NM_CONSTCAST (NMPObject, _obj)->link : NULL; \ + nm_assert (!_obj || NM_IN_SET (NMP_OBJECT_GET_TYPE (_obj), __VA_ARGS__)); \ + _obj ? &NM_CONSTCAST (NMPObject, _obj)->field : NULL; \ }) -#define NMP_OBJECT_CAST_IP_ADDRESS(obj) \ - ({ \ - typeof (obj) _obj = (obj); \ - \ - nm_assert (!_obj || NM_IN_SET (NMP_OBJECT_GET_TYPE (_obj), NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS)); \ - _obj ? &NM_CONSTCAST (NMPObject, _obj)->ip_address : NULL; \ - }) - -#define NMP_OBJECT_CAST_IPX_ADDRESS(obj) \ - ({ \ - typeof (obj) _obj = (obj); \ - \ - nm_assert (!_obj || NM_IN_SET (NMP_OBJECT_GET_TYPE (_obj), NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS)); \ - _obj ? &NM_CONSTCAST (NMPObject, _obj)->ipx_address : NULL; \ - }) - -#define NMP_OBJECT_CAST_IP4_ADDRESS(obj) \ - ({ \ - typeof (obj) _obj = (obj); \ - \ - nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_IP4_ADDRESS); \ - _obj ? &NM_CONSTCAST (NMPObject, _obj)->ip4_address : NULL; \ - }) - -#define NMP_OBJECT_CAST_IP6_ADDRESS(obj) \ - ({ \ - typeof (obj) _obj = (obj); \ - \ - nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_IP6_ADDRESS); \ - _obj ? &NM_CONSTCAST (NMPObject, _obj)->ip6_address : NULL; \ - }) - -#define NMP_OBJECT_CAST_IPX_ROUTE(obj) \ - ({ \ - typeof (obj) _obj = (obj); \ - \ - nm_assert (!_obj || NM_IN_SET (NMP_OBJECT_GET_TYPE (_obj), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); \ - _obj ? &NM_CONSTCAST (NMPObject, _obj)->ipx_route : NULL; \ - }) - -#define NMP_OBJECT_CAST_IP_ROUTE(obj) \ - ({ \ - typeof (obj) _obj = (obj); \ - \ - nm_assert (!_obj || NM_IN_SET (NMP_OBJECT_GET_TYPE (_obj), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); \ - _obj ? &NM_CONSTCAST (NMPObject, _obj)->ip_route : NULL; \ - }) - -#define NMP_OBJECT_CAST_IP4_ROUTE(obj) \ - ({ \ - typeof (obj) _obj = (obj); \ - \ - nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_IP4_ROUTE); \ - _obj ? &NM_CONSTCAST (NMPObject, _obj)->ip4_route : NULL; \ - }) - -#define NMP_OBJECT_CAST_IP6_ROUTE(obj) \ - ({ \ - typeof (obj) _obj = (obj); \ - \ - nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_IP6_ROUTE); \ - _obj ? &NM_CONSTCAST (NMPObject, _obj)->ip6_route : NULL; \ - }) - -#define NMP_OBJECT_CAST_ROUTING_RULE(obj) \ - ({ \ - typeof (obj) _obj = (obj); \ - \ - nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_ROUTING_RULE); \ - _obj ? &NM_CONSTCAST (NMPObject, _obj)->routing_rule : NULL; \ - }) - -#define NMP_OBJECT_CAST_QDISC(obj) \ - ({ \ - typeof (obj) _obj = (obj); \ - \ - nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_QDISC); \ - _obj ? &NM_CONSTCAST (NMPObject, _obj)->qdisc : NULL; \ - }) - -#define NMP_OBJECT_CAST_TFILTER(obj) \ - ({ \ - typeof (obj) _obj = (obj); \ - \ - nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_TFILTER); \ - _obj ? &NM_CONSTCAST (NMPObject, _obj)->tfilter : NULL; \ - }) +#define NMP_OBJECT_CAST_LINK(obj) _NMP_OBJECT_CAST (obj, link, NMP_OBJECT_TYPE_LINK) +#define NMP_OBJECT_CAST_IP_ADDRESS(obj) _NMP_OBJECT_CAST (obj, ip_address, NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS) +#define NMP_OBJECT_CAST_IPX_ADDRESS(obj) _NMP_OBJECT_CAST (obj, ipx_address, NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS) +#define NMP_OBJECT_CAST_IP4_ADDRESS(obj) _NMP_OBJECT_CAST (obj, ip4_address, NMP_OBJECT_TYPE_IP4_ADDRESS) +#define NMP_OBJECT_CAST_IP6_ADDRESS(obj) _NMP_OBJECT_CAST (obj, ip6_address, NMP_OBJECT_TYPE_IP6_ADDRESS) +#define NMP_OBJECT_CAST_IP_ROUTE(obj) _NMP_OBJECT_CAST (obj, ip_route, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE) +#define NMP_OBJECT_CAST_IPX_ROUTE(obj) _NMP_OBJECT_CAST (obj, ipx_route, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE) +#define NMP_OBJECT_CAST_IP4_ROUTE(obj) _NMP_OBJECT_CAST (obj, ip4_route, NMP_OBJECT_TYPE_IP4_ROUTE) +#define NMP_OBJECT_CAST_IP6_ROUTE(obj) _NMP_OBJECT_CAST (obj, ip6_route, NMP_OBJECT_TYPE_IP6_ROUTE) +#define NMP_OBJECT_CAST_ROUTING_RULE(obj) _NMP_OBJECT_CAST (obj, routing_rule, NMP_OBJECT_TYPE_ROUTING_RULE) +#define NMP_OBJECT_CAST_QDISC(obj) _NMP_OBJECT_CAST (obj, qdisc, NMP_OBJECT_TYPE_QDISC) +#define NMP_OBJECT_CAST_TFILTER(obj) _NMP_OBJECT_CAST (obj, tfilter, NMP_OBJECT_TYPE_TFILTER) +#define NMP_OBJECT_CAST_LNK_WIREGUARD(obj) _NMP_OBJECT_CAST (obj, lnk_wireguard, NMP_OBJECT_TYPE_LNK_WIREGUARD) static inline const NMPObject * nmp_object_ref (const NMPObject *obj) @@ -674,7 +599,7 @@ nmp_object_unref (const NMPObject *obj) _changed; \ }) -NMPObject *nmp_object_new (NMPObjectType obj_type, const NMPlatformObject *plob); +NMPObject *nmp_object_new (NMPObjectType obj_type, gconstpointer plobj); NMPObject *nmp_object_new_link (int ifindex); const NMPObject *nmp_object_stackinit (NMPObject *obj, NMPObjectType obj_type, gconstpointer plobj); @@ -1059,4 +984,15 @@ nm_platform_lookup_ip6_route_by_weak_id (NMPlatform *platform, return nm_platform_lookup (platform, &lookup); } +static inline const NMDedupMultiHeadEntry * +nm_platform_lookup_object_by_addr_family (NMPlatform *platform, + NMPObjectType obj_type, + int addr_family) +{ + NMPLookup lookup; + + nmp_lookup_init_object_by_addr_family (&lookup, obj_type, addr_family); + return nm_platform_lookup (platform, &lookup); +} + #endif /* __NMP_OBJECT_H__ */ diff --git a/src/platform/nmp-rules-manager.c b/src/platform/nmp-rules-manager.c index 970afcde..f982f04c 100644 --- a/src/platform/nmp-rules-manager.c +++ b/src/platform/nmp-rules-manager.c @@ -81,7 +81,7 @@ typedef struct { * But it will not remove or add them otherwise. * * Otherwise, the track_priority_val goes together with track_priority_present. - * In case of one rule being tracked multile times (with different priorities), + * In case of one rule being tracked multiple times (with different priorities), * the one with higher priority wins. See _rules_obj_get_best_data(). * Then, the winning present state either enforces that the rule is present * or absent. @@ -99,6 +99,17 @@ typedef enum { CONFIG_STATE_NONE = 0, CONFIG_STATE_ADDED_BY_US = 1, CONFIG_STATE_REMOVED_BY_US = 2, + + /* ConfigState encodes whether the rule was touched by us at all (CONFIG_STATE_NONE). + * + * Maybe we would only need to track whether we touched the rule at all. But we + * track it more in detail what we did: did we add it (CONFIG_STATE_ADDED_BY_US) + * or did we remove it (CONFIG_STATE_REMOVED_BY_US)? + * Finally, we need CONFIG_STATE_OWNED_BY_US, which means that we didn't actively + * add/remove it, but whenever we are about to undo the add/remove, we need to do it. + * In that sense, CONFIG_STATE_OWNED_BY_US is really just a flag that we unconditionally + * force the state next time when necessary. */ + CONFIG_STATE_OWNED_BY_US = 3, } ConfigState; typedef struct { @@ -111,8 +122,10 @@ typedef struct { * This makes NMPRulesManager stateful (beyond the configuration that indicates * which rules are tracked). * After a restart, NetworkManager would no longer remember which rules were added - * by us. That would need to be fixed by persisting the state and reloading it after - * restart. */ + * by us. + * + * That is partially fixed by NetworkManager taking over the rules that it + * actively configures (see %NMP_RULES_MANAGER_EXTERN_WEAKLY_TRACKED_USER_TAG). */ ConfigState config_state; } RulesObjData; @@ -121,6 +134,15 @@ typedef struct { CList user_tag_lst_head; } RulesUserTagData; +/*****************************************************************************/ + +static void _rules_data_untrack (NMPRulesManager *self, + RulesData *rules_data, + gboolean remove_user_tag_data, + gboolean make_owned_by_us); + +/*****************************************************************************/ + static void _rules_data_assert (const RulesData *rules_data, gboolean linked) { @@ -278,11 +300,31 @@ _rules_data_lookup (GHashTable *by_data, return g_hash_table_lookup (by_data, &rules_data_needle); } +/** + * nmp_rules_manager_track: + * @self: the #NMPRulesManager instance + * @routing_rule: the #NMPlatformRoutingRule to track or untrack + * @track_priority: the priority for tracking the rule. Note that + * negative values indicate a forced absence of the rule. Priorities + * are compared with their absolute values (with higher absolute + * value being more important). For example, if you track the same + * rule twice, once with priority -5 and +10, then the rule is + * present (because the positive number is more important). + * The special value 0 indicates weakly-tracked rules. + * @user_tag: the tag associated with tracking this rule. The same tag + * must be used to untrack the rule later. + * @user_tag_untrack: if not %NULL, at the same time untrack this user-tag + * for the same rule. Note that this is different from a plain nmp_rules_manager_untrack(), + * because it enforces ownership of the now tracked rule. On the other hand, + * a plain nmp_rules_manager_untrack() merely forgets about the tracking. + * The purpose here is to set this to %NMP_RULES_MANAGER_EXTERN_WEAKLY_TRACKED_USER_TAG. + */ void nmp_rules_manager_track (NMPRulesManager *self, const NMPlatformRoutingRule *routing_rule, gint32 track_priority, - gconstpointer user_tag) + gconstpointer user_tag, + gconstpointer user_tag_untrack) { NMPObject obj_stack; const NMPObject *p_obj_stack; @@ -359,6 +401,17 @@ nmp_rules_manager_track (NMPRulesManager *self, } } + if (user_tag_untrack) { + if (user_tag != user_tag_untrack) { + RulesData *rules_data_untrack; + + rules_data_untrack = _rules_data_lookup (self->by_data, p_obj_stack, user_tag_untrack); + if (rules_data_untrack) + _rules_data_untrack (self, rules_data_untrack, FALSE, TRUE); + } else + nm_assert_not_reached (); + } + _rules_data_assert (rules_data, TRUE); if (changed) { @@ -377,7 +430,8 @@ nmp_rules_manager_track (NMPRulesManager *self, static void _rules_data_untrack (NMPRulesManager *self, RulesData *rules_data, - gboolean remove_user_tag_data) + gboolean remove_user_tag_data, + gboolean make_owned_by_us) { RulesObjData *obj_data; @@ -401,15 +455,22 @@ _rules_data_untrack (NMPRulesManager *self, #endif nm_assert (!c_list_is_empty (&rules_data->user_tag_lst)); - if ( remove_user_tag_data - && c_list_length_is (&rules_data->user_tag_lst, 1)) - g_hash_table_remove (self->by_user_tag, &rules_data->user_tag); obj_data = g_hash_table_lookup (self->by_obj, &rules_data->obj); nm_assert (obj_data); nm_assert (c_list_contains (&obj_data->obj_lst_head, &rules_data->obj_lst)); nm_assert (obj_data == g_hash_table_lookup (self->by_obj, &rules_data->obj)); + if (make_owned_by_us) { + if (obj_data->config_state == CONFIG_STATE_NONE) { + /* we need to mark this entry that it requires a touch on the next + * sync. */ + obj_data->config_state = CONFIG_STATE_OWNED_BY_US; + } + } else if ( remove_user_tag_data + && c_list_length_is (&rules_data->user_tag_lst, 1)) + g_hash_table_remove (self->by_user_tag, &rules_data->user_tag); + /* if obj_data is marked to be "added_by_us" or "removed_by_us", we need to keep this entry * around for the next sync -- so that we can undo what we did earlier. */ if ( obj_data->config_state == CONFIG_STATE_NONE @@ -440,7 +501,7 @@ nmp_rules_manager_untrack (NMPRulesManager *self, rules_data = _rules_data_lookup (self->by_data, p_obj_stack, user_tag); if (rules_data) - _rules_data_untrack (self, rules_data, TRUE); + _rules_data_untrack (self, rules_data, TRUE, FALSE); } void @@ -486,7 +547,7 @@ nmp_rules_manager_untrack_all (NMPRulesManager *self, c_list_for_each_entry_safe (rules_data, rules_data_safe, &user_tag_data->user_tag_lst_head, user_tag_lst) { if ( all || rules_data->dirty) - _rules_data_untrack (self, rules_data, FALSE); + _rules_data_untrack (self, rules_data, FALSE, FALSE); } if (c_list_is_empty (&user_tag_data->user_tag_lst_head)) g_hash_table_remove (self->by_user_tag, user_tag_data); @@ -525,11 +586,17 @@ nmp_rules_manager_sync (NMPRulesManager *self, rd_best = _rules_obj_get_best_data (obj_data); if (rd_best) { - if (rd_best->track_priority_present) + if (rd_best->track_priority_present) { + if (obj_data->config_state == CONFIG_STATE_OWNED_BY_US) + obj_data->config_state = CONFIG_STATE_ADDED_BY_US; continue; + } if (rd_best->track_priority_val == 0) { - if (obj_data->config_state != CONFIG_STATE_ADDED_BY_US) + if (!NM_IN_SET (obj_data->config_state, CONFIG_STATE_ADDED_BY_US, + CONFIG_STATE_OWNED_BY_US)) { + obj_data->config_state = CONFIG_STATE_NONE; continue; + } obj_data->config_state = CONFIG_STATE_NONE; } } @@ -563,11 +630,17 @@ nmp_rules_manager_sync (NMPRulesManager *self, continue; } - if (!rd_best->track_priority_present) + if (!rd_best->track_priority_present) { + if (obj_data->config_state == CONFIG_STATE_OWNED_BY_US) + obj_data->config_state = CONFIG_STATE_REMOVED_BY_US; continue; + } if (rd_best->track_priority_val == 0) { - if (obj_data->config_state != CONFIG_STATE_REMOVED_BY_US) + if (!NM_IN_SET (obj_data->config_state, CONFIG_STATE_REMOVED_BY_US, + CONFIG_STATE_OWNED_BY_US)) { + obj_data->config_state = CONFIG_STATE_NONE; continue; + } obj_data->config_state = CONFIG_STATE_NONE; } @@ -610,7 +683,7 @@ nmp_rules_manager_track_from_platform (NMPRulesManager *self, && rr->addr_family != addr_family) continue; - nmp_rules_manager_track (self, rr, tracking_priority, user_tag); + nmp_rules_manager_track (self, rr, tracking_priority, user_tag, NULL); } } @@ -638,7 +711,8 @@ nmp_rules_manager_track_default (NMPRulesManager *self, .protocol = RTPROT_KERNEL, }), track_priority, - user_tag); + user_tag, + NULL); nmp_rules_manager_track (self, &((NMPlatformRoutingRule) { .addr_family = AF_INET, @@ -648,7 +722,8 @@ nmp_rules_manager_track_default (NMPRulesManager *self, .protocol = RTPROT_KERNEL, }), track_priority, - user_tag); + user_tag, + NULL); nmp_rules_manager_track (self, &((NMPlatformRoutingRule) { .addr_family = AF_INET, @@ -658,7 +733,8 @@ nmp_rules_manager_track_default (NMPRulesManager *self, .protocol = RTPROT_KERNEL, }), track_priority, - user_tag); + user_tag, + NULL); } if (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET6)) { nmp_rules_manager_track (self, @@ -670,7 +746,8 @@ nmp_rules_manager_track_default (NMPRulesManager *self, .protocol = RTPROT_KERNEL, }), track_priority, - user_tag); + user_tag, + NULL); nmp_rules_manager_track (self, &((NMPlatformRoutingRule) { .addr_family = AF_INET6, @@ -680,7 +757,8 @@ nmp_rules_manager_track_default (NMPRulesManager *self, .protocol = RTPROT_KERNEL, }), track_priority, - user_tag); + user_tag, + NULL); } } diff --git a/src/platform/nmp-rules-manager.h b/src/platform/nmp-rules-manager.h index 310c7971..645df5c2 100644 --- a/src/platform/nmp-rules-manager.h +++ b/src/platform/nmp-rules-manager.h @@ -22,6 +22,8 @@ /*****************************************************************************/ +#define NMP_RULES_MANAGER_EXTERN_WEAKLY_TRACKED_USER_TAG ((const void *) nmp_rules_manager_new) + typedef struct _NMPRulesManager NMPRulesManager; NMPRulesManager *nmp_rules_manager_new (NMPlatform *platform); @@ -35,7 +37,8 @@ NM_AUTO_DEFINE_FCN0 (NMPRulesManager *, _nmp_rules_manager_unref, nmp_rules_mana void nmp_rules_manager_track (NMPRulesManager *self, const NMPlatformRoutingRule *routing_rule, gint32 track_priority, - gconstpointer user_tag); + gconstpointer user_tag, + gconstpointer user_tag_untrack); void nmp_rules_manager_track_default (NMPRulesManager *self, int addr_family, diff --git a/src/platform/tests/meson.build b/src/platform/tests/meson.build index 8086a46c..aaee8964 100644 --- a/src/platform/tests/meson.build +++ b/src/platform/tests/meson.build @@ -1,23 +1,22 @@ test_units = [ - ['test-link-fake', 'test-link.c', test_nm_dep_fake, default_test_timeout], - ['test-link-linux', 'test-link.c', test_nm_dep_linux, 900], - ['test-address-fake', 'test-address.c', test_nm_dep_fake, default_test_timeout], - ['test-address-linux', 'test-address.c', test_nm_dep_linux, default_test_timeout], - ['test-general', 'test-general.c', test_nm_dep, default_test_timeout], - ['test-nmp-object', 'test-nmp-object.c', test_nm_dep, default_test_timeout], - ['test-route-fake', 'test-route.c', test_nm_dep_fake, default_test_timeout], - ['test-route-linux', 'test-route.c', test_nm_dep_linux, default_test_timeout], - ['test-cleanup-fake', 'test-cleanup.c', test_nm_dep_fake, default_test_timeout], - ['test-cleanup-linux', 'test-cleanup.c', test_nm_dep_linux, default_test_timeout], + [ 'test-address-fake', 'test-address.c', test_nm_dep_fake, default_test_timeout ], + [ 'test-address-linux', 'test-address.c', test_nm_dep_linux, default_test_timeout ], + [ 'test-cleanup-fake', 'test-cleanup.c', test_nm_dep_fake, default_test_timeout ], + [ 'test-cleanup-linux', 'test-cleanup.c', test_nm_dep_linux, default_test_timeout ], + [ 'test-link-fake', 'test-link.c', test_nm_dep_fake, default_test_timeout ], + [ 'test-link-linux', 'test-link.c', test_nm_dep_linux, 900 ], + [ 'test-nmp-object', 'test-nmp-object.c', test_nm_dep, default_test_timeout ], + [ 'test-platform-general', 'test-platform-general.c', test_nm_dep, default_test_timeout ], + [ 'test-route-fake', 'test-route.c', test_nm_dep_fake, default_test_timeout ], + [ 'test-route-linux', 'test-route.c', test_nm_dep_linux, default_test_timeout ], ] foreach test_unit: test_units exe = executable( - 'platform-' + test_unit[0], + test_unit[0], test_unit[1], dependencies: test_unit[2], ) - test( 'platform/' + test_unit[0], test_script, @@ -26,10 +25,8 @@ foreach test_unit: test_units ) endforeach -test = 'monitor' - executable( - test, - test + '.c', + 'monitor', + 'monitor.c', dependencies: test_nm_dep, ) diff --git a/src/platform/tests/monitor.c b/src/platform/tests/monitor.c index fff5967b..6a5e0e16 100644 --- a/src/platform/tests/monitor.c +++ b/src/platform/tests/monitor.c @@ -1,4 +1,3 @@ -/* -*- 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 diff --git a/src/platform/tests/test-address.c b/src/platform/tests/test-address.c index d84a806d..3a3e9009 100644 --- a/src/platform/tests/test-address.c +++ b/src/platform/tests/test-address.c @@ -1,4 +1,3 @@ -/* -*- 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 diff --git a/src/platform/tests/test-cleanup.c b/src/platform/tests/test-cleanup.c index 6c73a63e..70d64c15 100644 --- a/src/platform/tests/test-cleanup.c +++ b/src/platform/tests/test-cleanup.c @@ -1,4 +1,3 @@ -/* -*- 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 diff --git a/src/platform/tests/test-common.c b/src/platform/tests/test-common.c index 7ebda9ff..cd0bef45 100644 --- a/src/platform/tests/test-common.c +++ b/src/platform/tests/test-common.c @@ -1,4 +1,3 @@ -/* -*- 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 @@ -679,7 +678,7 @@ nmtstp_run_command_check_external_global (void) { if (!nmtstp_is_root_test ()) return FALSE; - switch (nmtst_get_rand_int () % 3) { + switch (nmtst_get_rand_uint32 () % 3) { case 0: return -1; case 1: @@ -699,7 +698,7 @@ nmtstp_run_command_check_external (int external_command) } if (!nmtstp_is_root_test ()) return FALSE; - return (nmtst_get_rand_int () % 2) == 0; + return (nmtst_get_rand_uint32 () % 2) == 0; } /*****************************************************************************/ @@ -843,7 +842,7 @@ _ip_address_add (NMPlatform *platform, g_assert (flags == 0); if ( peer_address->addr4 != address->addr4 - || nmtst_get_rand_int () % 2) { + || nmtst_get_rand_uint32 () % 2) { /* If the peer is the same as the local address, we can omit it. The result should be identical */ nm_sprintf_buf (s_peer, " peer %s", nm_utils_inet4_ntop (peer_address->addr4, b2)); } else @@ -1994,7 +1993,7 @@ nmtstp_netns_select_random (NMPlatform **platforms, gsize n_platforms, NMPNetns for (i = 0; i < n_platforms; i++) g_assert (NM_IS_PLATFORM (platforms[i])); - i = nmtst_get_rand_int () % (n_platforms + 1); + i = nmtst_get_rand_uint32 () % (n_platforms + 1); if (i == 0) return; g_assert (nm_platform_netns_push (platforms[i - 1], netns)); diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c index a0650cc1..5926dac1 100644 --- a/src/platform/tests/test-link.c +++ b/src/platform/tests/test-link.c @@ -1,4 +1,3 @@ -/* -*- 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 @@ -241,7 +240,7 @@ test_slave (int master, int type, SignalData *master_changed) /* Set master up */ g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, master, NULL)); g_assert (nm_platform_link_is_up (NM_PLATFORM_GET, master)); - accept_signals (master_changed, 1, 2); + accept_signals (master_changed, 1, 3); g_signal_handlers_disconnect_by_func (NM_PLATFORM_GET, G_CALLBACK (test_link_changed_signal_cb), &test_link_changed_signal_arg1); g_signal_handlers_disconnect_by_func (NM_PLATFORM_GET, G_CALLBACK (test_link_changed_signal_cb), &test_link_changed_signal_arg2); @@ -287,7 +286,7 @@ test_slave (int master, int type, SignalData *master_changed) g_assert (nm_platform_link_is_connected (NM_PLATFORM_GET, master)); accept_signals (link_changed, 1, 3); /* NM running, can cause additional change of addrgenmode */ - accept_signals (master_changed, 0, 2); + accept_signals (master_changed, 0, 3); /* Enslave again * @@ -329,7 +328,7 @@ test_slave (int master, int type, SignalData *master_changed) ensure_no_signal (link_changed); accept_signal (link_removed); } - accept_signals (master_changed, 0, 2); + accept_signals (master_changed, 0, 3); ensure_no_signal (master_changed); @@ -515,8 +514,8 @@ test_bridge_addr (void) link = *plink; g_assert_cmpstr (link.name, ==, DEVICE_NAME); - g_assert_cmpint (link.addr.len, ==, sizeof (addr)); - g_assert (!memcmp (link.addr.data, addr, sizeof (addr))); + g_assert_cmpint (link.l_address.len, ==, sizeof (addr)); + g_assert (!memcmp (link.l_address.data, addr, sizeof (addr))); plink = nm_platform_link_get (NM_PLATFORM_GET, link.ifindex); g_assert (plink); @@ -538,8 +537,8 @@ test_bridge_addr (void) g_assert_cmpint (_nm_platform_uint8_inv (plink->inet6_addr_gen_mode_inv), ==, NM_IN6_ADDR_GEN_MODE_EUI64); } - g_assert_cmpint (plink->addr.len, ==, sizeof (addr)); - g_assert (!memcmp (plink->addr.data, addr, sizeof (addr))); + g_assert_cmpint (plink->l_address.len, ==, sizeof (addr)); + g_assert (!memcmp (plink->l_address.data, addr, sizeof (addr))); nmtstp_link_delete (NULL, -1, link.ifindex, link.name, TRUE); } @@ -2100,7 +2099,7 @@ test_create_many_links_do (guint n_devices) char name[64]; const NMPlatformLink *pllink; gs_unref_array GArray *ifindexes = g_array_sized_new (FALSE, FALSE, sizeof (int), n_devices); - const int EX = ((int) (nmtst_get_rand_int () % 4)) - 1; + const int EX = ((int) (nmtst_get_rand_uint32 () % 4)) - 1; g_assert (EX >= -1 && EX <= 2); @@ -2459,17 +2458,17 @@ test_netns_general (gpointer fixture, gconstpointer test_data) NMPlatform *p = (k == 0 ? platform_1 : platform_2); const char *id = (k == 0 ? "a" : "b"); - for (i = 0, j = nmtst_get_rand_int () % 5; i < j; i++) + for (i = 0, j = nmtst_get_rand_uint32 () % 5; i < j; i++) _ADD_DUMMY (p, nm_sprintf_buf (sbuf, "other-a-%s-%02d", id, i)); _ADD_DUMMY (p, "dummy1_"); - for (i = 0, j = nmtst_get_rand_int () % 5; i < j; i++) + for (i = 0, j = nmtst_get_rand_uint32 () % 5; i < j; i++) _ADD_DUMMY (p, nm_sprintf_buf (sbuf, "other-b-%s-%02d", id, i)); _ADD_DUMMY (p, nm_sprintf_buf (sbuf, "dummy2%s", id)); - for (i = 0, j = nmtst_get_rand_int () % 5; i < j; i++) + for (i = 0, j = nmtst_get_rand_uint32 () % 5; i < j; i++) _ADD_DUMMY (p, nm_sprintf_buf (sbuf, "other-c-%s-%02d", id, i)); } @@ -2491,17 +2490,17 @@ test_netns_general (gpointer fixture, gconstpointer test_data) NMPlatform *pl; const char *path; - j = nmtst_get_rand_int () % 2; + j = nmtst_get_rand_uint32 () % 2; - if (nmtst_get_rand_int () % 2) { + if (nmtst_get_rand_uint32 () % 2) { pl = platform_1; - if (nmtst_get_rand_int () % 2) + if (nmtst_get_rand_uint32 () % 2) path = "/proc/sys/net/ipv6/conf/dummy1_/disable_ipv6"; else path = "/proc/sys/net/ipv6/conf/dummy2a/disable_ipv6"; } else { pl = platform_2; - if (nmtst_get_rand_int () % 2) + if (nmtst_get_rand_uint32 () % 2) path = "/proc/sys/net/ipv6/conf/dummy1_/disable_ipv6"; else path = "/proc/sys/net/ipv6/conf/dummy2b/disable_ipv6"; @@ -2654,7 +2653,7 @@ test_netns_push (gpointer fixture, gconstpointer test_data) pl[2].platform = platform_2 = _test_netns_create_platform (); pl_base = &pl[0]; - i = nmtst_get_rand_int () % (G_N_ELEMENTS (pl) + 1); + i = nmtst_get_rand_uint32 () % (G_N_ELEMENTS (pl) + 1); if (i < G_N_ELEMENTS (pl)) { pl_base = &pl[i]; g_assert (nm_platform_netns_push (pl[i].platform, &netns_pop)); @@ -2674,7 +2673,7 @@ test_netns_push (gpointer fixture, gconstpointer test_data) g_ptr_array_add (device_names, tmp); pl[i].sysctl_path = tmp; - pl[i].sysctl_value = nmtst_get_rand_int () % 2 ? "1" : "0"; + pl[i].sysctl_value = nmtst_get_rand_uint32 () % 2 ? "1" : "0"; _ADD_DUMMY (pl[i].platform, pl[i].device_name); @@ -2689,16 +2688,16 @@ test_netns_push (gpointer fixture, gconstpointer test_data) pl[i].ns_mnt = tmp; } - nstack = nmtst_get_rand_int () % (G_N_ELEMENTS (stack) + 1); + nstack = nmtst_get_rand_uint32 () % (G_N_ELEMENTS (stack) + 1); for (i = 0; i < nstack; i++) { - stack[i].pl = &pl[nmtst_get_rand_int () % G_N_ELEMENTS (pl)]; - stack[i].ns_types = ns_types_list[nmtst_get_rand_int () % G_N_ELEMENTS (ns_types_list)]; + stack[i].pl = &pl[nmtst_get_rand_uint32 () % G_N_ELEMENTS (pl)]; + stack[i].ns_types = ns_types_list[nmtst_get_rand_uint32 () % G_N_ELEMENTS (ns_types_list)]; nmp_netns_push_type (nm_platform_netns_get (stack[i].pl->platform), stack[i].ns_types); } /* pop some again. */ - for (i = nmtst_get_rand_int () % (nstack + 1); i > 0; i--) { + for (i = nmtst_get_rand_uint32 () % (nstack + 1); i > 0; i--) { g_assert (nstack > 0); nstack--; nmp_netns_pop (nm_platform_netns_get (stack[nstack].pl->platform)); @@ -2765,9 +2764,9 @@ test_netns_push (gpointer fixture, gconstpointer test_data) static void test_netns_bind_to_path (gpointer fixture, gconstpointer test_data) { -#define P_VAR_RUN "/var/run" -#define P_VAR_RUN_NETNS "/var/run/netns" -#define P_VAR_RUN_NETNS_BINDNAME "/var/run/netns/"P_NETNS_BINDNAME +#define P_VAR_RUN "/run" +#define P_VAR_RUN_NETNS "/run/netns" +#define P_VAR_RUN_NETNS_BINDNAME "/run/netns/"P_NETNS_BINDNAME #define P_NETNS_BINDNAME "nmtst-iproute2-netns" gs_unref_object NMPlatform *platform_0 = NULL; gs_unref_object NMPlatform *platform_1 = NULL; @@ -2789,7 +2788,7 @@ test_netns_bind_to_path (gpointer fixture, gconstpointer test_data) g_assert_cmpint (mount ("tmpfs", P_VAR_RUN, "tmpfs", MS_NOATIME | MS_NODEV | MS_NOSUID, "mode=0755,size=32K"), ==, 0); g_assert_cmpint (mkdir (P_VAR_RUN_NETNS, 755), ==, 0); - i = (nmtst_get_rand_int () % 2) + 1; + i = (nmtst_get_rand_uint32 () % 2) + 1; netns = nm_platform_netns_get (platforms[i]); _ADD_DUMMY (platforms[i], "dummy2b"); @@ -2832,9 +2831,9 @@ test_sysctl_rename (void) ifindex[0] = nmtstp_link_dummy_add (PL, -1, IFNAME[0])->ifindex; ifindex[1] = nmtstp_link_dummy_add (PL, -1, IFNAME[1])->ifindex; - s = (nmtst_get_rand_int () % 2) ? NULL : ifname_buf; + s = (nmtst_get_rand_uint32 () % 2) ? NULL : ifname_buf; - if (nmtst_get_rand_int () % 2) { + if (nmtst_get_rand_uint32 () % 2) { /* bring the platform cache out of sync */ nmtstp_run_command_check ("ip link set %s name %s", IFNAME[0], IFNAME[2]); nm_platform_process_events (PL); @@ -2847,7 +2846,7 @@ test_sysctl_rename (void) } /* open dirfd for IFNAME[0] */ - i = nmtst_get_rand_int () % (2 + G_N_ELEMENTS (IFNAME)); + i = nmtst_get_rand_uint32 () % (2 + G_N_ELEMENTS (IFNAME)); if (i == 0) { dirfd = nm_platform_sysctl_open_netdir (PL, ifindex[0], @@ -2866,7 +2865,7 @@ test_sysctl_rename (void) g_assert_cmpstr (s, ==, IFNAME[0]); /* possibly rename the interfaces. */ - switch (nmtst_get_rand_int () % 4) { + switch (nmtst_get_rand_uint32 () % 4) { case 0: break; case 1: @@ -2879,11 +2878,11 @@ test_sysctl_rename (void) } /* possibly, resync platform cache (should make no difference). */ - if (nmtst_get_rand_int () % 2) + if (nmtst_get_rand_uint32 () % 2) nm_platform_process_events (PL); /* check that we still read the same file. */ - switch (nmtst_get_rand_int () % 2) { + switch (nmtst_get_rand_uint32 () % 2) { case 0: { gs_free char *c = NULL; @@ -2930,7 +2929,7 @@ test_sysctl_netns_switch (void) platforms[0] = platform_0 = nm_linux_platform_new (TRUE, TRUE); platforms[1] = platform_1 = _test_netns_create_platform (); platforms[2] = platform_2 = _test_netns_create_platform (); - PL = platforms[nmtst_get_rand_int () % 3]; + PL = platforms[nmtst_get_rand_uint32 () % 3]; nmtstp_netns_select_random (platforms, G_N_ELEMENTS (platforms), &netns_pop_1); @@ -2938,7 +2937,7 @@ test_sysctl_netns_switch (void) nmtstp_netns_select_random (platforms, G_N_ELEMENTS (platforms), &netns_pop_2); - s = (nmtst_get_rand_int () % 2) ? NULL : ifname_buf; + s = (nmtst_get_rand_uint32 () % 2) ? NULL : ifname_buf; dirfd = nm_platform_sysctl_open_netdir (PL, ifindex, s); @@ -3015,6 +3014,92 @@ test_sysctl_netns_switch (void) nmtstp_link_delete (PL, FALSE, ifindex, NULL, TRUE); } +static void +sysctl_set_async_cb_assert_success (GError *error, gpointer data) +{ + g_assert_no_error (error); + g_main_loop_quit (data); +} + +static void +sysctl_set_async_cb_assert_failure (GError *error, gpointer data) +{ + g_assert (error); + g_main_loop_quit (data); +} + +static void +test_sysctl_set_async (void) +{ + NMPlatform *const PL = NM_PLATFORM_GET; + const char *const IFNAME = "nm-dummy-0"; + const char *const PATH = "/proc/sys/net/ipv4/conf/nm-dummy-0/rp_filter"; + gs_free GMainLoop *loop = NULL; + gs_unref_object GCancellable *cancellable = NULL; + int ifindex; + + ifindex = nmtstp_link_dummy_add (PL, -1, IFNAME)->ifindex; + loop = g_main_loop_new (NULL, FALSE); + cancellable = g_cancellable_new (); + + nm_platform_sysctl_set_async (PL, + NMP_SYSCTL_PATHID_ABSOLUTE (PATH), + (const char *[]) { "2", NULL}, + sysctl_set_async_cb_assert_success, + loop, + cancellable); + + if (!nmtst_main_loop_run (loop, 1000)) + g_assert_not_reached (); + + g_assert_cmpint (nm_platform_sysctl_get_int32 (PL, NMP_SYSCTL_PATHID_ABSOLUTE (PATH), -1), + ==, + 2); + + nm_platform_sysctl_set_async (PL, + NMP_SYSCTL_PATHID_ABSOLUTE (PATH), + (const char *[]) { "2", "0", "1", "0", "1", NULL}, + sysctl_set_async_cb_assert_success, + loop, + cancellable); + + if (!nmtst_main_loop_run (loop, 2000)) + g_assert_not_reached (); + + g_assert_cmpint (nm_platform_sysctl_get_int32 (PL, NMP_SYSCTL_PATHID_ABSOLUTE (PATH), -1), + ==, + 1); + + nmtstp_link_delete (NULL, -1, ifindex, IFNAME, TRUE); +} + +static void +test_sysctl_set_async_fail (void) +{ + NMPlatform *const PL = NM_PLATFORM_GET; + const char *const IFNAME = "nm-dummy-0"; + const char *const PATH = "/proc/sys/net/ipv4/conf/nm-dummy-0/does-not-exist"; + gs_free GMainLoop *loop = NULL; + gs_unref_object GCancellable *cancellable = NULL; + int ifindex; + + ifindex = nmtstp_link_dummy_add (PL, -1, IFNAME)->ifindex; + loop = g_main_loop_new (NULL, FALSE); + cancellable = g_cancellable_new (); + + nm_platform_sysctl_set_async (PL, + NMP_SYSCTL_PATHID_ABSOLUTE (PATH), + (const char *[]) { "2", NULL}, + sysctl_set_async_cb_assert_failure, + loop, + cancellable); + + if (!nmtst_main_loop_run (loop, 1000)) + g_assert_not_reached (); + + nmtstp_link_delete (NULL, -1, ifindex, IFNAME, TRUE); +} + /*****************************************************************************/ static gpointer @@ -3238,6 +3323,8 @@ _nmtstp_setup_tests (void) g_test_add_func ("/general/sysctl/rename", test_sysctl_rename); g_test_add_func ("/general/sysctl/netns-switch", test_sysctl_netns_switch); + g_test_add_func ("/general/sysctl/set-async", test_sysctl_set_async); + g_test_add_func ("/general/sysctl/set-async-fail", test_sysctl_set_async_fail); g_test_add_func ("/link/ethtool/features/get", test_ethtool_features_get); } diff --git a/src/platform/tests/test-nmp-object.c b/src/platform/tests/test-nmp-object.c index e2019e96..08bde437 100644 --- a/src/platform/tests/test-nmp-object.c +++ b/src/platform/tests/test-nmp-object.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-platform.c - Handle runtime kernel networking configuration * * This program is free software; you can redistribute it and/or modify @@ -267,7 +266,7 @@ test_cache_link (void) struct udev_device *udev_device_3 = g_list_nth_data (global.udev_devices, 0); NMPCacheOpsType ops_type; nm_auto_unref_dedup_multi_index NMDedupMultiIndex *multi_idx = NULL; - gboolean use_udev = nmtst_get_rand_int () % 2; + gboolean use_udev = nmtst_get_rand_uint32 () % 2; multi_idx = nm_dedup_multi_index_new (); @@ -503,7 +502,7 @@ test_cache_qdisc (void) nm_auto_nmpobj NMPObject *obj2 = nmp_object_new (NMP_OBJECT_TYPE_QDISC, (NMPlatformObject *) &pl_qdisc_2); multi_idx = nm_dedup_multi_index_new (); - cache = nmp_cache_new (multi_idx, nmtst_get_rand_int () % 2); + cache = nmp_cache_new (multi_idx, nmtst_get_rand_uint32 () % 2); g_assert (nmp_cache_lookup_obj (cache, obj1a) == NULL); diff --git a/src/platform/tests/test-general.c b/src/platform/tests/test-platform-general.c index eebc15d8..2b723810 100644 --- a/src/platform/tests/test-general.c +++ b/src/platform/tests/test-platform-general.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-platform.c - Handle runtime kernel networking configuration * * This program is free software; you can redistribute it and/or modify @@ -52,6 +51,31 @@ test_link_get_all (void) /*****************************************************************************/ +static void +test_nm_platform_link_flags2str (void) +{ + int i; + + for (i = 0; i < 100; i++) { + char buf[NM_PLATFORM_LINK_FLAGS2STR_MAX_LEN + 100]; + const char *s; + const guint flags = ((i == 0) ? ~0u : nmtst_get_rand_uint ()); + gsize l; + + s = nm_platform_link_flags2str (flags, buf, sizeof (buf)); + g_assert (s); + + l = strlen (s); + if (l > NM_PLATFORM_LINK_FLAGS2STR_MAX_LEN) + g_error ("nm_platform_link_flags2str(%x) produced a longer output than %zu chars: \"%s\"", flags, NM_PLATFORM_LINK_FLAGS2STR_MAX_LEN, s); + if ( flags == ~0u + && l != NM_PLATFORM_LINK_FLAGS2STR_MAX_LEN) + g_error ("nm_platform_link_flags2str(%x) is expected to produce %zu chars, but produced %zu: \"%s\"", flags, NM_PLATFORM_LINK_FLAGS2STR_MAX_LEN, l, s); + } +} + +/*****************************************************************************/ + NMTST_DEFINE (); int @@ -61,7 +85,7 @@ main (int argc, char **argv) g_test_add_func ("/general/init_linux_platform", test_init_linux_platform); g_test_add_func ("/general/link_get_all", test_link_get_all); + g_test_add_func ("/general/nm_platform_link_flags2str", test_nm_platform_link_flags2str); return g_test_run (); } - diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c index e6ffe1ae..44bfbc58 100644 --- a/src/platform/tests/test-route.c +++ b/src/platform/tests/test-route.c @@ -1,4 +1,3 @@ -/* -*- 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 @@ -445,7 +444,7 @@ test_ip4_route_get (void) result = nm_platform_ip_route_get (NM_PLATFORM_GET, AF_INET, &a, - nmtst_get_rand_int () % 2 ? 0 : ifindex, + nmtst_get_rand_uint32 () % 2 ? 0 : ifindex, &route); g_assert (NMTST_NM_ERR_SUCCESS (result)); @@ -607,7 +606,7 @@ test_ip6_route_get (void) result = nm_platform_ip_route_get (NM_PLATFORM_GET, AF_INET6, a, - nmtst_get_rand_int () % 2 ? 0 : ifindex, + nmtst_get_rand_uint32 () % 2 ? 0 : ifindex, &route); g_assert (NMTST_NM_ERR_SUCCESS (result)); @@ -815,9 +814,9 @@ test_ip (gconstpointer test_data) if ( order_len == 0 || ( order_len < G_N_ELEMENTS (order_idx) - && nmtst_get_rand_int () % 2)) { + && nmtst_get_rand_uint32 () % 2)) { again_find_idx: - idx = nmtst_get_rand_int () % G_N_ELEMENTS (iface_data); + idx = nmtst_get_rand_uint32 () % G_N_ELEMENTS (iface_data); for (i = 0; i < order_len; i++) { if (order_idx[i] == idx) goto again_find_idx; @@ -827,7 +826,7 @@ again_find_idx: r->ifindex = iface_data[idx].ifindex; 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; + i = nmtst_get_rand_uint32 () % order_len; idx = order_idx[i]; for (i++; i < order_len; i++) order_idx[i - 1] = order_idx[i]; @@ -1129,17 +1128,17 @@ _rule_create_random (NMPlatform *platform) addr_size = nm_utils_addr_family_to_size (rr->addr_family); - p = nmtst_get_rand_int (); + p = nmtst_get_rand_uint32 (); if ((p % 1000u) < 50) rr->priority = 10000 + ((~p) % 20u); - p = nmtst_get_rand_int (); + p = nmtst_get_rand_uint32 (); if ((p % 1000u) < 40) nm_sprintf_buf (rr->iifname, "t-iif-%u", (~p) % 20); else if ((p % 1000u) < 80) nm_sprintf_buf (rr->iifname, "%s", DEVICE_NAME); - p = nmtst_get_rand_int (); + p = nmtst_get_rand_uint32 (); if ((p % 1000u) < 40) nm_sprintf_buf (rr->oifname, "t-oif-%d", (~p) % 20); else if ((p % 1000u) < 80) @@ -1149,14 +1148,14 @@ _rule_create_random (NMPlatform *platform) NMIPAddr *p_addr = i ? &rr->src : &rr->dst; guint8 *p_len = i ? &rr->src_len : &rr->dst_len; - p = nmtst_get_rand_int (); + p = nmtst_get_rand_uint32 (); if ((p % 1000u) < 100) { /* if we set src_len/dst_len to zero, the src/dst is actually ignored. * * For fuzzying, still set the address. It shall have no further effect. * */ *p_len = (~p) % (addr_size * 8 + 1); - p = nmtst_get_rand_int (); + p = nmtst_get_rand_uint32 (); if ((p % 3u) == 0) { if (rr->addr_family == AF_INET) p_addr->addr4 = nmtst_inet4_from_string (nm_sprintf_buf (saddr, "192.192.5.%u", (~p) % 256u)); @@ -1167,12 +1166,12 @@ _rule_create_random (NMPlatform *platform) } } - p = nmtst_get_rand_int (); + p = nmtst_get_rand_uint32 (); if ((p % 1000u) < 50) rr->tun_id = 10000 + ((~p) % 20); again_action: - p = nmtst_get_rand_int (); + p = nmtst_get_rand_uint32 (); if ((p % 1000u) < 500) rr->action = FR_ACT_UNSPEC; else if ((p % 1000u) < 750) @@ -1180,22 +1179,22 @@ again_action: else rr->action = (~p) % 0x100u; - rr->priority = _rr_rand_choose_u32 (nmtst_get_rand_int ()); + rr->priority = _rr_rand_choose_u32 (nmtst_get_rand_uint32 ()); if ( rr->action == FR_ACT_GOTO && rr->priority == G_MAXINT32) goto again_action; - p = nmtst_get_rand_int (); + p = nmtst_get_rand_uint32 (); if ((p % 10000u) < 100) rr->goto_target = rr->priority + 1; else - rr->goto_target = _rr_rand_choose_u32 (nmtst_get_rand_int ()); + rr->goto_target = _rr_rand_choose_u32 (nmtst_get_rand_uint32 ()); if ( rr->action == FR_ACT_GOTO && rr->goto_target <= rr->priority) goto again_action; - p = nmtst_get_rand_int (); + p = nmtst_get_rand_uint32 (); if ((p % 1000u) < 25) { if (_rule_check_kernel_support (platform, FRA_L3MDEV)) { rr->l3mdev = TRUE; @@ -1205,7 +1204,7 @@ again_action: again_table: if (!rr->l3mdev) { - p = nmtst_get_rand_int (); + p = nmtst_get_rand_uint32 (); if ((p % 1000u) < 700) rr->table = RT_TABLE_UNSPEC; else if ((p % 1000u) < 850) @@ -1217,33 +1216,33 @@ again_table: goto again_table; } - rr->fwmark = _rr_rand_choose_u32 (nmtst_get_rand_int ()); - rr->fwmask = _rr_rand_choose_u32 (nmtst_get_rand_int ()); + rr->fwmark = _rr_rand_choose_u32 (nmtst_get_rand_uint32 ()); + rr->fwmask = _rr_rand_choose_u32 (nmtst_get_rand_uint32 ()); - rr->flow = _rr_rand_choose_u32 (nmtst_get_rand_int ()); + rr->flow = _rr_rand_choose_u32 (nmtst_get_rand_uint32 ()); if (_rule_check_kernel_support (platform, FRA_PROTOCOL)) - rr->protocol = _rr_rand_choose_u8 (nmtst_get_rand_int ()); + rr->protocol = _rr_rand_choose_u8 (nmtst_get_rand_uint32 ()); #define IPTOS_TOS_MASK 0x1E again_tos: - rr->tos = _rr_rand_choose_u8 (nmtst_get_rand_int ()); + rr->tos = _rr_rand_choose_u8 (nmtst_get_rand_uint32 ()); if ( rr->addr_family == AF_INET && rr->tos & ~IPTOS_TOS_MASK) goto again_tos; if (_rule_check_kernel_support (platform, FRA_IP_PROTO)) - rr->ip_proto = _rr_rand_choose_u8 (nmtst_get_rand_int ()); + rr->ip_proto = _rr_rand_choose_u8 (nmtst_get_rand_uint32 ()); if (_rule_check_kernel_support (platform, FRA_SUPPRESS_PREFIXLEN)) - rr->suppress_prefixlen_inverse = ~_rr_rand_choose_u32 (nmtst_get_rand_int ()); + rr->suppress_prefixlen_inverse = ~_rr_rand_choose_u32 (nmtst_get_rand_uint32 ()); if (_rule_check_kernel_support (platform, FRA_SUPPRESS_IFGROUP)) - rr->suppress_ifgroup_inverse = ~_rr_rand_choose_u32 (nmtst_get_rand_int ()); + rr->suppress_ifgroup_inverse = ~_rr_rand_choose_u32 (nmtst_get_rand_uint32 ()); if (_rule_check_kernel_support (platform, FRA_UID_RANGE)) { - p = nmtst_get_rand_int (); + p = nmtst_get_rand_uint32 (); rr->uid_range_has = (p % 10000u) < 200; } @@ -1265,10 +1264,10 @@ again_uid_range: if (!_rule_check_kernel_support (platform, attribute)) continue; - p = nmtst_get_rand_int (); + p = nmtst_get_rand_uint32 (); if ((p % 10000u) < 300) { while (range->start == 0) { - p = p ^ nmtst_get_rand_int (); + p = p ^ nmtst_get_rand_uint32 (); range->start = nmtst_rand_select (1u, 0xFFFEu, ((p ) % 0xFFFEu) + 1); range->end = nmtst_rand_select (1u, 0xFFFEu, ((p >> 16) % 0xFFFEu) + 1, range->start); if (range->end < range->start) @@ -1277,7 +1276,7 @@ again_uid_range: } } - p = nmtst_get_rand_int () % 1000u; + p = nmtst_get_rand_uint32 () % 1000u; if (p < 100) rr->flags |= FIB_RULE_INVERT; @@ -1345,8 +1344,7 @@ _rule_fuzzy_equal (const NMPObject *obj, rr_co.tos = 0; if (rr->ip_proto == 0) rr_co.ip_proto = 0; - if (rr->suppress_prefixlen_inverse == 0) - rr_co.suppress_prefixlen_inverse = 0; + rr_co.suppress_prefixlen_inverse = rr->suppress_prefixlen_inverse; if (rr->suppress_ifgroup_inverse == 0) rr_co.suppress_ifgroup_inverse = 0; if (!rr->uid_range_has) @@ -1483,10 +1481,10 @@ test_rule (gconstpointer test_data) if (TEST_IDX != 1) { nmtst_rand_perm (NULL, objs->pdata, NULL, sizeof (gpointer), objs->len); - g_ptr_array_set_size (objs, nmtst_get_rand_int () % (objs->len + 1)); + g_ptr_array_set_size (objs, nmtst_get_rand_uint32 () % (objs->len + 1)); } - n = (TEST_IDX != 1) ? nmtst_get_rand_int () % 50u : 0u; + n = (TEST_IDX != 1) ? nmtst_get_rand_uint32 () % 50u : 0u; for (i = 0; i < n; i++) { nm_auto_nmpobj const NMPObject *o = NULL; guint try = 0; @@ -1536,16 +1534,18 @@ again: nmp_rules_manager_track (rules_manager, NMP_OBJECT_CAST_ROUTING_RULE (objs_sync->pdata[i]), 1, - USER_TAG_1); + USER_TAG_1, + NULL); if (nmtst_get_rand_bool ()) { /* this has no effect, because a negative priority (of same absolute value) * has lower priority than the positive priority above. */ nmp_rules_manager_track (rules_manager, NMP_OBJECT_CAST_ROUTING_RULE (objs_sync->pdata[i]), -1, - USER_TAG_2); + USER_TAG_2, + NULL); } - if (nmtst_get_rand_int () % objs_sync->len == 0) { + if (nmtst_get_rand_uint32 () % objs_sync->len == 0) { nmp_rules_manager_sync (rules_manager, FALSE); g_assert_cmpint (nmtstp_platform_routing_rules_get_count (platform, AF_UNSPEC), ==, i + 1); } @@ -1555,7 +1555,7 @@ again: g_assert_cmpint (nmtstp_platform_routing_rules_get_count (platform, AF_UNSPEC), ==, objs_sync->len); for (i = 0; i < objs_sync->len; i++) { - switch (nmtst_get_rand_int () % 3) { + switch (nmtst_get_rand_uint32 () % 3) { case 0: nmp_rules_manager_untrack (rules_manager, NMP_OBJECT_CAST_ROUTING_RULE (objs_sync->pdata[i]), @@ -1568,16 +1568,18 @@ again: nmp_rules_manager_track (rules_manager, NMP_OBJECT_CAST_ROUTING_RULE (objs_sync->pdata[i]), -1, - USER_TAG_1); + USER_TAG_1, + NULL); break; case 2: nmp_rules_manager_track (rules_manager, NMP_OBJECT_CAST_ROUTING_RULE (objs_sync->pdata[i]), -2, - USER_TAG_2); + USER_TAG_2, + NULL); break; } - if (nmtst_get_rand_int () % objs_sync->len == 0) { + if (nmtst_get_rand_uint32 () % objs_sync->len == 0) { nmp_rules_manager_sync (rules_manager, FALSE); g_assert_cmpint (nmtstp_platform_routing_rules_get_count (platform, AF_UNSPEC), ==, objs_sync->len - i - 1); } diff --git a/src/platform/wifi/nm-wifi-utils-nl80211.c b/src/platform/wifi/nm-wifi-utils-nl80211.c index 4f7ede97..164dada4 100644 --- a/src/platform/wifi/nm-wifi-utils-nl80211.c +++ b/src/platform/wifi/nm-wifi-utils-nl80211.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -103,7 +102,7 @@ _nl80211_alloc_msg (int id, int ifindex, int phy, guint32 cmd, guint32 flags) return g_steal_pointer (&msg); nla_put_failure: - return NULL; + g_return_val_if_reached (NULL); } static struct nl_msg * @@ -200,6 +199,9 @@ nl80211_iface_info_handler (struct nl_msg *msg, void *arg) case NL80211_IFTYPE_STATION: info->mode = NM_802_11_MODE_INFRA; break; + case NL80211_IFTYPE_MESH_POINT: + info->mode = NM_802_11_MODE_MESH; + break; } return NL_SKIP; @@ -242,6 +244,9 @@ wifi_nl80211_set_mode (NMWifiUtils *data, const NM80211Mode mode) case NM_802_11_MODE_AP: NLA_PUT_U32 (msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_AP); break; + case NM_802_11_MODE_MESH: + NLA_PUT_U32 (msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_MESH_POINT); + break; default: g_assert_not_reached (); } @@ -250,7 +255,7 @@ wifi_nl80211_set_mode (NMWifiUtils *data, const NM80211Mode mode) return err >= 0; nla_put_failure: - return FALSE; + g_return_val_if_reached (FALSE); } static gboolean @@ -267,7 +272,7 @@ wifi_nl80211_set_powersave (NMWifiUtils *data, guint32 powersave) return err >= 0; nla_put_failure: - return FALSE; + g_return_val_if_reached (FALSE); } static int @@ -365,7 +370,7 @@ wifi_nl80211_set_wake_on_wlan (NMWifiUtils *data, NMSettingWirelessWakeOnWLan wo return err >= 0; nla_put_failure: - return FALSE; + g_return_val_if_reached (FALSE); } /* @divisor: pass what value @xbm should be divided by to get dBm */ @@ -642,7 +647,7 @@ nl80211_get_ap_info (NMWifiUtilsNl80211 *self, return; nla_put_failure: - return; + g_return_if_reached (); } static guint32 @@ -695,7 +700,7 @@ wifi_nl80211_indicate_addressing_running (NMWifiUtils *data, gboolean running) return err >= 0; nla_put_failure: - return FALSE; + g_return_val_if_reached (FALSE); } struct nl80211_device_info { @@ -893,10 +898,11 @@ static int nl80211_wiphy_info_handler (struct nl_msg *msg, void *arg) int i; nla_for_each_nested (nl_mode, tb[NL80211_ATTR_SUPPORTED_IFTYPES], i) { - if (nla_type (nl_mode) == NL80211_IFTYPE_AP) - info->caps |= NM_WIFI_DEVICE_CAP_AP; - else if (nla_type (nl_mode) == NL80211_IFTYPE_ADHOC) - info->caps |= NM_WIFI_DEVICE_CAP_ADHOC; + switch (nla_type (nl_mode)) { + case NL80211_IFTYPE_AP: info->caps |= NM_WIFI_DEVICE_CAP_AP; break; + case NL80211_IFTYPE_ADHOC: info->caps |= NM_WIFI_DEVICE_CAP_ADHOC; break; + case NL80211_IFTYPE_MESH_POINT: info->caps |= NM_WIFI_DEVICE_CAP_MESH; break; + } } } diff --git a/src/platform/wifi/nm-wifi-utils-nl80211.h b/src/platform/wifi/nm-wifi-utils-nl80211.h index 27f67697..a4c9ca55 100644 --- a/src/platform/wifi/nm-wifi-utils-nl80211.h +++ b/src/platform/wifi/nm-wifi-utils-nl80211.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/platform/wifi/nm-wifi-utils-private.h b/src/platform/wifi/nm-wifi-utils-private.h index bcbc1c51..4cae22e5 100644 --- a/src/platform/wifi/nm-wifi-utils-private.h +++ b/src/platform/wifi/nm-wifi-utils-private.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/platform/wifi/nm-wifi-utils-wext.c b/src/platform/wifi/nm-wifi-utils-wext.c index 3aa1720a..d19cf8a5 100644 --- a/src/platform/wifi/nm-wifi-utils-wext.c +++ b/src/platform/wifi/nm-wifi-utils-wext.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/platform/wifi/nm-wifi-utils-wext.h b/src/platform/wifi/nm-wifi-utils-wext.h index 44b11afb..70cae966 100644 --- a/src/platform/wifi/nm-wifi-utils-wext.h +++ b/src/platform/wifi/nm-wifi-utils-wext.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/platform/wifi/nm-wifi-utils.c b/src/platform/wifi/nm-wifi-utils.c index 96071faa..1e42db6a 100644 --- a/src/platform/wifi/nm-wifi-utils.c +++ b/src/platform/wifi/nm-wifi-utils.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -90,7 +89,8 @@ nm_wifi_utils_set_mode (NMWifiUtils *data, const NM80211Mode mode) g_return_val_if_fail (data != NULL, FALSE); g_return_val_if_fail ( (mode == NM_802_11_MODE_INFRA) || (mode == NM_802_11_MODE_AP) - || (mode == NM_802_11_MODE_ADHOC), FALSE); + || (mode == NM_802_11_MODE_ADHOC) + || (mode == NM_802_11_MODE_MESH), FALSE); klass = NM_WIFI_UTILS_GET_CLASS (data); diff --git a/src/platform/wifi/nm-wifi-utils.h b/src/platform/wifi/nm-wifi-utils.h index 36148b5a..2e89053b 100644 --- a/src/platform/wifi/nm-wifi-utils.h +++ b/src/platform/wifi/nm-wifi-utils.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/platform/wpan/nm-wpan-utils.c b/src/platform/wpan/nm-wpan-utils.c index b7a51e9b..0afc2a4d 100644 --- a/src/platform/wpan/nm-wpan-utils.c +++ b/src/platform/wpan/nm-wpan-utils.c @@ -92,7 +92,7 @@ _nl802154_alloc_msg (int id, int ifindex, guint32 cmd, guint32 flags) return g_steal_pointer (&msg); nla_put_failure: - return NULL; + g_return_val_if_reached (NULL); } static struct nl_msg * @@ -217,7 +217,7 @@ nm_wpan_utils_set_pan_id (NMWpanUtils *self, guint16 pan_id) return err >= 0; nla_put_failure: - return FALSE; + g_return_val_if_reached (FALSE); } guint16 @@ -244,7 +244,7 @@ nm_wpan_utils_set_short_addr (NMWpanUtils *self, guint16 short_addr) return err >= 0; nla_put_failure: - return FALSE; + g_return_val_if_reached (FALSE); } gboolean @@ -262,7 +262,7 @@ nm_wpan_utils_set_channel (NMWpanUtils *self, guint8 page, guint8 channel) return err >= 0; nla_put_failure: - return FALSE; + g_return_val_if_reached (FALSE); } /*****************************************************************************/ diff --git a/src/ppp/nm-ppp-manager-call.c b/src/ppp/nm-ppp-manager-call.c index d951e4fd..c134a2b2 100644 --- a/src/ppp/nm-ppp-manager-call.c +++ b/src/ppp/nm-ppp-manager-call.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/ppp/nm-ppp-manager-call.h b/src/ppp/nm-ppp-manager-call.h index a93ed11f..6561a388 100644 --- a/src/ppp/nm-ppp-manager-call.h +++ b/src/ppp/nm-ppp-manager-call.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/ppp/nm-ppp-manager.c b/src/ppp/nm-ppp-manager.c index 04c14dfb..3708b940 100644 --- a/src/ppp/nm-ppp-manager.c +++ b/src/ppp/nm-ppp-manager.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/ppp/nm-ppp-manager.h b/src/ppp/nm-ppp-manager.h index ec0ca46e..d2285e3e 100644 --- a/src/ppp/nm-ppp-manager.h +++ b/src/ppp/nm-ppp-manager.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/ppp/nm-ppp-plugin-api.h b/src/ppp/nm-ppp-plugin-api.h index 95ddd211..7177a7d6 100644 --- a/src/ppp/nm-ppp-plugin-api.h +++ b/src/ppp/nm-ppp-plugin-api.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/ppp/nm-ppp-status.h b/src/ppp/nm-ppp-status.h index d785cc2c..a57685a1 100644 --- a/src/ppp/nm-ppp-status.h +++ b/src/ppp/nm-ppp-status.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/ppp/nm-pppd-plugin.c b/src/ppp/nm-pppd-plugin.c index a8d6749a..f0b9027c 100644 --- a/src/ppp/nm-pppd-plugin.c +++ b/src/ppp/nm-pppd-plugin.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -46,17 +45,18 @@ int plugin_init (void); char pppd_version[] = VERSION; -static GDBusProxy *proxy = NULL; +static struct { + GDBusConnection *dbus_connection; + char *ipparam; +} gl; static void -nm_phasechange (void *data, int arg) +nm_phasechange (int arg) { NMPPPStatus ppp_status = NM_PPP_STATUS_UNKNOWN; - char new_name[IF_NAMESIZE]; char *ppp_phase; - int index; - g_return_if_fail (G_IS_DBUS_PROXY (proxy)); + g_return_if_fail (G_IS_DBUS_CONNECTION (gl.dbus_connection)); switch (arg) { case PHASE_DEAD: @@ -117,33 +117,49 @@ nm_phasechange (void *data, int arg) break; } - g_message ("nm-ppp-plugin: (%s): status %d / phase '%s'", - __func__, + g_message ("nm-ppp-plugin: status %d / phase '%s'", ppp_status, ppp_phase); if (ppp_status != NM_PPP_STATUS_UNKNOWN) { - g_dbus_proxy_call (proxy, - "SetState", - g_variant_new ("(u)", ppp_status), - G_DBUS_CALL_FLAGS_NONE, -1, - NULL, - NULL, NULL); + g_dbus_connection_call (gl.dbus_connection, + NM_DBUS_SERVICE, + gl.ipparam, + NM_DBUS_INTERFACE_PPP, + "SetState", + g_variant_new ("(u)", ppp_status), + G_VARIANT_TYPE ("()"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); } if (ppp_status == NM_PPP_STATUS_RUNNING) { - index = if_nametoindex (ifname); + gs_unref_variant GVariant *ret = NULL; + char new_name[IF_NAMESIZE]; + int ifindex; + + ifindex = if_nametoindex (ifname); + /* Make a sync call to ensure that when the call * terminates the interface already has its final * name. */ - g_dbus_proxy_call_sync (proxy, - "SetIfindex", - g_variant_new ("(i)", index), - G_DBUS_CALL_FLAGS_NONE, - 25000, - NULL, NULL); + ret = g_dbus_connection_call_sync (gl.dbus_connection, + NM_DBUS_SERVICE, + gl.ipparam, + NM_DBUS_INTERFACE_PPP, + "SetIfindex", + g_variant_new ("(i)", ifindex), + G_VARIANT_TYPE ("()"), + G_DBUS_CALL_FLAGS_NONE, + 25000, + NULL, + NULL); + /* Update the name in pppd if NM changed it */ - if ( if_indextoname (index, new_name) + if ( if_indextoname (ifindex, new_name) && !nm_streq0 (ifname, new_name)) { g_message ("nm-ppp-plugin: interface name changed from '%s' to '%s'", ifname, new_name); g_strlcpy (ifname, new_name, IF_NAMESIZE); @@ -152,6 +168,16 @@ nm_phasechange (void *data, int arg) } static void +nm_phasechange_hook (void *data, int arg) +{ + /* We send the nofication in exitnotify instead */ + if (arg == PHASE_DEAD) + return; + + nm_phasechange (arg); +} + +static void nm_ip_up (void *data, int arg) { ipcp_options opts = ipcp_gotoptions[0]; @@ -159,13 +185,13 @@ nm_ip_up (void *data, int arg) GVariantBuilder builder; guint32 pppd_made_up_address = htonl (0x0a404040 + ifunit); - g_return_if_fail (G_IS_DBUS_PROXY (proxy)); + g_return_if_fail (G_IS_DBUS_CONNECTION (gl.dbus_connection)); - g_message ("nm-ppp-plugin: (%s): ip-up event", __func__); + g_message ("nm-ppp-plugin: ip-up event"); if (!opts.ouraddr) { - g_warning ("nm-ppp-plugin: (%s): didn't receive an internal IP from pppd!", __func__); - nm_phasechange (NULL, PHASE_DEAD); + g_warning ("nm-ppp-plugin: didn't receive an internal IP from pppd!"); + nm_phasechange (PHASE_DEAD); return; } @@ -235,14 +261,20 @@ nm_ip_up (void *data, int arg) wins, len, sizeof (guint32))); } - g_message ("nm-ppp-plugin: (%s): sending IPv4 config to NetworkManager...", __func__); - - g_dbus_proxy_call (proxy, - "SetIp4Config", - g_variant_new ("(a{sv})", &builder), - G_DBUS_CALL_FLAGS_NONE, -1, - NULL, - NULL, NULL); + g_message ("nm-ppp-plugin: sending IPv4 config to NetworkManager..."); + + g_dbus_connection_call (gl.dbus_connection, + NM_DBUS_SERVICE, + gl.ipparam, + NM_DBUS_INTERFACE_PPP, + "SetIp4Config", + g_variant_new ("(a{sv})", &builder), + G_VARIANT_TYPE ("()"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); } static GVariant * @@ -263,9 +295,9 @@ nm_ip6_up (void *data, int arg) ipv6cp_options *go = &ipv6cp_gotoptions[0]; GVariantBuilder builder; - g_return_if_fail (G_IS_DBUS_PROXY (proxy)); + g_return_if_fail (G_IS_DBUS_CONNECTION (gl.dbus_connection)); - g_message ("nm-ppp-plugin: (%s): ip6-up event", __func__); + g_message ("nm-ppp-plugin: ip6-up event"); g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); /* Keep sending the interface name to be backwards compatible @@ -283,14 +315,20 @@ nm_ip6_up (void *data, int arg) /* DNS is done via DHCPv6 or router advertisements */ - g_message ("nm-ppp-plugin: (%s): sending IPv6 config to NetworkManager...", __func__); - - g_dbus_proxy_call (proxy, - "SetIp6Config", - g_variant_new ("(a{sv})", &builder), - G_DBUS_CALL_FLAGS_NONE, -1, - NULL, - NULL, NULL); + g_message ("nm-ppp-plugin: sending IPv6 config to NetworkManager..."); + + g_dbus_connection_call (gl.dbus_connection, + NM_DBUS_SERVICE, + gl.ipparam, + NM_DBUS_INTERFACE_PPP, + "SetIp6Config", + g_variant_new ("(a{sv})", &builder), + G_VARIANT_TYPE ("()"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); } static int @@ -308,10 +346,10 @@ get_pap_check (void) static int get_credentials (char *username, char *password) { - const char *my_username = NULL; - const char *my_password = NULL; - GVariant *ret; - GError *err = NULL; + gs_unref_variant GVariant *ret = NULL; + gs_free_error GError *error = NULL; + const char *my_username; + const char *my_password; if (!password) { /* pppd is checking pap support; return 1 for supported */ @@ -320,34 +358,33 @@ get_credentials (char *username, char *password) } g_return_val_if_fail (username, -1); - g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), -1); - - g_message ("nm-ppp-plugin: (%s): passwd-hook, requesting credentials...", __func__); - - ret = g_dbus_proxy_call_sync (proxy, - "NeedSecrets", - NULL, - G_DBUS_CALL_FLAGS_NONE, -1, - NULL, &err); + g_return_val_if_fail (G_IS_DBUS_CONNECTION (gl.dbus_connection), -1); + + g_message ("nm-ppp-plugin: passwd-hook, requesting credentials..."); + + ret = g_dbus_connection_call_sync (gl.dbus_connection, + NM_DBUS_SERVICE, + gl.ipparam, + NM_DBUS_INTERFACE_PPP, + "NeedSecrets", + NULL, + G_VARIANT_TYPE ("(ss)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); if (!ret) { - g_warning ("nm-ppp-plugin: (%s): could not get secrets: %s", - __func__, - err->message); - g_error_free (err); + g_warning ("nm-ppp-plugin: could not get secrets: %s", + error->message); return -1; } - g_message ("nm-ppp-plugin: (%s): got credentials from NetworkManager", __func__); + g_message ("nm-ppp-plugin: got credentials from NetworkManager"); g_variant_get (ret, "(&s&s)", &my_username, &my_password); - if (my_username) - g_strlcpy (username, my_username, MAXNAMELEN); - - if (my_password) - g_strlcpy (password, my_password, MAXSECRETLEN); - - g_variant_unref (ret); + g_strlcpy (username, my_username, MAXNAMELEN); + g_strlcpy (password, my_password, MAXSECRETLEN); return 1; } @@ -355,12 +392,17 @@ get_credentials (char *username, char *password) static void nm_exit_notify (void *data, int arg) { - g_return_if_fail (G_IS_DBUS_PROXY (proxy)); + g_return_if_fail (G_IS_DBUS_CONNECTION (gl.dbus_connection)); + + /* We wait until this point to notify dead phase to make sure that + * the serial port has recovered already its original settings. + */ + nm_phasechange (PHASE_DEAD); - g_message ("nm-ppp-plugin: (%s): cleaning up", __func__); + g_message ("nm-ppp-plugin: cleaning up"); - g_object_unref (proxy); - proxy = NULL; + g_clear_object (&gl.dbus_connection); + nm_clear_g_free (&gl.ipparam); } static void @@ -387,46 +429,30 @@ add_ip6_notifier (void) int plugin_init (void) { - GDBusConnection *bus; - GError *err = NULL; + gs_free_error GError *err = NULL; - g_message ("nm-ppp-plugin: (%s): initializing", __func__); + g_message ("nm-ppp-plugin: initializing"); - bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &err); - if (!bus) { - g_warning ("nm-pppd-plugin: (%s): couldn't connect to system bus: %s", - __func__, err->message); - g_error_free (err); - return -1; - } + nm_assert (!gl.dbus_connection); + nm_assert (!gl.ipparam); - /* NM passes in the object path of the corresponding PPPManager - * object as the 'ipparam' argument to pppd. - */ - proxy = g_dbus_proxy_new_sync (bus, - G_DBUS_PROXY_FLAGS_NONE, - NULL, - NM_DBUS_SERVICE, - ipparam, - NM_DBUS_INTERFACE_PPP, - NULL, &err); - g_object_unref (bus); - - if (!proxy) { - g_warning ("nm-pppd-plugin: (%s): couldn't create D-Bus proxy: %s", - __func__, err->message); - g_error_free (err); + gl.dbus_connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &err); + if (!gl.dbus_connection) { + g_warning ("nm-pppd-plugin: couldn't connect to system bus: %s", + err->message); return -1; } + gl.ipparam = g_strdup (ipparam); + chap_passwd_hook = get_credentials; chap_check_hook = get_chap_check; pap_passwd_hook = get_credentials; pap_check_hook = get_pap_check; - add_notifier (&phasechange, nm_phasechange, NULL); + add_notifier (&phasechange, nm_phasechange_hook, NULL); add_notifier (&ip_up_notifier, nm_ip_up, NULL); - add_notifier (&exitnotify, nm_exit_notify, proxy); + add_notifier (&exitnotify, nm_exit_notify, NULL); add_ip6_notifier (); return 0; diff --git a/src/ppp/nm-pppd-plugin.h b/src/ppp/nm-pppd-plugin.h index 1a2ea09e..e0d691bf 100644 --- a/src/ppp/nm-pppd-plugin.h +++ b/src/ppp/nm-pppd-plugin.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/settings/nm-agent-manager.c b/src/settings/nm-agent-manager.c index 814dee85..d361c06a 100644 --- a/src/settings/nm-agent-manager.c +++ b/src/settings/nm-agent-manager.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -311,7 +310,6 @@ validate_identifier (const char *identifier, GError **error) static void agent_register_permissions_done (NMAuthChain *chain, - GError *error, GDBusMethodInvocation *context, gpointer user_data) { @@ -319,47 +317,36 @@ agent_register_permissions_done (NMAuthChain *chain, NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self); NMSecretAgent *agent; const char *sender; - GError *local = NULL; NMAuthCallResult result; CList *iter; - g_assert (context); + nm_assert (G_IS_DBUS_METHOD_INVOCATION (context)); priv->chains = g_slist_remove (priv->chains, chain); - if (error) { - local = g_error_new (NM_AGENT_MANAGER_ERROR, - NM_AGENT_MANAGER_ERROR_PERMISSION_DENIED, - "Failed to request agent permissions: %s", - error->message); - g_dbus_method_invocation_take_error (context, local); - } else { - agent = nm_auth_chain_steal_data (chain, "agent"); - g_assert (agent); + agent = nm_auth_chain_steal_data (chain, "agent"); + nm_assert (agent); - result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED); - if (result == NM_AUTH_CALL_RESULT_YES) - nm_secret_agent_add_permission (agent, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED, TRUE); + result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED); + if (result == NM_AUTH_CALL_RESULT_YES) + nm_secret_agent_add_permission (agent, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED, TRUE); - result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN); - if (result == NM_AUTH_CALL_RESULT_YES) - nm_secret_agent_add_permission (agent, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN, TRUE); + result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN); + if (result == NM_AUTH_CALL_RESULT_YES) + nm_secret_agent_add_permission (agent, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN, TRUE); - priv->agent_version_id += 1; - sender = nm_secret_agent_get_dbus_owner (agent); - g_hash_table_insert (priv->agents, g_strdup (sender), agent); - _LOGI (agent, "agent registered"); - g_dbus_method_invocation_return_value (context, NULL); + priv->agent_version_id += 1; + sender = nm_secret_agent_get_dbus_owner (agent); + g_hash_table_insert (priv->agents, g_strdup (sender), agent); + _LOGI (agent, "agent registered"); + g_dbus_method_invocation_return_value (context, NULL); - /* Signal an agent was registered */ - g_signal_emit (self, signals[AGENT_REGISTERED], 0, agent); + /* Signal an agent was registered */ + g_signal_emit (self, signals[AGENT_REGISTERED], 0, agent); - /* Add this agent to any in-progress secrets requests */ - c_list_for_each (iter, &priv->requests) - request_add_agent (c_list_entry (iter, Request, lst_request), agent); - } - - nm_auth_chain_destroy (chain); + /* Add this agent to any in-progress secrets requests */ + c_list_for_each (iter, &priv->requests) + request_add_agent (c_list_entry (iter, Request, lst_request), agent); } static NMSecretAgent * @@ -537,8 +524,7 @@ request_free (Request *req) case REQUEST_TYPE_CON_DEL: g_object_unref (req->con.connection); g_free (req->con.path); - if (req->con.chain) - nm_auth_chain_destroy (req->con.chain); + nm_clear_pointer (&req->con.chain, nm_auth_chain_destroy); if (req->request_type == REQUEST_TYPE_CON_GET) { g_free (req->con.get.setting_name); g_strfreev (req->con.get.hints); @@ -815,11 +801,8 @@ request_remove_agent (Request *req, NMSecretAgent *agent) case REQUEST_TYPE_CON_GET: case REQUEST_TYPE_CON_SAVE: case REQUEST_TYPE_CON_DEL: - if (req->con.chain) { - /* This cancels the pending authorization requests. */ - nm_auth_chain_destroy (req->con.chain); - req->con.chain = NULL; - } + /* This cancels the pending authorization requests. */ + nm_clear_pointer (&req->con.chain, nm_auth_chain_destroy); break; default: g_assert_not_reached (); @@ -1017,7 +1000,6 @@ _con_get_request_start_proceed (Request *req, gboolean include_system_secrets) static void _con_get_request_start_validated (NMAuthChain *chain, - GError *error, GDBusMethodInvocation *context, gpointer user_data) { @@ -1031,30 +1013,20 @@ _con_get_request_start_validated (NMAuthChain *chain, req->con.chain = NULL; - if (error) { - _LOGD (req->current, "agent "LOG_REQ_FMT" MODIFY check error: %s", - LOG_REQ_ARG (req), - error->message); - /* Try the next agent */ - request_next_agent (req); - } else { - /* If the agent obtained the 'modify' permission, we send all system secrets - * to it. If it didn't, we still ask it for secrets, but we don't send - * any system secrets. - */ - perm = nm_auth_chain_get_data (chain, "perm"); - g_assert (perm); - if (nm_auth_chain_get_result (chain, perm) == NM_AUTH_CALL_RESULT_YES) - req->con.current_has_modify = TRUE; - - _LOGD (req->current, "agent "LOG_REQ_FMT" MODIFY check result %s", - LOG_REQ_ARG (req), - req->con.current_has_modify ? "YES" : "NO"); + /* If the agent obtained the 'modify' permission, we send all system secrets + * to it. If it didn't, we still ask it for secrets, but we don't send + * any system secrets. + */ + perm = nm_auth_chain_get_data (chain, "perm"); + g_assert (perm); + if (nm_auth_chain_get_result (chain, perm) == NM_AUTH_CALL_RESULT_YES) + req->con.current_has_modify = TRUE; - _con_get_request_start_proceed (req, req->con.current_has_modify); - } + _LOGD (req->current, "agent "LOG_REQ_FMT" MODIFY check result %s", + LOG_REQ_ARG (req), + req->con.current_has_modify ? "YES" : "NO"); - nm_auth_chain_destroy (chain); + _con_get_request_start_proceed (req, req->con.current_has_modify); } static void @@ -1100,7 +1072,7 @@ _con_get_request_start (Request *req) perm = NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM; nm_auth_chain_set_data (req->con.chain, "perm", (gpointer) perm, NULL); - nm_auth_chain_add_call (req->con.chain, perm, TRUE); + nm_auth_chain_add_call_unsafe (req->con.chain, perm, TRUE); } else { _LOGD (NULL, "("LOG_REQ_FMT") requesting user-owned secrets from agent %s", LOG_REQ_ARG (req), agent_dbus_owner); @@ -1478,7 +1450,6 @@ nm_agent_manager_all_agents_have_capability (NMAgentManager *manager, static void agent_permissions_changed_done (NMAuthChain *chain, - GError *error, GDBusMethodInvocation *context, gpointer user_data) { @@ -1492,21 +1463,15 @@ agent_permissions_changed_done (NMAuthChain *chain, agent = nm_auth_chain_get_data (chain, "agent"); g_assert (agent); - if (error) - _LOGD (agent, "failed to request updated agent permissions"); - else { - _LOGD (agent, "updated agent permissions"); - - if (nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED) == NM_AUTH_CALL_RESULT_YES) - share_protected = TRUE; - if (nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN) == NM_AUTH_CALL_RESULT_YES) - share_open = TRUE; - } + _LOGD (agent, "updated agent permissions"); + + if (nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED) == NM_AUTH_CALL_RESULT_YES) + share_protected = TRUE; + if (nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN) == NM_AUTH_CALL_RESULT_YES) + share_open = TRUE; nm_secret_agent_add_permission (agent, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED, share_protected); nm_secret_agent_add_permission (agent, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN, share_open); - - nm_auth_chain_destroy (chain); } static void diff --git a/src/settings/nm-agent-manager.h b/src/settings/nm-agent-manager.h index 8c27f26b..949ab6bc 100644 --- a/src/settings/nm-agent-manager.h +++ b/src/settings/nm-agent-manager.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/settings/nm-secret-agent.c b/src/settings/nm-secret-agent.c index 836ab21d..2aa1476d 100644 --- a/src/settings/nm-secret-agent.c +++ b/src/settings/nm-secret-agent.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -25,6 +24,7 @@ #include <sys/types.h> #include <pwd.h> +#include "nm-glib-aux/nm-dbus-aux.h" #include "nm-dbus-interface.h" #include "nm-dbus-manager.h" #include "nm-core-internal.h" @@ -54,7 +54,10 @@ typedef struct { NMDBusManager *bus_mgr; GDBusConnection *connection; CList requests; - gulong on_disconnected_id; + union { + gulong obj_signal; + guint dbus_signal; + } on_disconnected_id; bool connection_is_private:1; } NMSecretAgentPrivate; @@ -614,15 +617,12 @@ nm_secret_agent_delete_secrets (NMSecretAgent *self, static void _on_disconnected_cleanup (NMSecretAgentPrivate *priv) { - if (priv->on_disconnected_id) { - if (priv->connection_is_private) { - g_signal_handler_disconnect (priv->bus_mgr, - priv->on_disconnected_id); - } else { - g_dbus_connection_signal_unsubscribe (priv->connection, - priv->on_disconnected_id); - } - priv->on_disconnected_id = 0; + if (priv->connection_is_private) { + nm_clear_g_signal_handler (priv->bus_mgr, + &priv->on_disconnected_id.obj_signal); + } else { + nm_clear_g_dbus_connection_signal (priv->connection, + &priv->on_disconnected_id.dbus_signal); } g_clear_object (&priv->connection); @@ -744,21 +744,16 @@ nm_secret_agent_new (GDBusMethodInvocation *context, /* we cannot subscribe to notify::g-name-owner because that doesn't work * for unique names and it doesn't work for private connections. */ if (priv->connection_is_private) { - priv->on_disconnected_id = g_signal_connect (priv->bus_mgr, - NM_DBUS_MANAGER_PRIVATE_CONNECTION_DISCONNECTED, - G_CALLBACK (_on_disconnected_private_connection), - self); + priv->on_disconnected_id.obj_signal = g_signal_connect (priv->bus_mgr, + NM_DBUS_MANAGER_PRIVATE_CONNECTION_DISCONNECTED, + G_CALLBACK (_on_disconnected_private_connection), + self); } else { - priv->on_disconnected_id = g_dbus_connection_signal_subscribe (priv->connection, - "org.freedesktop.DBus", /* name */ - "org.freedesktop.DBus", /* interface */ - "NameOwnerChanged", /* signal name */ - "/org/freedesktop/DBus", /* path */ - priv->dbus_owner, /* arg0 */ - G_DBUS_SIGNAL_FLAGS_NONE, - _on_disconnected_name_owner_changed, - self, - NULL); + priv->on_disconnected_id.dbus_signal = nm_dbus_connection_signal_subscribe_name_owner_changed (priv->connection, + priv->dbus_owner, + _on_disconnected_name_owner_changed, + self, + NULL); } return self; diff --git a/src/settings/nm-secret-agent.h b/src/settings/nm-secret-agent.h index 64b103aa..209a1009 100644 --- a/src/settings/nm-secret-agent.h +++ b/src/settings/nm-secret-agent.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c index 3fdaa598..25a27e68 100644 --- a/src/settings/nm-settings-connection.c +++ b/src/settings/nm-settings-connection.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * This program is free software; you can redistribute it and/or modify @@ -25,6 +24,7 @@ #include "c-list/src/c-list.h" +#include "nm-glib-aux/nm-keyfile-aux.h" #include "nm-libnm-core-intern/nm-common-macros.h" #include "nm-config.h" #include "nm-config-data.h" @@ -37,14 +37,18 @@ #include "NetworkManagerUtils.h" #include "nm-core-internal.h" #include "nm-audit-manager.h" - -#define SETTINGS_TIMESTAMPS_FILE NMSTATEDIR "/timestamps" -#define SETTINGS_SEEN_BSSIDS_FILE NMSTATEDIR "/seen-bssids" +#include "nm-settings.h" +#include "settings/plugins/keyfile/nms-keyfile-storage.h" #define AUTOCONNECT_RETRIES_UNSET -2 #define AUTOCONNECT_RETRIES_FOREVER -1 #define AUTOCONNECT_RESET_RETRIES_TIMER 300 +#define _NM_SETTINGS_UPDATE2_FLAG_ALL_PERSIST_MODES ((NMSettingsUpdate2Flags) ( NM_SETTINGS_UPDATE2_FLAG_TO_DISK \ + | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY \ + | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED \ + | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY)) + /*****************************************************************************/ NMConnection ** @@ -70,13 +74,11 @@ nm_settings_connections_array_to_connections (NMSettingsConnection *const*connec NM_GOBJECT_PROPERTIES_DEFINE (NMSettingsConnection, PROP_UNSAVED, - PROP_READY, PROP_FLAGS, PROP_FILENAME, ); enum { - REMOVED, UPDATED_INTERNAL, FLAGS_CHANGED, LAST_SIGNAL @@ -86,18 +88,12 @@ static guint signals[LAST_SIGNAL] = { 0 }; typedef struct _NMSettingsConnectionPrivate { - NMAgentManager *agent_mgr; - NMSessionMonitor *session_monitor; - gulong session_changed_id; - - NMSettingsConnectionIntFlags flags:5; - - bool removed:1; - bool ready:1; + NMSettings *settings; - bool timestamp_set:1; + NMKeyFileDB *kf_db_timestamps; + NMKeyFileDB *kf_db_seen_bssids; - NMSettingsAutoconnectBlockedReason autoconnect_blocked_reason:4; + NMAgentManager *agent_mgr; /* List of pending authentication requests */ CList auth_lst_head; @@ -106,12 +102,18 @@ typedef struct _NMSettingsConnectionPrivate { NMConnection *connection; + NMSettingsStorage *storage; + + char *filename; + + NMDevice *default_wired_device; + /* Caches secrets from on-disk connections; were they not cached any * call to nm_connection_clear_secrets() wipes them out and we'd have * to re-read them from disk which defeats the purpose of having the * connection in-memory at all. */ - NMConnection *system_secrets; + GVariant *system_secrets; /* Caches secrets from agents during the activation process; if new system * secrets are returned from an agent, they get written out to disk, @@ -119,9 +121,7 @@ typedef struct _NMSettingsConnectionPrivate { * secrets, and would wipe out any agent-owned or not-saved secrets the * agent also returned. */ - NMConnection *agent_secrets; - - char *filename; + GVariant *agent_secrets; GHashTable *seen_bssids; /* Up-to-date BSSIDs that's been seen for the connection */ @@ -130,10 +130,21 @@ typedef struct _NMSettingsConnectionPrivate { guint64 last_secret_agent_version_id; int autoconnect_retries; + gint32 autoconnect_retries_blocked_until; + bool timestamp_set:1; + + NMSettingsAutoconnectBlockedReason autoconnect_blocked_reason:4; + + NMSettingsConnectionIntFlags flags:5; + } NMSettingsConnectionPrivate; +struct _NMSettingsConnectionClass { + NMDBusObjectClass parent; +}; + G_DEFINE_TYPE (NMSettingsConnection, nm_settings_connection, NM_TYPE_DBUS_OBJECT) #define NM_SETTINGS_CONNECTION_GET_PRIVATE(self) _NM_GET_PRIVATE_PTR (self, NMSettingsConnection, NM_IS_SETTINGS_CONNECTION) @@ -152,7 +163,7 @@ G_DEFINE_TYPE (NMSettingsConnection, nm_settings_connection, NM_TYPE_DBUS_OBJECT const char *__uuid = (self) ? nm_settings_connection_get_uuid (self) : NULL; \ \ if (self) { \ - g_snprintf (__prefix, sizeof (__prefix), "%s[%p%s%s]", _NMLOG_PREFIX_NAME, self, __uuid ? "," : "", __uuid ?: ""); \ + g_snprintf (__prefix, sizeof (__prefix), "%s["NM_HASH_OBFUSCATE_PTR_FMT"%s%s]", _NMLOG_PREFIX_NAME, NM_HASH_OBFUSCATE_PTR (self), __uuid ? "," : "", __uuid ?: ""); \ __p_prefix = __prefix; \ } \ _nm_log (__level, _NMLOG_DOMAIN, 0, NULL, __uuid, \ @@ -167,6 +178,92 @@ static const GDBusSignalInfo signal_info_updated; static const GDBusSignalInfo signal_info_removed; static const NMDBusInterfaceInfoExtended interface_info_settings_connection; +static void update_system_secrets_cache (NMSettingsConnection *self, NMConnection *new); +static void update_agent_secrets_cache (NMSettingsConnection *self, NMConnection *new); + +/*****************************************************************************/ + +NMDevice * +nm_settings_connection_default_wired_get_device (NMSettingsConnection *self) +{ + NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); + + nm_assert (!priv->default_wired_device || NM_IS_DEVICE (priv->default_wired_device)); + + return priv->default_wired_device; +} + +void +nm_settings_connection_default_wired_set_device (NMSettingsConnection *self, + NMDevice *device) +{ + NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); + + nm_assert (!priv->default_wired_device || NM_IS_DEVICE (priv->default_wired_device)); + nm_assert (!device || NM_IS_DEVICE (device)); + + nm_assert ((!!priv->default_wired_device) != (!!device)); + + priv->default_wired_device = device; +} + +/*****************************************************************************/ + +NMSettingsStorage * +nm_settings_connection_get_storage (NMSettingsConnection *self) +{ + g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NULL); + + return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->storage; +} + +void +_nm_settings_connection_set_storage (NMSettingsConnection *self, + NMSettingsStorage *storage) +{ + NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); + const char *filename; + + nm_assert (NM_IS_SETTINGS_STORAGE (storage)); + nm_assert ( !priv->storage + || nm_streq (nm_settings_storage_get_uuid (storage), + nm_settings_storage_get_uuid (priv->storage))); + + nm_g_object_ref_set (&priv->storage, storage); + + filename = nm_settings_storage_get_filename (priv->storage); + + if (!nm_streq0 (priv->filename, filename)) { + g_free (priv->filename); + priv->filename = g_strdup (filename); + _notify (self, PROP_FILENAME); + } +} + +/*****************************************************************************/ + +gboolean +nm_settings_connection_still_valid (NMSettingsConnection *self) +{ + gboolean valid; + + g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE); + + valid = !c_list_is_empty (&self->_connections_lst); + + nm_assert (valid == nm_settings_has_connection (NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->settings, self)); + + return valid; +} + +/*****************************************************************************/ + +static GHashTable * +_seen_bssids_hash_new (void) +{ + return g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL); +} + /*****************************************************************************/ NMConnection * @@ -177,6 +274,50 @@ nm_settings_connection_get_connection (NMSettingsConnection *self) return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->connection; } +void +_nm_settings_connection_set_connection (NMSettingsConnection *self, + NMConnection *new_connection, + NMConnection **out_connection_old, + NMSettingsConnectionUpdateReason update_reason) +{ + NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); + gs_unref_object NMConnection *connection_old = NULL; + + nm_assert (NM_IS_CONNECTION (new_connection)); + nm_assert (NM_IS_SETTINGS_STORAGE (priv->storage)); + nm_assert (nm_streq0 (nm_settings_storage_get_uuid (priv->storage), nm_connection_get_uuid (new_connection))); + nm_assert (!out_connection_old || !*out_connection_old); + + if ( !priv->connection + || !nm_connection_compare (priv->connection, + new_connection, + NM_SETTING_COMPARE_FLAG_EXACT)) { + connection_old = priv->connection; + priv->connection = g_object_ref (new_connection); + nmtst_connection_assert_unchanging (priv->connection); + + /* note that we only return @connection_old if the new connection actually differs from + * before. + * + * So, there are three cases: + * + * - return %NULL when setting the connection the first time. + * - return %NULL if setting a profile with the same content that we already have. + * - return the previous pointer if the connection changed. */ + NM_SET_OUT (out_connection_old, g_steal_pointer (&connection_old)); + } + + if (NM_FLAGS_HAS (update_reason, NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_SYSTEM_SECRETS)) + update_system_secrets_cache (self, NULL); + else if (NM_FLAGS_HAS (update_reason, NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS)) + update_system_secrets_cache (self, priv->connection); + + if (NM_FLAGS_HAS (update_reason, NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_AGENT_SECRETS)) + update_agent_secrets_cache (self, NULL); + else if (NM_FLAGS_HAS (update_reason, NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS)) + update_agent_secrets_cache (self, priv->connection); +} + /*****************************************************************************/ gboolean @@ -206,34 +347,23 @@ nm_settings_connection_get_last_secret_agent_version_id (NMSettingsConnection *s /*****************************************************************************/ -static void -set_visible (NMSettingsConnection *self, gboolean new_visible) -{ - nm_settings_connection_set_flags (self, - NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE, - new_visible); -} - -void -nm_settings_connection_recheck_visibility (NMSettingsConnection *self) +gboolean +nm_settings_connection_check_visibility (NMSettingsConnection *self, + NMSessionMonitor *session_monitor) { - NMSettingsConnectionPrivate *priv; NMSettingConnection *s_con; guint32 num, i; - g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self)); + g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE); - priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); + nm_assert (NM_IS_SESSION_MONITOR (session_monitor)); s_con = nm_connection_get_setting_connection (nm_settings_connection_get_connection (self)); /* Check every user in the ACL for a session */ num = nm_setting_connection_get_num_permissions (s_con); - if (num == 0) { - /* Visible to all */ - set_visible (self, TRUE); - return; - } + if (num == 0) + return TRUE; for (i = 0; i < num; i++) { const char *user; @@ -243,20 +373,13 @@ nm_settings_connection_recheck_visibility (NMSettingsConnection *self) continue; if (!nm_session_monitor_user_to_uid (user, &uid)) continue; - if (!nm_session_monitor_session_exists (priv->session_monitor, uid, FALSE)) + if (!nm_session_monitor_session_exists (session_monitor, uid, FALSE)) continue; - set_visible (self, TRUE); - return; + return TRUE; } - set_visible (self, FALSE); -} - -static void -session_changed_cb (NMSessionMonitor *self, NMSettingsConnection *sett_conn) -{ - nm_settings_connection_recheck_visibility (sett_conn); + return FALSE; } /*****************************************************************************/ @@ -302,7 +425,8 @@ nm_settings_connection_check_permission (NMSettingsConnection *self, if (nm_setting_connection_get_permission (s_con, i, NULL, &puser, NULL)) { NMSecretAgent *agent = nm_agent_manager_get_agent_by_user (priv->agent_mgr, puser); - if (agent && nm_secret_agent_has_permission (agent, permission)) + if ( agent + && nm_secret_agent_has_permission (agent, permission)) return TRUE; } } @@ -312,421 +436,196 @@ nm_settings_connection_check_permission (NMSettingsConnection *self, /*****************************************************************************/ -static gboolean -secrets_filter_cb (NMSetting *setting, - const char *secret, - NMSettingSecretFlags flags, - gpointer user_data) +static void +update_system_secrets_cache (NMSettingsConnection *self, NMConnection *new) { - NMSettingSecretFlags filter_flags = GPOINTER_TO_UINT (user_data); - - /* Returns TRUE to remove the secret */ + NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); + gs_unref_object NMConnection *connection_cloned = NULL; + gs_unref_variant GVariant *old_secrets = NULL; - /* Can't use bitops with SECRET_FLAG_NONE so handle that specifically */ - if ( (flags == NM_SETTING_SECRET_FLAG_NONE) - && (filter_flags == NM_SETTING_SECRET_FLAG_NONE)) - return FALSE; + old_secrets = g_steal_pointer (&priv->system_secrets); - /* Otherwise if the secret has at least one of the desired flags keep it */ - return (flags & filter_flags) ? FALSE : TRUE; -} + if (!new) + goto out; -static void -update_system_secrets_cache (NMSettingsConnection *self) -{ - NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); + /* FIXME: improve NMConnection API so we can avoid the overhead of cloning the connection, + * in particular if there are no secrets to begin with. */ - if (priv->system_secrets) - g_object_unref (priv->system_secrets); - priv->system_secrets = nm_simple_connection_new_clone (nm_settings_connection_get_connection (self)); + connection_cloned = nm_simple_connection_new_clone (new); /* Clear out non-system-owned and not-saved secrets */ - nm_connection_clear_secrets_with_flags (priv->system_secrets, - secrets_filter_cb, - GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_NONE)); + _nm_connection_clear_secrets_by_secret_flags (connection_cloned, + NM_SETTING_SECRET_FLAG_NONE); + + priv->system_secrets = nm_g_variant_ref_sink (nm_connection_to_dbus (connection_cloned, NM_CONNECTION_SERIALIZE_ONLY_SECRETS)); + +out: + if (_LOGT_ENABLED ()) { + if ((!!old_secrets) != (!!priv->system_secrets)) { + _LOGT ("update system secrets: secrets %s", + old_secrets ? "cleared" : "set"); + } else if ( priv->system_secrets + && !g_variant_equal (old_secrets, priv->system_secrets)) + _LOGT ("update system secrets: secrets updated"); + } } static void update_agent_secrets_cache (NMSettingsConnection *self, NMConnection *new) { NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); - NMSettingSecretFlags filter_flags = NM_SETTING_SECRET_FLAG_NOT_SAVED | NM_SETTING_SECRET_FLAG_AGENT_OWNED; + gs_unref_object NMConnection *connection_cloned = NULL; + gs_unref_variant GVariant *old_secrets = NULL; - if (priv->agent_secrets) - g_object_unref (priv->agent_secrets); - priv->agent_secrets = nm_simple_connection_new_clone ( new - ?: nm_settings_connection_get_connection (self)); + old_secrets = g_steal_pointer (&priv->agent_secrets); - /* Clear out non-system-owned secrets */ - nm_connection_clear_secrets_with_flags (priv->agent_secrets, - secrets_filter_cb, - GUINT_TO_POINTER (filter_flags)); -} + if (!new) + goto out; -static void -secrets_cleared_cb (NMConnection *connection, NMSettingsConnection *self) -{ - NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); + /* FIXME: improve NMConnection API so we can avoid the overhead of cloning the connection, + * in particular if there are no secrets to begin with. */ - /* Clear agent secrets when connection's secrets are cleared since agent - * secrets are transient. - */ - if (priv->agent_secrets) - g_object_unref (priv->agent_secrets); - priv->agent_secrets = NULL; -} + connection_cloned = nm_simple_connection_new_clone (new); -static void -set_persist_mode (NMSettingsConnection *self, NMSettingsConnectionPersistMode persist_mode) -{ - NMSettingsConnectionIntFlags flags = NM_SETTINGS_CONNECTION_INT_FLAGS_NONE; - const NMSettingsConnectionIntFlags ALL = NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED - | NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED - | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE; + /* Clear out non-system-owned secrets */ + _nm_connection_clear_secrets_by_secret_flags (connection_cloned, + NM_SETTING_SECRET_FLAG_NOT_SAVED + | NM_SETTING_SECRET_FLAG_AGENT_OWNED); - switch (persist_mode) { - case NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK: - flags = NM_SETTINGS_CONNECTION_INT_FLAGS_NONE; - break; - case NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY: - case NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED: - case NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY: - flags = NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED; - break; - case NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_DETACHED: - case NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_ONLY: - flags = NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED | - NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE; - break; - case NM_SETTINGS_CONNECTION_PERSIST_MODE_UNSAVED: - /* only set the connection as unsaved, but preserve the nm-generated - * and volatile flag. */ - nm_settings_connection_set_flags (self, - NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED, - TRUE); - return; - case NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP: - case NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP_SAVED: - /* Nothing to do */ - return; - } + priv->agent_secrets = nm_g_variant_ref_sink (nm_connection_to_dbus (connection_cloned, NM_CONNECTION_SERIALIZE_ONLY_SECRETS)); - nm_settings_connection_set_flags_full (self, ALL, flags); +out: + if (_LOGT_ENABLED ()) { + if ((!!old_secrets) != (!!priv->agent_secrets)) { + _LOGT ("update agent secrets: secrets %s", + old_secrets ? "cleared" : "set"); + } else if ( priv->agent_secrets + && !g_variant_equal (old_secrets, priv->agent_secrets)) + _LOGT ("update agent secrets: secrets updated"); + } } -static void -_emit_updated (NMSettingsConnection *self, gboolean by_user) +void +nm_settings_connection_clear_secrets (NMSettingsConnection *self, + gboolean clear_cached_system_secrets, + gboolean persist) { - nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self), - &interface_info_settings_connection, - &signal_info_updated, - "()"); - g_signal_emit (self, signals[UPDATED_INTERNAL], 0, by_user); -} + gs_unref_object NMConnection *connection_cloned = NULL; -static void -connection_changed_cb (NMConnection *connection, NMSettingsConnection *self) -{ - set_persist_mode (self, NM_SETTINGS_CONNECTION_PERSIST_MODE_UNSAVED); - _emit_updated (self, FALSE); -} + if (!nm_settings_connection_still_valid (self)) + return; -static gboolean -_delete (NMSettingsConnection *self, GError **error) -{ - NMSettingsConnectionClass *klass; - GError *local = NULL; - const char *filename; + /* FIXME: add API to NMConnection so that we can clone a profile without secrets. */ - nm_assert (NM_IS_SETTINGS_CONNECTION (self)); + connection_cloned = nm_simple_connection_new_clone (nm_settings_connection_get_connection (self)); - klass = NM_SETTINGS_CONNECTION_GET_CLASS (self); - if (!klass->delete) { - g_set_error (&local, - NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_FAILED, - "delete not supported"); - goto fail; - } - if (!klass->delete (self, - &local)) - goto fail; - - filename = nm_settings_connection_get_filename (self); - if (filename) { - _LOGD ("delete: success deleting connection (\"%s\")", filename); - nm_settings_connection_set_filename (self, NULL); - } else - _LOGT ("delete: success deleting connection (no-file)"); - return TRUE; -fail: - _LOGD ("delete: failure deleting connection: %s", local->message); - g_propagate_error (error, local); - return FALSE; + nm_connection_clear_secrets (connection_cloned); + + if (!nm_settings_connection_update (self, + connection_cloned, + persist + ? NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP + : NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST, + NM_SETTINGS_CONNECTION_INT_FLAGS_NONE, + NM_SETTINGS_CONNECTION_INT_FLAGS_NONE, + NM_SETTINGS_CONNECTION_UPDATE_REASON_IGNORE_PERSIST_FAILURE + | (clear_cached_system_secrets ? NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_SYSTEM_SECRETS : NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE) + | NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_AGENT_SECRETS, + "clear-secrets", + NULL)) + nm_assert_not_reached (); } static gboolean -_update_prepare (NMSettingsConnection *self, - NMConnection *new_connection, +_secrets_update (NMConnection *connection, + const char *setting_name, + GVariant *secrets, + NMConnection **out_new_connection, GError **error) { - g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE); - g_return_val_if_fail (NM_IS_CONNECTION (new_connection), FALSE); + gs_unref_variant GVariant *secrets_setting = NULL; - if (!nm_connection_normalize (new_connection, NULL, NULL, error)) - return FALSE; + nm_assert (NM_IS_CONNECTION (connection)); - if ( nm_dbus_object_get_path (NM_DBUS_OBJECT (self)) - && g_strcmp0 (nm_settings_connection_get_uuid (self), nm_connection_get_uuid (new_connection)) != 0) { - /* Updating the UUID is not allowed once the path is exported. */ - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "connection %s cannot change the UUID from %s to %s", nm_settings_connection_get_id (self), - nm_settings_connection_get_uuid (self), nm_connection_get_uuid (new_connection)); + if ( setting_name + && !nm_connection_get_setting_by_name (connection, setting_name)) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_SETTING_NOT_FOUND, + setting_name); return FALSE; } - return TRUE; -} - -gboolean -nm_settings_connection_update (NMSettingsConnection *self, - NMConnection *new_connection, - NMSettingsConnectionPersistMode persist_mode, - NMSettingsConnectionCommitReason commit_reason, - const char *log_diff_name, - GError **error) -{ - NMSettingsConnectionPrivate *priv; - NMSettingsConnectionClass *klass = NULL; - gs_unref_object NMConnection *reread_connection = NULL; - NMConnection *replace_connection; - 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); - - priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); - - if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK) { - klass = NM_SETTINGS_CONNECTION_GET_CLASS (self); - if (!klass->commit_changes) { - g_set_error (&local, - NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_FAILED, - "writing settings not supported"); - goto out; - } - } - - if ( new_connection - && !_update_prepare (self, - new_connection, - &local)) - goto out; - - if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK) { - if (!klass->commit_changes (self, - new_connection ?: nm_settings_connection_get_connection (self), - commit_reason, - &reread_connection, - &logmsg_change, - &local)) - goto out; - - if ( reread_connection - && !_update_prepare (self, - reread_connection, - &local)) - goto out; - } - - 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. - */ - g_signal_handlers_block_by_func (priv->connection, G_CALLBACK (connection_changed_cb), self); - - /* Do nothing if there's nothing to update */ - if ( replace_connection - && !nm_connection_compare (nm_settings_connection_get_connection (self), - replace_connection, - NM_SETTING_COMPARE_FLAG_EXACT)) { - - if (log_diff_name) { - nm_utils_log_connection_diff (replace_connection, nm_settings_connection_get_connection (self), LOGL_DEBUG, LOGD_CORE, log_diff_name, "++ ", - nm_dbus_object_get_path (NM_DBUS_OBJECT (self))); - } - - /* Make a copy of agent-owned secrets because they won't be present in - * the connection returned by plugins, as plugins return only what was - * reread from the file. */ - simple = nm_simple_connection_new_clone (nm_settings_connection_get_connection (self)); - nm_connection_clear_secrets_with_flags (simple, - secrets_filter_cb, - GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED)); - con_agent_secrets = nm_connection_to_dbus (simple, NM_CONNECTION_SERIALIZE_ONLY_SECRETS); - - nm_connection_replace_settings_from_connection (nm_settings_connection_get_connection (self), replace_connection); - - replaced = TRUE; - } + if (!secrets) + return TRUE; - nm_settings_connection_set_flags (self, - NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE, - FALSE); + nm_assert ( g_variant_is_of_type (secrets, NM_VARIANT_TYPE_SETTING) + || g_variant_is_of_type (secrets, NM_VARIANT_TYPE_CONNECTION)); - if (replaced) { - /* Cache the just-updated system secrets in case something calls - * nm_connection_clear_secrets() and clears them. - */ - update_system_secrets_cache (self); - - /* Add agent and always-ask secrets back; they won't necessarily be - * in the replacement connection data if it was eg reread from disk. - */ - if (priv->agent_secrets) { - GVariant *dict; + if (g_variant_n_children (secrets) == 0) + return TRUE; - dict = nm_connection_to_dbus (priv->agent_secrets, NM_CONNECTION_SERIALIZE_ONLY_SECRETS); - if (dict) { - (void) nm_connection_update_secrets (nm_settings_connection_get_connection (self), NULL, dict, NULL); - g_variant_unref (dict); - } + if ( setting_name + && g_variant_is_of_type (secrets, NM_VARIANT_TYPE_CONNECTION)) { + secrets_setting = g_variant_lookup_value (secrets, setting_name, NM_VARIANT_TYPE_SETTING); + if (!secrets_setting) { + /* The connection dictionary didn't contain any secrets for + * @setting_name; just return success. + */ + return TRUE; } - if (con_agent_secrets) - (void) nm_connection_update_secrets (nm_settings_connection_get_connection (self), NULL, con_agent_secrets, NULL); + secrets = secrets_setting; } - /* 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); + /* if @out_new_connection is provided, we don't modify @connection but clone + * and return it. Otherwise, we update @connection inplace. */ + if (out_new_connection) { + nm_assert (!*out_new_connection); + connection = nm_simple_connection_new_clone (connection); + *out_new_connection = connection; } - nm_settings_connection_recheck_visibility (self); - - if ( replaced - && persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP) - set_persist_mode (self, NM_SETTINGS_CONNECTION_PERSIST_MODE_UNSAVED); - else - set_persist_mode (self, persist_mode); - - if (NM_IN_SET (persist_mode, NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY, - NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_ONLY)) - _delete (self, NULL); - else if (NM_IN_SET (persist_mode, NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED, - NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_DETACHED)) - nm_settings_connection_set_filename (self, NULL); - - g_signal_handlers_unblock_by_func (priv->connection, G_CALLBACK (connection_changed_cb), self); - - _emit_updated (self, TRUE); - -out: - if (local) { - _LOGI ("write: failure to update connection: %s", local->message); - g_propagate_error (error, local); + if (!nm_connection_update_secrets (connection, + setting_name, + secrets, + error)) return FALSE; - } - if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK) { - if (reread_connection) - _LOGI ("write: successfully updated (%s), connection was modified in the process", logmsg_change); - else if (new_connection) - _LOGI ("write: successfully updated (%s)", logmsg_change); - else - _LOGI ("write: successfully committed (%s)", logmsg_change); - } return TRUE; } -static void -remove_entry_from_db (NMSettingsConnection *self, const char* db_name) -{ - GKeyFile *key_file; - const char *db_file; - - if (strcmp (db_name, "timestamps") == 0) - db_file = SETTINGS_TIMESTAMPS_FILE; - else if (strcmp (db_name, "seen-bssids") == 0) - db_file = SETTINGS_SEEN_BSSIDS_FILE; - else - return; - - key_file = g_key_file_new (); - if (g_key_file_load_from_file (key_file, db_file, G_KEY_FILE_KEEP_COMMENTS, NULL)) { - const char *connection_uuid; - char *data; - gsize len; - GError *error = NULL; - - connection_uuid = nm_settings_connection_get_uuid (self); - - g_key_file_remove_key (key_file, db_name, connection_uuid, NULL); - data = g_key_file_to_data (key_file, &len, &error); - if (data) { - g_file_set_contents (db_file, data, len, &error); - g_free (data); - } - if (error) { - _LOGW ("error writing %s file '%s': %s", db_name, db_file, error->message); - g_error_free (error); - } - } - g_key_file_free (key_file); -} - gboolean -nm_settings_connection_delete (NMSettingsConnection *self, +nm_settings_connection_update (NMSettingsConnection *self, + NMConnection *new_connection, + NMSettingsConnectionPersistMode persist_mode, + NMSettingsConnectionIntFlags sett_flags, + NMSettingsConnectionIntFlags sett_mask, + NMSettingsConnectionUpdateReason update_reason, + const char *log_context_name, GError **error) { - gs_unref_object NMSettingsConnection *self_keep_alive = NULL; - NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); - NMConnection *for_agents; - g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE); - self_keep_alive = g_object_ref (self); - - if (!_delete (self, error)) - return FALSE; - - set_visible (self, FALSE); - - /* Tell agents to remove secrets for this connection */ - for_agents = nm_simple_connection_new_clone (nm_settings_connection_get_connection (self)); - nm_connection_clear_secrets (for_agents); - nm_agent_manager_delete_secrets (priv->agent_mgr, - nm_dbus_object_get_path (NM_DBUS_OBJECT (self)), - for_agents); - g_object_unref (for_agents); - - /* Remove timestamp from timestamps database file */ - remove_entry_from_db (self, "timestamps"); + return nm_settings_update_connection (NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->settings, + self, + new_connection, + persist_mode, + sett_flags, + sett_mask, + update_reason, + log_context_name, + error); +} - /* Remove connection from seen-bssids database file */ - remove_entry_from_db (self, "seen-bssids"); +void +nm_settings_connection_delete (NMSettingsConnection *self, + gboolean allow_add_to_no_auto_default) +{ + g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self)); - nm_settings_connection_signal_remove (self); - return TRUE; + nm_settings_delete_connection (NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->settings, + self, + allow_add_to_no_auto_default); } /*****************************************************************************/ @@ -788,13 +687,6 @@ _get_secrets_info_free (NMSettingsConnectionCallId *call_id) g_slice_free (NMSettingsConnectionCallId, call_id); } -static gboolean -supports_secrets (NMSettingsConnection *self, const char *setting_name) -{ - /* All secrets supported */ - return TRUE; -} - typedef struct { NMSettingSecretFlags required; NMSettingSecretFlags forbidden; @@ -918,25 +810,37 @@ nm_settings_connection_new_secrets (NMSettingsConnection *self, GVariant *secrets, GError **error) { - if (!nm_settings_connection_has_unmodified_applied_connection (self, applied_connection, - NM_SETTING_COMPARE_FLAG_NONE)) { + gs_unref_object NMConnection *new_connection = NULL; + NMConnection *connection; + + if (!nm_settings_connection_has_unmodified_applied_connection (self, + applied_connection, + NM_SETTING_COMPARE_FLAG_NONE)) { g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, "The connection was modified since activation"); return FALSE; } - if (!nm_connection_update_secrets (nm_settings_connection_get_connection (self), setting_name, secrets, error)) - return FALSE; + connection = nm_settings_connection_get_connection (self); - update_system_secrets_cache (self); - update_agent_secrets_cache (self, NULL); + if (!_secrets_update (connection, + setting_name, + secrets, + &new_connection, + error)) + return FALSE; - nm_settings_connection_update (self, - NULL, - NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK, - NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, - "new-secrets", - NULL); + if (!nm_settings_connection_update (self, + new_connection ?: connection, + NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP, + NM_SETTINGS_CONNECTION_INT_FLAGS_NONE, + NM_SETTINGS_CONNECTION_INT_FLAGS_NONE, + NM_SETTINGS_CONNECTION_UPDATE_REASON_IGNORE_PERSIST_FAILURE + | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS + | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS, + "new-secrets", + NULL)) + nm_assert_not_reached (); return TRUE; } @@ -957,9 +861,11 @@ get_secrets_done_cb (NMAgentManager *manager, NMSettingsConnectionPrivate *priv; NMConnection *applied_connection; gs_free_error GError *local = NULL; - GVariant *dict = NULL; + gs_unref_variant GVariant *system_secrets = NULL; + gs_unref_object NMConnection *new_connection = NULL; gboolean agent_had_system = FALSE; ForEachSecretFlags cmp_flags = { NM_SETTING_SECRET_FLAG_NONE, NM_SETTING_SECRET_FLAG_NONE }; + gs_unref_variant GVariant *filtered_secrets = NULL; if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) return; @@ -1020,62 +926,69 @@ get_secrets_done_cb (NMAgentManager *manager, setting_name, call_id); - if (priv->system_secrets) - dict = nm_connection_to_dbus (priv->system_secrets, NM_CONNECTION_SERIALIZE_ONLY_SECRETS); + system_secrets = nm_g_variant_ref (priv->system_secrets); - /* Update the connection with our existing secrets from backing storage */ - nm_connection_clear_secrets (nm_settings_connection_get_connection (self)); - if (!dict || nm_connection_update_secrets (nm_settings_connection_get_connection (self), setting_name, dict, &local)) { - gs_unref_variant GVariant *filtered_secrets = NULL; - - /* Update the connection with the agent's secrets; by this point if any - * system-owned secrets exist in 'secrets' the agent that provided them - * will have been authenticated, so those secrets can replace the existing - * system secrets. - */ - filtered_secrets = validate_secret_flags (nm_settings_connection_get_connection (self), secrets, &cmp_flags); - if (nm_connection_update_secrets (nm_settings_connection_get_connection (self), setting_name, filtered_secrets, &local)) { - /* Now that all secrets are updated, copy and cache new secrets, - * then save them to backing storage. - */ - update_system_secrets_cache (self); - update_agent_secrets_cache (self, NULL); + new_connection = nm_simple_connection_new_clone (nm_settings_connection_get_connection (self)); - /* Only save secrets to backing storage if the agent returned any - * new system secrets. If it didn't, then the secrets are agent- - * owned and there's no point to writing out the connection when - * nothing has changed, since agent-owned secrets don't get saved here. - */ - if (agent_had_system) { - _LOGD ("(%s:%p) saving new secrets to backing storage", - setting_name, - call_id); - - nm_settings_connection_update (self, - NULL, - NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK, - NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, - "get-new-secrets", - NULL); - } else { - _LOGD ("(%s:%p) new agent secrets processed", - setting_name, - call_id); - } + nm_connection_clear_secrets (new_connection); - } else { - _LOGD ("(%s:%p) failed to update with agent secrets: %s", - setting_name, - call_id, - local->message); - } - } else { + if (!_secrets_update (new_connection, + setting_name, + system_secrets, + NULL, + &local)) { _LOGD ("(%s:%p) failed to update with existing secrets: %s", setting_name, call_id, local->message); } + /* Update the connection with the agent's secrets; by this point if any + * system-owned secrets exist in 'secrets' the agent that provided them + * will have been authenticated, so those secrets can replace the existing + * system secrets. + */ + filtered_secrets = validate_secret_flags (new_connection, secrets, &cmp_flags); + + if (!_secrets_update (new_connection, + setting_name, + filtered_secrets, + NULL, + &local)) { + _LOGD ("(%s:%p) failed to update with agent secrets: %s", + setting_name, + call_id, + local->message); + } + + /* Only save secrets to backing storage if the agent returned any + * new system secrets. If it didn't, then the secrets are agent- + * owned and there's no point to writing out the connection when + * nothing has changed, since agent-owned secrets don't get saved here. + */ + if (agent_had_system) { + _LOGD ("(%s:%p) saving new secrets to backing storage", + setting_name, + call_id); + } else { + _LOGD ("(%s:%p) new agent secrets processed", + setting_name, + call_id); + } + if (!nm_settings_connection_update (self, + new_connection, + agent_had_system + ? NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP + : NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST, + NM_SETTINGS_CONNECTION_INT_FLAGS_NONE, + NM_SETTINGS_CONNECTION_INT_FLAGS_NONE, + NM_SETTINGS_CONNECTION_UPDATE_REASON_IGNORE_PERSIST_FAILURE + | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS + | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS, + "get-new-secrets", + NULL)) + nm_assert_not_reached (); + applied_connection = call_id->applied_connection; if (applied_connection) { get_cmp_flags (self, @@ -1091,18 +1004,17 @@ get_secrets_done_cb (NMAgentManager *manager, nm_connection_clear_secrets (applied_connection); - if (!dict || nm_connection_update_secrets (applied_connection, setting_name, dict, NULL)) { - gs_unref_variant GVariant *filtered_secrets = NULL; + if ( !system_secrets + || nm_connection_update_secrets (applied_connection, setting_name, system_secrets, NULL)) { + gs_unref_variant GVariant *filtered_secrets2 = NULL; - filtered_secrets = validate_secret_flags (applied_connection, secrets, &cmp_flags); - nm_connection_update_secrets (applied_connection, setting_name, filtered_secrets, NULL); + filtered_secrets2 = validate_secret_flags (applied_connection, secrets, &cmp_flags); + nm_connection_update_secrets (applied_connection, setting_name, filtered_secrets2, NULL); } } _get_secrets_info_callback (call_id, agent_username, setting_name, local); g_clear_error (&local); - if (dict) - g_variant_unref (dict); out: _get_secrets_info_free (call_id); @@ -1163,7 +1075,6 @@ nm_settings_connection_get_secrets (NMSettingsConnection *self, gpointer callback_data) { NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); - GVariant *existing_secrets = NULL; NMAgentManagerCallId call_id_a; gs_free char *joined_hints = NULL; NMSettingsConnectionCallId *call_id; @@ -1200,14 +1111,6 @@ nm_settings_connection_get_secrets (NMSettingsConnection *self, goto schedule_dummy; } - /* Use priv->system_secrets to work around the fact that nm_connection_clear_secrets() - * will clear secrets on this object's settings. - */ - if (priv->system_secrets) - existing_secrets = nm_connection_to_dbus (priv->system_secrets, NM_CONNECTION_SERIALIZE_ONLY_SECRETS); - if (existing_secrets) - g_variant_ref_sink (existing_secrets); - /* 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. @@ -1215,19 +1118,20 @@ nm_settings_connection_get_secrets (NMSettingsConnection *self, * 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); + /* Use priv->system_secrets to work around the fact that nm_connection_clear_secrets() + * will clear secrets on this object's settings. + */ call_id_a = nm_agent_manager_get_secrets (priv->agent_mgr, nm_dbus_object_get_path (NM_DBUS_OBJECT (self)), nm_settings_connection_get_connection (self), subject, - existing_secrets, + priv->system_secrets, setting_name, flags, hints, get_secrets_done_cb, call_id); - g_assert (call_id_a); - if (existing_secrets) - g_variant_unref (existing_secrets); + nm_assert (call_id_a); _LOGD ("(%s:%p) secrets requested flags 0x%X hints '%s'", setting_name, @@ -1420,37 +1324,6 @@ auth_start (NMSettingsConnection *self, /**** DBus method handlers ************************************/ -static gboolean -check_writable (NMConnection *self, GError **error) -{ - NMSettingConnection *s_con; - - g_return_val_if_fail (NM_IS_CONNECTION (self), FALSE); - - s_con = nm_connection_get_setting_connection (self); - if (!s_con) { - g_set_error_literal (error, - NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Connection did not have required 'connection' setting"); - return FALSE; - } - - /* If the connection is read-only, that has to be changed at the source of - * the problem (ex a system settings plugin that can't write connections out) - * instead of over D-Bus. - */ - if (nm_setting_connection_get_read_only (s_con)) { - g_set_error_literal (error, - NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_READ_ONLY_CONNECTION, - "Connection is read-only"); - return FALSE; - } - - return TRUE; -} - static void get_settings_auth_cb (NMSettingsConnection *self, GDBusMethodInvocation *context, @@ -1458,46 +1331,41 @@ get_settings_auth_cb (NMSettingsConnection *self, GError *error, gpointer data) { - if (error) + gs_free const char **seen_bssids = NULL; + NMConnectionSerializationOptions options = { + }; + GVariant *settings; + + if (error) { g_dbus_method_invocation_return_gerror (context, error); - else { - gs_unref_object NMConnection *dupl_con = NULL; - GVariant *settings; - NMSettingConnection *s_con; - NMSettingWireless *s_wifi; - guint64 timestamp = 0; - gs_free char **bssids = NULL; - - dupl_con = nm_simple_connection_new_clone (nm_settings_connection_get_connection (self)); - - /* Timestamp is not updated in connection's 'timestamp' property, - * because it would force updating the connection and in turn - * writing to /etc periodically, which we want to avoid. Rather real - * timestamps are kept track of in a private variable. So, substitute - * timestamp property with the real one here before returning the settings. - */ - nm_settings_connection_get_timestamp (self, ×tamp); - if (timestamp) { - s_con = nm_connection_get_setting_connection (dupl_con); - g_object_set (s_con, NM_SETTING_CONNECTION_TIMESTAMP, timestamp, NULL); - } - /* Seen BSSIDs are not updated in 802-11-wireless 'seen-bssids' property - * from the same reason as timestamp. Thus we put it here to GetSettings() - * return settings too. - */ - bssids = nm_settings_connection_get_seen_bssids (self); - s_wifi = nm_connection_get_setting_wireless (dupl_con); - if (bssids && bssids[0] && s_wifi) - g_object_set (s_wifi, NM_SETTING_WIRELESS_SEEN_BSSIDS, bssids, NULL); - - /* Secrets should *never* be returned by the GetSettings method, they - * get returned by the GetSecrets method which can be better - * protected against leakage of secrets to unprivileged callers. - */ - settings = nm_connection_to_dbus (dupl_con, NM_CONNECTION_SERIALIZE_NO_SECRETS); - g_dbus_method_invocation_return_value (context, - g_variant_new ("(@a{sa{sv}})", settings)); + return; } + + /* Timestamp is not updated in connection's 'timestamp' property, + * because it would force updating the connection and in turn + * writing to /etc periodically, which we want to avoid. Rather real + * timestamps are kept track of in a private variable. So, substitute + * timestamp property with the real one here before returning the settings. + */ + options.timestamp.has = TRUE; + nm_settings_connection_get_timestamp (self, &options.timestamp.val); + + /* Seen BSSIDs are not updated in 802-11-wireless 'seen-bssids' property + * from the same reason as timestamp. Thus we put it here to GetSettings() + * return settings too. + */ + seen_bssids = nm_settings_connection_get_seen_bssids (self); + options.seen_bssids = seen_bssids; + + /* Secrets should *never* be returned by the GetSettings method, they + * get returned by the GetSecrets method which can be better + * protected against leakage of secrets to unprivileged callers. + */ + settings = nm_connection_to_dbus_full (nm_settings_connection_get_connection (self), + NM_CONNECTION_SERIALIZE_NO_SECRETS, + &options); + g_dbus_method_invocation_return_value (context, + g_variant_new ("(@a{sa{sv}})", settings)); } static void @@ -1533,28 +1401,6 @@ typedef struct { } UpdateInfo; static void -cached_secrets_to_connection (NMSettingsConnection *self, NMConnection *connection) -{ - NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); - GVariant *secrets_dict; - - if (priv->agent_secrets) { - secrets_dict = nm_connection_to_dbus (priv->agent_secrets, NM_CONNECTION_SERIALIZE_ONLY_SECRETS); - if (secrets_dict) { - (void) nm_connection_update_secrets (connection, NULL, secrets_dict, NULL); - g_variant_unref (secrets_dict); - } - } - if (priv->system_secrets) { - secrets_dict = nm_connection_to_dbus (priv->system_secrets, NM_CONNECTION_SERIALIZE_ONLY_SECRETS); - if (secrets_dict) { - (void) nm_connection_update_secrets (connection, NULL, secrets_dict, NULL); - g_variant_unref (secrets_dict); - } - } -} - -static void update_complete (NMSettingsConnection *self, UpdateInfo *info, GError *error) @@ -1566,7 +1412,7 @@ update_complete (NMSettingsConnection *self, g_variant_builder_init (&result, G_VARIANT_TYPE ("a{sv}")); g_dbus_method_invocation_return_value (info->context, - g_variant_new ("(@a{sv})", g_variant_builder_end (&result))); + g_variant_new ("(a{sv})", &result)); } else g_dbus_method_invocation_return_value (info->context, NULL); @@ -1587,24 +1433,28 @@ update_auth_cb (NMSettingsConnection *self, GError *error, gpointer data) { + NMSettingsConnectionPrivate *priv; UpdateInfo *info = data; - NMSettingsConnectionCommitReason commit_reason; gs_free_error GError *local = NULL; NMSettingsConnectionPersistMode persist_mode; - const char *log_diff_name; if (error) { update_complete (self, info, error); return; } + priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); + if (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. */ - cached_secrets_to_connection (self, info->new_settings); + if (priv->agent_secrets) + nm_connection_update_secrets (info->new_settings, NULL, priv->agent_secrets, NULL); + if (priv->system_secrets) + nm_connection_update_secrets (info->new_settings, NULL, priv->system_secrets, NULL); } else { /* Cache the new secrets from the agent, as stuff like inotify-triggered * changes to connection's backing config files will blow them away if @@ -1628,45 +1478,38 @@ update_auth_cb (NMSettingsConnection *self, } } - commit_reason = NM_SETTINGS_CONNECTION_COMMIT_REASON_USER_ACTION; - if ( info->new_settings - && !nm_streq0 (nm_connection_get_id (nm_settings_connection_get_connection (self)), - nm_connection_get_id (info->new_settings))) - commit_reason |= NM_SETTINGS_CONNECTION_COMMIT_REASON_ID_CHANGED; + nm_assert ( !NM_FLAGS_ANY (info->flags, _NM_SETTINGS_UPDATE2_FLAG_ALL_PERSIST_MODES) + || nm_utils_is_power_of_two (info->flags & _NM_SETTINGS_UPDATE2_FLAG_ALL_PERSIST_MODES)); if (NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_TO_DISK)) - persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK; - else if (NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY)) + persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK; + else if (NM_FLAGS_ANY (info->flags, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY)) persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY; - else if (NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED)) { - persist_mode = NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_VOLATILE) - ? NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_DETACHED - : NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED; - } else if (NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY)) { - persist_mode = NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_VOLATILE) - ? NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_ONLY - : NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY; + else if (NM_FLAGS_ANY (info->flags, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED)) + persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED; + else if (NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY)) { + persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY; } else persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP; - if ( persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK - || ( persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP - && !nm_settings_connection_get_unsaved (self))) - log_diff_name = info->new_settings ? "update-settings" : "write-out-to-disk"; - else - log_diff_name = info->new_settings ? "update-unsaved" : "make-unsaved"; - - if (NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT)) { - nm_settings_connection_autoconnect_blocked_reason_set (self, - NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_USER_REQUEST, - TRUE); - } - nm_settings_connection_update (self, info->new_settings, persist_mode, - commit_reason, - log_diff_name, + ( NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_VOLATILE) + ? NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE + : NM_SETTINGS_CONNECTION_INT_FLAGS_NONE), + NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED + | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE, + NM_SETTINGS_CONNECTION_UPDATE_REASON_FORCE_RENAME + | ( NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_NO_REAPPLY) + ? NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE + : NM_SETTINGS_CONNECTION_UPDATE_REASON_REAPPLY_PARTIAL) + | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS + | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS + | ( NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT) + ? NM_SETTINGS_CONNECTION_UPDATE_REASON_BLOCK_AUTOCONNECT + : NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE), + "update-from-dbus", &local); if (!local) { @@ -1677,15 +1520,17 @@ update_auth_cb (NMSettingsConnection *self, * Only send secrets to agents of the same UID that called update too. */ for_agent = nm_simple_connection_new_clone (nm_settings_connection_get_connection (self)); - nm_connection_clear_secrets_with_flags (for_agent, - secrets_filter_cb, - GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED)); + _nm_connection_clear_secrets_by_secret_flags (for_agent, + NM_SETTING_SECRET_FLAG_AGENT_OWNED); nm_agent_manager_save_secrets (info->agent_mgr, nm_dbus_object_get_path (NM_DBUS_OBJECT (self)), for_agent, info->subject); } + /* Reset auto retries back to default since connection was updated */ + nm_settings_connection_autoconnect_retries_reset (self); + update_complete (self, info, local); } @@ -1727,13 +1572,6 @@ settings_connection_update (NMSettingsConnection *self, UpdateInfo *info; const char *permission; - /* If the connection is read-only, that has to be changed at the source of - * the problem (ex a system settings plugin that can't write connections out) - * instead of over D-Bus. - */ - if (!check_writable (nm_settings_connection_get_connection (self), &error)) - goto error; - /* Check if the settings are valid first */ if (new_settings) { if (!g_variant_is_of_type (new_settings, NM_VARIANT_TYPE_CONNECTION)) { @@ -1858,16 +1696,13 @@ impl_settings_connection_update2 (NMDBusObject *obj, GVariantIter iter; const char *args_name; NMSettingsUpdate2Flags flags; - const NMSettingsUpdate2Flags ALL_PERSIST_MODES = NM_SETTINGS_UPDATE2_FLAG_TO_DISK - | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY - | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED - | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY; g_variant_get (parameters, "(@a{sa{sv}}u@a{sv})", &settings, &flags_u, &args); - if (NM_FLAGS_ANY (flags_u, ~((guint32) (ALL_PERSIST_MODES | - NM_SETTINGS_UPDATE2_FLAG_VOLATILE | - NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT)))) { + if (NM_FLAGS_ANY (flags_u, ~((guint32) ( _NM_SETTINGS_UPDATE2_FLAG_ALL_PERSIST_MODES + | NM_SETTINGS_UPDATE2_FLAG_VOLATILE + | NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT + | NM_SETTINGS_UPDATE2_FLAG_NO_REAPPLY)))) { error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_ARGUMENTS, "Unknown flags"); @@ -1877,11 +1712,12 @@ impl_settings_connection_update2 (NMDBusObject *obj, flags = (NMSettingsUpdate2Flags) flags_u; - if ( ( NM_FLAGS_ANY (flags, ALL_PERSIST_MODES) - && !nm_utils_is_power_of_two (flags & ALL_PERSIST_MODES)) + if ( ( NM_FLAGS_ANY (flags, _NM_SETTINGS_UPDATE2_FLAG_ALL_PERSIST_MODES) + && !nm_utils_is_power_of_two (flags & _NM_SETTINGS_UPDATE2_FLAG_ALL_PERSIST_MODES)) || ( NM_FLAGS_HAS (flags, NM_SETTINGS_UPDATE2_FLAG_VOLATILE) - && !NM_FLAGS_ANY (flags, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED | - NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY))) { + && !NM_FLAGS_ANY (flags, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY + | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED + | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY))) { error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_ARGUMENTS, "Conflicting flags"); @@ -1889,13 +1725,7 @@ impl_settings_connection_update2 (NMDBusObject *obj, return; } - if (!g_variant_is_of_type (args, G_VARIANT_TYPE ("a{sv}"))) { - error = g_error_new_literal (NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_INVALID_ARGUMENTS, - "args is of invalid type"); - g_dbus_method_invocation_take_error (invocation, error); - return; - } + nm_assert (g_variant_is_of_type (args, G_VARIANT_TYPE ("a{sv}"))); g_variant_iter_init (&iter, args); while (g_variant_iter_next (&iter, "{&sv}", &args_name, NULL)) { @@ -1921,7 +1751,6 @@ delete_auth_cb (NMSettingsConnection *self, gpointer data) { gs_unref_object NMSettingsConnection *self_keep_alive = NULL; - gs_free_error GError *local = NULL; self_keep_alive = g_object_ref (self); @@ -1932,15 +1761,11 @@ delete_auth_cb (NMSettingsConnection *self, return; } - nm_settings_connection_delete (self, &local); + nm_settings_connection_delete (self, TRUE); nm_audit_log_connection_op (NM_AUDIT_OP_CONN_DELETE, self, - !local, NULL, subject, local ? local->message : NULL); - - if (local) - g_dbus_method_invocation_return_gerror (context, local); - else - g_dbus_method_invocation_return_value (context, NULL); + TRUE, NULL, subject, NULL); + g_dbus_method_invocation_return_value (context, NULL); } static const char * @@ -1972,8 +1797,7 @@ impl_settings_connection_delete (NMDBusObject *obj, gs_unref_object NMAuthSubject *subject = NULL; GError *error = NULL; - if (!check_writable (nm_settings_connection_get_connection (self), &error)) - goto err; + nm_assert (nm_settings_connection_still_valid (self)); subject = _new_auth_subject (invocation, &error); if (!subject) @@ -2088,25 +1912,13 @@ dbus_clear_secrets_auth_cb (NMSettingsConnection *self, return; } - /* Clear secrets in connection and caches */ - nm_connection_clear_secrets (nm_settings_connection_get_connection (self)); - if (priv->system_secrets) - nm_connection_clear_secrets (priv->system_secrets); - if (priv->agent_secrets) - nm_connection_clear_secrets (priv->agent_secrets); + nm_settings_connection_clear_secrets (self, TRUE, TRUE); /* Tell agents to remove secrets for this connection */ nm_agent_manager_delete_secrets (priv->agent_mgr, nm_dbus_object_get_path (NM_DBUS_OBJECT (self)), nm_settings_connection_get_connection (self)); - nm_settings_connection_update (self, - NULL, - NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK, - NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, - "clear-secrets", - &local); - nm_audit_log_connection_op (NM_AUDIT_OP_CONN_CLEAR_SECRETS, self, !local, NULL, subject, local ? local->message : NULL); @@ -2147,40 +1959,28 @@ impl_settings_connection_clear_secrets (NMDBusObject *obj, /*****************************************************************************/ void -nm_settings_connection_added (NMSettingsConnection *self) +_nm_settings_connection_emit_dbus_signal_updated (NMSettingsConnection *self) { - NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); - - /* FIXME: we should always dispose connections that are removed - * and not reuse them, but currently plugins keep alive unmanaged - * (e.g. NM_CONTROLLED=no) connections. */ - priv->removed = FALSE; + nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self), + &interface_info_settings_connection, + &signal_info_updated, + "()"); } void -nm_settings_connection_signal_remove (NMSettingsConnection *self) +_nm_settings_connection_emit_dbus_signal_removed (NMSettingsConnection *self) { - NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); - AuthData *auth_data; - - if (priv->removed) - return; - priv->removed = TRUE; - - while ((auth_data = c_list_first_entry (&priv->auth_lst_head, AuthData, auth_lst))) - nm_auth_manager_check_authorization_cancel (auth_data->call_id); - nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self), &interface_info_settings_connection, &signal_info_removed, "()"); - g_signal_emit (self, signals[REMOVED], 0); } -gboolean -nm_settings_connection_get_unsaved (NMSettingsConnection *self) +void +_nm_settings_connection_emit_signal_updated_internal (NMSettingsConnection *self, + NMSettingsConnectionUpdateReason update_reason) { - return NM_FLAGS_HAS (nm_settings_connection_get_flags (self), NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED); + g_signal_emit (self, signals[UPDATED_INTERNAL], 0, (guint) update_reason); } /*****************************************************************************/ @@ -2202,14 +2002,6 @@ nm_settings_connection_get_flags (NMSettingsConnection *self) } NMSettingsConnectionIntFlags -nm_settings_connection_set_flags (NMSettingsConnection *self, NMSettingsConnectionIntFlags flags, gboolean set) -{ - return nm_settings_connection_set_flags_full (self, - flags, - set ? flags : NM_SETTINGS_CONNECTION_INT_FLAGS_NONE); -} - -NMSettingsConnectionIntFlags nm_settings_connection_set_flags_full (NMSettingsConnection *self, NMSettingsConnectionIntFlags mask, NMSettingsConnectionIntFlags value) @@ -2218,7 +2010,8 @@ nm_settings_connection_set_flags_full (NMSettingsConnection *self, NMSettingsConnectionIntFlags old_flags; g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NM_SETTINGS_CONNECTION_INT_FLAGS_NONE); - nm_assert (mask && !NM_FLAGS_ANY (mask, ~NM_SETTINGS_CONNECTION_INT_FLAGS_ALL)); + + nm_assert (!NM_FLAGS_ANY (mask, ~_NM_SETTINGS_CONNECTION_INT_FLAGS_ALL)); nm_assert (!NM_FLAGS_ANY (value, ~mask)); priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); @@ -2338,11 +2131,13 @@ gboolean nm_settings_connection_get_timestamp (NMSettingsConnection *self, guint64 *out_timestamp) { + NMSettingsConnectionPrivate *priv; + g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE); - if (out_timestamp) - *out_timestamp = NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->timestamp; - return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->timestamp_set; + priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); + NM_SET_OUT (out_timestamp, priv->timestamp); + return priv->timestamp_set; } /** @@ -2350,98 +2145,113 @@ nm_settings_connection_get_timestamp (NMSettingsConnection *self, * @self: the #NMSettingsConnection * @timestamp: timestamp to set into the connection and to store into * the timestamps database - * @flush_to_disk: if %TRUE, commit timestamp update to persistent storage * * Updates the connection and timestamps database with the provided timestamp. **/ void nm_settings_connection_update_timestamp (NMSettingsConnection *self, - guint64 timestamp, - gboolean flush_to_disk) + guint64 timestamp) { NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); const char *connection_uuid; - GKeyFile *timestamps_file; - char *data, *tmp; - gsize len; - GError *error = NULL; + char sbuf[60]; g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self)); - /* Update timestamp in private storage */ priv->timestamp = timestamp; priv->timestamp_set = TRUE; - if (flush_to_disk == FALSE) - return; - if (nm_config_get_configure_and_quit (nm_config_get ()) == NM_CONFIG_CONFIGURE_AND_QUIT_INITRD) + if (!priv->kf_db_timestamps) return; - /* Save timestamp to timestamps database file */ - timestamps_file = g_key_file_new (); - if (!g_key_file_load_from_file (timestamps_file, SETTINGS_TIMESTAMPS_FILE, G_KEY_FILE_KEEP_COMMENTS, &error)) { - if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) - _LOGW ("error parsing timestamps file '%s': %s", SETTINGS_TIMESTAMPS_FILE, error->message); - g_clear_error (&error); - } - connection_uuid = nm_settings_connection_get_uuid (self); - tmp = g_strdup_printf ("%" G_GUINT64_FORMAT, timestamp); - g_key_file_set_value (timestamps_file, "timestamps", connection_uuid, tmp); - g_free (tmp); - - data = g_key_file_to_data (timestamps_file, &len, &error); - if (data) { - g_file_set_contents (SETTINGS_TIMESTAMPS_FILE, data, len, &error); - g_free (data); - } - if (error) { - _LOGW ("error saving timestamp to file '%s': %s", SETTINGS_TIMESTAMPS_FILE, error->message); - g_error_free (error); + if (connection_uuid) { + nm_key_file_db_set_value (priv->kf_db_timestamps, + connection_uuid, + nm_sprintf_buf (sbuf, "%" G_GUINT64_FORMAT, timestamp)); } - g_key_file_free (timestamps_file); } -/** - * nm_settings_connection_read_and_fill_timestamp: - * @self: the #NMSettingsConnection - * - * Retrieves timestamp of the connection's last usage from database file and - * stores it into the connection private data. - **/ void -nm_settings_connection_read_and_fill_timestamp (NMSettingsConnection *self) +_nm_settings_connection_register_kf_dbs (NMSettingsConnection *self, + NMKeyFileDB *kf_db_timestamps, + NMKeyFileDB *kf_db_seen_bssids) { - NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); - gs_unref_keyfile GKeyFile *timestamps_file = NULL; - gs_free_error GError *error = NULL; - gs_free char *tmp_str = NULL; + NMSettingsConnectionPrivate *priv; const char *connection_uuid; - gint64 timestamp; g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self)); + g_return_if_fail (kf_db_timestamps); + g_return_if_fail (kf_db_seen_bssids); - timestamps_file = g_key_file_new (); - if (!g_key_file_load_from_file (timestamps_file, SETTINGS_TIMESTAMPS_FILE, G_KEY_FILE_KEEP_COMMENTS, &error)) { - _LOGD ("failed to read connection timestamp: %s", error->message); - return; - } + priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); connection_uuid = nm_settings_connection_get_uuid (self); - tmp_str = g_key_file_get_value (timestamps_file, "timestamps", connection_uuid, &error); - if (!tmp_str) { - _LOGD ("failed to read connection timestamp: %s", error->message); - return; - } - timestamp = _nm_utils_ascii_str_to_int64 (tmp_str, 10, 0, G_MAXINT64, -1); - if (timestamp < 0) { - _LOGD ("failed to read connection timestamp: %s", "invalid number"); - return; + if (priv->kf_db_timestamps != kf_db_timestamps) { + gs_free char *tmp_str = NULL; + guint64 timestamp; + + nm_key_file_db_unref (priv->kf_db_timestamps); + priv->kf_db_timestamps = nm_key_file_db_ref (kf_db_timestamps); + + tmp_str = nm_key_file_db_get_value (priv->kf_db_timestamps, connection_uuid); + + timestamp = _nm_utils_ascii_str_to_uint64 (tmp_str, 10, 0, G_MAXUINT64, G_MAXUINT64); + if (timestamp != G_MAXUINT64) { + priv->timestamp = timestamp; + priv->timestamp_set = TRUE; + _LOGT ("read timestamp %"G_GUINT64_FORMAT" from keyfile database \"%s\"", + timestamp, nm_key_file_db_get_filename (priv->kf_db_timestamps)); + } else + _LOGT ("no timestamp from keyfile database \"%s\"", + nm_key_file_db_get_filename (priv->kf_db_timestamps)); } - priv->timestamp = timestamp; - priv->timestamp_set = TRUE; + if (priv->kf_db_seen_bssids != kf_db_seen_bssids) { + gs_strfreev char **tmp_strv = NULL; + gsize i, len; + + nm_key_file_db_unref (priv->kf_db_seen_bssids); + priv->kf_db_seen_bssids = nm_key_file_db_ref (kf_db_seen_bssids); + + tmp_strv = nm_key_file_db_get_string_list (priv->kf_db_seen_bssids, connection_uuid, &len); + + nm_clear_pointer (&priv->seen_bssids, g_hash_table_unref); + + if (len > 0) { + _LOGT ("read %zu seen-bssids from keyfile database \"%s\"", + len, + nm_key_file_db_get_filename (priv->kf_db_seen_bssids)); + priv->seen_bssids = _seen_bssids_hash_new (); + for (i = len; i > 0; ) + g_hash_table_add (priv->seen_bssids, g_steal_pointer (&tmp_strv[--i])); + nm_clear_g_free (&tmp_strv); + } else { + NMSettingWireless *s_wifi; + + _LOGT ("no seen-bssids from keyfile database \"%s\"", + nm_key_file_db_get_filename (priv->kf_db_seen_bssids)); + + /* If this connection didn't have an entry in the seen-bssids database, + * maybe this is the first time we've read it in, so populate the + * seen-bssids list from the deprecated seen-bssids property of the + * wifi setting. + */ + s_wifi = nm_connection_get_setting_wireless (nm_settings_connection_get_connection (self)); + if (s_wifi) { + len = nm_setting_wireless_get_num_seen_bssids (s_wifi); + if (len > 0) { + priv->seen_bssids = _seen_bssids_hash_new (); + for (i = 0; i < len; i++) { + const char *bssid = nm_setting_wireless_get_seen_bssid (s_wifi, i); + + g_hash_table_add (priv->seen_bssids, g_strdup (bssid)); + } + } + } + } + } } /** @@ -2453,25 +2263,14 @@ nm_settings_connection_read_and_fill_timestamp (NMSettingsConnection *self) * Returns: (transfer container) list of seen BSSIDs (in the standard hex-digits-and-colons notation). * The caller is responsible for freeing the list, but not the content. **/ -char ** +const char ** nm_settings_connection_get_seen_bssids (NMSettingsConnection *self) { - NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); - GHashTableIter iter; - char **bssids, *bssid; - int i; - g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NULL); - bssids = g_new (char *, g_hash_table_size (priv->seen_bssids) + 1); - - i = 0; - g_hash_table_iter_init (&iter, priv->seen_bssids); - while (g_hash_table_iter_next (&iter, NULL, (gpointer) &bssid)) - bssids[i++] = bssid; - bssids[i] = NULL; - - return bssids; + return nm_utils_strdict_get_keys (NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->seen_bssids, + TRUE, + NULL); } /** @@ -2485,10 +2284,15 @@ gboolean nm_settings_connection_has_seen_bssid (NMSettingsConnection *self, const char *bssid) { + NMSettingsConnectionPrivate *priv; + g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE); - g_return_val_if_fail (bssid != NULL, FALSE); + g_return_val_if_fail (bssid, FALSE); + + priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); - return !!g_hash_table_lookup (NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->seen_bssids, bssid); + return priv->seen_bssids + && g_hash_table_contains (NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->seen_bssids, bssid); } /** @@ -2504,108 +2308,29 @@ nm_settings_connection_add_seen_bssid (NMSettingsConnection *self, const char *seen_bssid) { NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); + gs_free const char **strv = NULL; const char *connection_uuid; - GKeyFile *seen_bssids_file; - char *data, *bssid_str; - const char **list; - gsize len; - GError *error = NULL; - GHashTableIter iter; - guint n; g_return_if_fail (seen_bssid != NULL); - if (g_hash_table_lookup (priv->seen_bssids, seen_bssid)) - return; /* Already in the list */ - - /* Add the new BSSID; let the hash take ownership of the allocated BSSID string */ - bssid_str = g_strdup (seen_bssid); - g_hash_table_insert (priv->seen_bssids, bssid_str, bssid_str); - - /* Build up a list of all the BSSIDs in string form */ - n = 0; - list = g_malloc0 (g_hash_table_size (priv->seen_bssids) * sizeof (char *)); - g_hash_table_iter_init (&iter, priv->seen_bssids); - while (g_hash_table_iter_next (&iter, NULL, (gpointer) &bssid_str)) - list[n++] = bssid_str; - - /* Save BSSID to seen-bssids file */ - seen_bssids_file = g_key_file_new (); - g_key_file_set_list_separator (seen_bssids_file, ','); - if (!g_key_file_load_from_file (seen_bssids_file, SETTINGS_SEEN_BSSIDS_FILE, G_KEY_FILE_KEEP_COMMENTS, &error)) { - if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) { - _LOGW ("error parsing seen-bssids file '%s': %s", - SETTINGS_SEEN_BSSIDS_FILE, error->message); - } - g_clear_error (&error); - } + if (!priv->seen_bssids) + priv->seen_bssids = _seen_bssids_hash_new (); - connection_uuid = nm_settings_connection_get_uuid (self); - g_key_file_set_string_list (seen_bssids_file, "seen-bssids", connection_uuid, list, n); - g_free (list); + g_hash_table_add (priv->seen_bssids, g_strdup (seen_bssid)); - data = g_key_file_to_data (seen_bssids_file, &len, &error); - if (data) { - g_file_set_contents (SETTINGS_SEEN_BSSIDS_FILE, data, len, &error); - g_free (data); - } - g_key_file_free (seen_bssids_file); + if (!priv->kf_db_seen_bssids) + return; - if (error) { - _LOGW ("error saving seen-bssids to file '%s': %s", - SETTINGS_SEEN_BSSIDS_FILE, error->message); - g_error_free (error); - } -} + connection_uuid = nm_settings_connection_get_uuid (self); + if (!connection_uuid) + return; -/** - * nm_settings_connection_read_and_fill_seen_bssids: - * @self: the #NMSettingsConnection - * - * Retrieves seen BSSIDs of the connection from database file and stores then into the - * connection private data. - **/ -void -nm_settings_connection_read_and_fill_seen_bssids (NMSettingsConnection *self) -{ - NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); - const char *connection_uuid; - GKeyFile *seen_bssids_file; - char **tmp_strv = NULL; - gsize i, len = 0; - NMSettingWireless *s_wifi; - - /* Get seen BSSIDs from database file */ - seen_bssids_file = g_key_file_new (); - g_key_file_set_list_separator (seen_bssids_file, ','); - if (g_key_file_load_from_file (seen_bssids_file, SETTINGS_SEEN_BSSIDS_FILE, G_KEY_FILE_KEEP_COMMENTS, NULL)) { - connection_uuid = nm_settings_connection_get_uuid (self); - tmp_strv = g_key_file_get_string_list (seen_bssids_file, "seen-bssids", connection_uuid, &len, NULL); - } - g_key_file_free (seen_bssids_file); - - /* Update connection's seen-bssids */ - if (tmp_strv) { - g_hash_table_remove_all (priv->seen_bssids); - for (i = 0; i < len; i++) - g_hash_table_insert (priv->seen_bssids, tmp_strv[i], tmp_strv[i]); - g_free (tmp_strv); - } else { - /* If this connection didn't have an entry in the seen-bssids database, - * maybe this is the first time we've read it in, so populate the - * seen-bssids list from the deprecated seen-bssids property of the - * wifi setting. - */ - s_wifi = nm_connection_get_setting_wireless (nm_settings_connection_get_connection (self)); - if (s_wifi) { - len = nm_setting_wireless_get_num_seen_bssids (s_wifi); - for (i = 0; i < len; i++) { - char *bssid_dup = g_strdup (nm_setting_wireless_get_seen_bssid (s_wifi, i)); + strv = nm_utils_strdict_get_keys (priv->seen_bssids, TRUE, NULL); - g_hash_table_insert (priv->seen_bssids, bssid_dup, bssid_dup); - } - } - } + nm_key_file_db_set_string_list (priv->kf_db_seen_bssids, + connection_uuid, + strv ?: NM_PTRARRAY_EMPTY (const char *), + -1); } /*****************************************************************************/ @@ -2767,46 +2492,6 @@ nm_settings_connection_autoconnect_is_blocked (NMSettingsConnection *self) /*****************************************************************************/ -gboolean -nm_settings_connection_get_ready (NMSettingsConnection *self) -{ - return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->ready; -} - -void -nm_settings_connection_set_ready (NMSettingsConnection *self, - gboolean ready) -{ - NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); - - ready = !!ready; - if (priv->ready != ready) { - priv->ready = ready; - _notify (self, PROP_READY); - } -} - -/** - * nm_settings_connection_set_filename: - * @self: an #NMSettingsConnection - * @filename: @self's filename - * - * Called by a backend to sets the filename that @self is read - * from/written to. - */ -void -nm_settings_connection_set_filename (NMSettingsConnection *self, - const char *filename) -{ - NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); - - if (g_strcmp0 (filename, priv->filename) != 0) { - g_free (priv->filename); - priv->filename = g_strdup (filename); - _notify (self, PROP_FILENAME); - } -} - /** * nm_settings_connection_get_filename: * @self: an #NMSettingsConnection @@ -2820,9 +2505,9 @@ nm_settings_connection_set_filename (NMSettingsConnection *self, const char * nm_settings_connection_get_filename (NMSettingsConnection *self) { - NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); + g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NULL); - return priv->filename; + return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->filename; } const char * @@ -2834,7 +2519,18 @@ nm_settings_connection_get_id (NMSettingsConnection *self) const char * nm_settings_connection_get_uuid (NMSettingsConnection *self) { - return nm_connection_get_uuid (nm_settings_connection_get_connection (self)); + NMSettingsConnectionPrivate *priv; + const char *uuid; + + g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NULL); + + priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); + + uuid = nm_settings_storage_get_uuid (priv->storage); + + nm_assert (uuid && nm_streq0 (uuid, nm_connection_get_uuid (nm_settings_connection_get_connection (self)))); + + return uuid; } const char * @@ -2845,6 +2541,43 @@ nm_settings_connection_get_connection_type (NMSettingsConnection *self) /*****************************************************************************/ +void +_nm_settings_connection_cleanup_after_remove (NMSettingsConnection *self) +{ + NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); + AuthData *auth_data; + + while ((auth_data = c_list_first_entry (&priv->auth_lst_head, AuthData, auth_lst))) + nm_auth_manager_check_authorization_cancel (auth_data->call_id); +} + +/*****************************************************************************/ + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMSettingsConnection *self = NM_SETTINGS_CONNECTION (object); + + switch (prop_id) { + case PROP_UNSAVED: + g_value_set_boolean (value, nm_settings_connection_get_unsaved (self)); + break; + case PROP_FLAGS: + g_value_set_uint (value, + nm_settings_connection_get_flags (self) & _NM_SETTINGS_CONNECTION_INT_FLAGS_EXPORTED_MASK); + break; + case PROP_FILENAME: + g_value_set_string (value, nm_settings_connection_get_filename (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + static void nm_settings_connection_init (NMSettingsConnection *self) { @@ -2855,35 +2588,19 @@ nm_settings_connection_init (NMSettingsConnection *self) c_list_init (&self->_connections_lst); - priv->ready = TRUE; c_list_init (&priv->call_ids_lst_head); c_list_init (&priv->auth_lst_head); - priv->session_monitor = g_object_ref (nm_session_monitor_get ()); - priv->session_changed_id = g_signal_connect (priv->session_monitor, - NM_SESSION_MONITOR_CHANGED, - G_CALLBACK (session_changed_cb), self); - priv->agent_mgr = g_object_ref (nm_agent_manager_get ()); - - priv->seen_bssids = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL); + priv->settings = g_object_ref (nm_settings_get ()); priv->autoconnect_retries = AUTOCONNECT_RETRIES_UNSET; - - priv->connection = nm_simple_connection_new (); - - g_signal_connect (priv->connection, NM_CONNECTION_SECRETS_CLEARED, G_CALLBACK (secrets_cleared_cb), self); - g_signal_connect (priv->connection, NM_CONNECTION_CHANGED, G_CALLBACK (connection_changed_cb), self); } -static void -constructed (GObject *object) +NMSettingsConnection * +nm_settings_connection_new (void) { - NMSettingsConnection *self = NM_SETTINGS_CONNECTION (object); - - _LOGD ("constructed (%s)", G_OBJECT_TYPE_NAME (self)); - - G_OBJECT_CLASS (nm_settings_connection_parent_class)->constructed (object); + return g_object_new (NM_TYPE_SETTINGS_CONNECTION, NULL); } static void @@ -2895,6 +2612,8 @@ dispose (GObject *object) _LOGD ("disposing"); + nm_assert (!priv->default_wired_device); + nm_assert (c_list_is_empty (&self->_connections_lst)); nm_assert (c_list_is_empty (&priv->auth_lst_head)); @@ -2904,80 +2623,29 @@ dispose (GObject *object) _get_secrets_cancel (self, call_id, TRUE); } - set_visible (self, FALSE); - - if (priv->connection) { - /* Disconnect handlers. - * connection_changed_cb() has to be disconnected *before* nm_connection_clear_secrets(), - * because nm_connection_clear_secrets() emits NM_CONNECTION_CHANGED signal. - */ - g_signal_handlers_disconnect_by_func (priv->connection, G_CALLBACK (secrets_cleared_cb), self); - g_signal_handlers_disconnect_by_func (priv->connection, G_CALLBACK (connection_changed_cb), self); - - /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */ - nm_connection_clear_secrets (priv->connection); - } - - g_clear_object (&priv->system_secrets); - g_clear_object (&priv->agent_secrets); + nm_clear_pointer (&priv->system_secrets, g_variant_unref); + nm_clear_pointer (&priv->agent_secrets, g_variant_unref); g_clear_pointer (&priv->seen_bssids, g_hash_table_destroy); - nm_clear_g_signal_handler (priv->session_monitor, &priv->session_changed_id); - g_clear_object (&priv->session_monitor); - g_clear_object (&priv->agent_mgr); g_clear_object (&priv->connection); - g_clear_pointer (&priv->filename, g_free); + g_clear_pointer (&priv->kf_db_timestamps, nm_key_file_db_unref); + g_clear_pointer (&priv->kf_db_seen_bssids, nm_key_file_db_unref); G_OBJECT_CLASS (nm_settings_connection_parent_class)->dispose (object); -} -static void -get_property (GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) -{ - NMSettingsConnection *self = NM_SETTINGS_CONNECTION (object); + g_clear_object (&priv->storage); - switch (prop_id) { - case PROP_UNSAVED: - g_value_set_boolean (value, nm_settings_connection_get_unsaved (self)); - break; - case PROP_READY: - g_value_set_boolean (value, nm_settings_connection_get_ready (self)); - break; - case PROP_FLAGS: - g_value_set_uint (value, - nm_settings_connection_get_flags (self) & NM_SETTINGS_CONNECTION_INT_FLAGS_EXPORTED_MASK); - break; - case PROP_FILENAME: - g_value_set_string (value, nm_settings_connection_get_filename (self)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) -{ - NMSettingsConnection *self = NM_SETTINGS_CONNECTION (object); + nm_clear_g_free (&priv->filename); - switch (prop_id) { - case PROP_FILENAME: - /* construct-only */ - nm_settings_connection_set_filename (self, g_value_get_string (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } + g_clear_object (&priv->settings); } +/*****************************************************************************/ + static const GDBusSignalInfo signal_info_updated = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT ( "Updated", ); @@ -3087,12 +2755,8 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *klass) dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_NUMBERED (NM_DBUS_PATH_SETTINGS); dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_settings_connection); - object_class->constructed = constructed; object_class->dispose = dispose; object_class->get_property = get_property; - object_class->set_property = set_property; - - klass->supports_secrets = supports_secrets; obj_properties[PROP_UNSAVED] = g_param_spec_boolean (NM_SETTINGS_CONNECTION_UNSAVED, "", "", @@ -3100,12 +2764,6 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *klass) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_READY] = - g_param_spec_boolean (NM_SETTINGS_CONNECTION_READY, "", "", - TRUE, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS); - obj_properties[PROP_FLAGS] = g_param_spec_uint (NM_SETTINGS_CONNECTION_FLAGS, "", "", 0, G_MAXUINT32, 0, @@ -3115,29 +2773,20 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *klass) obj_properties[PROP_FILENAME] = g_param_spec_string (NM_SETTINGS_CONNECTION_FILENAME, "", "", NULL, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); - /* internal signal, with an argument (gboolean by_user). */ + /* internal signal, with an argument (NMSettingsConnectionUpdateReason update_reason) as + * guint. */ signals[UPDATED_INTERNAL] = g_signal_new (NM_SETTINGS_CONNECTION_UPDATED_INTERNAL, G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, 0, NULL, NULL, - g_cclosure_marshal_VOID__BOOLEAN, - G_TYPE_NONE, 1, G_TYPE_BOOLEAN); - - signals[REMOVED] = - g_signal_new (NM_SETTINGS_CONNECTION_REMOVED, - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_FIRST, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, 1, G_TYPE_UINT); signals[FLAGS_CHANGED] = g_signal_new (NM_SETTINGS_CONNECTION_FLAGS_CHANGED, diff --git a/src/settings/nm-settings-connection.h b/src/settings/nm-settings-connection.h index e796b716..61d5c246 100644 --- a/src/settings/nm-settings-connection.h +++ b/src/settings/nm-settings-connection.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * This program is free software; you can redistribute it and/or modify @@ -22,11 +21,116 @@ #ifndef __NETWORKMANAGER_SETTINGS_CONNECTION_H__ #define __NETWORKMANAGER_SETTINGS_CONNECTION_H__ -#include <net/ethernet.h> - #include "nm-dbus-object.h" #include "nm-connection.h" +#include "nm-settings-storage.h" + +/*****************************************************************************/ + +typedef enum { + + NM_SETTINGS_CONNECTION_ADD_REASON_NONE = 0, + + NM_SETTINGS_CONNECTION_ADD_REASON_BLOCK_AUTOCONNECT = (1u << 0), + +} NMSettingsConnectionAddReason; + +typedef enum { + + NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE = 0, + + /* with persist-mode != NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY, and + * update tries to update the profile on disk (which can always fail). + * In some cases we want to ignore such failure and proceed. For example, + * when we receive secrets from a secret-agent, we want to update the connection + * at all cost and ignore failures to write them to disk. */ + NM_SETTINGS_CONNECTION_UPDATE_REASON_IGNORE_PERSIST_FAILURE = (1u << 0), + + /* When updating the profile, force renaming the file on disk. That matters + * only for keyfile plugin. Keyfile prefers a filename based on connection.id. + * When the connection.id changes we might want to rename the file on disk + * (that is, don't overwrite the existing file, but delete it and write it + * with the new name). + * This flag forces such rename. */ + NM_SETTINGS_CONNECTION_UPDATE_REASON_FORCE_RENAME = (1u << 1), + + /* Usually, changing a profile that is currently active does not immediately + * reapply the changes. The exception are connection.zone and connection.metered + * properties. When this flag is set, then these two properties are reapplied + * right away. + * + * See also %NM_SETTINGS_UPDATE2_FLAG_NO_REAPPLY flag, to prevent partial reapply + * during Update2(). */ + NM_SETTINGS_CONNECTION_UPDATE_REASON_REAPPLY_PARTIAL = (1u << 2), + + NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_SYSTEM_SECRETS = (1u << 3), + NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS = (1u << 4), + + NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_AGENT_SECRETS = (1u << 5), + NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS = (1u << 6), + + /* if a profile was greated as default-wired connection for a device, then + * when the user modifies it via D-Bus, the profile should become persisted + * to disk and it the purpose why the profile was created should be forgotten. */ + NM_SETTINGS_CONNECTION_UPDATE_REASON_CLEAR_DEFAULT_WIRED = (1u << 7), + + NM_SETTINGS_CONNECTION_UPDATE_REASON_BLOCK_AUTOCONNECT = (1u << 8), + +} NMSettingsConnectionUpdateReason; + +typedef enum { + + /* if the profile is in-memory, update it in-memory and keep it. + * if the profile is on-disk, update it on-disk, and keep it. */ + NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP, + + /* persist to disk. If the profile is currently in-memory, remove + * it from /run. Depending on the shadowed-storage, the pre-existing + * file is reused when moving the storage. + * + * Corresponds to %NM_SETTINGS_UPDATE2_FLAG_TO_DISK. */ + NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK, + + /* Update in-memory (i.e. persist to /run). If the profile is currently on disk, + * then a reference to the profile is remembered as "shadowed-storage". + * Later, when storing again to persistant storage, the shawowed-storage is + * updated. When deleting the profile, the shadowed-storage is also deleted + * from disk. + * + * Corresponds to %NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY. */ + NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY, + + /* Update in-memory (i.e. persist to /run). This is almost like + * %NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY, except the in-memory profile + * remembers not to own the shadowed-storage ("shadowed-owned"). + * The diffrence is that when deleting the in-memory profile, the original + * profile is not deleted but instead the nmmeta tombstone remembers the + * shadowed-storage and re-used it when re-adding the profile. + * + * Corresponds to %NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED. */ + NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED, + + /* Update in-memory (i.e. persist to /run). If the profile is currently on disk, + * delete it from disk. + * + * If the profile is in-memory and has a shadowed-storage, the original profile + * will be deleted from disk. + * + * Corresponds to %NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY. */ + NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY, + + /* This only updates the connection in-memory. Note that "in-memory" above + * means to write to keyfile in /run. This mode really means to not notify the + * settings plugin about the change. This should be only used for updating + * secrets. + */ + NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST, + +} NMSettingsConnectionPersistMode; + +/*****************************************************************************/ + #define NM_TYPE_SETTINGS_CONNECTION (nm_settings_connection_get_type ()) #define NM_SETTINGS_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTINGS_CONNECTION, NMSettingsConnection)) #define NM_SETTINGS_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTINGS_CONNECTION, NMSettingsConnectionClass)) @@ -34,7 +138,6 @@ #define NM_IS_SETTINGS_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTINGS_CONNECTION)) #define NM_SETTINGS_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTINGS_CONNECTION, NMSettingsConnectionClass)) -#define NM_SETTINGS_CONNECTION_REMOVED "removed" #define NM_SETTINGS_CONNECTION_GET_SECRETS "get-secrets" #define NM_SETTINGS_CONNECTION_CANCEL_SECRETS "cancel-secrets" #define NM_SETTINGS_CONNECTION_UPDATED_INTERNAL "updated-internal" @@ -45,9 +148,6 @@ #define NM_SETTINGS_CONNECTION_FLAGS "flags" #define NM_SETTINGS_CONNECTION_FILENAME "filename" -/* Internal properties */ -#define NM_SETTINGS_CONNECTION_READY "ready" - /** * NMSettingsConnectionIntFlags: * @NM_SETTINGS_CONNECTION_INT_FLAGS_NONE: no flag set @@ -62,38 +162,37 @@ * currently active but cleanup on disconnect. * See also #NM_SETTINGS_CONNECTION_FLAG_VOLATILE. * @NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE: The connection is visible - * @NM_SETTINGS_CONNECTION_INT_FLAGS_EXPORTED_MASK: the entire enum is + * @_NM_SETTINGS_CONNECTION_INT_FLAGS_EXPORTED_MASK: the entire enum is * internal, however, parts of it is public API as #NMSettingsConnectionFlags. * This mask, are the public flags. - * @NM_SETTINGS_CONNECTION_INT_FLAGS_ALL: special mask, for all known flags + * @_NM_SETTINGS_CONNECTION_INT_FLAGS_ALL: special mask, for all known flags * * #NMSettingsConnection flags. **/ -typedef enum { +typedef enum _NMSettingsConnectionIntFlags { NM_SETTINGS_CONNECTION_INT_FLAGS_NONE = 0, NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED = NM_SETTINGS_CONNECTION_FLAG_UNSAVED, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED = NM_SETTINGS_CONNECTION_FLAG_NM_GENERATED, NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE = NM_SETTINGS_CONNECTION_FLAG_VOLATILE, - NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE = (1LL << 3), + NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE = 0x08, - __NM_SETTINGS_CONNECTION_INT_FLAGS_LAST, + _NM_SETTINGS_CONNECTION_INT_FLAGS_LAST, - NM_SETTINGS_CONNECTION_INT_FLAGS_EXPORTED_MASK = 0 + _NM_SETTINGS_CONNECTION_INT_FLAGS_EXPORTED_MASK = 0 | NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED | NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE | 0, - NM_SETTINGS_CONNECTION_INT_FLAGS_ALL = ((__NM_SETTINGS_CONNECTION_INT_FLAGS_LAST - 1) << 1) - 1, -} NMSettingsConnectionIntFlags; + _NM_SETTINGS_CONNECTION_INT_FLAGS_PERSISTENT_MASK = 0 + | NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED + | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE + | 0, -typedef enum { - NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE = 0, - NM_SETTINGS_CONNECTION_COMMIT_REASON_USER_ACTION = (1LL << 0), - NM_SETTINGS_CONNECTION_COMMIT_REASON_ID_CHANGED = (1LL << 1), -} NMSettingsConnectionCommitReason; + _NM_SETTINGS_CONNECTION_INT_FLAGS_ALL = ((_NM_SETTINGS_CONNECTION_INT_FLAGS_LAST - 1) << 1) - 1, +} NMSettingsConnectionIntFlags; typedef enum { NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NONE = 0, @@ -107,7 +206,6 @@ typedef enum { | NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NO_SECRETS), } NMSettingsAutoconnectBlockedReason; -struct _NMSettingsConnectionCallId; typedef struct _NMSettingsConnectionCallId NMSettingsConnectionCallId; typedef struct _NMSettingsConnectionClass NMSettingsConnectionClass; @@ -116,30 +214,29 @@ struct _NMSettingsConnectionPrivate; struct _NMSettingsConnection { NMDBusObject parent; - struct _NMSettingsConnectionPrivate *_priv; CList _connections_lst; + struct _NMSettingsConnectionPrivate *_priv; }; -struct _NMSettingsConnectionClass { - NMDBusObjectClass parent; +GType nm_settings_connection_get_type (void); - gboolean (*commit_changes) (NMSettingsConnection *self, - NMConnection *new_connection, - NMSettingsConnectionCommitReason commit_reason, - NMConnection **out_reread_connection, - char **out_logmsg_change, - GError **error); +NMSettingsConnection *nm_settings_connection_new (void); - gboolean (*delete) (NMSettingsConnection *self, - GError **error); +NMConnection *nm_settings_connection_get_connection (NMSettingsConnection *self); - gboolean (*supports_secrets) (NMSettingsConnection *self, - const char *setting_name); -}; +void _nm_settings_connection_set_connection (NMSettingsConnection *self, + NMConnection *new_connection, + NMConnection **out_old_connection, + NMSettingsConnectionUpdateReason update_reason); -GType nm_settings_connection_get_type (void); +NMSettingsStorage *nm_settings_connection_get_storage (NMSettingsConnection *self); -NMConnection *nm_settings_connection_get_connection (NMSettingsConnection *self); +void _nm_settings_connection_set_storage (NMSettingsConnection *self, + NMSettingsStorage *storage); + +gboolean nm_settings_connection_still_valid (NMSettingsConnection *self); + +const char *nm_settings_connection_get_filename (NMSettingsConnection *self); guint64 nm_settings_connection_get_last_secret_agent_version_id (NMSettingsConnection *self); @@ -147,34 +244,18 @@ gboolean nm_settings_connection_has_unmodified_applied_connection (NMSettingsCon NMConnection *applied_connection, NMSettingCompareFlags compare_flage); -typedef enum { - NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP, - - /* like KEEP, but always clears the UNSAVED flag */ - NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP_SAVED, - NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK, - - /* unsaved, only sets the unsaved flag, but it doesn't touch - * the NM_GENERATED nor VOLATILE flag. */ - NM_SETTINGS_CONNECTION_PERSIST_MODE_UNSAVED, - - NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY, - NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED, - NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY, - NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_DETACHED, - NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_ONLY, -} NMSettingsConnectionPersistMode; - -gboolean nm_settings_connection_update (NMSettingsConnection *self, - NMConnection *new_connection, - NMSettingsConnectionPersistMode persist_mode, - NMSettingsConnectionCommitReason commit_reason, - const char *log_diff_name, - GError **error); - -gboolean nm_settings_connection_delete (NMSettingsConnection *self, +gboolean nm_settings_connection_update (NMSettingsConnection *self, + NMConnection *new_connection, + NMSettingsConnectionPersistMode persist_mode, + NMSettingsConnectionIntFlags sett_flags, + NMSettingsConnectionIntFlags sett_mask, + NMSettingsConnectionUpdateReason update_reason, + const char *log_context_name, GError **error); +void nm_settings_connection_delete (NMSettingsConnection *self, + gboolean allow_add_to_no_auto_default); + typedef void (*NMSettingsConnectionSecretsFunc) (NMSettingsConnection *self, NMSettingsConnectionCallId *call_id, const char *agent_username, @@ -200,36 +281,62 @@ NMSettingsConnectionCallId *nm_settings_connection_get_secrets (NMSettingsConnec void nm_settings_connection_cancel_secrets (NMSettingsConnection *self, NMSettingsConnectionCallId *call_id); -void nm_settings_connection_recheck_visibility (NMSettingsConnection *self); +void nm_settings_connection_clear_secrets (NMSettingsConnection *self, + gboolean clear_cached_system_secrets, + gboolean persist); + +gboolean nm_settings_connection_check_visibility (NMSettingsConnection *self, + NMSessionMonitor *session_monitor); gboolean nm_settings_connection_check_permission (NMSettingsConnection *self, const char *permission); -void nm_settings_connection_added (NMSettingsConnection *self); +/*****************************************************************************/ -void nm_settings_connection_signal_remove (NMSettingsConnection *self); +NMDevice *nm_settings_connection_default_wired_get_device (NMSettingsConnection *self); +void nm_settings_connection_default_wired_set_device (NMSettingsConnection *self, + NMDevice *device); -gboolean nm_settings_connection_get_unsaved (NMSettingsConnection *self); +/*****************************************************************************/ NMSettingsConnectionIntFlags nm_settings_connection_get_flags (NMSettingsConnection *self); -NMSettingsConnectionIntFlags nm_settings_connection_set_flags (NMSettingsConnection *self, NMSettingsConnectionIntFlags flags, gboolean set); + +static inline gboolean +nm_settings_connection_get_unsaved (NMSettingsConnection *self) +{ + return NM_FLAGS_HAS (nm_settings_connection_get_flags (self), NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED); +} + NMSettingsConnectionIntFlags nm_settings_connection_set_flags_full (NMSettingsConnection *self, NMSettingsConnectionIntFlags mask, NMSettingsConnectionIntFlags value); +static inline NMSettingsConnectionIntFlags +nm_settings_connection_set_flags (NMSettingsConnection *self, NMSettingsConnectionIntFlags flags, gboolean set) +{ + return nm_settings_connection_set_flags_full (self, + flags, + set ? flags : NM_SETTINGS_CONNECTION_INT_FLAGS_NONE); +} + +/*****************************************************************************/ + int nm_settings_connection_cmp_timestamp (NMSettingsConnection *ac, NMSettingsConnection *ab); int nm_settings_connection_cmp_timestamp_p_with_data (gconstpointer pa, gconstpointer pb, gpointer user_data); int nm_settings_connection_cmp_autoconnect_priority (NMSettingsConnection *a, NMSettingsConnection *b); int nm_settings_connection_cmp_autoconnect_priority_p_with_data (gconstpointer pa, gconstpointer pb, gpointer user_data); +struct _NMKeyFileDB; + +void _nm_settings_connection_register_kf_dbs (NMSettingsConnection *self, + struct _NMKeyFileDB *kf_db_timestamps, + struct _NMKeyFileDB *kf_db_seen_bssids); + gboolean nm_settings_connection_get_timestamp (NMSettingsConnection *self, guint64 *out_timestamp); void nm_settings_connection_update_timestamp (NMSettingsConnection *self, - guint64 timestamp, - gboolean flush_to_disk); - -void nm_settings_connection_read_and_fill_timestamp (NMSettingsConnection *self); + guint64 timestamp); -char **nm_settings_connection_get_seen_bssids (NMSettingsConnection *self); +const char **nm_settings_connection_get_seen_bssids (NMSettingsConnection *self); gboolean nm_settings_connection_has_seen_bssid (NMSettingsConnection *self, const char *bssid); @@ -237,8 +344,6 @@ gboolean nm_settings_connection_has_seen_bssid (NMSettingsConnection *self, void nm_settings_connection_add_seen_bssid (NMSettingsConnection *self, const char *seen_bssid); -void nm_settings_connection_read_and_fill_seen_bssids (NMSettingsConnection *self); - int nm_settings_connection_autoconnect_retries_get (NMSettingsConnection *self); void nm_settings_connection_autoconnect_retries_set (NMSettingsConnection *self, int retries); @@ -262,14 +367,6 @@ nm_settings_connection_autoconnect_blocked_reason_set (NMSettingsConnection *sel gboolean nm_settings_connection_autoconnect_is_blocked (NMSettingsConnection *self); -gboolean nm_settings_connection_get_ready (NMSettingsConnection *self); -void nm_settings_connection_set_ready (NMSettingsConnection *self, - gboolean ready); - -void nm_settings_connection_set_filename (NMSettingsConnection *self, - const char *filename); -const char *nm_settings_connection_get_filename (NMSettingsConnection *self); - const char *nm_settings_connection_get_id (NMSettingsConnection *connection); const char *nm_settings_connection_get_uuid (NMSettingsConnection *connection); const char *nm_settings_connection_get_connection_type (NMSettingsConnection *connection); @@ -279,4 +376,14 @@ const char *nm_settings_connection_get_connection_type (NMSettingsConnection *co NMConnection **nm_settings_connections_array_to_connections (NMSettingsConnection *const*connections, gssize n_connections); +/*****************************************************************************/ + +void _nm_settings_connection_emit_dbus_signal_updated (NMSettingsConnection *self); +void _nm_settings_connection_emit_dbus_signal_removed (NMSettingsConnection *self); + +void _nm_settings_connection_emit_signal_updated_internal (NMSettingsConnection *self, + NMSettingsConnectionUpdateReason update_reason); + +void _nm_settings_connection_cleanup_after_remove (NMSettingsConnection *self); + #endif /* __NETWORKMANAGER_SETTINGS_CONNECTION_H__ */ diff --git a/src/settings/nm-settings-plugin.c b/src/settings/nm-settings-plugin.c index e7275631..09010931 100644 --- a/src/settings/nm-settings-plugin.c +++ b/src/settings/nm-settings-plugin.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * This program is free software; you can redistribute it and/or modify @@ -23,12 +22,14 @@ #include "nm-settings-plugin.h" +#include "nm-utils.h" +#include "nm-core-internal.h" + #include "nm-settings-connection.h" /*****************************************************************************/ enum { - CONNECTION_ADDED, UNMANAGED_SPECS_CHANGED, UNRECOGNIZED_SPECS_CHANGED, @@ -41,114 +42,228 @@ G_DEFINE_TYPE (NMSettingsPlugin, nm_settings_plugin, G_TYPE_OBJECT) /*****************************************************************************/ -void -nm_settings_plugin_initialize (NMSettingsPlugin *self) +int +nm_settings_plugin_cmp_by_priority (const NMSettingsPlugin *a, + const NMSettingsPlugin *b, + const GSList *plugin_list) { - g_return_if_fail (NM_IS_SETTINGS_PLUGIN (self)); + nm_assert (NM_IS_SETTINGS_PLUGIN (a)); + nm_assert (NM_IS_SETTINGS_PLUGIN (b)); - if (NM_SETTINGS_PLUGIN_GET_CLASS (self)->initialize) - NM_SETTINGS_PLUGIN_GET_CLASS (self)->initialize (self); + if (a != b) { + int idx_a = g_slist_index ((GSList *) plugin_list, a); + int idx_b = g_slist_index ((GSList *) plugin_list, b); + + /* the plugins must be found in the list. */ + nm_assert (idx_a >= 0); + nm_assert (idx_b >= 0); + + /* plugins that appear first in @plugin_list have higher priority. + * That means: smaller index -> higher priority. Reverse sort. */ + NM_CMP_DIRECT (idx_b, idx_a); + } + + return 0; } +/*****************************************************************************/ + GSList * -nm_settings_plugin_get_connections (NMSettingsPlugin *self) +nm_settings_plugin_get_unmanaged_specs (NMSettingsPlugin *self) { + NMSettingsPluginClass *klass; + g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), NULL); - if (NM_SETTINGS_PLUGIN_GET_CLASS (self)->get_connections) - return NM_SETTINGS_PLUGIN_GET_CLASS (self)->get_connections (self); - return NULL; + klass = NM_SETTINGS_PLUGIN_GET_CLASS (self); + if (!klass->get_unmanaged_specs) + return NULL; + return klass->get_unmanaged_specs (self); } -gboolean -nm_settings_plugin_load_connection (NMSettingsPlugin *self, - const char *filename) +GSList * +nm_settings_plugin_get_unrecognized_specs (NMSettingsPlugin *self) { - g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), FALSE); + NMSettingsPluginClass *klass; - if (NM_SETTINGS_PLUGIN_GET_CLASS (self)->load_connection) - return NM_SETTINGS_PLUGIN_GET_CLASS (self)->load_connection (self, filename); - return FALSE; + g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), NULL); + + klass = NM_SETTINGS_PLUGIN_GET_CLASS (self); + if (!klass->get_unrecognized_specs) + return NULL; + return klass->get_unrecognized_specs (self); } void -nm_settings_plugin_reload_connections (NMSettingsPlugin *self) +nm_settings_plugin_reload_connections (NMSettingsPlugin *self, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data) { + NMSettingsPluginClass *klass; + g_return_if_fail (NM_IS_SETTINGS_PLUGIN (self)); + g_return_if_fail (callback); - if (NM_SETTINGS_PLUGIN_GET_CLASS (self)->reload_connections) - NM_SETTINGS_PLUGIN_GET_CLASS (self)->reload_connections (self); + klass = NM_SETTINGS_PLUGIN_GET_CLASS (self); + if (klass->reload_connections) + klass->reload_connections (self, callback, user_data); } -GSList * -nm_settings_plugin_get_unmanaged_specs (NMSettingsPlugin *self) +NMSettingsPluginConnectionLoadEntry * +nm_settings_plugin_create_connection_load_entries (const char *const*filenames, + gsize *out_len) { - g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), NULL); + NMSettingsPluginConnectionLoadEntry *entries; + gsize len; + gsize i; + + len = NM_PTRARRAY_LEN (filenames); + if (len == 0) { + *out_len = 0; + return NULL; + } + + entries = g_new (NMSettingsPluginConnectionLoadEntry, len); + for (i = 0; i < len; i++) { + entries[i] = (NMSettingsPluginConnectionLoadEntry) { + .filename = filenames[i], + .error = NULL, + .handled = FALSE, + }; + } - if (NM_SETTINGS_PLUGIN_GET_CLASS (self)->get_unmanaged_specs) - return NM_SETTINGS_PLUGIN_GET_CLASS (self)->get_unmanaged_specs (self); - return NULL; + *out_len = len; + return entries; } -GSList * -nm_settings_plugin_get_unrecognized_specs (NMSettingsPlugin *self) +void +nm_settings_plugin_load_connections (NMSettingsPlugin *self, + NMSettingsPluginConnectionLoadEntry *entries, + gsize n_entries, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data) { - g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), NULL); + NMSettingsPluginClass *klass; - if (NM_SETTINGS_PLUGIN_GET_CLASS (self)->get_unrecognized_specs) - return NM_SETTINGS_PLUGIN_GET_CLASS (self)->get_unrecognized_specs (self); - return NULL; + g_return_if_fail (NM_IS_SETTINGS_PLUGIN (self)); + + klass = NM_SETTINGS_PLUGIN_GET_CLASS (self); + if (klass->load_connections) + klass->load_connections (self, entries, n_entries, callback, user_data); } -/** - * nm_settings_plugin_add_connection: - * @self: the #NMSettingsPlugin - * @connection: the source connection to create a plugin-specific - * #NMSettingsConnection from - * @save_to_disk: %TRUE to save the connection to disk immediately, %FALSE to - * not save to disk - * @error: on return, a location to store any errors that may occur - * - * Creates a new #NMSettingsConnection for the given source @connection. If the - * plugin cannot handle the given connection type, it should return %NULL and - * set @error. The plugin owns the returned object and the caller must reference - * the object if it wishes to continue using it. - * - * Returns: the new #NMSettingsConnection or %NULL - */ -NMSettingsConnection * +void +nm_settings_plugin_load_connections_done (NMSettingsPlugin *self) +{ + NMSettingsPluginClass *klass; + + g_return_if_fail (NM_IS_SETTINGS_PLUGIN (self)); + + klass = NM_SETTINGS_PLUGIN_GET_CLASS (self); + if (klass->load_connections_done) + klass->load_connections_done (self); +} + +gboolean nm_settings_plugin_add_connection (NMSettingsPlugin *self, NMConnection *connection, - gboolean save_to_disk, + NMSettingsStorage **out_storage, + NMConnection **out_connection, GError **error) { NMSettingsPluginClass *klass; - g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), NULL); - g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), FALSE); + g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); + +#if NM_MORE_ASSERTS > 5 + nm_assert (nm_connection_verify (connection, NULL)); +#endif + + NM_SET_OUT (out_storage, NULL); + NM_SET_OUT (out_connection, NULL); klass = NM_SETTINGS_PLUGIN_GET_CLASS (self); if (!klass->add_connection) { g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_NOT_SUPPORTED, - "Plugin does not support adding connections"); - return NULL; + "settings plugin does not support adding connections"); + return FALSE; } - - return klass->add_connection (self, connection, save_to_disk, error); + return klass->add_connection (self, + connection, + out_storage, + out_connection, + error); } -/*****************************************************************************/ +gboolean +nm_settings_plugin_update_connection (NMSettingsPlugin *self, + NMSettingsStorage *storage, + NMConnection *connection, + NMSettingsStorage **out_storage, + NMConnection **out_connection, + GError **error) +{ + NMSettingsPluginClass *klass = NULL; -void -_nm_settings_plugin_emit_signal_connection_added (NMSettingsPlugin *self, - NMSettingsConnection *sett_conn) + g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), FALSE); + g_return_val_if_fail (NM_IS_SETTINGS_STORAGE (storage), FALSE); + g_return_val_if_fail (nm_settings_storage_get_plugin (storage) == self, FALSE); + g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); + +#if NM_MORE_ASSERTS > 5 + nm_assert (nm_connection_verify (connection, NULL)); + nm_assert (nm_streq (nm_connection_get_uuid (connection), nm_settings_storage_get_uuid (storage))); +#endif + + klass = NM_SETTINGS_PLUGIN_GET_CLASS (self); + + NM_SET_OUT (out_storage, NULL); + NM_SET_OUT (out_connection, NULL); + + if (!klass->update_connection) { + g_set_error (error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_NOT_SUPPORTED, + "settings plugin does not support modifying connections"); + return FALSE; + } + return klass->update_connection (self, + storage, + connection, + out_storage, + out_connection, + error); +} + +gboolean +nm_settings_plugin_delete_connection (NMSettingsPlugin *self, + NMSettingsStorage *storage, + GError **error) { - nm_assert (NM_IS_SETTINGS_PLUGIN (self)); - nm_assert (NM_IS_SETTINGS_CONNECTION (sett_conn)); + NMSettingsPluginClass *klass = NULL; + + g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), FALSE); + g_return_val_if_fail (NM_IS_SETTINGS_STORAGE (storage), FALSE); + g_return_val_if_fail (nm_settings_storage_get_plugin (storage) == self, FALSE); + + klass = NM_SETTINGS_PLUGIN_GET_CLASS (self); - g_signal_emit (self, signals[CONNECTION_ADDED], 0, sett_conn); + if (!klass->delete_connection) { + g_set_error (error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_NOT_SUPPORTED, + "settings plugin does not support deleting connections"); + return FALSE; + } + + return klass->delete_connection (self, + storage, + error); } +/*****************************************************************************/ + void _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NMSettingsPlugin *self) { @@ -177,15 +292,6 @@ nm_settings_plugin_class_init (NMSettingsPluginClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); - signals[CONNECTION_ADDED] = - g_signal_new (NM_SETTINGS_PLUGIN_CONNECTION_ADDED, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, - G_TYPE_NONE, 1, - NM_TYPE_SETTINGS_CONNECTION); - signals[UNMANAGED_SPECS_CHANGED] = g_signal_new (NM_SETTINGS_PLUGIN_UNMANAGED_SPECS_CHANGED, G_OBJECT_CLASS_TYPE (object_class), diff --git a/src/settings/nm-settings-plugin.h b/src/settings/nm-settings-plugin.h index fdd48f2b..4df3472e 100644 --- a/src/settings/nm-settings-plugin.h +++ b/src/settings/nm-settings-plugin.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * This program is free software; you can redistribute it and/or modify @@ -24,6 +23,21 @@ #include "nm-connection.h" +#include "nm-settings-storage.h" + +typedef struct _NMSettingsPlugin NMSettingsPlugin; + +typedef void (*NMSettingsPluginConnectionLoadCallback) (NMSettingsPlugin *self, + NMSettingsStorage *storage, + NMConnection *connection, + gpointer user_data); + +typedef struct { + const char *filename; + GError *error; + bool handled:1; +} NMSettingsPluginConnectionLoadEntry; + #define NM_TYPE_SETTINGS_PLUGIN (nm_settings_plugin_get_type ()) #define NM_SETTINGS_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTINGS_PLUGIN, NMSettingsPlugin)) #define NM_SETTINGS_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTINGS_PLUGIN, NMSettingsPluginClass)) @@ -33,35 +47,14 @@ #define NM_SETTINGS_PLUGIN_UNMANAGED_SPECS_CHANGED "unmanaged-specs-changed" #define NM_SETTINGS_PLUGIN_UNRECOGNIZED_SPECS_CHANGED "unrecognized-specs-changed" -#define NM_SETTINGS_PLUGIN_CONNECTION_ADDED "connection-added" -typedef struct { +struct _NMSettingsPlugin { GObject parent; -} NMSettingsPlugin; +}; typedef struct { GObjectClass parent; - /* Called when the plugin is loaded to initialize it */ - void (*initialize) (NMSettingsPlugin *plugin); - - /* Returns a GSList of NMSettingsConnection objects that represent - * connections the plugin knows about. The returned list is freed by the - * system settings service. - */ - GSList * (*get_connections) (NMSettingsPlugin *plugin); - - /* Requests that the plugin load/reload a single connection, if it - * recognizes the filename. Returns success or failure. - */ - gboolean (*load_connection) (NMSettingsPlugin *plugin, - const char *filename); - - /* Requests that the plugin reload all connection files from disk, - * and emit signals reflecting new, changed, and removed connections. - */ - void (*reload_connections) (NMSettingsPlugin *plugin); - /* * Return a string list of specifications of devices which NetworkManager * should not manage. Returned list will be freed by the system settings @@ -71,7 +64,7 @@ typedef struct { * Each string in the list must be in one of the formats recognized by * nm_device_spec_match_list(). */ - GSList * (*get_unmanaged_specs) (NMSettingsPlugin *plugin); + GSList * (*get_unmanaged_specs) (NMSettingsPlugin *self); /* * Return a string list of specifications of devices for which at least @@ -83,51 +76,137 @@ typedef struct { * Each string in the list must be in one of the formats recognized by * nm_device_spec_match_list(). */ - GSList * (*get_unrecognized_specs) (NMSettingsPlugin *plugin); + GSList * (*get_unrecognized_specs) (NMSettingsPlugin *self); - /* - * Initialize the plugin-specific connection and return a new - * NMSettingsConnection subclass that contains the same settings as the - * original connection. The connection should only be saved to backing - * storage if @save_to_disk is TRUE. The returned object is owned by the - * plugin and must be referenced by the owner if necessary. + /* Requests that the plugin load/reload a set of filenames. */ - NMSettingsConnection * (*add_connection) (NMSettingsPlugin *plugin, - NMConnection *connection, - gboolean save_to_disk, - GError **error); + void (*load_connections) (NMSettingsPlugin *self, + NMSettingsPluginConnectionLoadEntry *entries, + gsize n_entries, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data); + + /* Requests that the plugin reload all connection files from disk, + * and emit signals reflecting new, changed, and removed connections. + */ + void (*reload_connections) (NMSettingsPlugin *self, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data); + + void (*load_connections_done) (NMSettingsPlugin *self); + + gboolean (*add_connection) (NMSettingsPlugin *self, + NMConnection *connection, + NMSettingsStorage **out_storage, + NMConnection **out_connection, + GError **error); + + gboolean (*update_connection) (NMSettingsPlugin *self, + NMSettingsStorage *storage, + NMConnection *connection, + NMSettingsStorage **out_storage, + NMConnection **out_connection, + GError **error); + + gboolean (*delete_connection) (NMSettingsPlugin *self, + NMSettingsStorage *storage, + GError **error); + + const char *plugin_name; + } NMSettingsPluginClass; +/*****************************************************************************/ + GType nm_settings_plugin_get_type (void); +/*****************************************************************************/ + +#define NM_SETTINGS_STORAGE_PRINT_FMT \ + NM_HASH_OBFUSCATE_PTR_FMT"/%s" + +#define NM_SETTINGS_STORAGE_PRINT_ARG(storage) \ + NM_HASH_OBFUSCATE_PTR (storage), \ + nm_settings_plugin_get_plugin_name (nm_settings_storage_get_plugin (storage)) + +static inline const char * +nm_settings_plugin_get_plugin_name (NMSettingsPlugin *self) +{ + NMSettingsPluginClass *klass; + + nm_assert (NM_SETTINGS_PLUGIN (self)); + + klass = NM_SETTINGS_PLUGIN_GET_CLASS (self); + + nm_assert (klass && klass->plugin_name && strlen (klass->plugin_name) > 0); + + return klass->plugin_name; +} + +/*****************************************************************************/ + +GSList *nm_settings_plugin_get_unmanaged_specs (NMSettingsPlugin *self); +GSList *nm_settings_plugin_get_unrecognized_specs (NMSettingsPlugin *self); + +void nm_settings_plugin_reload_connections (NMSettingsPlugin *self, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data); + +NMSettingsPluginConnectionLoadEntry *nm_settings_plugin_create_connection_load_entries (const char *const*filenames, + gsize *out_len); + +void nm_settings_plugin_load_connections (NMSettingsPlugin *self, + NMSettingsPluginConnectionLoadEntry *entries, + gsize n_entries, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data); + +void nm_settings_plugin_load_connections_done (NMSettingsPlugin *self); + +gboolean nm_settings_plugin_add_connection (NMSettingsPlugin *self, + NMConnection *connection, + NMSettingsStorage **out_storage, + NMConnection **out_connection, + GError **error); + +gboolean nm_settings_plugin_update_connection (NMSettingsPlugin *self, + NMSettingsStorage *storage, + NMConnection *connection, + NMSettingsStorage **out_storage, + NMConnection **out_connection, + GError **error); + +gboolean nm_settings_plugin_delete_connection (NMSettingsPlugin *self, + NMSettingsStorage *storage, + GError **error); + +/*****************************************************************************/ + typedef NMSettingsPlugin *(*NMSettingsPluginFactoryFunc) (void); -/* Plugin's factory function that returns a #NMSettingsPlugin */ NMSettingsPlugin *nm_settings_plugin_factory (void); -void nm_settings_plugin_initialize (NMSettingsPlugin *config); - -GSList *nm_settings_plugin_get_connections (NMSettingsPlugin *plugin); +/***************************************************************************** + * Internal API + *****************************************************************************/ -gboolean nm_settings_plugin_load_connection (NMSettingsPlugin *plugin, - const char *filename); -void nm_settings_plugin_reload_connections (NMSettingsPlugin *plugin); +void _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NMSettingsPlugin *self); -GSList *nm_settings_plugin_get_unmanaged_specs (NMSettingsPlugin *plugin); -GSList *nm_settings_plugin_get_unrecognized_specs (NMSettingsPlugin *plugin); +void _nm_settings_plugin_emit_signal_unrecognized_specs_changed (NMSettingsPlugin *self); -NMSettingsConnection *nm_settings_plugin_add_connection (NMSettingsPlugin *plugin, - NMConnection *connection, - gboolean save_to_disk, - GError **error); +/*****************************************************************************/ -/* internal API */ +int nm_settings_plugin_cmp_by_priority (const NMSettingsPlugin *a, + const NMSettingsPlugin *b, + const GSList *plugin_list); -void _nm_settings_plugin_emit_signal_connection_added (NMSettingsPlugin *plugin, - NMSettingsConnection *sett_conn); +/*****************************************************************************/ -void _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NMSettingsPlugin *plugin); +/* forward declare this function from NMSettings. It's used by the ifcfg-rh plugin, + * but that shouldn't include all "nm-settings.h" header. */ +NMSettings *nm_settings_get (void); -void _nm_settings_plugin_emit_signal_unrecognized_specs_changed (NMSettingsPlugin *plugin); +const char *nm_settings_get_dbus_path_for_uuid (NMSettings *self, + const char *uuid); #endif /* __NM_SETTINGS_PLUGIN_H__ */ diff --git a/src/settings/nm-settings-storage.c b/src/settings/nm-settings-storage.c new file mode 100644 index 00000000..935b5b48 --- /dev/null +++ b/src/settings/nm-settings-storage.c @@ -0,0 +1,178 @@ +/* + * 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-settings-storage.h" + +#include "nm-utils.h" +#include "nm-settings-plugin.h" + +#include "settings/plugins/keyfile/nms-keyfile-storage.h" + +/*****************************************************************************/ + +int +nm_settings_storage_cmp (NMSettingsStorage *a, + NMSettingsStorage *b, + const GSList *plugin_list) +{ + NMSettingsStorageClass *klass; + + /* Sort by priority. + * + * If a > b (by priority), we return a positive number (as one + * would expect by a cmp() function). */ + + nm_assert (NM_IS_SETTINGS_STORAGE (a)); + nm_assert (NM_IS_SETTINGS_STORAGE (b)); + nm_assert (a != b); + nm_assert (nm_streq (nm_settings_storage_get_uuid (a), nm_settings_storage_get_uuid (b))); + + /* in-memory has always higher priority */ + NM_CMP_DIRECT (nm_settings_storage_is_keyfile_run (a), + nm_settings_storage_is_keyfile_run (b)); + + NM_CMP_RETURN (nm_settings_plugin_cmp_by_priority (nm_settings_storage_get_plugin (a), + nm_settings_storage_get_plugin (b), + plugin_list)); + + klass = NM_SETTINGS_STORAGE_GET_CLASS (a); + if (klass != NM_SETTINGS_STORAGE_GET_CLASS (b)) { + /* one plugin must return storages of the same type. Otherwise, it's + * unclear how cmp_fcn() should compare them. */ + nm_assert_not_reached (); + return 0; + } + + if (klass->cmp_fcn) + NM_CMP_RETURN (klass->cmp_fcn (a, b)); + + return 0; +} + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE_BASE ( + PROP_PLUGIN, + PROP_UUID, + PROP_FILENAME, +); + +G_DEFINE_TYPE (NMSettingsStorage, nm_settings_storage, G_TYPE_OBJECT) + +/*****************************************************************************/ + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSettingsStorage *self = NM_SETTINGS_STORAGE (object); + + switch (prop_id) { + case PROP_PLUGIN: + /* construct-only */ + self->_plugin = g_object_ref (g_value_get_object (value)); + nm_assert (NM_IS_SETTINGS_PLUGIN (self->_plugin)); + break; + case PROP_UUID: + /* construct-only */ + self->_uuid = g_value_dup_string (value); + nm_assert (!self->_uuid || nm_utils_is_uuid (self->_uuid)); + break; + case PROP_FILENAME: + /* construct-only */ + self->_filename = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + +static void +nm_settings_storage_init (NMSettingsStorage *self) +{ + c_list_init (&self->_storage_lst); + c_list_init (&self->_storage_by_uuid_lst); +} + +NMSettingsStorage * +nm_settings_storage_new (NMSettingsPlugin *plugin, + const char *uuid, + const char *filename) +{ + nm_assert (NM_IS_SETTINGS_PLUGIN (plugin)); + nm_assert (nm_utils_is_uuid (uuid)); + + return g_object_new (NM_TYPE_SETTINGS_STORAGE, + NM_SETTINGS_STORAGE_PLUGIN, plugin, + NM_SETTINGS_STORAGE_UUID, uuid, + NM_SETTINGS_STORAGE_FILENAME, filename, + NULL); +} + +static void +finalize (GObject *object) +{ + NMSettingsStorage *self = NM_SETTINGS_STORAGE (object); + + c_list_unlink_stale (&self->_storage_lst); + c_list_unlink_stale (&self->_storage_by_uuid_lst); + + g_object_unref (self->_plugin); + g_free (self->_uuid); + g_free (self->_filename); + + G_OBJECT_CLASS (nm_settings_storage_parent_class)->finalize (object); +} + +static void +nm_settings_storage_class_init (NMSettingsStorageClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->set_property = set_property; + object_class->finalize = finalize; + + obj_properties[PROP_PLUGIN] = + g_param_spec_object (NM_SETTINGS_STORAGE_PLUGIN, "", "", + NM_TYPE_SETTINGS_PLUGIN, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_UUID] = + g_param_spec_string (NM_SETTINGS_STORAGE_UUID, "", "", + NULL, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_FILENAME] = + g_param_spec_string (NM_SETTINGS_STORAGE_FILENAME, "", "", + NULL, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); +} diff --git a/src/settings/nm-settings-storage.h b/src/settings/nm-settings-storage.h new file mode 100644 index 00000000..c43145b5 --- /dev/null +++ b/src/settings/nm-settings-storage.h @@ -0,0 +1,122 @@ +/* + * 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 __NM_SETTINGS_STORAGE_H__ +#define __NM_SETTINGS_STORAGE_H__ + +/*****************************************************************************/ + +#include "c-list/src/c-list.h" + +#define NM_TYPE_SETTINGS_STORAGE (nm_settings_storage_get_type ()) +#define NM_SETTINGS_STORAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTINGS_STORAGE, NMSettingsStorage)) +#define NM_SETTINGS_STORAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTINGS_STORAGE, NMSettingsStorageClass)) +#define NM_IS_SETTINGS_STORAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTINGS_STORAGE)) +#define NM_IS_SETTINGS_STORAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTINGS_STORAGE)) +#define NM_SETTINGS_STORAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTINGS_STORAGE, NMSettingsStorageClass)) + +#define NM_SETTINGS_STORAGE_PLUGIN "plugin" +#define NM_SETTINGS_STORAGE_UUID "uuid" +#define NM_SETTINGS_STORAGE_FILENAME "filename" + +struct _NMSettingsPlugin; + +typedef struct NMSettingsStorage { + GObject parent; + struct _NMSettingsPlugin *_plugin; + char *_uuid; + char *_filename; + CList _storage_lst; + CList _storage_by_uuid_lst; +} NMSettingsStorage; + +typedef struct { + GObjectClass parent; + + int (*cmp_fcn) (NMSettingsStorage *a, + NMSettingsStorage *b); + +} NMSettingsStorageClass; + +GType nm_settings_storage_get_type (void); + +NMSettingsStorage *nm_settings_storage_new (struct _NMSettingsPlugin *plugin, + const char *uuid, + const char *filename); + +static inline struct _NMSettingsPlugin * +nm_settings_storage_get_plugin (const NMSettingsStorage *self) +{ + GType nm_settings_plugin_get_type (void); + + g_return_val_if_fail (NM_IS_SETTINGS_STORAGE (self), NULL); + + nm_assert (G_TYPE_CHECK_INSTANCE_TYPE (self->_plugin, nm_settings_plugin_get_type ())); + return self->_plugin; +} + +static inline const char * +nm_settings_storage_get_uuid (const NMSettingsStorage *self) +{ + gboolean nm_utils_is_uuid (const char *str); + + g_return_val_if_fail (NM_IS_SETTINGS_STORAGE (self), NULL); + + nm_assert (nm_utils_is_uuid (self->_uuid)); + return self->_uuid; +} + +static inline const char * +nm_settings_storage_get_uuid_opt (const NMSettingsStorage *self) +{ + gboolean nm_utils_is_uuid (const char *str); + + g_return_val_if_fail (NM_IS_SETTINGS_STORAGE (self), NULL); + + nm_assert (!self->_uuid || nm_utils_is_uuid (self->_uuid)); + return self->_uuid; +} + +static inline const char * +nm_settings_storage_get_filename (const NMSettingsStorage *self) +{ + g_return_val_if_fail (NM_IS_SETTINGS_STORAGE (self), NULL); + + return self->_filename; +} + +/*****************************************************************************/ + +#define nm_assert_valid_settings_storage(plugin, storage) \ + G_STMT_START { \ + NMSettingsPlugin *const _plugin = (plugin); \ + NMSettingsStorage *const _storage = (storage); \ + \ + nm_assert (!_plugin || NM_IS_SETTINGS_PLUGIN (_plugin)); \ + nm_assert (NM_IS_SETTINGS_STORAGE (_storage)); \ + nm_assert (!_plugin || nm_settings_storage_get_plugin (_storage) == _plugin); \ + } G_STMT_END + +/*****************************************************************************/ + +int nm_settings_storage_cmp (NMSettingsStorage *sd_a, + NMSettingsStorage *sd_b, + const GSList *plugin_list); + +#endif /* __NM_SETTINGS_STORAGE_H__ */ diff --git a/src/settings/nm-settings-utils.c b/src/settings/nm-settings-utils.c new file mode 100644 index 00000000..0d636537 --- /dev/null +++ b/src/settings/nm-settings-utils.c @@ -0,0 +1,175 @@ +/* NetworkManager system settings service - keyfile plugin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2019 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-settings-utils.h" + +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> + +#include "nm-settings-plugin.h" + +/*****************************************************************************/ + +const struct timespec * +nm_sett_util_stat_mtime (const char *filename, + gboolean do_lstat, + struct timespec *out_val) +{ + struct stat st; + struct timeval now_tv; + + if (filename) { + if (do_lstat) { + if (lstat (filename, &st) == 0) { + *out_val = st.st_mtim; + return out_val; + } + } else { + if (stat (filename, &st) == 0) { + *out_val = st.st_mtim; + return out_val; + } + } + } + + if (gettimeofday (&now_tv, NULL) == 0) { + *out_val = (struct timespec) { + .tv_sec = now_tv.tv_sec, + .tv_nsec = now_tv.tv_usec * 1000u, + }; + return out_val; + } + + *out_val = (struct timespec) { }; + return out_val; +} + +/*****************************************************************************/ + +gboolean +nm_sett_util_allow_filename_cb (const char *filename, + gpointer user_data) +{ + const NMSettUtilAllowFilenameData *allow_filename_data = user_data; + + if ( allow_filename_data->allowed_filename + && nm_streq (allow_filename_data->allowed_filename, filename)) + return TRUE; + + return !g_hash_table_contains (allow_filename_data->idx_by_filename, filename); +} + +/*****************************************************************************/ + +void +nm_sett_util_storage_by_uuid_head_destroy (NMSettUtilStorageByUuidHead *sbuh) +{ + CList *iter; + + while ((iter = c_list_first (&sbuh->_storage_by_uuid_lst_head))) + c_list_unlink (iter); + g_free (sbuh); +} + +/*****************************************************************************/ + +void +nm_sett_util_storages_clear (NMSettUtilStorages *storages) +{ + nm_clear_pointer (&storages->idx_by_uuid, g_hash_table_destroy); + nm_clear_pointer (&storages->idx_by_filename, g_hash_table_destroy); + nm_assert (c_list_is_empty (&storages->_storage_lst_head)); +} + +void +nm_sett_util_storages_add_take (NMSettUtilStorages *storages, + gpointer storage_take_p /* NMSettingsStorage *, take reference */) +{ + NMSettingsStorage *storage_take = storage_take_p; + NMSettUtilStorageByUuidHead *sbuh; + const char *uuid; + + nm_assert (storage_take); + nm_assert (c_list_is_empty (&storage_take->_storage_lst)); + nm_assert (c_list_is_empty (&storage_take->_storage_by_uuid_lst)); + nm_assert (nm_settings_storage_get_filename (storage_take)); + + if (!g_hash_table_replace (storages->idx_by_filename, + (char *) nm_settings_storage_get_filename (storage_take), + storage_take /* takes ownership of reference. */)) + nm_assert_not_reached (); + + uuid = nm_settings_storage_get_uuid_opt (storage_take); + + if (uuid) { + sbuh = nm_sett_util_storages_lookup_by_uuid (storages, uuid); + if (!sbuh) { + gsize l = strlen (uuid) + 1; + + sbuh = g_malloc (sizeof (NMSettUtilStorageByUuidHead) + l); + sbuh->uuid = sbuh->uuid_data; + c_list_init (&sbuh->_storage_by_uuid_lst_head); + memcpy (sbuh->uuid_data, uuid, l); + g_hash_table_add (storages->idx_by_uuid, sbuh); + } + c_list_link_tail (&sbuh->_storage_by_uuid_lst_head, &storage_take->_storage_by_uuid_lst); + } + + c_list_link_tail (&storages->_storage_lst_head, &storage_take->_storage_lst); +} + +gpointer /* NMSettingsStorage * */ +nm_sett_util_storages_steal (NMSettUtilStorages *storages, + gpointer storage_p /* NMSettingsStorage **/) +{ + NMSettingsStorage *storage = storage_p; + NMSettUtilStorageByUuidHead *sbuh; + const char *uuid; + + nm_assert (storage); + nm_assert (nm_sett_util_storages_lookup_by_filename (storages, nm_settings_storage_get_filename (storage)) == storage); + nm_assert (c_list_contains (&storages->_storage_lst_head, &storage->_storage_lst)); + + uuid = nm_settings_storage_get_uuid_opt (storage); + + if (!uuid) { + nm_assert (c_list_is_empty (&storage->_storage_by_uuid_lst)); + } else { + nm_assert (!c_list_is_empty (&storage->_storage_by_uuid_lst)); + + sbuh = nm_sett_util_storages_lookup_by_uuid (storages, uuid); + + nm_assert (sbuh); + nm_assert (c_list_contains (&sbuh->_storage_by_uuid_lst_head, &storage->_storage_by_uuid_lst)); + c_list_unlink (&storage->_storage_by_uuid_lst); + + if (c_list_is_empty (&sbuh->_storage_by_uuid_lst_head)) + g_hash_table_remove (storages->idx_by_uuid, sbuh); + } + + c_list_unlink (&storage->_storage_lst); + + g_hash_table_steal (storages->idx_by_filename, nm_settings_storage_get_filename (storage)); + + return storage; +} diff --git a/src/settings/nm-settings-utils.h b/src/settings/nm-settings-utils.h new file mode 100644 index 00000000..a2a22dc4 --- /dev/null +++ b/src/settings/nm-settings-utils.h @@ -0,0 +1,110 @@ +/* + * 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 2019 Red Hat, Inc. + */ + +#ifndef __NM_SETTINGS_UTILS_H__ +#define __NM_SETTINGS_UTILS_H__ + +#include "nm-settings-storage.h" + +/*****************************************************************************/ + +struct timespec; + +const struct timespec *nm_sett_util_stat_mtime (const char *filename, + gboolean do_lstat, + struct timespec *out_val); + +/*****************************************************************************/ + +typedef struct { + const char *uuid; + + CList _storage_by_uuid_lst_head; + + char uuid_data[]; +} NMSettUtilStorageByUuidHead; + +typedef struct { + CList _storage_lst_head; + GHashTable *idx_by_filename; + GHashTable *idx_by_uuid; +} NMSettUtilStorages; + +void nm_sett_util_storage_by_uuid_head_destroy (NMSettUtilStorageByUuidHead *sbuh); + +#define NM_SETT_UTIL_STORAGES_INIT(storages, storage_destroy_fcn) \ + { \ + ._storage_lst_head = C_LIST_INIT (((storages)._storage_lst_head)), \ + .idx_by_filename = g_hash_table_new_full (nm_str_hash, \ + g_str_equal, \ + NULL, \ + (GDestroyNotify) storage_destroy_fcn), \ + .idx_by_uuid = g_hash_table_new_full (nm_pstr_hash, \ + nm_pstr_equal, \ + NULL, \ + (GDestroyNotify) nm_sett_util_storage_by_uuid_head_destroy), \ + } + +void nm_sett_util_storages_clear (NMSettUtilStorages *storages); + +#define nm_auto_clear_sett_util_storages nm_auto(nm_sett_util_storages_clear) + +void nm_sett_util_storages_add_take (NMSettUtilStorages *storages, + gpointer storage_take_p); + +gpointer nm_sett_util_storages_steal (NMSettUtilStorages *storages, + gpointer storage_p); + +/*****************************************************************************/ + +static inline gpointer /* NMSettingsStorage * */ +nm_sett_util_storages_lookup_by_filename (NMSettUtilStorages *storages, + const char *filename) +{ + nm_assert (filename); + + return g_hash_table_lookup (storages->idx_by_filename, filename); +} + +static inline NMSettUtilStorageByUuidHead * +nm_sett_util_storages_lookup_by_uuid (NMSettUtilStorages *storages, + const char *uuid) +{ + nm_assert (uuid); + + return g_hash_table_lookup (storages->idx_by_uuid, &uuid); +} + +/*****************************************************************************/ + +typedef struct { + GHashTable *idx_by_filename; + const char *allowed_filename; +} NMSettUtilAllowFilenameData; + +#define NM_SETT_UTIL_ALLOW_FILENAME_DATA(_storages, _allowed_filename) \ + (&((NMSettUtilAllowFilenameData) { \ + .idx_by_filename = (_storages)->idx_by_filename, \ + .allowed_filename = (_allowed_filename), \ + })) + +gboolean nm_sett_util_allow_filename_cb (const char *filename, + gpointer user_data); + +#endif /* __NM_SETTINGS_UTILS_H__ */ diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c index 8e18a33e..430d2776 100644 --- a/src/settings/nm-settings.c +++ b/src/settings/nm-settings.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * Søren Sandmann <sandmann@daimi.au.dk> @@ -37,6 +36,8 @@ #endif #include "nm-libnm-core-intern/nm-common-macros.h" +#include "nm-glib-aux/nm-keyfile-aux.h" +#include "nm-keyfile-internal.h" #include "nm-dbus-interface.h" #include "nm-connection.h" #include "nm-setting-8021x.h" @@ -60,6 +61,7 @@ #include "nm-utils.h" #include "nm-core-internal.h" +#include "nm-std-aux/c-list-util.h" #include "nm-glib-aux/nm-c-list.h" #include "nm-dbus-object.h" #include "devices/nm-device-ethernet.h" @@ -70,6 +72,7 @@ #include "nm-auth-subject.h" #include "nm-session-monitor.h" #include "plugins/keyfile/nms-keyfile-plugin.h" +#include "plugins/keyfile/nms-keyfile-storage.h" #include "nm-agent-manager.h" #include "nm-config.h" #include "nm-audit-manager.h" @@ -79,16 +82,255 @@ /*****************************************************************************/ -#define EXPORT(sym) void * __export_##sym = &sym; +static NM_CACHED_QUARK_FCN ("default-wired-connection", _default_wired_connection_quark) + +/*****************************************************************************/ + +typedef struct _StorageData { + CList sd_lst; + NMSettingsStorage *storage; + NMConnection *connection; + bool prioritize:1; +} StorageData; + +static StorageData * +_storage_data_new_stale (NMSettingsStorage *storage, + NMConnection *connection) +{ + StorageData *sd; + + sd = g_slice_new (StorageData); + sd->storage = g_object_ref (storage); + sd->connection = nm_g_object_ref (connection); + sd->prioritize = FALSE; + return sd; +} + +static void +_storage_data_destroy (StorageData *sd) +{ + c_list_unlink_stale (&sd->sd_lst); + g_object_unref (sd->storage); + nm_g_object_unref (sd->connection); + g_slice_free (StorageData, sd); +} + +static StorageData * +_storage_data_find_in_lst (CList *head, + NMSettingsStorage *storage) +{ + StorageData *sd; + + nm_assert (head); + nm_assert (NM_IS_SETTINGS_STORAGE (storage)); + + c_list_for_each_entry (sd, head, sd_lst) { + if (sd->storage == storage) + return sd; + } + return NULL; +} + +static void +nm_assert_storage_data_lst (CList *head) +{ +#if NM_MORE_ASSERTS > 5 + const char *uuid = NULL; + StorageData *sd; + CList *iter; -EXPORT(nm_settings_connection_get_type) -EXPORT(nm_settings_connection_update) + nm_assert (head); + + if (c_list_is_empty (head)) + return; + + c_list_for_each_entry (sd, head, sd_lst) { + const char *u; + + nm_assert (NM_IS_SETTINGS_STORAGE (sd->storage)); + nm_assert (!sd->connection || NM_IS_CONNECTION (sd->connection)); + u = nm_settings_storage_get_uuid (sd->storage); + if (!uuid) { + uuid = u; + nm_assert (nm_utils_is_uuid (uuid)); + } else + nm_assert (nm_streq0 (uuid, u)); + } + + /* assert that all storages are unique. */ + c_list_for_each_entry (sd, head, sd_lst) { + for (iter = sd->sd_lst.next; iter != head; iter = iter->next) + nm_assert (c_list_entry (iter, StorageData, sd_lst)->storage != sd->storage); + } +#endif +} + +static gboolean +_storage_data_is_alive (StorageData *sd) +{ + /* If the storage tracks a connection, it is considered alive. + * + * Meta-data storages are special: they never track a connection. + * We need to check them specially to know when to drop them. */ + return sd->connection + || nm_settings_storage_is_meta_data_alive (sd->storage); +} /*****************************************************************************/ -static NM_CACHED_QUARK_FCN ("plugin-module-path", plugin_module_path_quark) -static NM_CACHED_QUARK_FCN ("default-wired-connection", _default_wired_connection_quark) -static NM_CACHED_QUARK_FCN ("default-wired-device", _default_wired_device_quark) +typedef struct { + const char *uuid; + NMSettingsConnection *sett_conn; + NMSettingsStorage *storage; + CList sd_lst_head; + CList dirty_sd_lst_head; + + CList sce_dirty_lst; + + char _uuid_data[]; +} SettConnEntry; + +static SettConnEntry * +_sett_conn_entry_new (const char *uuid) +{ + SettConnEntry *sett_conn_entry; + gsize l_p_1; + + nm_assert (nm_utils_is_uuid (uuid)); + + l_p_1 = strlen (uuid) + 1; + + sett_conn_entry = g_malloc (sizeof (SettConnEntry) + l_p_1); + sett_conn_entry->uuid = sett_conn_entry->_uuid_data; + sett_conn_entry->sett_conn = NULL; + sett_conn_entry->storage = NULL; + c_list_init (&sett_conn_entry->sd_lst_head); + c_list_init (&sett_conn_entry->dirty_sd_lst_head); + c_list_init (&sett_conn_entry->sce_dirty_lst); + memcpy (sett_conn_entry->_uuid_data, uuid, l_p_1); + return sett_conn_entry; +} + +static void +_sett_conn_entry_free (SettConnEntry *sett_conn_entry) +{ + c_list_unlink_stale (&sett_conn_entry->sce_dirty_lst); + nm_c_list_free_all (&sett_conn_entry->sd_lst_head, StorageData, sd_lst, _storage_data_destroy); + nm_c_list_free_all (&sett_conn_entry->dirty_sd_lst_head, StorageData, sd_lst, _storage_data_destroy); + nm_g_object_unref (sett_conn_entry->sett_conn); + nm_g_object_unref (sett_conn_entry->storage); + g_free (sett_conn_entry); +} + +static NMSettingsConnection * +_sett_conn_entry_get_conn (SettConnEntry *sett_conn_entry) +{ + return sett_conn_entry ? sett_conn_entry->sett_conn : NULL; +} + +/** + * _sett_conn_entry_storage_find_conflicting_storage: + * @sett_conn_entry: the list of settings-storages for the given UUID. + * @target_plugin: the settings plugin to check + * @storage_check_including: (allow-none): optionally compare against this storage. + * @plugins: the list of plugins sorted in descending priority. This determines + * the priority and whether a storage conflicts. + * + * If we were to add the a storage to @target_plugin, then this function checks + * whether there are already other storages that would hide the storage after we + * add it. Those conflicting/hiding storages are a problem, because they have higher + * priority, so we cannot add the storage. + * + * @storage_check_including is optional, and if given then it checks whether updating + * the profile in this storage would result in confict. This is the check before + * update-connection. If this parameter is omitted, then it's about what happens + * when adding a new profile (add-connection). + * + * Returns: the conflicting storage or %NULL if there is none. + */ +static NMSettingsStorage * +_sett_conn_entry_storage_find_conflicting_storage (SettConnEntry *sett_conn_entry, + NMSettingsPlugin *target_plugin, + NMSettingsStorage *storage_check_including, + const GSList *plugins) +{ + StorageData *sd; + + if (!sett_conn_entry) + return NULL; + + if ( storage_check_including + && nm_settings_storage_is_keyfile_run (storage_check_including)) { + /* the storage we check against is in-memory. It always has highest + * priority, so there can be no other conflicting storages. */ + return NULL; + } + + /* Finds the first (highest priority) storage that has a connection. + * Note that due to tombstones (that have a high priority), the connection + * may not actually be exposed. This is to find hidden/shadowed storages + * that provide a connection. */ + c_list_for_each_entry (sd, &sett_conn_entry->sd_lst_head, sd_lst) { + nm_assert (NM_IS_SETTINGS_STORAGE (sd->storage)); + + if (!sd->connection) { + /* We only consider storages with connection. In particular, + * tombstones are not relevant, because we can delete them to + * resolve the conflict. */ + continue; + } + + if (sd->storage == storage_check_including) { + /* ok, the storage is the one we are about to check. All other + * storages are lower priority, so there is no storage that hides + * our storage_check_including. */ + return NULL; + } + + if (nm_settings_plugin_cmp_by_priority (nm_settings_storage_get_plugin (sd->storage), + target_plugin, + plugins) <= 0) { + /* the plugin of the existing storage is less important than @target_plugin. + * We have no conflicting/hiding storage. */ + return NULL; + } + + /* Found. If we would add the profile to @target_plugin, then it would be hidden + * by existing_storage. */ + return sd->storage; + } + + return NULL; +} + +static NMSettingsStorage * +_sett_conn_entry_find_shadowed_storage (SettConnEntry *sett_conn_entry, + const char *shadowed_storage_filename, + NMSettingsStorage *blacklisted_storage) +{ + StorageData *sd; + + if (!shadowed_storage_filename) + return NULL; + + c_list_for_each_entry (sd, &sett_conn_entry->sd_lst_head, sd_lst) { + + nm_assert (NM_IS_SETTINGS_STORAGE (sd->storage)); + + if (!sd->connection) + continue; + + if (blacklisted_storage == sd->storage) + continue; + + if (!nm_streq0 (nm_settings_storage_get_filename_for_shadowed_storage (sd->storage), shadowed_storage_filename)) + continue; + + return sd->storage; + } + + return NULL; +} /*****************************************************************************/ @@ -115,25 +357,45 @@ typedef struct { NMConfig *config; - GSList *auths; + NMPlatform *platform; + + NMHostnameManager *hostname_manager; + + NMSessionMonitor *session_monitor; + + CList auth_lst_head; + + NMSKeyfilePlugin *keyfile_plugin; GSList *plugins; + NMKeyFileDB *kf_db_timestamps; + NMKeyFileDB *kf_db_seen_bssids; + + GHashTable *sce_idx; + + CList sce_dirty_lst_head; + CList connections_lst_head; NMSettingsConnection **connections_cached_list; + GSList *unmanaged_specs; GSList *unrecognized_specs; - NMHostnameManager *hostname_manager; - + GHashTable *startup_complete_idx; NMSettingsConnection *startup_complete_blocked_by; + gulong startup_complete_platform_change_id; + guint startup_complete_timeout_id; guint connections_len; + guint connections_generation; + + guint kf_db_flush_idle_id_timestamps; + guint kf_db_flush_idle_id_seen_bssids; + bool started:1; - bool startup_complete:1; - bool connections_loaded:1; } NMSettingsPrivate; @@ -152,6 +414,9 @@ G_DEFINE_TYPE (NMSettings, nm_settings, NM_TYPE_DBUS_OBJECT); /*****************************************************************************/ +/* FIXME: a lot of logging lines are directly connected to a profile. Set the @con_uuid + * argument for structured logging. */ + #define _NMLOG_DOMAIN LOGD_SETTINGS #define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "settings", __VA_ARGS__) @@ -161,367 +426,286 @@ static const NMDBusInterfaceInfoExtended interface_info_settings; static const GDBusSignalInfo signal_info_new_connection; static const GDBusSignalInfo signal_info_connection_removed; -static void claim_connection (NMSettings *self, - NMSettingsConnection *connection); - -static void unmanaged_specs_changed (NMSettingsPlugin *config, gpointer user_data); -static void unrecognized_specs_changed (NMSettingsPlugin *config, gpointer user_data); - -static void connection_ready_changed (NMSettingsConnection *conn, - GParamSpec *pspec, - gpointer user_data); - static void default_wired_clear_tag (NMSettings *self, NMDevice *device, - NMSettingsConnection *connection, + NMSettingsConnection *sett_conn, gboolean add_to_no_auto_default); +static void _clear_connections_cached_list (NMSettingsPrivate *priv); + +static void _startup_complete_check (NMSettings *self, + gint64 now_us); + /*****************************************************************************/ static void -check_startup_complete (NMSettings *self) +_emit_connection_added (NMSettings *self, + NMSettingsConnection *sett_conn) { - NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - NMSettingsConnection *sett_conn; - - if (priv->startup_complete) - return; - - 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 (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); + g_signal_emit (self, signals[CONNECTION_ADDED], 0, sett_conn); } static void -connection_ready_changed (NMSettingsConnection *conn, - GParamSpec *pspec, - gpointer user_data) +_emit_connection_updated (NMSettings *self, + NMSettingsConnection *sett_conn, + NMSettingsConnectionUpdateReason update_reason) { - NMSettings *self = NM_SETTINGS (user_data); - - if (nm_settings_connection_get_ready (conn)) - check_startup_complete (self); + _nm_settings_connection_emit_signal_updated_internal (sett_conn, update_reason); + g_signal_emit (self, signals[CONNECTION_UPDATED], 0, sett_conn, (guint) update_reason); } static void -plugin_connection_added (NMSettingsPlugin *config, - NMSettingsConnection *connection, - NMSettings *self) +_emit_connection_removed (NMSettings *self, + NMSettingsConnection *sett_conn) { - claim_connection (self, connection); + g_signal_emit (self, signals[CONNECTION_REMOVED], 0, sett_conn); } static void -load_connections (NMSettings *self) +_emit_connection_flags_changed (NMSettings *self, + NMSettingsConnection *sett_conn) { - NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - GSList *iter; + g_signal_emit (self, signals[CONNECTION_FLAGS_CHANGED], 0, sett_conn); +} - for (iter = priv->plugins; iter; iter = g_slist_next (iter)) { - NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data); - GSList *plugin_connections; - GSList *elt; +/*****************************************************************************/ - plugin_connections = nm_settings_plugin_get_connections (plugin); +typedef struct { + NMSettingsConnection *sett_conn; + gint64 start_at; + gint64 timeout; +} StartupCompleteData; - // FIXME: ensure connections from plugins loaded with a lower priority - // get rejected when they conflict with connections from a higher - // priority plugin. +static void +_startup_complete_data_destroy (StartupCompleteData *scd) +{ + g_object_unref (scd->sett_conn); + g_slice_free (StartupCompleteData, scd); +} - for (elt = plugin_connections; elt; elt = g_slist_next (elt)) - claim_connection (self, elt->data); +static gboolean +_startup_complete_check_is_ready (NMPlatform *platform, + NMSettingsConnection *sett_conn) +{ + const NMPlatformLink *plink; + const char *ifname; - g_slist_free (plugin_connections); + /* FIXME: instead of just looking for the interface name, it would be better + * to wait for a device that is compatible with the profile. */ - g_signal_connect (plugin, NM_SETTINGS_PLUGIN_CONNECTION_ADDED, - G_CALLBACK (plugin_connection_added), self); - g_signal_connect (plugin, NM_SETTINGS_PLUGIN_UNMANAGED_SPECS_CHANGED, - G_CALLBACK (unmanaged_specs_changed), self); - g_signal_connect (plugin, NM_SETTINGS_PLUGIN_UNRECOGNIZED_SPECS_CHANGED, - G_CALLBACK (unrecognized_specs_changed), self); - } + ifname = nm_connection_get_interface_name (nm_settings_connection_get_connection (sett_conn)); - priv->connections_loaded = TRUE; - _notify (self, PROP_CONNECTIONS); + if (!ifname) + return TRUE; - unmanaged_specs_changed (NULL, self); - unrecognized_specs_changed (NULL, self); + plink = nm_platform_link_get_by_ifname (platform, ifname); + return plink && plink->initialized; } -static void -impl_settings_list_connections (NMDBusObject *obj, - const NMDBusInterfaceInfoExtended *interface_info, - const NMDBusMethodInfoExtended *method_info, - GDBusConnection *dbus_connection, - const char *sender, - GDBusMethodInvocation *invocation, - GVariant *parameters) +static gboolean +_startup_complete_timeout_cb (gpointer user_data) { - NMSettings *self = NM_SETTINGS (obj); + NMSettings *self = user_data; NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - gs_free const char **strv = NULL; - strv = nm_dbus_utils_get_paths_for_clist (&priv->connections_lst_head, - priv->connections_len, - G_STRUCT_OFFSET (NMSettingsConnection, _connections_lst), - TRUE); - g_dbus_method_invocation_return_value (invocation, - g_variant_new ("(^ao)", strv)); -} - -NMSettingsConnection * -nm_settings_get_connection_by_uuid (NMSettings *self, const char *uuid) -{ - NMSettingsPrivate *priv; - NMSettingsConnection *candidate; - - g_return_val_if_fail (NM_IS_SETTINGS (self), NULL); - g_return_val_if_fail (uuid != NULL, NULL); - - priv = NM_SETTINGS_GET_PRIVATE (self); - - c_list_for_each_entry (candidate, &priv->connections_lst_head, _connections_lst) { - if (nm_streq (uuid, nm_settings_connection_get_uuid (candidate))) - return candidate; - } - - return NULL; + priv->startup_complete_timeout_id = 0; + _startup_complete_check (self, 0); + return G_SOURCE_REMOVE; } static void -impl_settings_get_connection_by_uuid (NMDBusObject *obj, - const NMDBusInterfaceInfoExtended *interface_info, - const NMDBusMethodInfoExtended *method_info, - GDBusConnection *dbus_connection, - const char *sender, - GDBusMethodInvocation *invocation, - GVariant *parameters) +_startup_complete_platform_change_cb (NMPlatform *platform, + int obj_type_i, + int ifindex, + const NMPlatformLink *link, + int change_type_i, + NMSettings *self) { - NMSettings *self = NM_SETTINGS (obj); - NMSettingsConnection *sett_conn; - gs_unref_object NMAuthSubject *subject = NULL; - GError *error = NULL; - const char *uuid; + const NMPlatformSignalChangeType change_type = change_type_i; + NMSettingsPrivate *priv; + const char *ifname; - g_variant_get (parameters, "(&s)", &uuid); + if (change_type == NM_PLATFORM_SIGNAL_REMOVED) + return; - sett_conn = nm_settings_get_connection_by_uuid (self, uuid); - if (!sett_conn) { - error = g_error_new_literal (NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_INVALID_CONNECTION, - "No connection with the UUID was found."); - goto error; - } + if (!link->initialized) + return; - subject = nm_auth_subject_new_unix_process_from_context (invocation); - if (!subject) { - error = g_error_new_literal (NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_PERMISSION_DENIED, - "Unable to determine UID of request."); - goto error; - } + priv = NM_SETTINGS_GET_PRIVATE (self); - if (!nm_auth_is_subject_in_acl_set_error (nm_settings_connection_get_connection (sett_conn), - subject, - NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_PERMISSION_DENIED, - &error)) - goto error; + ifname = nm_connection_get_interface_name (nm_settings_connection_get_connection (priv->startup_complete_blocked_by)); + if ( ifname + && !nm_streq (ifname, link->name)) + return; - g_dbus_method_invocation_return_value (invocation, - g_variant_new ("(o)", - nm_dbus_object_get_path (NM_DBUS_OBJECT (sett_conn)))); - return; + nm_assert (priv->startup_complete_timeout_id > 0); -error: - g_dbus_method_invocation_take_error (invocation, error); + nm_clear_g_source (&priv->startup_complete_timeout_id); + priv->startup_complete_timeout_id = g_idle_add (_startup_complete_timeout_cb, self); } static void -_clear_connections_cached_list (NMSettingsPrivate *priv) +_startup_complete_check (NMSettings *self, + gint64 now_us) { - if (!priv->connections_cached_list) + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + gint64 next_expiry; + StartupCompleteData *scd; + NMSettingsConnection *next_sett_conn = NULL; + GHashTableIter iter; + + if (!priv->started) { + /* before we are started, we don't setup the timers... */ return; + } - nm_assert (priv->connections_len == NM_PTRARRAY_LEN (priv->connections_cached_list)); + if (!priv->startup_complete_idx) + goto ready; -#if NM_MORE_ASSERTS - /* set the pointer to a bogus value. This makes it more apparent - * if somebody has a reference to the cached list and still uses - * it. That is a bug, this code just tries to make it blow up - * more eagerly. */ - memset (priv->connections_cached_list, - 0xdeaddead, - sizeof (NMSettingsConnection *) * (priv->connections_len + 1)); -#endif + if (!now_us) + now_us = nm_utils_get_monotonic_timestamp_us (); - nm_clear_g_free (&priv->connections_cached_list); -} + next_expiry = 0; -/** - * nm_settings_get_connections: - * @self: the #NMSettings - * @out_len: (out) (allow-none): returns the number of returned - * connections. - * - * 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 - * NMSettings operation. - */ -NMSettingsConnection *const* -nm_settings_get_connections (NMSettings *self, guint *out_len) -{ - NMSettingsPrivate *priv; - NMSettingsConnection **v; - NMSettingsConnection *con; - guint i; + g_hash_table_iter_init (&iter, priv->startup_complete_idx); + while (g_hash_table_iter_next (&iter, (gpointer *) &scd, NULL)) { + gint64 expiry; - g_return_val_if_fail (NM_IS_SETTINGS (self), NULL); + if (scd->start_at == 0) { + /* once ready, the decision is remembered and there is nothing + * left to check. */ + continue; + } - priv = NM_SETTINGS_GET_PRIVATE (self); + expiry = scd->start_at + scd->timeout; + if (expiry <= now_us) { + scd->start_at = 0; + continue; + } - nm_assert (priv->connections_len == c_list_length (&priv->connections_lst_head)); + if (_startup_complete_check_is_ready (priv->platform, scd->sett_conn)) { + scd->start_at = 0; + continue; + } - if (G_UNLIKELY (!priv->connections_cached_list)) { - v = g_new (NMSettingsConnection *, priv->connections_len + 1); + next_expiry = expiry; + next_sett_conn = scd->sett_conn; + /* we found one timeout for which to wait. that's good enough. */ + break; + } - i = 0; - c_list_for_each_entry (con, &priv->connections_lst_head, _connections_lst) { - nm_assert (i < priv->connections_len); - v[i++] = con; + nm_clear_g_source (&priv->startup_complete_timeout_id); + nm_g_object_ref_set (&priv->startup_complete_blocked_by, next_sett_conn); + if (next_expiry > 0) { + nm_assert (priv->startup_complete_blocked_by); + if (priv->startup_complete_platform_change_id == 0) { + priv->startup_complete_platform_change_id = g_signal_connect (priv->platform, + NM_PLATFORM_SIGNAL_LINK_CHANGED, + G_CALLBACK (_startup_complete_platform_change_cb), + self); } - nm_assert (i == priv->connections_len); - v[i] = NULL; - - priv->connections_cached_list = v; + priv->startup_complete_timeout_id = g_timeout_add (NM_MIN (3600u*1000u, (next_expiry - now_us) / 1000u), + _startup_complete_timeout_cb, + self); + _LOGT ("startup-complete: wait for device \"%s\" due to connection %s (%s)", + nm_connection_get_interface_name (nm_settings_connection_get_connection (priv->startup_complete_blocked_by)), + nm_settings_connection_get_uuid (priv->startup_complete_blocked_by), + nm_settings_connection_get_id (priv->startup_complete_blocked_by)); + return; } - NM_SET_OUT (out_len, priv->connections_len); - return priv->connections_cached_list; + nm_clear_pointer (&priv->startup_complete_idx, g_hash_table_destroy); + nm_clear_g_signal_handler (priv->platform, &priv->startup_complete_platform_change_id); + +ready: + _LOGT ("startup-complete: ready, no profiles to wait for"); + nm_assert (priv->started); + nm_assert (!priv->startup_complete_blocked_by); + nm_assert (!priv->startup_complete_idx); + nm_assert (priv->startup_complete_timeout_id == 0); + nm_assert (priv->startup_complete_platform_change_id == 0); + _notify (self, PROP_STARTUP_COMPLETE); } -/** - * nm_settings_get_connections_clone: - * @self: the #NMSetting - * @out_len: (allow-none): optional output argument - * @func: caller-supplied function for filtering connections - * @func_data: caller-supplied data passed to @func - * @sort_compare_func: (allow-none): optional function pointer for - * sorting the returned list. - * @sort_data: user data for @sort_compare_func. - * - * Returns: (transfer container) (element-type NMSettingsConnection): - * an NULL terminated array of #NMSettingsConnection objects that were - * filtered by @func (or all connections if no filter was specified). - * The order is arbitrary. - * Caller is responsible for freeing the returned array with free(), - * the contained values do not need to be unrefed. - */ -NMSettingsConnection ** -nm_settings_get_connections_clone (NMSettings *self, - guint *out_len, - NMSettingsConnectionFilterFunc func, - gpointer func_data, - GCompareDataFunc sort_compare_func, - gpointer sort_data) +static void +_startup_complete_notify_connection (NMSettings *self, + NMSettingsConnection *sett_conn, + gboolean forget) { - NMSettingsConnection *const*list_cached; - NMSettingsConnection **list; - guint len, i, j; - - g_return_val_if_fail (NM_IS_SETTINGS (self), NULL); - - list_cached = nm_settings_get_connections (self, &len); - -#if NM_MORE_ASSERTS - nm_assert (list_cached); - for (i = 0; i < len; i++) - nm_assert (NM_IS_SETTINGS_CONNECTION (list_cached[i])); - nm_assert (!list_cached[i]); -#endif - - list = g_new (NMSettingsConnection *, ((gsize) len + 1)); - if (func) { - for (i = 0, j = 0; i < len; i++) { - if (func (self, list_cached[i], func_data)) - list[j++] = list_cached[i]; + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + gint64 timeout; + gint64 now_us = 0; + + nm_assert ( !priv->started + || priv->startup_complete_idx); + + timeout = 0; + if (!forget) { + NMSettingConnection *s_con; + gint32 v; + + s_con = nm_connection_get_setting_connection (nm_settings_connection_get_connection (sett_conn)); + v = nm_setting_connection_get_wait_device_timeout (s_con); + if (v > 0) { + nm_assert (nm_setting_connection_get_interface_name (s_con)); + timeout = ((gint64) v) * 1000; } - list[j] = NULL; - len = j; - } else - memcpy (list, list_cached, sizeof (list[0]) * ((gsize) len + 1)); - - if ( len > 1 - && sort_compare_func) { - g_qsort_with_data (list, len, sizeof (NMSettingsConnection *), - sort_compare_func, sort_data); } - NM_SET_OUT (out_len, len); - return list; -} - -NMSettingsConnection * -nm_settings_get_connection_by_path (NMSettings *self, const char *path) -{ - NMSettingsPrivate *priv; - NMSettingsConnection *connection; - - g_return_val_if_fail (NM_IS_SETTINGS (self), NULL); - g_return_val_if_fail (path, NULL); - - priv = NM_SETTINGS_GET_PRIVATE (self); - connection = nm_dbus_manager_lookup_object (nm_dbus_object_get_manager (NM_DBUS_OBJECT (self)), - path); - if ( !connection - || !NM_IS_SETTINGS_CONNECTION (connection)) - return NULL; + if (timeout == 0) { + if ( !priv->startup_complete_idx + || !g_hash_table_remove (priv->startup_complete_idx, &sett_conn)) + return; + } else { + StartupCompleteData *scd; + + if (!priv->startup_complete_idx) { + nm_assert (!priv->started); + priv->startup_complete_idx = g_hash_table_new_full (nm_pdirect_hash, + nm_pdirect_equal, + NULL, + (GDestroyNotify) _startup_complete_data_destroy); + scd = NULL; + } else + scd = g_hash_table_lookup (priv->startup_complete_idx, &sett_conn); + if (!scd) { + now_us = nm_utils_get_monotonic_timestamp_us (); + scd = g_slice_new (StartupCompleteData); + *scd = (StartupCompleteData) { + .sett_conn = g_object_ref (sett_conn), + .start_at = now_us, + .timeout = timeout, + }; + g_hash_table_add (priv->startup_complete_idx, scd); + } else { + if (scd->start_at == 0) { + /* the entry already is ready and no longer relevant. Ignore it. */ + return; + } + scd->timeout = timeout; + } + } - nm_assert (c_list_contains (&priv->connections_lst_head, &connection->_connections_lst)); - return connection; + _startup_complete_check (self, now_us); } -gboolean -nm_settings_has_connection (NMSettings *self, NMSettingsConnection *connection) +const char * +nm_settings_get_startup_complete_blocked_reason (NMSettings *self) { - gboolean has; - - g_return_val_if_fail (NM_IS_SETTINGS (self), FALSE); - g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (connection), FALSE); - - has = !c_list_is_empty (&connection->_connections_lst); - - nm_assert (has == nm_c_list_contains_entry (&NM_SETTINGS_GET_PRIVATE (self)->connections_lst_head, - connection, - _connections_lst)); - nm_assert (({ - NMSettingsConnection *candidate = NULL; - const char *path; - - path = nm_dbus_object_get_path (NM_DBUS_OBJECT (connection)); - if (path) - candidate = nm_settings_get_connection_by_path (self, path); - - (has == (connection == candidate)); - })); + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + const char *uuid = NULL; - return has; + if (priv->started) { + if (!priv->startup_complete_idx) + return NULL; + if (priv->startup_complete_blocked_by) + uuid = nm_settings_connection_get_uuid (priv->startup_complete_blocked_by); + } + return uuid ?: "unknown"; } +/*****************************************************************************/ + const GSList * nm_settings_get_unmanaged_specs (NMSettings *self) { @@ -531,57 +715,57 @@ nm_settings_get_unmanaged_specs (NMSettings *self) } static gboolean -find_spec (GSList *spec_list, const char *spec) -{ - GSList *iter; - - for (iter = spec_list; iter; iter = g_slist_next (iter)) { - if (!strcmp ((const char *) iter->data, spec)) - return TRUE; - } - return FALSE; -} - -static void update_specs (NMSettings *self, GSList **specs_ptr, GSList * (*get_specs_func) (NMSettingsPlugin *)) { NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + GSList *new = NULL; GSList *iter; - g_slist_free_full (*specs_ptr, g_free); - *specs_ptr = NULL; - for (iter = priv->plugins; iter; iter = g_slist_next (iter)) { - GSList *specs, *specs_iter; + GSList *specs; - specs = get_specs_func (NM_SETTINGS_PLUGIN (iter->data)); - for (specs_iter = specs; specs_iter; specs_iter = specs_iter->next) { - if (!find_spec (*specs_ptr, (const char *) specs_iter->data)) { - *specs_ptr = g_slist_prepend (*specs_ptr, specs_iter->data); - } else - g_free (specs_iter->data); + specs = get_specs_func (iter->data); + while (specs) { + GSList *s = specs; + + specs = g_slist_remove_link (specs, s); + if (nm_utils_g_slist_find_str (new, s->data)) { + g_free (s->data); + g_slist_free_1 (s); + continue; + } + s->next = new; + new = s; } + } - g_slist_free (specs); + if (nm_utils_g_slist_strlist_cmp (new, *specs_ptr) == 0) { + g_slist_free_full (new, g_free); + return FALSE; } + + g_slist_free_full (*specs_ptr, g_free); + *specs_ptr = new; + return TRUE; + } static void -unmanaged_specs_changed (NMSettingsPlugin *config, - gpointer user_data) +_plugin_unmanaged_specs_changed (NMSettingsPlugin *config, + gpointer user_data) { NMSettings *self = NM_SETTINGS (user_data); NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - update_specs (self, &priv->unmanaged_specs, - nm_settings_plugin_get_unmanaged_specs); - _notify (self, PROP_UNMANAGED_SPECS); + if (update_specs (self, &priv->unmanaged_specs, + nm_settings_plugin_get_unmanaged_specs)) + _notify (self, PROP_UNMANAGED_SPECS); } static void -unrecognized_specs_changed (NMSettingsPlugin *config, - gpointer user_data) +_plugin_unrecognized_specs_changed (NMSettingsPlugin *config, + gpointer user_data) { NMSettings *self = NM_SETTINGS (user_data); NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); @@ -590,521 +774,1605 @@ unrecognized_specs_changed (NMSettingsPlugin *config, nm_settings_plugin_get_unrecognized_specs); } +/*****************************************************************************/ + static void -add_plugin (NMSettings *self, NMSettingsPlugin *plugin, const char *path) +connection_flags_changed (NMSettingsConnection *sett_conn, + gpointer user_data) { - NMSettingsPrivate *priv; + _emit_connection_flags_changed (NM_SETTINGS (user_data), sett_conn); +} - nm_assert (NM_IS_SETTINGS (self)); - nm_assert (NM_IS_SETTINGS_PLUGIN (plugin)); +/*****************************************************************************/ - priv = NM_SETTINGS_GET_PRIVATE (self); +static SettConnEntry * +_sett_conn_entries_get (NMSettings *self, + const char *uuid) +{ + nm_assert (uuid); + return g_hash_table_lookup (NM_SETTINGS_GET_PRIVATE (self)->sce_idx, &uuid); +} - nm_assert (!g_slist_find (priv->plugins, plugin)); +static SettConnEntry * +_sett_conn_entries_create_and_add (NMSettings *self, + const char *uuid) +{ + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + SettConnEntry *sett_conn_entry; - priv->plugins = g_slist_append (priv->plugins, g_object_ref (plugin)); + sett_conn_entry = _sett_conn_entry_new (uuid); - nm_settings_plugin_initialize (plugin); + if (!g_hash_table_add (priv->sce_idx, sett_conn_entry)) + nm_assert_not_reached (); + else if (g_hash_table_size (priv->sce_idx) == 1) + g_object_ref (self); - _LOGI ("Loaded settings plugin: %s (%s%s%s)", - G_OBJECT_TYPE_NAME (plugin), - NM_PRINT_FMT_QUOTED (path, "\"", path, "\"", "internal")); + return sett_conn_entry; } -static gboolean -add_plugin_load_file (NMSettings *self, const char *pname, GError **error) +static void +_sett_conn_entries_remove_and_destroy (NMSettings *self, + SettConnEntry *sett_conn_entry) { NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - gs_free char *full_name = NULL; - gs_free char *path = NULL; - gs_unref_object NMSettingsPlugin *plugin = NULL; - GModule *module; - NMSettingsPluginFactoryFunc factory_func; - GSList *iter; - struct stat st; - int errsv; - - full_name = g_strdup_printf ("nm-settings-plugin-%s", pname); - path = g_module_build_path (NMPLUGINDIR, full_name); - for (iter = priv->plugins; iter; iter = iter->next) { - if (nm_streq0 (path, - g_object_get_qdata (iter->data, - plugin_module_path_quark ()))) - return TRUE; - } - - if (stat (path, &st) != 0) { - errsv = errno; - _LOGW ("could not load plugin '%s' from file '%s': %s", pname, path, nm_strerror_native (errsv)); - return TRUE; - } - if (!S_ISREG (st.st_mode)) { - _LOGW ("could not load plugin '%s' from file '%s': not a file", pname, path); - return TRUE; - } - if (st.st_uid != 0) { - _LOGW ("could not load plugin '%s' from file '%s': file must be owned by root", pname, path); - return TRUE; - } - if (st.st_mode & (S_IWGRP | S_IWOTH | S_ISUID)) { - _LOGW ("could not load plugin '%s' from file '%s': invalid file permissions", pname, path); - return TRUE; - } + if (!g_hash_table_remove (priv->sce_idx, sett_conn_entry)) + nm_assert_not_reached (); + else if (g_hash_table_size (priv->sce_idx) == 0) + g_object_unref (self); +} - module = g_module_open (path, G_MODULE_BIND_LOCAL); - if (!module) { - _LOGW ("could not load plugin '%s' from file '%s': %s", - pname, path, g_module_error ()); - return TRUE; - } +/*****************************************************************************/ - /* errors after this point are fatal, because we loaded the shared library already. */ +static int +_sett_conn_entry_sds_update_cmp_ascending (const StorageData *sd_a, + const StorageData *sd_b, + const GSList *plugins) +{ + const NMSettingsMetaData *meta_data_a; + const NMSettingsMetaData *meta_data_b; + bool is_keyfile_run_a; + bool is_keyfile_run_b; + + /* Sort storages by priority. More important storages are sorted + * higher (ascending sort). For example, if "sd_a" is more important than + * "sd_b" (sd_a>sd_b), a positive integer is returned. */ + + meta_data_a = nm_settings_storage_is_meta_data (sd_a->storage); + meta_data_b = nm_settings_storage_is_meta_data (sd_b->storage); + + /* runtime storages (both connections and meta-data) are always more + * important. */ + is_keyfile_run_a = nm_settings_storage_is_keyfile_run (sd_a->storage); + is_keyfile_run_b = nm_settings_storage_is_keyfile_run (sd_b->storage); + if (is_keyfile_run_a != is_keyfile_run_b) { + + if ( !meta_data_a + && !meta_data_b) { + /* Ok, both are non-meta-data providing actual profiles. But one is in /run and one is in + * another storage. In this case we first honor whether one of the storages is explicitly + * prioritized. The prioritize flag is an in-memory hack to overwrite relative priorities + * contrary to what exists on-disk. + * + * This is done because when we use explicit D-Bus API (like update-connection) + * to update a profile, then we really want to prioritize the candidate + * despite having multiple other profiles. + * + * The example is if you have the same UUID twice in /run (one of them shadowed). + * If you move it to disk, then one of the profiles gets deleted and re-created + * on disk, but that on-disk profile must win against the remainging profile in + * /run. At least until the next reload/restart. */ + NM_CMP_FIELD_UNSAFE (sd_a, sd_b, prioritize); + } - if (!g_module_symbol (module, "nm_settings_plugin_factory", (gpointer) (&factory_func))) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Could not find plugin '%s' factory function.", - pname); - g_module_close (module); - return FALSE; + /* in-memory has higher priority. That is regardless of whether any of + * them is meta-data/tombstone or a profile. + * + * That works, because if any of them are tombstones/metadata, then we are in full + * control. There can by only one meta-data file, which is fully owned (and accordingly + * created/deleted) by NetworkManager. + * + * The only case where this might not be right is if we have profiles + * in /run that are shadowed. When we move such a profile to disk, then + * a conflict might arise. That is handled by "prioritize" above! */ + NM_CMP_DIRECT (is_keyfile_run_a, is_keyfile_run_b); } - /* after accessing the plugin we cannot unload it anymore, because the glib - * types cannot be properly unregistered. */ - g_module_make_resident (module); + /* After we determined that both profiles are either in /run or not, + * tombstones are always more important than non-tombstones. */ + NM_CMP_DIRECT (meta_data_a && meta_data_a->is_tombstone, + meta_data_b && meta_data_b->is_tombstone); - plugin = (*factory_func) (); - if (!NM_IS_SETTINGS_PLUGIN (plugin)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "plugin '%s' returned invalid settings plugin", - pname); - return FALSE; - } + /* Again, prioritized entries are sorted first (higher priority). */ + NM_CMP_FIELD_UNSAFE (sd_a, sd_b, prioritize); - add_plugin (self, NM_SETTINGS_PLUGIN (plugin), path); - g_object_set_qdata_full (G_OBJECT (plugin), - plugin_module_path_quark (), - g_steal_pointer (&path), - g_free); - return TRUE; + /* finally, compare the storages. This basically honors the timestamp + * of the profile and the relative order of the source plugin (via the + * @plugins list). */ + return nm_settings_storage_cmp (sd_a->storage, sd_b->storage, plugins); } -static void -add_plugin_keyfile (NMSettings *self) +static int +_sett_conn_entry_sds_update_cmp (const CList *ls_a, + const CList *ls_b, + gconstpointer user_data) { - gs_unref_object NMSKeyfilePlugin *keyfile_plugin = NULL; - - keyfile_plugin = nms_keyfile_plugin_new (); - add_plugin (self, NM_SETTINGS_PLUGIN (keyfile_plugin), NULL); + /* we sort highest priority storages first (descending). Hence, the order is swapped. */ + return _sett_conn_entry_sds_update_cmp_ascending (c_list_entry (ls_b, StorageData, sd_lst), + c_list_entry (ls_a, StorageData, sd_lst), + user_data); } -static gboolean -load_plugins (NMSettings *self, const char **plugins, GError **error) +static void +_sett_conn_entry_sds_update (NMSettings *self, + SettConnEntry *sett_conn_entry) { - const char **iter; - gboolean keyfile_added = FALSE; - gboolean success = TRUE; - gboolean add_ibft = FALSE; - gboolean has_no_ibft; - gssize idx_no_ibft, idx_ibft; - - idx_ibft = nm_utils_strv_find_first ((char **) plugins, -1, "ibft"); - idx_no_ibft = nm_utils_strv_find_first ((char **) plugins, -1, "no-ibft"); - has_no_ibft = idx_no_ibft >= 0 && idx_no_ibft > idx_ibft; -#if WITH_SETTINGS_PLUGIN_IBFT - add_ibft = idx_no_ibft < 0 && idx_ibft < 0; -#endif + StorageData *sd; + StorageData *sd_safe; + StorageData *sd_dirty; + gboolean reprioritize; + + nm_assert_storage_data_lst (&sett_conn_entry->sd_lst_head); + nm_assert_storage_data_lst (&sett_conn_entry->dirty_sd_lst_head); + + /* we merge the dirty list with the previous list. + * + * The idea is: + * + * - _connection_changed_track() appends events for the same UUID. Meaning: + * if the storage is new, it get appended (having lower priority). + * If it already exist and is an update for an event that we already + * track it, it keeps the list position in @dirty_sd_lst_head unchanged. + * + * - during merge, we want to preserve the previous order (with higher + * priority first in the list). + */ - for (iter = plugins; iter && *iter; iter++) { - const char *pname = *iter; + /* first go through all storages that we track and check whether they + * got an update...*/ - if (!*pname || strchr (pname, '/')) { - _LOGW ("ignore invalid plugin \"%s\"", pname); - continue; + reprioritize = FALSE; + c_list_for_each_entry (sd, &sett_conn_entry->dirty_sd_lst_head, sd_lst) { + if (sd->prioritize) { + reprioritize = TRUE; + break; } + } - if (NM_IN_STRSET (pname, "ifcfg-suse", "ifnet")) { - _LOGW ("skipping deprecated plugin %s", pname); - continue; - } + nm_assert_storage_data_lst (&sett_conn_entry->sd_lst_head); - if (nm_streq (pname, "no-ibft")) - continue; - if (has_no_ibft && nm_streq (pname, "ibft")) - continue; + c_list_for_each_entry_safe (sd, sd_safe, &sett_conn_entry->sd_lst_head, sd_lst) { - /* keyfile plugin is built-in now */ - if (nm_streq (pname, "keyfile")) { - if (!keyfile_added) { - add_plugin_keyfile (self); - keyfile_added = TRUE; - } + sd_dirty = _storage_data_find_in_lst (&sett_conn_entry->dirty_sd_lst_head, sd->storage); + if (!sd_dirty) { + /* there is no update for this storage (except maybe reprioritize). */ + if (reprioritize) + sd->prioritize = FALSE; continue; } - if (nm_utils_strv_find_first ((char **) plugins, - iter - plugins, - pname) >= 0) { - /* the plugin is already mentioned in the list previously. - * Don't load a duplicate. */ - continue; - } + nm_g_object_ref_set (&sd->connection, sd_dirty->connection); + sd->prioritize = sd_dirty->prioritize; - success = add_plugin_load_file (self, pname, error); - if (!success) - break; + _storage_data_destroy (sd_dirty); + } - if (add_ibft && nm_streq (pname, "ifcfg-rh")) { - /* The plugin ibft is not explicitly mentioned but we just enabled "ifcfg-rh". - * Enable "ibft" by default after "ifcfg-rh". */ - pname = "ibft"; - add_ibft = FALSE; + nm_assert_storage_data_lst (&sett_conn_entry->sd_lst_head); - success = add_plugin_load_file (self, "ibft", error); - if (!success) - break; - } + /* all remaining (so far unseen) dirty entries are appended to the merged list. + * (append means lower priority). */ + + c_list_splice (&sett_conn_entry->sd_lst_head, &sett_conn_entry->dirty_sd_lst_head); + + nm_assert_storage_data_lst (&sett_conn_entry->sd_lst_head); + + /* we drop the entries that are no longer "alive" (meaning, they no longer + * indicate a connection and are not a tombstone). */ + c_list_for_each_entry_safe (sd, sd_safe, &sett_conn_entry->sd_lst_head, sd_lst) { + if (!_storage_data_is_alive (sd)) + _storage_data_destroy (sd); } - /* If keyfile plugin was not among configured plugins, add it as the last one */ - if (!keyfile_added && success) - add_plugin_keyfile (self); + nm_assert_storage_data_lst (&sett_conn_entry->sd_lst_head); + nm_assert (c_list_is_empty (&sett_conn_entry->dirty_sd_lst_head)); - return success; + /* as last, we sort the entries. Note that this is a stable-sort... */ + c_list_sort (&sett_conn_entry->sd_lst_head, + _sett_conn_entry_sds_update_cmp, + NM_SETTINGS_GET_PRIVATE (self)->plugins); + + nm_assert_storage_data_lst (&sett_conn_entry->sd_lst_head); + nm_assert (c_list_is_empty (&sett_conn_entry->dirty_sd_lst_head)); } -static void -connection_updated (NMSettingsConnection *connection, gboolean by_user, gpointer user_data) +/*****************************************************************************/ + +static NMConnection * +_connection_changed_normalize_connection (NMSettingsStorage *storage, + NMConnection *connection, + GVariant *secrets_to_merge, + NMConnection **out_connection_cloned) { - g_signal_emit (NM_SETTINGS (user_data), - signals[CONNECTION_UPDATED], - 0, - connection, - by_user); + gs_unref_object NMConnection *connection_cloned = NULL; + gs_free_error GError *error = NULL; + const char *uuid; + + nm_assert (NM_IS_SETTINGS_STORAGE (storage)); + nm_assert (out_connection_cloned && !*out_connection_cloned); + + if (!connection) + return NULL; + + nm_assert (NM_IS_CONNECTION (connection)); + + uuid = nm_settings_storage_get_uuid (storage); + + if (secrets_to_merge) { + connection_cloned = nm_simple_connection_new_clone (connection); + connection = connection_cloned; + nm_connection_update_secrets (connection, + NULL, + secrets_to_merge, + NULL); + } + + if (!_nm_connection_ensure_normalized (connection, + !!connection_cloned, + uuid, + FALSE, + connection_cloned ? NULL : &connection_cloned, + &error)) { + /* this is most likely a bug in the plugin. It provided a connection that no longer verifies. + * Well, I guess it could also happen when we merge @secrets_to_merge above. In any case + * somewhere is a bug. */ + _LOGT ("storage[%s,"NM_SETTINGS_STORAGE_PRINT_FMT"]: plugin provided an invalid connection: %s", + uuid, + NM_SETTINGS_STORAGE_PRINT_ARG (storage), + error->message); + return NULL; + } + if (connection_cloned) + connection = connection_cloned; + + *out_connection_cloned = g_steal_pointer (&connection_cloned); + return connection; } +/*****************************************************************************/ + static void -connection_flags_changed (NMSettingsConnection *connection, - gpointer user_data) +_connection_changed_update (NMSettings *self, + SettConnEntry *sett_conn_entry, + NMConnection *connection, + NMSettingsConnectionIntFlags sett_flags, + NMSettingsConnectionIntFlags sett_mask, + NMSettingsConnectionUpdateReason update_reason) { - g_signal_emit (NM_SETTINGS (user_data), - signals[CONNECTION_FLAGS_CHANGED], - 0, - connection); + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + gs_unref_object NMConnection *connection_old = NULL; + NMSettingsStorage *storage = sett_conn_entry->storage; + gs_unref_object NMSettingsConnection *sett_conn = g_object_ref (sett_conn_entry->sett_conn); + const char *path; + gboolean is_new; + + nm_assert (!NM_FLAGS_ANY (sett_mask, ~_NM_SETTINGS_CONNECTION_INT_FLAGS_PERSISTENT_MASK)); + nm_assert (!NM_FLAGS_ANY (sett_flags, ~sett_mask)); + + is_new = c_list_is_empty (&sett_conn->_connections_lst); + + _LOGT ("update[%s]: %s connection \"%s\" ("NM_SETTINGS_STORAGE_PRINT_FMT")", + nm_settings_storage_get_uuid (storage), + is_new ? "adding" : "updating", + nm_connection_get_id (connection), + NM_SETTINGS_STORAGE_PRINT_ARG (storage)); + + _nm_settings_connection_set_storage (sett_conn, storage); + + _nm_settings_connection_set_connection (sett_conn, connection, &connection_old, update_reason); + + + if (is_new) { + _nm_settings_connection_register_kf_dbs (sett_conn, + priv->kf_db_timestamps, + priv->kf_db_seen_bssids); + + _clear_connections_cached_list (priv); + c_list_link_tail (&priv->connections_lst_head, &sett_conn->_connections_lst); + priv->connections_len++; + priv->connections_generation++; + + g_signal_connect (sett_conn, NM_SETTINGS_CONNECTION_FLAGS_CHANGED, G_CALLBACK (connection_flags_changed), self); + } + + if (NM_FLAGS_HAS (update_reason, NM_SETTINGS_CONNECTION_UPDATE_REASON_BLOCK_AUTOCONNECT)) { + nm_settings_connection_autoconnect_blocked_reason_set (sett_conn, + NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_USER_REQUEST, + TRUE); + } + + sett_mask |= NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE; + if (nm_settings_connection_check_visibility (sett_conn, priv->session_monitor)) + sett_flags |= NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE; + else + nm_assert (!NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE)); + + sett_mask |= NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED; + if (nm_settings_storage_is_keyfile_run (storage)) + sett_flags |= NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED; + else { + nm_assert (!NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED)); + + /* Profiles that don't reside in /run, are never nm-generated + * and never volatile. */ + sett_mask |= ( NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED + | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE); + sett_flags &= ~( NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED + | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE); + } + + nm_settings_connection_set_flags_full (sett_conn, + sett_mask, + sett_flags); + + if (is_new) { + /* FIXME(shutdown): The NMSettings instance can't be disposed + * while there is any exported connection. Ideally we should + * unexport all connections on NMSettings' disposal, but for now + * leak @self on termination when there are connections alive. */ + path = nm_dbus_object_export (NM_DBUS_OBJECT (sett_conn)); + } else + path = nm_dbus_object_get_path (NM_DBUS_OBJECT (sett_conn)); + + if ( is_new + || connection_old) { + nm_utils_log_connection_diff (nm_settings_connection_get_connection (sett_conn), + connection_old, + LOGL_DEBUG, + LOGD_CORE, + is_new ? "new connection" : "update connection", + "++ ", + path); + } + + if (is_new) { + nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self), + &interface_info_settings, + &signal_info_new_connection, + "(o)", + path); + _notify (self, PROP_CONNECTIONS); + _emit_connection_added (self, sett_conn); + } else { + _nm_settings_connection_emit_dbus_signal_updated (sett_conn); + _emit_connection_updated (self, sett_conn, update_reason); + } + + if ( !priv->started + || priv->startup_complete_idx) { + if (nm_settings_has_connection (self, sett_conn)) + _startup_complete_notify_connection (self, sett_conn, FALSE); + } } static void -connection_removed (NMSettingsConnection *connection, gpointer user_data) +_connection_changed_delete (NMSettings *self, + NMSettingsStorage *storage, + NMSettingsConnection *sett_conn, + gboolean allow_add_to_no_auto_default) { - NMSettings *self = NM_SETTINGS (user_data); NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + gs_unref_object NMConnection *connection_for_agents = NULL; NMDevice *device; + const char *uuid; - g_return_if_fail (NM_IS_SETTINGS_CONNECTION (connection)); - g_return_if_fail (!c_list_is_empty (&connection->_connections_lst)); - nm_assert (c_list_contains (&priv->connections_lst_head, &connection->_connections_lst)); + nm_assert (NM_IS_SETTINGS_CONNECTION (sett_conn)); + nm_assert (c_list_contains (&priv->connections_lst_head, &sett_conn->_connections_lst)); + nm_assert (nm_dbus_object_is_exported (NM_DBUS_OBJECT (sett_conn))); + + uuid = nm_settings_storage_get_uuid (storage); + + _LOGT ("update[%s]: delete connection \"%s\" ("NM_SETTINGS_STORAGE_PRINT_FMT")", + uuid, + nm_settings_connection_get_id (sett_conn), + NM_SETTINGS_STORAGE_PRINT_ARG (storage)); - /* When the default wired connection is removed (either deleted or saved to - * a new persistent connection by a plugin), write the MAC address of the + /* When the default wired sett_conn is removed (either deleted or saved to + * a new persistent sett_conn by a plugin), write the MAC address of the * wired device to the config file and don't create a new default wired - * connection for that device again. + * sett_conn for that device again. */ - device = g_object_get_qdata (G_OBJECT (connection), _default_wired_device_quark ()); + device = nm_settings_connection_default_wired_get_device (sett_conn); if (device) - default_wired_clear_tag (self, device, connection, TRUE); + default_wired_clear_tag (self, device, sett_conn, allow_add_to_no_auto_default); - /* Disconnect signal handlers, as plugins might still keep references - * to the connection (and thus the signal handlers would still be live) - * even after NMSettings has dropped all its references. - */ - - g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_removed), self); - g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_updated), self); - g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_flags_changed), self); - if (!priv->startup_complete) - g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_ready_changed), self); + g_signal_handlers_disconnect_by_func (sett_conn, G_CALLBACK (connection_flags_changed), self); - /* Forget about the connection internally */ _clear_connections_cached_list (priv); + c_list_unlink (&sett_conn->_connections_lst); priv->connections_len--; - c_list_unlink (&connection->_connections_lst); + priv->connections_generation++; - if (priv->connections_loaded) { - _notify (self, PROP_CONNECTIONS); + /* Tell agents to remove secrets for this connection */ + connection_for_agents = nm_simple_connection_new_clone (nm_settings_connection_get_connection (sett_conn)); + nm_connection_clear_secrets (connection_for_agents); + nm_agent_manager_delete_secrets (priv->agent_mgr, + nm_dbus_object_get_path (NM_DBUS_OBJECT (self)), + connection_for_agents); - nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self), - &interface_info_settings, - &signal_info_connection_removed, - "(o)", - nm_dbus_object_get_path (NM_DBUS_OBJECT (connection))); - } + _notify (self, PROP_CONNECTIONS); + _nm_settings_connection_emit_dbus_signal_removed (sett_conn); + nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self), + &interface_info_settings, + &signal_info_connection_removed, + "(o)", + nm_dbus_object_get_path (NM_DBUS_OBJECT (sett_conn))); - nm_dbus_object_unexport (NM_DBUS_OBJECT (connection)); + nm_dbus_object_unexport (NM_DBUS_OBJECT (sett_conn)); - if (priv->connections_loaded) - g_signal_emit (self, signals[CONNECTION_REMOVED], 0, connection); + nm_settings_connection_set_flags (sett_conn, + NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE + | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE, + FALSE); - check_startup_complete (self); + _emit_connection_removed (self, sett_conn); - g_object_unref (connection); + _nm_settings_connection_cleanup_after_remove (sett_conn); - g_object_unref (self); /* Balanced by a ref in claim_connection() */ -} + nm_key_file_db_remove_key (priv->kf_db_timestamps, uuid); + nm_key_file_db_remove_key (priv->kf_db_seen_bssids, uuid); -#define NM_DBUS_SERVICE_OPENCONNECT "org.freedesktop.NetworkManager.openconnect" -#define NM_OPENCONNECT_KEY_GATEWAY "gateway" -#define NM_OPENCONNECT_KEY_COOKIE "cookie" -#define NM_OPENCONNECT_KEY_GWCERT "gwcert" -#define NM_OPENCONNECT_KEY_XMLCONFIG "xmlconfig" -#define NM_OPENCONNECT_KEY_LASTHOST "lasthost" -#define NM_OPENCONNECT_KEY_AUTOCONNECT "autoconnect" -#define NM_OPENCONNECT_KEY_CERTSIGS "certsigs" + if ( !priv->started + || priv->startup_complete_idx) + _startup_complete_notify_connection (self, sett_conn, TRUE); +} static void -openconnect_migrate_hack (NMConnection *connection) +_connection_changed_process_one (NMSettings *self, + SettConnEntry *sett_conn_entry, + gboolean allow_add_to_no_auto_default, + NMSettingsConnectionIntFlags sett_flags, + NMSettingsConnectionIntFlags sett_mask, + gboolean override_sett_flags, + NMSettingsConnectionUpdateReason update_reason) { - NMSettingVpn *s_vpn; - NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NOT_SAVED; + StorageData *sd_best; - /* Huge hack. There were some openconnect changes that needed to happen - * pretty late, too late to get into distros. Migration has already - * happened for many people, and their secret flags are wrong. But we - * don't want to requrie re-migration, so we have to fix it up here. Ugh. - */ + c_list_unlink (&sett_conn_entry->sce_dirty_lst); + + _sett_conn_entry_sds_update (self, sett_conn_entry); + + sd_best = c_list_first_entry (&sett_conn_entry->sd_lst_head, StorageData, sd_lst);; + + if ( !sd_best + || !sd_best->connection) { + gs_unref_object NMSettingsConnection *sett_conn = NULL; + gs_unref_object NMSettingsStorage *storage = NULL; + + if (!sett_conn_entry->sett_conn) { + + if (!sd_best) { + _sett_conn_entries_remove_and_destroy (self, sett_conn_entry); + return; + } - s_vpn = nm_connection_get_setting_vpn (connection); - if (s_vpn == NULL) + if (sett_conn_entry->storage != sd_best->storage) { + _LOGT ("update[%s]: shadow UUID ("NM_SETTINGS_STORAGE_PRINT_FMT")", + sett_conn_entry->uuid, + NM_SETTINGS_STORAGE_PRINT_ARG (sd_best->storage)); + } + + nm_g_object_ref_set (&sett_conn_entry->storage, sd_best->storage); + return; + } + + sett_conn = g_steal_pointer (&sett_conn_entry->sett_conn); + if (sd_best) { + storage = g_object_ref (sd_best->storage); + nm_g_object_ref_set (&sett_conn_entry->storage, storage); + nm_assert_valid_settings_storage (NULL, storage); + } else { + storage = g_object_ref (sett_conn_entry->storage); + _sett_conn_entries_remove_and_destroy (self, sett_conn_entry); + } + + _connection_changed_delete (self, storage, sett_conn, allow_add_to_no_auto_default); return; + } - if (g_strcmp0 (nm_setting_vpn_get_service_type (s_vpn), NM_DBUS_SERVICE_OPENCONNECT) == 0) { - /* These are different for every login session, and should not be stored */ - nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_GATEWAY, flags, NULL); - nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_COOKIE, flags, NULL); - nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_GWCERT, flags, NULL); + if (override_sett_flags) { + NMSettingsConnectionIntFlags s_f, s_m; - /* These are purely internal data for the auth-dialog, and should be stored */ - flags = 0; - nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_XMLCONFIG, flags, NULL); - nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_LASTHOST, flags, NULL); - nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_AUTOCONNECT, flags, NULL); - nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_CERTSIGS, flags, NULL); + nm_settings_storage_load_sett_flags (sd_best->storage, &s_f, &s_m); + + nm_assert (!NM_FLAGS_ANY (s_f, ~s_m)); + + sett_mask |= s_m; + sett_flags = (sett_flags & ~s_m) | (s_f & s_m); } + + nm_g_object_ref_set (&sett_conn_entry->storage, sd_best->storage); + + if (!sett_conn_entry->sett_conn) + sett_conn_entry->sett_conn = nm_settings_connection_new (); + + _connection_changed_update (self, + sett_conn_entry, + sd_best->connection, + sett_flags, + sett_mask, + update_reason); } static void -claim_connection (NMSettings *self, NMSettingsConnection *sett_conn) +_connection_changed_process_all_dirty (NMSettings *self, + gboolean allow_add_to_no_auto_default, + NMSettingsConnectionIntFlags sett_flags, + NMSettingsConnectionIntFlags sett_mask, + gboolean override_sett_flags, + NMSettingsConnectionUpdateReason update_reason) { - NMSettingsPrivate *priv; - GError *error = NULL; - const char *path; - NMSettingsConnection *existing; - - g_return_if_fail (NM_IS_SETTINGS (self)); - g_return_if_fail (NM_IS_SETTINGS_CONNECTION (sett_conn)); - g_return_if_fail (!nm_dbus_object_is_exported (NM_DBUS_OBJECT (sett_conn))); + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + SettConnEntry *sett_conn_entry; + + while ((sett_conn_entry = c_list_first_entry (&priv->sce_dirty_lst_head, SettConnEntry, sce_dirty_lst))) { + _connection_changed_process_one (self, + sett_conn_entry, + allow_add_to_no_auto_default, + sett_flags, + sett_mask, + override_sett_flags, + update_reason); + } +} - priv = NM_SETTINGS_GET_PRIVATE (self); +static SettConnEntry * +_connection_changed_track (NMSettings *self, + NMSettingsStorage *storage, + NMConnection *connection, + gboolean prioritize) +{ + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + SettConnEntry *sett_conn_entry; + StorageData *sd; + const char *uuid; - /* prevent duplicates */ - if (!c_list_is_empty (&sett_conn->_connections_lst)) { - nm_assert (c_list_contains (&priv->connections_lst_head, &sett_conn->_connections_lst)); - return; + nm_assert_valid_settings_storage (NULL, storage); + + uuid = nm_settings_storage_get_uuid (storage); + + nm_assert (!connection || NM_IS_CONNECTION (connection)); + nm_assert (!connection || (_nm_connection_verify (connection, NULL) == NM_SETTING_VERIFY_SUCCESS)); + nm_assert (!connection || nm_streq0 (uuid, nm_connection_get_uuid (connection))); + + nmtst_connection_assert_unchanging (connection); + + sett_conn_entry = _sett_conn_entries_get (self, uuid) + ?: _sett_conn_entries_create_and_add (self, uuid); + + if (_LOGT_ENABLED ()) { + const char *filename; + const NMSettingsMetaData *meta_data; + const char *shadowed_storage; + gboolean shadowed_owned; + + filename = nm_settings_storage_get_filename (storage); + if (connection) { + shadowed_storage = nm_settings_storage_get_shadowed_storage (storage, &shadowed_owned); + _LOGT ("storage[%s,"NM_SETTINGS_STORAGE_PRINT_FMT"]: change event with connection \"%s\"%s%s%s%s%s%s", + sett_conn_entry->uuid, + NM_SETTINGS_STORAGE_PRINT_ARG (storage), + nm_connection_get_id (connection), + NM_PRINT_FMT_QUOTED (filename, " (file \"", filename, "\")", ""), + NM_PRINT_FMT_QUOTED (shadowed_storage, shadowed_owned ? " (owns \"" : " (shadows \"", shadowed_storage, "\")", "")); + } else if ((meta_data = nm_settings_storage_is_meta_data (storage))) { + nm_assert (meta_data->is_tombstone); + shadowed_storage = nm_settings_storage_get_shadowed_storage (storage, &shadowed_owned); + _LOGT ("storage[%s,"NM_SETTINGS_STORAGE_PRINT_FMT"]: change event for %shiding profile%s%s%s%s%s%s", + sett_conn_entry->uuid, + NM_SETTINGS_STORAGE_PRINT_ARG (storage), + nm_settings_storage_is_meta_data_alive (storage) ? "" : "dropping ", + NM_PRINT_FMT_QUOTED (filename, " (file \"", filename, "\")", ""), + NM_PRINT_FMT_QUOTED (shadowed_storage, shadowed_owned ? " (owns \"" : " (shadows \"", shadowed_storage, "\")", "")); + } else { + _LOGT ("storage[%s,"NM_SETTINGS_STORAGE_PRINT_FMT"]: change event for dropping profile%s%s%s", + sett_conn_entry->uuid, + NM_SETTINGS_STORAGE_PRINT_ARG (storage), + NM_PRINT_FMT_QUOTED (filename, " (file \"", filename, "\")", "")); + } } - /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */ - if (!nm_connection_normalize (nm_settings_connection_get_connection (sett_conn), NULL, NULL, &error)) { - _LOGW ("plugin provided invalid connection: %s", error->message); - g_error_free (error); - return; + /* see _sett_conn_entry_sds_update() for why we append the new events + * and leave existing ones at their position. */ + sd = _storage_data_find_in_lst (&sett_conn_entry->dirty_sd_lst_head, storage); + if (sd) + nm_g_object_ref_set (&sd->connection, connection); + else { + sd = _storage_data_new_stale (storage, connection); + c_list_link_tail (&sett_conn_entry->dirty_sd_lst_head, &sd->sd_lst); } - existing = nm_settings_get_connection_by_uuid (self, nm_settings_connection_get_uuid (sett_conn)); - if (existing) { - /* Cannot add duplicate connections per UUID. Just return without action and - * log a warning. - * - * This means, that plugins must not provide duplicate connections (UUID). - * In fact, none of the plugins currently would do that. - * - * But globaly, over different setting plugins, there could be duplicates - * without the individual plugins being aware. Don't handle that at all, just - * error out. That should not happen unless the admin misconfigured the system - * to create conflicting connections. */ - _LOGW ("plugin provided duplicate connection with UUID %s", - nm_settings_connection_get_uuid (sett_conn)); - return; + if (prioritize) { + StorageData *sd2; + + /* only one entry can be prioritized. */ + c_list_for_each_entry (sd2, &sett_conn_entry->dirty_sd_lst_head, sd_lst) + sd2->prioritize = FALSE; + sd->prioritize = TRUE; } - /* Read timestamp from look-aside file and put it into the connection's data */ - nm_settings_connection_read_and_fill_timestamp (sett_conn); + nm_c_list_move_tail (&priv->sce_dirty_lst_head, &sett_conn_entry->sce_dirty_lst); - /* Read seen-bssids from look-aside file and put it into the connection's data */ - nm_settings_connection_read_and_fill_seen_bssids (sett_conn); + return sett_conn_entry; +} - /* Ensure its initial visibility is up-to-date */ - nm_settings_connection_recheck_visibility (sett_conn); +/*****************************************************************************/ - /* Evil openconnect migration hack */ - /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */ - openconnect_migrate_hack (nm_settings_connection_get_connection (sett_conn)); +static void +_plugin_connections_reload_cb (NMSettingsPlugin *plugin, + NMSettingsStorage *storage, + NMConnection *connection, + gpointer user_data) +{ + _connection_changed_track (user_data, storage, connection, FALSE); +} - /* This one unexports the connection, it needs to run late to give the active - * connection a chance to deal with its reference to this settings connection. */ - g_signal_connect_after (sett_conn, NM_SETTINGS_CONNECTION_REMOVED, - G_CALLBACK (connection_removed), self); - g_signal_connect (sett_conn, NM_SETTINGS_CONNECTION_UPDATED_INTERNAL, - G_CALLBACK (connection_updated), self); - g_signal_connect (sett_conn, NM_SETTINGS_CONNECTION_FLAGS_CHANGED, - G_CALLBACK (connection_flags_changed), - self); - if (!priv->startup_complete) { - g_signal_connect (sett_conn, "notify::" NM_SETTINGS_CONNECTION_READY, - G_CALLBACK (connection_ready_changed), - self); +static void +_plugin_connections_reload (NMSettings *self) +{ + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + GSList *iter; + + for (iter = priv->plugins; iter; iter = iter->next) { + nm_settings_plugin_reload_connections (iter->data, + _plugin_connections_reload_cb, + self); } - _clear_connections_cached_list (priv); + _connection_changed_process_all_dirty (self, + FALSE, + NM_SETTINGS_CONNECTION_INT_FLAGS_NONE, + NM_SETTINGS_CONNECTION_INT_FLAGS_NONE, + TRUE, + NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS + | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS); - g_object_ref (sett_conn); - /* FIXME(shutdown): The NMSettings instance can't be disposed - * while there is any exported connection. Ideally we should - * unexport all connections on NMSettings' disposal, but for now - * leak @self on termination when there are connections alive. */ - g_object_ref (self); - priv->connections_len++; - c_list_link_tail (&priv->connections_lst_head, &sett_conn->_connections_lst); - - path = nm_dbus_object_export (NM_DBUS_OBJECT (sett_conn)); - - nm_utils_log_connection_diff (nm_settings_connection_get_connection (sett_conn), - NULL, - LOGL_DEBUG, - LOGD_CORE, - "new connection", "++ ", - path); - - /* Only emit the individual connection-added signal after connections - * have been initially loaded. - */ - if (priv->connections_loaded) { - nm_dbus_object_emit_signal (NM_DBUS_OBJECT (self), - &interface_info_settings, - &signal_info_new_connection, - "(o)", - nm_dbus_object_get_path (NM_DBUS_OBJECT (sett_conn))); + for (iter = priv->plugins; iter; iter = iter->next) + nm_settings_plugin_load_connections_done (iter->data); +} - g_signal_emit (self, signals[CONNECTION_ADDED], 0, sett_conn); - _notify (self, PROP_CONNECTIONS); +/*****************************************************************************/ + +static gboolean +_add_connection_to_first_plugin (NMSettings *self, + SettConnEntry *sett_conn_entry, + NMConnection *new_connection, + gboolean in_memory, + NMSettingsConnectionIntFlags sett_flags, + const char *shadowed_storage, + gboolean shadowed_owned, + NMSettingsStorage **out_new_storage, + NMConnection **out_new_connection, + GError **error) +{ + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + GError *first_error = NULL; + GSList *iter; + const char *uuid; + + uuid = nm_connection_get_uuid (new_connection); + + nm_assert (nm_utils_is_uuid (uuid)); + + for (iter = priv->plugins; iter; iter = iter->next) { + NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data); + gs_unref_object NMSettingsStorage *storage = NULL; + gs_unref_object NMConnection *connection_to_add = NULL; + gs_unref_object NMConnection *connection_to_add_cloned = NULL; + NMConnection *connection_to_add_real = NULL; + gs_unref_variant GVariant *agent_owned_secrets = NULL; + gs_free_error GError *add_error = NULL; + gboolean success; + const char *filename; + + if (!in_memory) { + NMSettingsStorage *conflicting_storage; + + conflicting_storage = _sett_conn_entry_storage_find_conflicting_storage (sett_conn_entry, plugin, NULL, priv->plugins); + if (conflicting_storage) { + /* we have a connection provided by a plugin with higher priority than the one + * we would want to add the connection. We cannot do that, because doing so + * would result in adding a connection that gets hidden by the existing profile. + * Also, since we test the plugins in order of priority, all following plugins + * are unsuitable. + * + * Multiple connection plugins are so cumbersome, especially if they are unable + * to add the connection. I suggest to disable all plugins except keyfile. */ + _LOGT ("add-connection: failed to add %s/'%s': there is an existing storage "NM_SETTINGS_STORAGE_PRINT_FMT" with higher priority", + nm_connection_get_uuid (new_connection), + nm_connection_get_id (new_connection), + NM_SETTINGS_STORAGE_PRINT_ARG (conflicting_storage)); + nm_assert (first_error); + break; + } + } + + if (plugin == (NMSettingsPlugin *) priv->keyfile_plugin) { + success = nms_keyfile_plugin_add_connection (priv->keyfile_plugin, + new_connection, + in_memory, + NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED), + NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE), + shadowed_storage, + shadowed_owned, + &storage, + &connection_to_add, + &add_error); + } else { + if (in_memory) + continue; + nm_assert (!NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)); + nm_assert (!NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE)); + success = nm_settings_plugin_add_connection (plugin, + new_connection, + &storage, + &connection_to_add, + &add_error); + } + + if (!success) { + _LOGT ("add-connection: failed to add %s/'%s': %s", + nm_connection_get_uuid (new_connection), + nm_connection_get_id (new_connection), + add_error->message); + if (!first_error) + first_error = g_steal_pointer (&add_error); + continue; + } + + if (!nm_streq0 (nm_settings_storage_get_uuid (storage), uuid)) { + nm_assert_not_reached (); + continue; + } + + agent_owned_secrets = nm_connection_to_dbus (new_connection, + NM_CONNECTION_SERIALIZE_ONLY_SECRETS + | NM_CONNECTION_SERIALIZE_WITH_SECRETS_AGENT_OWNED); + connection_to_add_real = _connection_changed_normalize_connection (storage, + connection_to_add, + agent_owned_secrets, + &connection_to_add_cloned); + if (!connection_to_add_real) { + nm_assert_not_reached (); + continue; + } + + filename = nm_settings_storage_get_filename (storage); + _LOGT ("add-connection: successfully added connection %s,'%s' ("NM_SETTINGS_STORAGE_PRINT_FMT"%s%s%s", + nm_settings_storage_get_uuid (storage), + nm_connection_get_id (new_connection), + NM_SETTINGS_STORAGE_PRINT_ARG (storage), + NM_PRINT_FMT_QUOTED (filename, ", \"", filename, "\")", ")")); + + *out_new_storage = g_steal_pointer (&storage); + *out_new_connection = g_steal_pointer (&connection_to_add_cloned) + ?: g_steal_pointer (&connection_to_add); + nm_assert (NM_IS_CONNECTION (*out_new_connection)); + return TRUE; } - nm_settings_connection_added (sett_conn); + nm_assert (first_error); + g_propagate_error (error, first_error); + return FALSE; } static gboolean -secrets_filter_cb (NMSetting *setting, - const char *secret, - NMSettingSecretFlags flags, - gpointer user_data) +_update_connection_to_plugin (NMSettings *self, + NMSettingsStorage *storage, + NMConnection *connection, + NMSettingsConnectionIntFlags sett_flags, + gboolean force_rename, + const char *shadowed_storage, + gboolean shadowed_owned, + NMSettingsStorage **out_new_storage, + NMConnection **out_new_connection, + GError **error) { - NMSettingSecretFlags filter_flags = GPOINTER_TO_UINT (user_data); + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + NMSettingsPlugin *plugin; + gboolean success; + + plugin = nm_settings_storage_get_plugin (storage); + + if (plugin == (NMSettingsPlugin *) priv->keyfile_plugin) { + success = nms_keyfile_plugin_update_connection (priv->keyfile_plugin, + storage, + connection, + NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED), + NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE), + shadowed_storage, + shadowed_owned, + force_rename, + out_new_storage, + out_new_connection, + error); + } else { + nm_assert (!shadowed_storage); + nm_assert (!shadowed_owned); + success = nm_settings_plugin_update_connection (plugin, + storage, + connection, + out_new_storage, + out_new_connection, + error); + } - /* Returns TRUE to remove the secret */ + return success; +} - /* Can't use bitops with SECRET_FLAG_NONE so handle that specifically */ - if ( (flags == NM_SETTING_SECRET_FLAG_NONE) - && (filter_flags == NM_SETTING_SECRET_FLAG_NONE)) - return FALSE; +static void +_set_nmmeta_tombstone (NMSettings *self, + const char *uuid, + gboolean tombstone_on_disk, + gboolean tombstone_in_memory, + const char *shadowed_storage) +{ + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + gs_unref_object NMSettingsStorage *tombstone_1_storage = NULL; + gs_unref_object NMSettingsStorage *tombstone_2_storage = NULL; + + if (tombstone_on_disk) { + if (!nms_keyfile_plugin_set_nmmeta_tombstone (priv->keyfile_plugin, + FALSE, + uuid, + FALSE, + TRUE, + NULL, + &tombstone_1_storage, + NULL)) + tombstone_in_memory = TRUE; + if (tombstone_1_storage) + _connection_changed_track (self, tombstone_1_storage, NULL, FALSE); + } - /* Otherwise if the secret has at least one of the desired flags keep it */ - return (flags & filter_flags) ? FALSE : TRUE; + if (tombstone_in_memory) { + if (!nms_keyfile_plugin_set_nmmeta_tombstone (priv->keyfile_plugin, + FALSE, + uuid, + TRUE, + TRUE, + shadowed_storage, + &tombstone_2_storage, + NULL)) { + nms_keyfile_plugin_set_nmmeta_tombstone (priv->keyfile_plugin, + TRUE, + uuid, + TRUE, + TRUE, + shadowed_storage, + &tombstone_2_storage, + NULL); + } + _connection_changed_track (self, tombstone_2_storage, NULL, FALSE); + } } /** * nm_settings_add_connection: * @self: the #NMSettings object * @connection: the source connection to create a new #NMSettingsConnection from - * @save_to_disk: %TRUE to save the connection to disk immediately, %FALSE to - * not save to disk + * @persist_mode: the persist-mode for this profile. + * @add_reason: the add-reason flags. + * @sett_flags: the settings flags to set. + * @out_sett_conn: (allow-none) (transfer none): the added settings connection on success. * @error: on return, a location to store any errors that may occur * * Creates a new #NMSettingsConnection for the given source @connection. * The returned object is owned by @self and the caller must reference * the object to continue using it. * - * Returns: the new #NMSettingsConnection or %NULL + * Returns: TRUE on success. */ -NMSettingsConnection * +gboolean nm_settings_add_connection (NMSettings *self, NMConnection *connection, - gboolean save_to_disk, + NMSettingsConnectionPersistMode persist_mode, + NMSettingsConnectionAddReason add_reason, + NMSettingsConnectionIntFlags sett_flags, + NMSettingsConnection **out_sett_conn, GError **error) { - NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - GSList *iter; - NMSettingsConnection *added = NULL; - NMSettingsConnection *candidate = NULL; + NMSettingsPrivate *priv; + gs_unref_object NMConnection *connection_cloned_1 = NULL; + gs_unref_object NMConnection *new_connection = NULL; + gs_unref_object NMSettingsStorage *new_storage = NULL; + gs_unref_object NMSettingsStorage *shadowed_storage = NULL; + NMSettingsStorage *update_storage = NULL; + gs_free_error GError *local = NULL; + SettConnEntry *sett_conn_entry; const char *uuid; + StorageData *sd; + gboolean new_in_memory; + gboolean success; + const char *shadowed_storage_filename = NULL; + + priv = NM_SETTINGS_GET_PRIVATE (self); + + nm_assert (NM_IN_SET (persist_mode, NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK, + NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY)); + + new_in_memory = (persist_mode != NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK); + + nm_assert (!NM_FLAGS_ANY (sett_flags, ~_NM_SETTINGS_CONNECTION_INT_FLAGS_PERSISTENT_MASK)); + + if (NM_FLAGS_ANY (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE + | NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)) { + nm_assert (new_in_memory); + new_in_memory = TRUE; + } + + nm_assert (!NM_FLAGS_ANY (add_reason, ~NM_SETTINGS_CONNECTION_ADD_REASON_BLOCK_AUTOCONNECT)); + + NM_SET_OUT (out_sett_conn, NULL); uuid = nm_connection_get_uuid (connection); - /* Make sure a connection with this UUID doesn't already exist */ - c_list_for_each_entry (candidate, &priv->connections_lst_head, _connections_lst) { - if (nm_streq0 (uuid, nm_settings_connection_get_uuid (candidate))) { - g_set_error_literal (error, - NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_UUID_EXISTS, - "A connection with this UUID already exists."); - return NULL; + sett_conn_entry = _sett_conn_entries_get (self, uuid); + if (_sett_conn_entry_get_conn (sett_conn_entry)) { + g_set_error_literal (error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_UUID_EXISTS, + "a connection with this UUID already exists"); + return FALSE; + } + + if (!_nm_connection_ensure_normalized (connection, + FALSE, + NULL, + FALSE, + &connection_cloned_1, + &local)) { + g_set_error (error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "connection is invalid: %s", + local->message); + return FALSE; + } + if (connection_cloned_1) + connection = connection_cloned_1; + + if (sett_conn_entry) { + c_list_for_each_entry (sd, &sett_conn_entry->sd_lst_head, sd_lst) { + if (!nm_settings_storage_is_meta_data (sd->storage)) + continue; + shadowed_storage = nm_g_object_ref (_sett_conn_entry_find_shadowed_storage (sett_conn_entry, + nm_settings_storage_get_shadowed_storage (sd->storage, NULL), + NULL)); + if (shadowed_storage) { + /* We have a nmmeta tombstone that indicates that a storage is shadowed. + * + * This happens when deleting a in-memory profile that was decoupled from + * the persitant storage with NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED. + * We need to take over this storage again... */ + break; + } } } - /* 1) plugin writes the NMConnection to disk - * 2) plugin creates a new NMSettingsConnection subclass with the settings - * from the NMConnection and returns it to the settings service - * 3) settings service exports the new NMSettingsConnection subclass - * 4) plugin notices that something on the filesystem has changed - * 5) plugin reads the changes and ignores them because they will - * contain the same data as the connection it already knows about - */ - for (iter = priv->plugins; iter; iter = g_slist_next (iter)) { - NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data); - GError *add_error = NULL; - gs_unref_object NMConnection *simple = NULL; - gs_unref_variant GVariant *secrets = NULL; - - /* Make a copy of agent-owned secrets because they won't be present in - * the connection returned by plugins, as plugins return only what was - * reread from the file. */ - simple = nm_simple_connection_new_clone (connection); - nm_connection_clear_secrets_with_flags (simple, - secrets_filter_cb, - GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED)); - secrets = nm_connection_to_dbus (simple, NM_CONNECTION_SERIALIZE_ONLY_SECRETS); - - added = nm_settings_plugin_add_connection (plugin, connection, save_to_disk, &add_error); - if (added) { - if (secrets) { - /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */ - nm_connection_update_secrets (nm_settings_connection_get_connection (added), - NULL, - secrets, - NULL); + if ( shadowed_storage + && !new_in_memory) { + NMSettingsStorage *conflicting_storage; + + conflicting_storage = _sett_conn_entry_storage_find_conflicting_storage (sett_conn_entry, + nm_settings_storage_get_plugin (shadowed_storage), + shadowed_storage, + priv->plugins); + if (conflicting_storage) { + /* We cannot add the profile as @shadowed_storage, because there is another, existing storage + * that would hide it. Just add it as new storage. In general, this leads to duplication of profiles, + * but the circumstances where this happens are very exotic (you need at least one additional settings + * plugin, then going through the paths of making shadowed_storage in-memory-detached and delete it, + * and finally adding the conflicting storage outside of NM and restart/reload). */ + _LOGT ("ignore shadowed storage "NM_SETTINGS_STORAGE_PRINT_FMT" due to conflicting storage "NM_SETTINGS_STORAGE_PRINT_FMT, + NM_SETTINGS_STORAGE_PRINT_ARG (shadowed_storage), + NM_SETTINGS_STORAGE_PRINT_ARG (conflicting_storage)); + } else + update_storage = shadowed_storage; + } + + shadowed_storage_filename = ( shadowed_storage + && !update_storage) + ? nm_settings_storage_get_filename_for_shadowed_storage (shadowed_storage) + : NULL; + +again_add_connection: + + if (!update_storage) { + success = _add_connection_to_first_plugin (self, + sett_conn_entry, + connection, + new_in_memory, + sett_flags, + shadowed_storage_filename, + FALSE, + &new_storage, + &new_connection, + &local); + } else { + success = _update_connection_to_plugin (self, + update_storage, + connection, + sett_flags, + FALSE, + shadowed_storage_filename, + FALSE, + &new_storage, + &new_connection, + &local); + if (!success) { + if (!NMS_IS_KEYFILE_STORAGE (update_storage)) { + /* hm, the intended storage is not keyfile (it's ifcfg-rh). This settings + * plugin may not support the new connection. So step back and retry adding + * the profile anew. */ + _LOGT ("failure to add profile as existing storage \"%s\": %s", + nm_settings_storage_get_filename (update_storage), + local->message); + update_storage = NULL; + g_clear_object (&shadowed_storage); + shadowed_storage_filename = NULL; + g_clear_error (&local); + goto again_add_connection; } - claim_connection (self, added); - return added; } - _LOGD ("Failed to add %s/'%s': %s", - nm_connection_get_uuid (connection), - nm_connection_get_id (connection), - add_error->message); - g_clear_error (&add_error); } - g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "No plugin supported adding this connection"); - return NULL; + if (!success) { + if (!update_storage) { + g_set_error (error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "failure adding connection: %s", + local->message); + } else { + g_set_error (error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_FAILED, + "failure writing connection to existing storage \"%s\": %s", + nm_settings_storage_get_filename (update_storage), + local->message); + } + return FALSE; + } + + sett_conn_entry = _connection_changed_track (self, new_storage, new_connection, TRUE); + + c_list_for_each_entry (sd, &sett_conn_entry->sd_lst_head, sd_lst) { + const NMSettingsMetaData *meta_data; + gs_unref_object NMSettingsStorage *new_tombstone_storage = NULL; + gboolean in_memory; + gboolean simulate; + + meta_data = nm_settings_storage_is_meta_data_alive (sd->storage); + if ( !meta_data + || !meta_data->is_tombstone) + continue; + + if (nm_settings_storage_is_keyfile_run (sd->storage)) + in_memory = TRUE; + else { + if (nm_settings_storage_is_keyfile_run (new_storage)) { + /* Don't remove the file from /etc if we just wrote an in-memory connection */ + continue; + } + in_memory = FALSE; + } + + simulate = FALSE; +again_delete_tombstone: + if (!nms_keyfile_plugin_set_nmmeta_tombstone (priv->keyfile_plugin, + simulate, + uuid, + in_memory, + FALSE, + NULL, + &new_tombstone_storage, + NULL)) { + /* Ups, something went wrong. We really need to get rid of the tombstone. At least + * forget about it in-memory. Upong next restart/reload, this might be reverted + * however :( .*/ + if (!simulate) { + simulate = TRUE; + goto again_delete_tombstone; + } + } + if (new_tombstone_storage) + _connection_changed_track (self, new_tombstone_storage, NULL, FALSE); + } + + _connection_changed_process_all_dirty (self, + FALSE, + sett_flags, + _NM_SETTINGS_CONNECTION_INT_FLAGS_PERSISTENT_MASK, + FALSE, + NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS + | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS + | ( NM_FLAGS_HAS (add_reason, NM_SETTINGS_CONNECTION_ADD_REASON_BLOCK_AUTOCONNECT) + ? NM_SETTINGS_CONNECTION_UPDATE_REASON_BLOCK_AUTOCONNECT + : NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE)); + + nm_assert (sett_conn_entry == _sett_conn_entries_get (self, sett_conn_entry->uuid)); + nm_assert (NM_IS_SETTINGS_CONNECTION (sett_conn_entry->sett_conn)); + + NM_SET_OUT (out_sett_conn, _sett_conn_entry_get_conn (sett_conn_entry)); + return TRUE; +} + +/*****************************************************************************/ + +gboolean +nm_settings_update_connection (NMSettings *self, + NMSettingsConnection *sett_conn, + NMConnection *connection, + NMSettingsConnectionPersistMode persist_mode, + NMSettingsConnectionIntFlags sett_flags, + NMSettingsConnectionIntFlags sett_mask, + NMSettingsConnectionUpdateReason update_reason, + const char *log_context_name, + GError **error) +{ + gs_unref_object NMConnection *connection_cloned_1 = NULL; + gs_unref_object NMConnection *new_connection_cloned = NULL; + gs_unref_object NMConnection *new_connection = NULL; + NMConnection *new_connection_real; + gs_unref_object NMSettingsStorage *cur_storage = NULL; + gs_unref_object NMSettingsStorage *new_storage = NULL; + NMSettingsStorage *drop_storage = NULL; + SettConnEntry *sett_conn_entry; + gboolean cur_in_memory; + gboolean new_in_memory; + const char *uuid; + gboolean tombstone_in_memory = FALSE; + gboolean tombstone_on_disk = FALSE; + + g_return_val_if_fail (NM_IS_SETTINGS (self), FALSE); + g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (sett_conn), FALSE); + g_return_val_if_fail (!connection || NM_IS_CONNECTION (connection), FALSE); + + nm_assert (!NM_FLAGS_ANY (sett_mask, ~_NM_SETTINGS_CONNECTION_INT_FLAGS_PERSISTENT_MASK)); + nm_assert (!NM_FLAGS_ANY (sett_flags, ~sett_mask)); + nm_assert (NM_IN_SET (persist_mode, NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP, + NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST, + NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK, + NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY, + NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED, + NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY)); + + cur_storage = g_object_ref (nm_settings_connection_get_storage (sett_conn)); + + uuid = nm_settings_storage_get_uuid (cur_storage); + + nm_assert (NM_IS_SETTINGS_STORAGE (cur_storage)); + + sett_conn_entry = _sett_conn_entries_get (self, uuid); + + nm_assert (_sett_conn_entry_get_conn (sett_conn_entry) == sett_conn); + + if (connection) { + gs_free_error GError *local = NULL; + + if (!_nm_connection_ensure_normalized (connection, + FALSE, + uuid, + TRUE, + &connection_cloned_1, + &local)) { + _LOGT ("update[%s]: %s: failed because profile is invalid: %s", + nm_settings_storage_get_uuid (cur_storage), + log_context_name, + local->message); + g_set_error (error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "connection is invalid: %s", + local->message); + return FALSE; + } + if (connection_cloned_1) + connection = connection_cloned_1; + } else + connection = nm_settings_connection_get_connection (sett_conn); + + cur_in_memory = nm_settings_storage_is_keyfile_run (cur_storage); + + if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP) { + persist_mode = cur_in_memory + ? NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY + : NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK; + } + + if ( NM_FLAGS_HAS (sett_mask, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED) + && !NM_FLAGS_HAS (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)) { + NMDevice *device; + + /* The connection has been changed by the user, it should no longer be + * considered a default wired connection, and should no longer affect + * the no-auto-default configuration option. + */ + device = nm_settings_connection_default_wired_get_device (sett_conn); + if (device) { + nm_assert (cur_in_memory); + nm_assert (!NM_FLAGS_ANY (nm_settings_connection_get_flags (sett_conn), + NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED + | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE)); + + default_wired_clear_tag (self, device, sett_conn, FALSE); + + if (NM_IN_SET (persist_mode, NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST)) { + /* making a default-wired-connection a regular connection implies persisting + * it to disk (unless specified differently). + * + * Actually, this line is probably unreached, because we should not use + * NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST to toggle the nm-generated + * flag. */ + nm_assert_not_reached (); + persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK; + } + } + } + + if ( persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST + && NM_FLAGS_ANY (sett_mask, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED + | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE) + && NM_FLAGS_ANY ((sett_flags ^ nm_settings_connection_get_flags (sett_conn)) & sett_mask, + NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED + | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE)) { + /* we update the nm-generated/volatile setting of a profile (which is inherrently + * in-memory. The caller did not request to persist this to disk, however we need + * to store the flags in run. */ + nm_assert (cur_in_memory); + persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY; + } + + if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK) + new_in_memory = FALSE; + else if (NM_IN_SET (persist_mode, NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY, + NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED, + NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY)) + new_in_memory = TRUE; + else { + nm_assert (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST); + new_in_memory = cur_in_memory; + } + + if (!new_in_memory) { + /* Persistent connections cannot be volatile nor nm-generated. + * + * That is obviously true for volatile, as it is enforced by Update2() API. + * + * For nm-generated profiles also, because the nm-generated flag is only stored + * for in-memory profiles. If we would persist the profile to /etc it would loose + * the nm-generated flag after restart/reload, and that cannot be right. If a profile + * ends up on disk, the information who created it gets lost. */ + nm_assert (!NM_FLAGS_ANY (sett_flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED + | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE)); + sett_mask |= NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED + | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE; + sett_flags &= ~( NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED + | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE); + } + + if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST) { + new_storage = g_object_ref (cur_storage); + new_connection_real = connection; + _LOGT ("update[%s]: %s: update profile \"%s\" (not persisted)", + nm_settings_storage_get_uuid (cur_storage), + log_context_name, + nm_connection_get_id (connection)); + } else { + NMSettingsStorage *shadowed_storage; + const char *cur_shadowed_storage_filename; + const char *new_shadowed_storage_filename = NULL; + gboolean cur_shadowed_owned; + gboolean new_shadowed_owned = FALSE; + NMSettingsStorage *update_storage = NULL; + gs_free_error GError *local = NULL; + gboolean success; + + cur_shadowed_storage_filename = nm_settings_storage_get_shadowed_storage (cur_storage, &cur_shadowed_owned); + + shadowed_storage = _sett_conn_entry_find_shadowed_storage (sett_conn_entry, cur_shadowed_storage_filename, cur_storage); + if (!shadowed_storage) { + cur_shadowed_storage_filename = NULL; + cur_shadowed_owned = FALSE; + } + + if ( new_in_memory + && persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY) { + if (cur_in_memory) { + drop_storage = shadowed_storage; + update_storage = cur_storage; + } else + drop_storage = cur_storage; + } else if ( !new_in_memory + && cur_in_memory + && shadowed_storage) { + drop_storage = cur_storage; + update_storage = shadowed_storage; + } else if (new_in_memory != cur_in_memory) { + if (!new_in_memory) + drop_storage = cur_storage; + else if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY) + drop_storage = cur_storage; + else { + nm_assert (NM_IN_SET (persist_mode, NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY, + NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED)); + } + } else if (nm_settings_storage_is_keyfile_lib (cur_storage)) { + /* the profile is a keyfile in /usr/lib. It cannot be overwritten, we must migrate it + * from /usr/lib to /etc. */ + } else + update_storage = cur_storage; + + if (new_in_memory) { + if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY) { + /* pass */ + } else if (!cur_in_memory) { + new_shadowed_storage_filename = nm_settings_storage_get_filename_for_shadowed_storage (cur_storage); + if ( new_shadowed_storage_filename + && persist_mode != NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED) + new_shadowed_owned = TRUE; + } else { + new_shadowed_storage_filename = cur_shadowed_storage_filename; + if ( new_shadowed_storage_filename + && persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY) + new_shadowed_owned = TRUE; + } + } + + if (!update_storage) { + success = _add_connection_to_first_plugin (self, + sett_conn_entry, + connection, + new_in_memory, + sett_flags, + new_shadowed_storage_filename, + new_shadowed_owned, + &new_storage, + &new_connection, + &local); + } else { + success = _update_connection_to_plugin (self, + update_storage, + connection, + sett_flags, + update_reason, + new_shadowed_storage_filename, + new_shadowed_owned, + &new_storage, + &new_connection, + &local); + } + if (!success) { + gboolean ignore_failure; + + ignore_failure = NM_FLAGS_ANY (update_reason, NM_SETTINGS_CONNECTION_UPDATE_REASON_IGNORE_PERSIST_FAILURE); + + _LOGT ("update[%s]: %s: %sfailure to %s connection \"%s\" on storage: %s", + nm_settings_storage_get_uuid (cur_storage), + log_context_name, + ignore_failure ? "ignore " : "", + update_storage ? "update" : "write", + nm_connection_get_id (connection), + local->message); + if (!ignore_failure) { + g_set_error (error, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "failed to %s connection: %s", + update_storage ? "update" : "write", + local->message); + return FALSE; + } + + new_storage = g_object_ref (cur_storage); + new_connection_real = connection; + } else { + gs_unref_variant GVariant *agent_owned_secrets = NULL; + + _LOGT ("update[%s]: %s: %s profile \"%s\"", + nm_settings_storage_get_uuid (cur_storage), + log_context_name, + update_storage ? "update" : "write", + nm_connection_get_id (connection)); + + nm_assert_valid_settings_storage (NULL, new_storage); + nm_assert (NM_IS_CONNECTION (new_connection)); + nm_assert (nm_streq (uuid, nm_settings_storage_get_uuid (new_storage))); + + + agent_owned_secrets = nm_connection_to_dbus (connection, + NM_CONNECTION_SERIALIZE_ONLY_SECRETS + | NM_CONNECTION_SERIALIZE_WITH_SECRETS_AGENT_OWNED); + new_connection_real = _connection_changed_normalize_connection (new_storage, + new_connection, + agent_owned_secrets, + &new_connection_cloned); + if (!new_connection_real) { + nm_assert_not_reached (); + new_connection_real = new_connection; + } + } + } + + nm_assert (NM_IS_SETTINGS_STORAGE (new_storage)); + nm_assert (NM_IS_CONNECTION (new_connection_real)); + + _connection_changed_track (self, new_storage, new_connection_real, TRUE); + + if ( drop_storage + && drop_storage != new_storage) { + gs_free_error GError *local = NULL; + + if (!nm_settings_plugin_delete_connection (nm_settings_storage_get_plugin (drop_storage), + drop_storage, + &local)) { + const char *filename; + + filename = nm_settings_storage_get_filename (drop_storage); + _LOGT ("update[%s]: failed to delete moved storage "NM_SETTINGS_STORAGE_PRINT_FMT"%s%s%s: %s", + nm_settings_storage_get_uuid (drop_storage), + NM_SETTINGS_STORAGE_PRINT_ARG (drop_storage), + NM_PRINT_FMT_QUOTED (filename, " (file \"", filename, "\")", ""), + local->message); + /* there is no aborting back form this. We must get rid of the connection and + * cannot do better than log a message. Proceed, but remember to write tombstones. */ + if (nm_settings_storage_is_keyfile_run (cur_storage)) + tombstone_in_memory = TRUE; + else + tombstone_on_disk = TRUE; + } else + _connection_changed_track (self, drop_storage, NULL, FALSE); + } + + _set_nmmeta_tombstone (self, + uuid, + tombstone_on_disk, + tombstone_in_memory, + NULL); + + _connection_changed_process_all_dirty (self, + FALSE, + sett_flags, + sett_mask, + FALSE, + update_reason); + + return TRUE; } +void +nm_settings_delete_connection (NMSettings *self, + NMSettingsConnection *sett_conn, + gboolean allow_add_to_no_auto_default) +{ + NMSettingsStorage *cur_storage; + NMSettingsStorage *shadowed_storage; + NMSettingsStorage *shadowed_storage_unowned = NULL; + NMSettingsStorage *drop_storages[2] = { }; + gs_free_error GError *local = NULL; + SettConnEntry *sett_conn_entry; + const char *cur_shadowed_storage_filename; + const char *new_shadowed_storage_filename = NULL; + gboolean cur_shadowed_owned; + const char *uuid; + gboolean tombstone_in_memory = FALSE; + gboolean tombstone_on_disk = FALSE; + int i; + + g_return_if_fail (NM_IS_SETTINGS (self)); + g_return_if_fail (NM_IS_SETTINGS_CONNECTION (sett_conn)); + g_return_if_fail (nm_settings_has_connection (self, sett_conn)); + + cur_storage = nm_settings_connection_get_storage (sett_conn); + + nm_assert (NM_IS_SETTINGS_STORAGE (cur_storage)); + + uuid = nm_settings_storage_get_uuid (cur_storage); + nm_assert (nm_utils_is_uuid (uuid)); + + sett_conn_entry = _sett_conn_entries_get (self, uuid); + + g_return_if_fail (sett_conn_entry); + nm_assert (sett_conn_entry->sett_conn == sett_conn); + g_return_if_fail (sett_conn_entry->storage == cur_storage); + + if (NMS_IS_KEYFILE_STORAGE (cur_storage)) { + NMSKeyfileStorage *s = NMS_KEYFILE_STORAGE (cur_storage); + + if (NM_IN_SET (s->storage_type, NMS_KEYFILE_STORAGE_TYPE_RUN, + NMS_KEYFILE_STORAGE_TYPE_ETC)) + drop_storages[0] = cur_storage; + else + tombstone_on_disk = TRUE; + } else + drop_storages[0] = cur_storage; + + cur_shadowed_storage_filename = nm_settings_storage_get_shadowed_storage (cur_storage, &cur_shadowed_owned); + + shadowed_storage = _sett_conn_entry_find_shadowed_storage (sett_conn_entry, cur_shadowed_storage_filename, cur_storage); + if (shadowed_storage) { + if (!cur_shadowed_owned) + shadowed_storage_unowned = g_steal_pointer (&shadowed_storage); + } + drop_storages[1] = shadowed_storage; + + for (i = 0; i < (int) G_N_ELEMENTS (drop_storages); i++) { + NMSettingsStorage *storage; + StorageData *sd; + + storage = drop_storages[i]; + if (!storage) + continue; + + if (!nm_settings_plugin_delete_connection (nm_settings_storage_get_plugin (storage), + storage, + &local)) { + _LOGT ("delete-connection: failed to delete storage "NM_SETTINGS_STORAGE_PRINT_FMT": %s", + NM_SETTINGS_STORAGE_PRINT_ARG (storage), + local->message); + g_clear_error (&local); + /* there is no aborting back form this. We must get rid of the connection and + * cannot do better than log a message. Proceed, but remember to write tombstones. */ + if (nm_settings_storage_is_keyfile_run (cur_storage)) + tombstone_in_memory = TRUE; + else + tombstone_on_disk = TRUE; + sett_conn_entry = _sett_conn_entries_get (self, uuid); + } else + sett_conn_entry = _connection_changed_track (self, storage, NULL, FALSE); + + c_list_for_each_entry (sd, &sett_conn_entry->sd_lst_head, sd_lst) { + if (NM_IN_SET (sd->storage, drop_storages[0], + drop_storages[1])) + continue; + if (!_storage_data_is_alive (sd)) + continue; + if (nm_settings_storage_is_meta_data (sd->storage)) + continue; + + if (sd->storage == shadowed_storage_unowned) { + /* this only happens if we leak a profile on disk after NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED. + * We need to write a tombstone and remember the shadowed-storage. */ + tombstone_in_memory = TRUE; + new_shadowed_storage_filename = nm_settings_storage_get_filename (shadowed_storage_unowned); + continue; + } + + /* we have still conflicting storages. We need to hide them with tombstones. */ + if (nm_settings_storage_is_keyfile_run (sd->storage)) { + tombstone_in_memory = TRUE; + continue; + } + tombstone_on_disk = TRUE; + } + } + + _set_nmmeta_tombstone (self, + uuid, + tombstone_on_disk, + tombstone_in_memory, + new_shadowed_storage_filename); + + _connection_changed_process_all_dirty (self, + allow_add_to_no_auto_default, + NM_SETTINGS_CONNECTION_INT_FLAGS_NONE, + NM_SETTINGS_CONNECTION_INT_FLAGS_NONE, + FALSE, + NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE); +} + +/*****************************************************************************/ + static void send_agent_owned_secrets (NMSettings *self, NMSettingsConnection *sett_conn, @@ -1118,9 +2386,8 @@ send_agent_owned_secrets (NMSettings *self, * Only send secrets to agents of the same UID that called update too. */ for_agent = nm_simple_connection_new_clone (nm_settings_connection_get_connection (sett_conn)); - nm_connection_clear_secrets_with_flags (for_agent, - secrets_filter_cb, - GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED)); + _nm_connection_clear_secrets_by_secret_flags (for_agent, + NM_SETTING_SECRET_FLAG_AGENT_OWNED); nm_agent_manager_save_secrets (priv->agent_mgr, nm_dbus_object_get_path (NM_DBUS_OBJECT (sett_conn)), for_agent, @@ -1129,52 +2396,50 @@ send_agent_owned_secrets (NMSettings *self, static void pk_add_cb (NMAuthChain *chain, - GError *chain_error, GDBusMethodInvocation *context, gpointer user_data) { NMSettings *self = NM_SETTINGS (user_data); - NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); NMAuthCallResult result; - GError *error = NULL; + gs_free_error GError *error = NULL; NMConnection *connection = NULL; gs_unref_object NMSettingsConnection *added = NULL; NMSettingsAddCallback callback; gpointer callback_data; NMAuthSubject *subject; const char *perm; - gboolean save_to_disk; - g_assert (context); + nm_assert (G_IS_DBUS_METHOD_INVOCATION (context)); - priv->auths = g_slist_remove (priv->auths, chain); + c_list_unlink (nm_auth_chain_parent_lst_list (chain)); perm = nm_auth_chain_get_data (chain, "perm"); - g_assert (perm); + nm_assert (perm); + result = nm_auth_chain_get_result (chain, perm); - if (chain_error) { - error = g_error_new (NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_FAILED, - "Error checking authorization: %s", - chain_error->message); - } else if (result != NM_AUTH_CALL_RESULT_YES) { + if (result != NM_AUTH_CALL_RESULT_YES) { error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_PERMISSION_DENIED, "Insufficient privileges."); } else { /* Authorized */ connection = nm_auth_chain_get_data (chain, "connection"); - nm_assert (connection); + nm_assert (NM_IS_CONNECTION (connection)); - save_to_disk = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "save-to-disk")); - added = nm_settings_add_connection (self, connection, save_to_disk, &error); + nm_settings_add_connection (self, + connection, + GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "persist-mode")), + GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "add-reason")), + GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "sett-flags")), + &added, + &error); /* The callback may remove the connection from the settings manager (e.g. * because it's found to be incompatible with the device on AddAndActivate). * But we need to keep it alive for a bit longer, precisely to check wehther * it's still known to the setting manager. */ - g_object_ref (added); + nm_g_object_ref (added); } callback = nm_auth_chain_get_data (chain, "callback"); @@ -1184,49 +2449,17 @@ pk_add_cb (NMAuthChain *chain, callback (self, added, error, context, subject, callback_data); /* Send agent-owned secrets to the agents */ - if (!error && added && nm_settings_has_connection (self, added)) + if ( added + && nm_settings_has_connection (self, added)) send_agent_owned_secrets (self, added, subject); - - g_clear_error (&error); - nm_auth_chain_destroy (chain); -} - -/* FIXME: remove if/when kernel supports adhoc wpa */ -static gboolean -is_adhoc_wpa (NMConnection *connection) -{ - NMSettingWireless *s_wifi; - NMSettingWirelessSecurity *s_wsec; - const char *mode, *key_mgmt; - - /* The kernel doesn't support Ad-Hoc WPA connections well at this time, - * and turns them into open networks. It's been this way since at least - * 2.6.30 or so; until that's fixed, disable WPA-protected Ad-Hoc networks. - */ - - s_wifi = nm_connection_get_setting_wireless (connection); - if (!s_wifi) - return FALSE; - - mode = nm_setting_wireless_get_mode (s_wifi); - if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_ADHOC) != 0) - return FALSE; - - s_wsec = nm_connection_get_setting_wireless_security (connection); - if (!s_wsec) - return FALSE; - - key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec); - if (g_strcmp0 (key_mgmt, "wpa-none") != 0) - return FALSE; - - return TRUE; } void nm_settings_add_connection_dbus (NMSettings *self, NMConnection *connection, - gboolean save_to_disk, + NMSettingsConnectionPersistMode persist_mode, + NMSettingsConnectionAddReason add_reason, + NMSettingsConnectionIntFlags sett_flags, NMAuthSubject *subject, GDBusMethodInvocation *context, NMSettingsAddCallback callback, @@ -1242,8 +2475,10 @@ nm_settings_add_connection_dbus (NMSettings *self, g_return_if_fail (NM_IS_AUTH_SUBJECT (subject)); g_return_if_fail (G_IS_DBUS_METHOD_INVOCATION (context)); + nm_assert (!NM_FLAGS_ANY (sett_flags, ~_NM_SETTINGS_CONNECTION_INT_FLAGS_PERSISTENT_MASK)); + /* Connection must be valid, of course */ - if (!nm_connection_verify (connection, &tmp_error)) { + if (_nm_connection_verify (connection, &tmp_error) != NM_SETTING_VERIFY_SUCCESS) { error = g_error_new (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "The connection was invalid: %s", @@ -1252,11 +2487,11 @@ nm_settings_add_connection_dbus (NMSettings *self, goto done; } - /* The kernel doesn't support Ad-Hoc WPA connections well at this time, + /* FIXME: The kernel doesn't support Ad-Hoc WPA connections well at this time, * and turns them into open networks. It's been this way since at least * 2.6.30 or so; until that's fixed, disable WPA-protected Ad-Hoc networks. */ - if (is_adhoc_wpa (connection)) { + if (nm_utils_connection_is_adhoc_wpa (connection)) { error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "WPA Ad-Hoc disabled due to kernel bugs"); @@ -1275,7 +2510,7 @@ nm_settings_add_connection_dbus (NMSettings *self, * request affects more than just the caller, require 'modify.system'. */ s_con = nm_connection_get_setting_connection (connection); - g_assert (s_con); + nm_assert (s_con); if (nm_setting_connection_get_num_permissions (s_con) == 1) perm = NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN; else @@ -1290,14 +2525,17 @@ nm_settings_add_connection_dbus (NMSettings *self, goto done; } - priv->auths = g_slist_append (priv->auths, chain); + c_list_link_tail (&priv->auth_lst_head, nm_auth_chain_parent_lst_list (chain)); + nm_auth_chain_set_data (chain, "perm", (gpointer) perm, NULL); nm_auth_chain_set_data (chain, "connection", g_object_ref (connection), g_object_unref); nm_auth_chain_set_data (chain, "callback", callback, NULL); nm_auth_chain_set_data (chain, "callback-data", user_data, NULL); nm_auth_chain_set_data (chain, "subject", g_object_ref (subject), g_object_unref); - nm_auth_chain_set_data (chain, "save-to-disk", GUINT_TO_POINTER (save_to_disk), NULL); - nm_auth_chain_add_call (chain, perm, TRUE); + nm_auth_chain_set_data (chain, "persist-mode", GUINT_TO_POINTER (persist_mode), NULL); + nm_auth_chain_set_data (chain, "add-reason", GUINT_TO_POINTER (add_reason), NULL); + nm_auth_chain_set_data (chain, "sett-flags", GUINT_TO_POINTER (sett_flags), NULL); + nm_auth_chain_add_call_unsafe (chain, perm, TRUE); return; done: @@ -1314,27 +2552,42 @@ settings_add_connection_add_cb (NMSettings *self, NMAuthSubject *subject, gpointer user_data) { + gboolean is_add_connection_2 = GPOINTER_TO_INT (user_data); + if (error) { g_dbus_method_invocation_return_gerror (context, error); nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD, NULL, FALSE, NULL, subject, error->message); + return; + } + + if (is_add_connection_2) { + GVariantBuilder builder; + + g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); + g_dbus_method_invocation_return_value (context, + g_variant_new ("(oa{sv})", + nm_dbus_object_get_path (NM_DBUS_OBJECT (connection)), + &builder)); } else { g_dbus_method_invocation_return_value (context, g_variant_new ("(o)", nm_dbus_object_get_path (NM_DBUS_OBJECT (connection)))); - nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD, connection, TRUE, NULL, - subject, NULL); } + nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD, connection, TRUE, NULL, + subject, NULL); } static void settings_add_connection_helper (NMSettings *self, GDBusMethodInvocation *context, + gboolean is_add_connection_2, GVariant *settings, - gboolean save_to_disk) + NMSettingsAddConnection2Flags flags) { gs_unref_object NMConnection *connection = NULL; GError *error = NULL; gs_unref_object NMAuthSubject *subject = NULL; + NMSettingsConnectionPersistMode persist_mode; connection = _nm_simple_connection_new_from_dbus (settings, NM_SETTING_PARSE_FLAGS_STRICT @@ -1356,13 +2609,24 @@ settings_add_connection_helper (NMSettings *self, return; } + if (NM_FLAGS_HAS (flags, NM_SETTINGS_ADD_CONNECTION2_FLAG_TO_DISK)) + persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK; + else { + nm_assert (NM_FLAGS_HAS (flags, NM_SETTINGS_ADD_CONNECTION2_FLAG_IN_MEMORY)); + persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY; + } + nm_settings_add_connection_dbus (self, connection, - save_to_disk, + persist_mode, + NM_FLAGS_HAS (flags, NM_SETTINGS_ADD_CONNECTION2_FLAG_BLOCK_AUTOCONNECT) + ? NM_SETTINGS_CONNECTION_ADD_REASON_BLOCK_AUTOCONNECT + : NM_SETTINGS_CONNECTION_ADD_REASON_NONE, + NM_SETTINGS_CONNECTION_INT_FLAGS_NONE, subject, context, settings_add_connection_add_cb, - NULL); + GINT_TO_POINTER (!!is_add_connection_2)); } static void @@ -1378,7 +2642,7 @@ impl_settings_add_connection (NMDBusObject *obj, gs_unref_variant GVariant *settings = NULL; g_variant_get (parameters, "(@a{sa{sv}})", &settings); - settings_add_connection_helper (self, invocation, settings, TRUE); + settings_add_connection_helper (self, invocation, FALSE, settings, NM_SETTINGS_ADD_CONNECTION2_FLAG_TO_DISK); } static void @@ -1394,14 +2658,79 @@ impl_settings_add_connection_unsaved (NMDBusObject *obj, gs_unref_variant GVariant *settings = NULL; g_variant_get (parameters, "(@a{sa{sv}})", &settings); - settings_add_connection_helper (self, invocation, settings, FALSE); + settings_add_connection_helper (self, invocation, FALSE, settings, NM_SETTINGS_ADD_CONNECTION2_FLAG_IN_MEMORY); +} + +static void +impl_settings_add_connection2 (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + NMSettings *self = NM_SETTINGS (obj); + gs_unref_variant GVariant *settings = NULL; + gs_unref_variant GVariant *args = NULL; + NMSettingsAddConnection2Flags flags; + const char *args_name; + GVariantIter iter; + guint32 flags_u; + + g_variant_get (parameters, "(@a{sa{sv}}u@a{sv})", &settings, &flags_u, &args); + + if (NM_FLAGS_ANY (flags_u, ~((guint32) ( NM_SETTINGS_ADD_CONNECTION2_FLAG_TO_DISK + | NM_SETTINGS_ADD_CONNECTION2_FLAG_IN_MEMORY + | NM_SETTINGS_ADD_CONNECTION2_FLAG_BLOCK_AUTOCONNECT)))) { + g_dbus_method_invocation_take_error (invocation, + g_error_new_literal (NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_ARGUMENTS, + "Unknown flags")); + return; + } + + flags = flags_u; + + if (!NM_FLAGS_ANY (flags, NM_SETTINGS_ADD_CONNECTION2_FLAG_TO_DISK + | NM_SETTINGS_ADD_CONNECTION2_FLAG_IN_MEMORY)) { + g_dbus_method_invocation_take_error (invocation, + g_error_new_literal (NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_ARGUMENTS, + "Requires either to-disk (0x1) or in-memory (0x2) flags")); + return; + } + + if (NM_FLAGS_ALL (flags, NM_SETTINGS_ADD_CONNECTION2_FLAG_TO_DISK + | NM_SETTINGS_ADD_CONNECTION2_FLAG_IN_MEMORY)) { + g_dbus_method_invocation_take_error (invocation, + g_error_new_literal (NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_ARGUMENTS, + "Cannot set to-disk (0x1) and in-memory (0x2) flags together")); + return; + } + + nm_assert (g_variant_is_of_type (args, G_VARIANT_TYPE ("a{sv}"))); + + g_variant_iter_init (&iter, args); + while (g_variant_iter_next (&iter, "{&sv}", &args_name, NULL)) { + g_dbus_method_invocation_take_error (invocation, + g_error_new (NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_ARGUMENTS, + "Unsupported argument '%s'", args_name)); + return; + } + + settings_add_connection_helper (self, invocation, TRUE, settings, flags); } +/*****************************************************************************/ + static void impl_settings_load_connections (NMDBusObject *obj, const NMDBusInterfaceInfoExtended *interface_info, const NMDBusMethodInfoExtended *method_info, - GDBusConnection *connection, + GDBusConnection *dbus_connection, const char *sender, GDBusMethodInvocation *invocation, GVariant *parameters) @@ -1409,9 +2738,8 @@ impl_settings_load_connections (NMDBusObject *obj, NMSettings *self = NM_SETTINGS (obj); NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); gs_unref_ptrarray GPtrArray *failures = NULL; - GSList *iter; - guint i; gs_free const char **filenames = NULL; + gs_free char *op_result_str = NULL; g_variant_get (parameters, "(^a&s)", &filenames); @@ -1426,31 +2754,68 @@ impl_settings_load_connections (NMDBusObject *obj, NM_SETTINGS_ERROR_PERMISSION_DENIED)) return; - if (filenames) { - for (i = 0; filenames[i]; i++) { - for (iter = priv->plugins; iter; iter = g_slist_next (iter)) { - NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data); + if ( filenames + && filenames[0]) { + NMSettingsPluginConnectionLoadEntry *entries; + gsize n_entries; + gsize i; + GSList *iter; - if (nm_settings_plugin_load_connection (plugin, filenames[i])) - break; - } + entries = nm_settings_plugin_create_connection_load_entries (filenames, &n_entries); - if (!iter) { - if (!g_path_is_absolute (filenames[i])) - _LOGW ("connection filename '%s' is not an absolute path", filenames[i]); - if (!failures) - failures = g_ptr_array_new (); - g_ptr_array_add (failures, (char *) filenames[i]); - } + for (iter = priv->plugins; iter; iter = iter->next) { + NMSettingsPlugin *plugin = iter->data; + + nm_settings_plugin_load_connections (plugin, + entries, + n_entries, + _plugin_connections_reload_cb, + self); + } + + for (i = 0; i < n_entries; i++) { + NMSettingsPluginConnectionLoadEntry *entry = &entries[i]; + + if (!entry->handled) + _LOGW ("load: no settings plugin could load \"%s\"", entry->filename); + else if (entry->error) { + _LOGW ("load: failure to load \"%s\": %s", entry->filename, entry->error->message); + g_clear_error (&entry->error); + } else + continue; + + if (!failures) + failures = g_ptr_array_new (); + g_ptr_array_add (failures, (char *) entry->filename); } + + nm_clear_g_free (&entries); + + _connection_changed_process_all_dirty (self, + TRUE, + NM_SETTINGS_CONNECTION_INT_FLAGS_NONE, + NM_SETTINGS_CONNECTION_INT_FLAGS_NONE, + TRUE, + NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_SYSTEM_SECRETS + | NM_SETTINGS_CONNECTION_UPDATE_REASON_RESET_AGENT_SECRETS); + + for (iter = priv->plugins; iter; iter = iter->next) + nm_settings_plugin_load_connections_done (iter->data); } if (failures) g_ptr_array_add (failures, NULL); + nm_audit_log_connection_op (NM_AUDIT_OP_CONNS_LOAD, + NULL, + !failures, + (op_result_str = g_strjoinv (",", (char **) filenames)), + invocation, + NULL); + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b^as)", - (gboolean) (!!failures), + (gboolean) (!failures), failures ? (const char **) failures->pdata : NM_PTRARRAY_EMPTY (const char *))); @@ -1466,8 +2831,6 @@ impl_settings_reload_connections (NMDBusObject *obj, GVariant *parameters) { NMSettings *self = NM_SETTINGS (obj); - NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - GSList *iter; /* The permission is already enforced by the D-Bus daemon, but we ensure * that the caller is still alive so that clients are forced to wait and @@ -1480,11 +2843,9 @@ impl_settings_reload_connections (NMDBusObject *obj, NM_SETTINGS_ERROR_PERMISSION_DENIED)) return; - for (iter = priv->plugins; iter; iter = g_slist_next (iter)) { - NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data); + _plugin_connections_reload (self); - nm_settings_plugin_reload_connections (plugin); - } + nm_audit_log_connection_op (NM_AUDIT_OP_CONNS_RELOAD, NULL, TRUE, NULL, invocation, NULL); g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", TRUE)); } @@ -1492,8 +2853,427 @@ impl_settings_reload_connections (NMDBusObject *obj, /*****************************************************************************/ static void +_clear_connections_cached_list (NMSettingsPrivate *priv) +{ + if (!priv->connections_cached_list) + return; + + nm_assert (priv->connections_len == NM_PTRARRAY_LEN (priv->connections_cached_list)); + +#if NM_MORE_ASSERTS + /* set the pointer to a bogus value. This makes it more apparent + * if somebody has a reference to the cached list and still uses + * it. That is a bug, this code just tries to make it blow up + * more eagerly. */ + memset (priv->connections_cached_list, + 0xdeaddead, + sizeof (NMSettingsConnection *) * (priv->connections_len + 1)); +#endif + + nm_clear_g_free (&priv->connections_cached_list); +} + +static void +impl_settings_list_connections (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *dbus_connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + NMSettings *self = NM_SETTINGS (obj); + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + gs_free const char **strv = NULL; + + strv = nm_dbus_utils_get_paths_for_clist (&priv->connections_lst_head, + priv->connections_len, + G_STRUCT_OFFSET (NMSettingsConnection, _connections_lst), + TRUE); + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(^ao)", strv)); +} + +NMSettingsConnection * +nm_settings_get_connection_by_uuid (NMSettings *self, const char *uuid) +{ + g_return_val_if_fail (NM_IS_SETTINGS (self), NULL); + g_return_val_if_fail (uuid != NULL, NULL); + + return _sett_conn_entry_get_conn (_sett_conn_entries_get (self, uuid)); +} + +const char * +nm_settings_get_dbus_path_for_uuid (NMSettings *self, + const char *uuid) +{ + NMSettingsConnection *sett_conn; + + sett_conn = nm_settings_get_connection_by_uuid (self, uuid); + + if (!sett_conn) + return NULL; + + return nm_dbus_object_get_path (NM_DBUS_OBJECT (sett_conn)); +} + +static void +impl_settings_get_connection_by_uuid (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *dbus_connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + NMSettings *self = NM_SETTINGS (obj); + NMSettingsConnection *sett_conn; + gs_unref_object NMAuthSubject *subject = NULL; + GError *error = NULL; + const char *uuid; + + g_variant_get (parameters, "(&s)", &uuid); + + sett_conn = nm_settings_get_connection_by_uuid (self, uuid); + if (!sett_conn) { + error = g_error_new_literal (NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "No connection with the UUID was found."); + goto error; + } + + subject = nm_auth_subject_new_unix_process_from_context (invocation); + if (!subject) { + error = g_error_new_literal (NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_PERMISSION_DENIED, + "Unable to determine UID of request."); + goto error; + } + + if (!nm_auth_is_subject_in_acl_set_error (nm_settings_connection_get_connection (sett_conn), + subject, + NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_PERMISSION_DENIED, + &error)) + goto error; + + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(o)", + nm_dbus_object_get_path (NM_DBUS_OBJECT (sett_conn)))); + return; + +error: + g_dbus_method_invocation_take_error (invocation, error); +} + +/** + * nm_settings_get_connections: + * @self: the #NMSettings + * @out_len: (out) (allow-none): returns the number of returned + * connections. + * + * 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 + * NMSettings operation. + */ +NMSettingsConnection *const* +nm_settings_get_connections (NMSettings *self, guint *out_len) +{ + NMSettingsPrivate *priv; + NMSettingsConnection **v; + NMSettingsConnection *con; + guint i; + + g_return_val_if_fail (NM_IS_SETTINGS (self), NULL); + + priv = NM_SETTINGS_GET_PRIVATE (self); + + nm_assert (priv->connections_len == c_list_length (&priv->connections_lst_head)); + + if (G_UNLIKELY (!priv->connections_cached_list)) { + v = g_new (NMSettingsConnection *, priv->connections_len + 1); + + i = 0; + c_list_for_each_entry (con, &priv->connections_lst_head, _connections_lst) { + nm_assert (i < priv->connections_len); + v[i++] = con; + } + nm_assert (i == priv->connections_len); + v[i] = NULL; + + priv->connections_cached_list = v; + } + + NM_SET_OUT (out_len, priv->connections_len); + return priv->connections_cached_list; +} + +/** + * nm_settings_get_connections_clone: + * @self: the #NMSetting + * @out_len: (allow-none): optional output argument + * @func: caller-supplied function for filtering connections + * @func_data: caller-supplied data passed to @func + * @sort_compare_func: (allow-none): optional function pointer for + * sorting the returned list. + * @sort_data: user data for @sort_compare_func. + * + * Returns: (transfer container) (element-type NMSettingsConnection): + * an NULL terminated array of #NMSettingsConnection objects that were + * filtered by @func (or all connections if no filter was specified). + * The order is arbitrary. + * Caller is responsible for freeing the returned array with free(), + * the contained values do not need to be unrefed. + */ +NMSettingsConnection ** +nm_settings_get_connections_clone (NMSettings *self, + guint *out_len, + NMSettingsConnectionFilterFunc func, + gpointer func_data, + GCompareDataFunc sort_compare_func, + gpointer sort_data) +{ + NMSettingsConnection *const*list_cached; + NMSettingsConnection **list; + guint len, i, j; + + g_return_val_if_fail (NM_IS_SETTINGS (self), NULL); + + list_cached = nm_settings_get_connections (self, &len); + +#if NM_MORE_ASSERTS + nm_assert (list_cached); + for (i = 0; i < len; i++) + nm_assert (NM_IS_SETTINGS_CONNECTION (list_cached[i])); + nm_assert (!list_cached[i]); +#endif + + list = g_new (NMSettingsConnection *, ((gsize) len + 1)); + if (func) { + for (i = 0, j = 0; i < len; i++) { + if (func (self, list_cached[i], func_data)) + list[j++] = list_cached[i]; + } + list[j] = NULL; + len = j; + } else + memcpy (list, list_cached, sizeof (list[0]) * ((gsize) len + 1)); + + if ( len > 1 + && sort_compare_func) { + g_qsort_with_data (list, len, sizeof (NMSettingsConnection *), + sort_compare_func, sort_data); + } + NM_SET_OUT (out_len, len); + return list; +} + +NMSettingsConnection * +nm_settings_get_connection_by_path (NMSettings *self, const char *path) +{ + NMSettingsPrivate *priv; + NMSettingsConnection *connection; + + g_return_val_if_fail (NM_IS_SETTINGS (self), NULL); + g_return_val_if_fail (path, NULL); + + priv = NM_SETTINGS_GET_PRIVATE (self); + + connection = nm_dbus_manager_lookup_object (nm_dbus_object_get_manager (NM_DBUS_OBJECT (self)), + path); + if ( !connection + || !NM_IS_SETTINGS_CONNECTION (connection)) + return NULL; + + nm_assert (c_list_contains (&priv->connections_lst_head, &connection->_connections_lst)); + return connection; +} + +gboolean +nm_settings_has_connection (NMSettings *self, NMSettingsConnection *connection) +{ + gboolean has; + + g_return_val_if_fail (NM_IS_SETTINGS (self), FALSE); + g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (connection), FALSE); + + has = !c_list_is_empty (&connection->_connections_lst); + + nm_assert (has == nm_c_list_contains_entry (&NM_SETTINGS_GET_PRIVATE (self)->connections_lst_head, + connection, + _connections_lst)); + nm_assert (({ + NMSettingsConnection *candidate = NULL; + const char *path; + + path = nm_dbus_object_get_path (NM_DBUS_OBJECT (connection)); + if (path) + candidate = nm_settings_get_connection_by_path (self, path); + + (has == (connection == candidate)); + })); + + return has; +} + +/*****************************************************************************/ + +static void +add_plugin (NMSettings *self, + NMSettingsPlugin *plugin, + const char *pname, + const char *path) +{ + NMSettingsPrivate *priv; + + nm_assert (NM_IS_SETTINGS (self)); + nm_assert (NM_IS_SETTINGS_PLUGIN (plugin)); + + nm_assert (pname); + nm_assert (nm_streq0 (pname, nm_settings_plugin_get_plugin_name (plugin))); + + priv = NM_SETTINGS_GET_PRIVATE (self); + + nm_assert (!g_slist_find (priv->plugins, plugin)); + + priv->plugins = g_slist_append (priv->plugins, g_object_ref (plugin)); + + nm_shutdown_wait_obj_register_full (G_OBJECT (plugin), + g_strdup_printf ("%s-settings-plugin", pname), + TRUE); + + _LOGI ("Loaded settings plugin: %s (%s%s%s)", + pname, + NM_PRINT_FMT_QUOTED (path, "\"", path, "\"", "internal")); +} + +static gboolean +add_plugin_load_file (NMSettings *self, const char *pname, GError **error) +{ + gs_free char *full_name = NULL; + gs_free char *path = NULL; + gs_unref_object NMSettingsPlugin *plugin = NULL; + GModule *module; + NMSettingsPluginFactoryFunc factory_func; + struct stat st; + int errsv; + + full_name = g_strdup_printf ("nm-settings-plugin-%s", pname); + path = g_module_build_path (NMPLUGINDIR, full_name); + + if (stat (path, &st) != 0) { + errsv = errno; + _LOGW ("could not load plugin '%s' from file '%s': %s", pname, path, nm_strerror_native (errsv)); + return TRUE; + } + if (!S_ISREG (st.st_mode)) { + _LOGW ("could not load plugin '%s' from file '%s': not a file", pname, path); + return TRUE; + } + if (st.st_uid != 0) { + _LOGW ("could not load plugin '%s' from file '%s': file must be owned by root", pname, path); + return TRUE; + } + if (st.st_mode & (S_IWGRP | S_IWOTH | S_ISUID)) { + _LOGW ("could not load plugin '%s' from file '%s': invalid file permissions", pname, path); + return TRUE; + } + + module = g_module_open (path, G_MODULE_BIND_LOCAL); + if (!module) { + _LOGW ("could not load plugin '%s' from file '%s': %s", + pname, path, g_module_error ()); + return TRUE; + } + + /* errors after this point are fatal, because we loaded the shared library already. */ + + if (!g_module_symbol (module, "nm_settings_plugin_factory", (gpointer) (&factory_func))) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, + "Could not find plugin '%s' factory function.", + pname); + g_module_close (module); + return FALSE; + } + + /* after accessing the plugin we cannot unload it anymore, because the glib + * types cannot be properly unregistered. */ + g_module_make_resident (module); + + plugin = (*factory_func) (); + if (!NM_IS_SETTINGS_PLUGIN (plugin)) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, + "plugin '%s' returned invalid settings plugin", + pname); + return FALSE; + } + + add_plugin (self, NM_SETTINGS_PLUGIN (plugin), pname, path); + return TRUE; +} + +static void +add_plugin_keyfile (NMSettings *self) +{ + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + + if (priv->keyfile_plugin) + return; + priv->keyfile_plugin = nms_keyfile_plugin_new (); + add_plugin (self, NM_SETTINGS_PLUGIN (priv->keyfile_plugin), "keyfile", NULL); +} + +static gboolean +load_plugins (NMSettings *self, const char *const*plugins, GError **error) +{ + const char *const*iter; + gboolean success = TRUE; + + for (iter = plugins; iter && *iter; iter++) { + const char *pname = *iter; + + if (!*pname || strchr (pname, '/')) { + _LOGW ("ignore invalid plugin \"%s\"", pname); + continue; + } + + if (NM_IN_STRSET (pname, "ifcfg-suse", "ifnet", "ibft", "no-ibft")) { + _LOGW ("skipping deprecated plugin %s", pname); + continue; + } + + /* keyfile plugin is built-in now */ + if (nm_streq (pname, "keyfile")) { + add_plugin_keyfile (self); + continue; + } + + if (nm_utils_strv_find_first ((char **) plugins, + iter - plugins, + pname) >= 0) { + /* the plugin is already mentioned in the list previously. + * Don't load a duplicate. */ + continue; + } + + success = add_plugin_load_file (self, pname, error); + if (!success) + break; + } + + /* If keyfile plugin was not among configured plugins, add it as the last one */ + if (success) + add_plugin_keyfile (self); + + return success; +} + +/*****************************************************************************/ + +static void pk_hostname_cb (NMAuthChain *chain, - GError *chain_error, GDBusMethodInvocation *context, gpointer user_data) { @@ -1503,19 +3283,14 @@ pk_hostname_cb (NMAuthChain *chain, GError *error = NULL; const char *hostname; - g_assert (context); + nm_assert (G_IS_DBUS_METHOD_INVOCATION (context)); - priv->auths = g_slist_remove (priv->auths, chain); + c_list_unlink (nm_auth_chain_parent_lst_list (chain)); result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME); /* If our NMSettingsConnection is already gone, do nothing */ - if (chain_error) { - error = g_error_new (NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_FAILED, - "Error checking authorization: %s", - chain_error->message); - } else if (result != NM_AUTH_CALL_RESULT_YES) { + if (result != NM_AUTH_CALL_RESULT_YES) { error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_PERMISSION_DENIED, "Insufficient privileges."); @@ -1533,8 +3308,6 @@ pk_hostname_cb (NMAuthChain *chain, g_dbus_method_invocation_take_error (context, error); else g_dbus_method_invocation_return_value (context, NULL); - - nm_auth_chain_destroy (chain); } static void @@ -1571,18 +3344,27 @@ impl_settings_save_hostname (NMDBusObject *obj, return; } - priv->auths = g_slist_append (priv->auths, chain); + c_list_link_tail (&priv->auth_lst_head, nm_auth_chain_parent_lst_list (chain)); nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME, TRUE); nm_auth_chain_set_data (chain, "hostname", g_strdup (hostname), g_free); } /*****************************************************************************/ +static void +_hostname_changed_cb (NMHostnameManager *hostname_manager, + GParamSpec *pspec, + gpointer user_data) +{ + _notify (user_data, PROP_HOSTNAME); +} + +/*****************************************************************************/ + static gboolean have_connection_for_device (NMSettings *self, NMDevice *device) { NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - NMSettingConnection *s_con; NMSettingWired *s_wired; const char *setting_hwaddr; const char *perm_hw_addr; @@ -1595,32 +3377,36 @@ have_connection_for_device (NMSettings *self, NMDevice *device) /* Find a wired connection locked to the given MAC address, if any */ c_list_for_each_entry (sett_conn, &priv->connections_lst_head, _connections_lst) { NMConnection *connection = nm_settings_connection_get_connection (sett_conn); - const char *ctype, *iface; + NMSettingConnection *s_con = nm_connection_get_setting_connection (connection); + const char *ctype; + const char *iface; + + ctype = nm_setting_connection_get_connection_type (s_con); + if (!NM_IN_STRSET (ctype, NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_PPPOE_SETTING_NAME)) + continue; if (!nm_device_check_connection_compatible (device, connection, NULL)) continue; - s_con = nm_connection_get_setting_connection (connection); + if (nm_settings_connection_default_wired_get_device (sett_conn)) + continue; - iface = nm_setting_connection_get_interface_name (s_con); - if (iface && strcmp (iface, nm_device_get_iface (device)) != 0) + if (NM_FLAGS_ANY (nm_settings_connection_get_flags (sett_conn), + NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE)) continue; - ctype = nm_setting_connection_get_connection_type (s_con); - if ( strcmp (ctype, NM_SETTING_WIRED_SETTING_NAME) - && strcmp (ctype, NM_SETTING_PPPOE_SETTING_NAME)) + iface = nm_setting_connection_get_interface_name (s_con); + if (!nm_streq0 (iface, nm_device_get_iface (device))) continue; s_wired = nm_connection_get_setting_wired (connection); - if ( !s_wired && nm_streq (ctype, NM_SETTING_PPPOE_SETTING_NAME)) { /* No wired setting; therefore the PPPoE connection applies to any device */ return TRUE; } - nm_assert (s_wired); - setting_hwaddr = nm_setting_wired_get_mac_address (s_wired); if (setting_hwaddr) { /* A connection mac-locked to this device */ @@ -1641,38 +3427,26 @@ have_connection_for_device (NMSettings *self, NMDevice *device) } static void -default_wired_connection_updated_by_user_cb (NMSettingsConnection *connection, gboolean by_user, NMSettings *self) -{ - NMDevice *device; - - if (!by_user) - return; - - /* The connection has been changed by the user, it should no longer be - * considered a default wired connection, and should no longer affect - * the no-auto-default configuration option. - */ - device = g_object_get_qdata (G_OBJECT (connection), _default_wired_device_quark ()); - if (device) - default_wired_clear_tag (self, device, connection, FALSE); -} - -static void default_wired_clear_tag (NMSettings *self, NMDevice *device, - NMSettingsConnection *connection, + NMSettingsConnection *sett_conn, gboolean add_to_no_auto_default) { nm_assert (NM_IS_SETTINGS (self)); nm_assert (NM_IS_DEVICE (device)); - nm_assert (NM_IS_SETTINGS_CONNECTION (connection)); - nm_assert (device == g_object_get_qdata (G_OBJECT (connection), _default_wired_device_quark ())); - nm_assert (connection == g_object_get_qdata (G_OBJECT (device), _default_wired_connection_quark ())); + nm_assert (NM_IS_SETTINGS_CONNECTION (sett_conn)); + nm_assert (device == nm_settings_connection_default_wired_get_device (sett_conn)); + nm_assert (sett_conn == g_object_get_qdata (G_OBJECT (device), _default_wired_connection_quark ())); - g_object_set_qdata (G_OBJECT (connection), _default_wired_device_quark (), NULL); - g_object_set_qdata (G_OBJECT (device), _default_wired_connection_quark (), NULL); + _LOGT ("auto-default: forget association between %s (%s) and device %s (%s)", + nm_settings_connection_get_uuid (sett_conn), + nm_settings_connection_get_id (sett_conn), + nm_device_get_iface (device), + add_to_no_auto_default ? "persisted" : "temporary"); + + nm_settings_connection_default_wired_set_device (sett_conn, NULL); - g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (default_wired_connection_updated_by_user_cb), self); + g_object_set_qdata (G_OBJECT (device), _default_wired_connection_quark (), NULL); if (add_to_no_auto_default) nm_config_set_no_auto_default_for_device (NM_SETTINGS_GET_PRIVATE (self)->config, device); @@ -1681,7 +3455,8 @@ default_wired_clear_tag (NMSettings *self, static void device_realized (NMDevice *device, GParamSpec *pspec, NMSettings *self) { - NMConnection *connection; + gs_unref_object NMConnection *connection = NULL; + NMSettingsPrivate *priv; NMSettingsConnection *added; GError *error = NULL; @@ -1692,22 +3467,33 @@ device_realized (NMDevice *device, GParamSpec *pspec, NMSettings *self) G_CALLBACK (device_realized), self); + priv = NM_SETTINGS_GET_PRIVATE (self); + /* If the device isn't managed or it already has a default wired connection, * ignore it. */ if ( !nm_device_get_managed (device, FALSE) || g_object_get_qdata (G_OBJECT (device), _default_wired_connection_quark ()) - || have_connection_for_device (self, device)) + || have_connection_for_device (self, device) + || nm_config_get_no_auto_default_for_device (priv->config, device)) return; connection = nm_device_new_default_connection (device); if (!connection) return; - /* Add the connection */ - added = nm_settings_add_connection (self, connection, FALSE, &error); - g_object_unref (connection); - + _LOGT ("auto-default: creating in-memory connection %s (%s) for device %s", + nm_connection_get_uuid (connection), + nm_connection_get_id (connection), + nm_device_get_iface (device)); + + nm_settings_add_connection (self, + connection, + NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY, + NM_SETTINGS_CONNECTION_ADD_REASON_NONE, + NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED, + &added, + &error); if (!added) { if (!g_error_matches (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_UUID_EXISTS)) { _LOGW ("(%s) couldn't create default wired connection: %s", @@ -1718,11 +3504,9 @@ device_realized (NMDevice *device, GParamSpec *pspec, NMSettings *self) return; } - g_object_set_qdata (G_OBJECT (added), _default_wired_device_quark (), device); - g_object_set_qdata (G_OBJECT (device), _default_wired_connection_quark (), added); + nm_settings_connection_default_wired_set_device (added, device); - g_signal_connect (added, NM_SETTINGS_CONNECTION_UPDATED_INTERNAL, - G_CALLBACK (default_wired_connection_updated_by_user_cb), self); + g_object_set_qdata (G_OBJECT (device), _default_wired_connection_quark (), added); _LOGI ("(%s): created default wired connection '%s'", nm_device_get_iface (device), @@ -1735,6 +3519,8 @@ nm_settings_device_added (NMSettings *self, NMDevice *device) if (nm_device_is_real (device)) device_realized (device, NULL, self); else { + /* FIXME(shutdown): we need to disconnect this signal handler during + * shutdown. */ g_signal_connect_after (device, "notify::" NM_DEVICE_REAL, G_CALLBACK (device_realized), self); @@ -1758,33 +3544,166 @@ nm_settings_device_removed (NMSettings *self, NMDevice *device, gboolean quittin * remains up and can be assumed if NM starts again. */ if (quitting == FALSE) - nm_settings_connection_delete (connection, NULL); + nm_settings_connection_delete (connection, TRUE); } } /*****************************************************************************/ -const char * -nm_settings_get_startup_complete_blocked_reason (NMSettings *self) +static void +session_monitor_changed_cb (NMSessionMonitor *session_monitor, + NMSettings *self) { NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - const char *uuid = NULL; - - 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"; + NMSettingsConnection *const*list; + guint i, len; + guint generation; + +again: + list = nm_settings_get_connections (self, &len); + generation = priv->connections_generation; + for (i = 0; i < len; i++) { + gboolean is_visible; + + is_visible = nm_settings_connection_check_visibility (list[i], + session_monitor); + nm_settings_connection_set_flags (list[i], + NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE, + is_visible); + if (generation != priv->connections_generation) { + /* the cached list was invalidated. Start again. + * + * Note that nm_settings_connection_recheck_visibility() will do nothing + * if the visibility didn't change (including emitting no signals, + * and not invalidating the list). + * + * Hence, for this to be an endless loop, the settings would have + * to constantly change the visibility flag and also invalidate the list. */ + goto again; + } + } } /*****************************************************************************/ +G_GNUC_PRINTF (4, 5) static void -_hostname_changed_cb (NMHostnameManager *hostname_manager, - GParamSpec *pspec, +_kf_db_log_fcn (NMKeyFileDB *kf_db, + int syslog_level, + gpointer user_data, + const char *fmt, + ...) +{ + NMSettings *self = user_data; + NMLogLevel level = nm_log_level_from_syslog (syslog_level); + + if (_NMLOG_ENABLED (level)) { + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + gs_free char *msg = NULL; + va_list ap; + const char *prefix; + + va_start (ap, fmt); + msg = g_strdup_vprintf (fmt, ap); + va_end (ap); + + if (priv->kf_db_timestamps == kf_db) + prefix = "timestamps"; + else if (priv->kf_db_seen_bssids == kf_db) + prefix = "seen-bssids"; + else { + nm_assert_not_reached (); + prefix = "???"; + } + + _NMLOG (level, "[%s-keyfile]: %s", prefix, msg); + } +} + +static gboolean +_kf_db_got_dirty_flush (NMSettings *self, + gboolean is_timestamps) +{ + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + const char *prefix; + NMKeyFileDB *kf_db; + + if (is_timestamps) { + prefix = "timestamps"; + kf_db = priv->kf_db_timestamps; + priv->kf_db_flush_idle_id_timestamps = 0; + } else { + prefix = "seen-bssids"; + kf_db = priv->kf_db_seen_bssids; + priv->kf_db_flush_idle_id_seen_bssids = 0; + } + + if (nm_key_file_db_is_dirty (kf_db)) + nm_key_file_db_to_file (kf_db, FALSE); + else { + _LOGT ("[%s-keyfile]: skip saving changes to \"%s\"", + prefix, + nm_key_file_db_get_filename (kf_db)); + } + + return G_SOURCE_REMOVE; +} + +static gboolean +_kf_db_got_dirty_flush_timestamps_cb (gpointer user_data) +{ + return _kf_db_got_dirty_flush (user_data, + TRUE); +} + +static gboolean +_kf_db_got_dirty_flush_seen_bssids_cb (gpointer user_data) +{ + return _kf_db_got_dirty_flush (user_data, + FALSE); +} + +static void +_kf_db_got_dirty_fcn (NMKeyFileDB *kf_db, gpointer user_data) { - _notify (user_data, PROP_HOSTNAME); + NMSettings *self = user_data; + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + GSourceFunc idle_func; + guint *p_id; + const char *prefix; + + if (priv->kf_db_timestamps == kf_db) { + prefix = "timestamps"; + p_id = &priv->kf_db_flush_idle_id_timestamps; + idle_func = _kf_db_got_dirty_flush_timestamps_cb; + } else if (priv->kf_db_seen_bssids == kf_db) { + prefix = "seen-bssids"; + p_id = &priv->kf_db_flush_idle_id_seen_bssids; + idle_func = _kf_db_got_dirty_flush_seen_bssids_cb; + } else { + nm_assert_not_reached (); + return; + } + + if (*p_id != 0) + return; + _LOGT ("[%s-keyfile]: schedule flushing changes to disk", prefix); + *p_id = g_idle_add_full (G_PRIORITY_LOW, idle_func, self, NULL); +} + +void +nm_settings_kf_db_write (NMSettings *self) +{ + NMSettingsPrivate *priv; + + g_return_if_fail (NM_IS_SETTINGS (self)); + + priv = NM_SETTINGS_GET_PRIVATE (self); + if (priv->kf_db_timestamps) + nm_key_file_db_to_file (priv->kf_db_timestamps, TRUE); + if (priv->kf_db_seen_bssids) + nm_key_file_db_to_file (priv->kf_db_seen_bssids, TRUE); } /*****************************************************************************/ @@ -1794,19 +3713,47 @@ nm_settings_start (NMSettings *self, GError **error) { NMSettingsPrivate *priv; gs_strfreev char **plugins = NULL; + GSList *iter; priv = NM_SETTINGS_GET_PRIVATE (self); + nm_assert (!priv->started); + + priv->hostname_manager = g_object_ref (nm_hostname_manager_get ()); + + priv->kf_db_timestamps = nm_key_file_db_new (NMSTATEDIR "/timestamps", + "timestamps", + _kf_db_log_fcn, + _kf_db_got_dirty_fcn, + self); + priv->kf_db_seen_bssids = nm_key_file_db_new (NMSTATEDIR "/seen-bssids", + "seen-bssids", + _kf_db_log_fcn, + _kf_db_got_dirty_fcn, + self); + nm_key_file_db_start (priv->kf_db_timestamps); + nm_key_file_db_start (priv->kf_db_seen_bssids); + /* Load the plugins; fail if a plugin is not found. */ plugins = nm_config_data_get_plugins (nm_config_get_data_orig (priv->config), TRUE); - if (!load_plugins (self, (const char **) plugins, error)) + if (!load_plugins (self, (const char *const*) plugins, error)) return FALSE; - load_connections (self); - check_startup_complete (self); + for (iter = priv->plugins; iter; iter = iter->next) { + NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data); + + g_signal_connect (plugin, NM_SETTINGS_PLUGIN_UNMANAGED_SPECS_CHANGED, + G_CALLBACK (_plugin_unmanaged_specs_changed), self); + g_signal_connect (plugin, NM_SETTINGS_PLUGIN_UNRECOGNIZED_SPECS_CHANGED, + G_CALLBACK (_plugin_unrecognized_specs_changed), self); + } + + _plugin_unmanaged_specs_changed (NULL, self); + _plugin_unrecognized_specs_changed (NULL, self); + + _plugin_connections_reload (self); - priv->hostname_manager = g_object_ref (nm_hostname_manager_get ()); g_signal_connect (priv->hostname_manager, "notify::"NM_HOSTNAME_MANAGER_HOSTNAME, G_CALLBACK (_hostname_changed_cb), @@ -1814,6 +3761,14 @@ nm_settings_start (NMSettings *self, GError **error) if (nm_hostname_manager_get_hostname (priv->hostname_manager)) _notify (self, PROP_HOSTNAME); + priv->started = TRUE; + _startup_complete_check (self, 0); + + /* FIXME(shutdown): we also need a nm_settings_stop() during shutdown. + * + * In particular, we need to remove all in-memory keyfiles from /run that are nm-generated. + * alternatively, the nm-generated flag must also be persisted and loaded to /run. */ + return TRUE; } @@ -1825,39 +3780,29 @@ get_property (GObject *object, guint prop_id, { NMSettings *self = NM_SETTINGS (object); NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - const GSList *specs, *iter; - guint i; - char **strvs; const char **strv; switch (prop_id) { case PROP_UNMANAGED_SPECS: - specs = nm_settings_get_unmanaged_specs (self); - strvs = g_new (char *, g_slist_length ((GSList *) specs) + 1); - i = 0; - for (iter = specs; iter; iter = iter->next) - strvs[i++] = g_strdup (iter->data); - strvs[i] = NULL; - g_value_take_boxed (value, strvs); + g_value_take_boxed (value, + _nm_utils_slist_to_strv (nm_settings_get_unmanaged_specs (self), + TRUE)); break; case PROP_HOSTNAME: g_value_set_string (value, - priv->hostname_manager - ? nm_hostname_manager_get_hostname (priv->hostname_manager) - : NULL); + priv->hostname_manager + ? nm_hostname_manager_get_hostname (priv->hostname_manager) + : NULL); break; case PROP_CAN_MODIFY: g_value_set_boolean (value, TRUE); break; case PROP_CONNECTIONS: - if (priv->connections_loaded) { - strv = nm_dbus_utils_get_paths_for_clist (&priv->connections_lst_head, - priv->connections_len, - G_STRUCT_OFFSET (NMSettingsConnection, _connections_lst), - TRUE); - g_value_take_boxed (value, nm_utils_strv_make_deep_copied (strv)); - } else - g_value_set_boxed (value, NULL); + strv = nm_dbus_utils_get_paths_for_clist (&priv->connections_lst_head, + priv->connections_len, + G_STRUCT_OFFSET (NMSettingsConnection, _connections_lst), + TRUE); + g_value_take_boxed (value, nm_utils_strv_make_deep_copied (strv)); break; case PROP_STARTUP_COMPLETE: g_value_set_boolean (value, !nm_settings_get_startup_complete_blocked_reason (self)); @@ -1875,10 +3820,24 @@ nm_settings_init (NMSettings *self) { NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + c_list_init (&priv->auth_lst_head); c_list_init (&priv->connections_lst_head); - priv->agent_mgr = g_object_ref (nm_agent_manager_get ()); + c_list_init (&priv->sce_dirty_lst_head); + priv->sce_idx = g_hash_table_new_full (nm_pstr_hash, nm_pstr_equal, + NULL, (GDestroyNotify) _sett_conn_entry_free); + priv->config = g_object_ref (nm_config_get ()); + + priv->agent_mgr = g_object_ref (nm_agent_manager_get ()); + + priv->platform = g_object_ref (NM_PLATFORM_GET); + + priv->session_monitor = g_object_ref (nm_session_monitor_get ()); + g_signal_connect (priv->session_monitor, + NM_SESSION_MONITOR_CHANGED, + G_CALLBACK (session_monitor_changed_cb), + self); } NMSettings * @@ -1892,11 +3851,18 @@ dispose (GObject *object) { NMSettings *self = NM_SETTINGS (object); NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + CList *iter; + nm_assert (c_list_is_empty (&priv->sce_dirty_lst_head)); + nm_assert (g_hash_table_size (priv->sce_idx) == 0); + + nm_clear_g_source (&priv->startup_complete_timeout_id); + nm_clear_g_signal_handler (priv->platform, &priv->startup_complete_platform_change_id); + nm_clear_pointer (&priv->startup_complete_idx, g_hash_table_destroy); g_clear_object (&priv->startup_complete_blocked_by); - g_slist_free_full (priv->auths, (GDestroyNotify) nm_auth_chain_destroy); - priv->auths = NULL; + while ((iter = c_list_first (&priv->auth_lst_head))) + nm_auth_chain_destroy (nm_auth_chain_parent_lst_entry (iter)); if (priv->hostname_manager) { g_signal_handlers_disconnect_by_func (priv->hostname_manager, @@ -1905,6 +3871,13 @@ dispose (GObject *object) g_clear_object (&priv->hostname_manager); } + if (priv->session_monitor) { + g_signal_handlers_disconnect_by_func (priv->session_monitor, + G_CALLBACK (session_monitor_changed_cb), + self); + g_clear_object (&priv->session_monitor); + } + G_OBJECT_CLASS (nm_settings_parent_class)->dispose (object); } @@ -1919,6 +3892,11 @@ finalize (GObject *object) nm_assert (c_list_is_empty (&priv->connections_lst_head)); + nm_assert (c_list_is_empty (&priv->sce_dirty_lst_head)); + nm_assert (g_hash_table_size (priv->sce_idx) == 0); + + nm_clear_pointer (&priv->sce_idx, g_hash_table_destroy); + g_slist_free_full (priv->unmanaged_specs, g_free); g_slist_free_full (priv->unrecognized_specs, g_free); @@ -1929,11 +3907,22 @@ finalize (GObject *object) g_signal_handlers_disconnect_by_data (plugin, self); } + g_clear_object (&priv->keyfile_plugin); + g_clear_object (&priv->agent_mgr); - g_clear_object (&priv->config); + nm_clear_g_source (&priv->kf_db_flush_idle_id_timestamps); + nm_clear_g_source (&priv->kf_db_flush_idle_id_seen_bssids); + nm_key_file_db_to_file (priv->kf_db_timestamps, FALSE); + nm_key_file_db_to_file (priv->kf_db_seen_bssids, FALSE); + nm_key_file_db_destroy (priv->kf_db_timestamps); + nm_key_file_db_destroy (priv->kf_db_seen_bssids); G_OBJECT_CLASS (nm_settings_parent_class)->finalize (object); + + g_clear_object (&priv->config); + + g_clear_object (&priv->platform); } static const GDBusSignalInfo signal_info_new_connection = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT ( @@ -2001,6 +3990,21 @@ static const NMDBusInterfaceInfoExtended interface_info_settings = { ), NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "AddConnection2", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("settings", "a{sa{sv}}"), + NM_DEFINE_GDBUS_ARG_INFO ("flags", "u"), + NM_DEFINE_GDBUS_ARG_INFO ("args", "a{sv}"), + ), + .out_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("path", "o"), + NM_DEFINE_GDBUS_ARG_INFO ("result", "a{sv}"), + ), + ), + .handle = impl_settings_add_connection2, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( "LoadConnections", .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( NM_DEFINE_GDBUS_ARG_INFO ("filenames", "as"), @@ -2104,7 +4108,7 @@ nm_settings_class_init (NMSettingsClass *class) G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, - G_TYPE_NONE, 2, NM_TYPE_SETTINGS_CONNECTION, G_TYPE_BOOLEAN); + G_TYPE_NONE, 2, NM_TYPE_SETTINGS_CONNECTION, G_TYPE_UINT); signals[CONNECTION_REMOVED] = g_signal_new (NM_SETTINGS_SIGNAL_CONNECTION_REMOVED, diff --git a/src/settings/nm-settings.h b/src/settings/nm-settings.h index eb74c09c..d2bf72d6 100644 --- a/src/settings/nm-settings.h +++ b/src/settings/nm-settings.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * Søren Sandmann <sandmann@daimi.au.dk> @@ -28,6 +27,8 @@ #include "nm-connection.h" +#include "nm-settings-connection.h" + #define NM_TYPE_SETTINGS (nm_settings_get_type ()) #define NM_SETTINGS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTINGS, NMSettings)) #define NM_SETTINGS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTINGS, NMSettingsClass)) @@ -68,6 +69,7 @@ NMSettings *nm_settings_get (void); #define NM_SETTINGS_GET (nm_settings_get ()) NMSettings *nm_settings_new (void); + gboolean nm_settings_start (NMSettings *self, GError **error); typedef void (*NMSettingsAddCallback) (NMSettings *settings, @@ -79,7 +81,9 @@ typedef void (*NMSettingsAddCallback) (NMSettings *settings, void nm_settings_add_connection_dbus (NMSettings *self, NMConnection *connection, - gboolean save_to_disk, + NMSettingsConnectionPersistMode persist_mode, + NMSettingsConnectionAddReason add_reason, + NMSettingsConnectionIntFlags sett_flags, NMAuthSubject *subject, GDBusMethodInvocation *context, NMSettingsAddCallback callback, @@ -94,16 +98,37 @@ NMSettingsConnection **nm_settings_get_connections_clone (NMSettings *self, GCompareDataFunc sort_compare_func, gpointer sort_data); -NMSettingsConnection *nm_settings_add_connection (NMSettings *settings, - NMConnection *connection, - gboolean save_to_disk, - GError **error); +gboolean nm_settings_add_connection (NMSettings *settings, + NMConnection *connection, + NMSettingsConnectionPersistMode persist_mode, + NMSettingsConnectionAddReason add_reason, + NMSettingsConnectionIntFlags sett_flags, + NMSettingsConnection **out_sett_conn, + GError **error); + +gboolean nm_settings_update_connection (NMSettings *self, + NMSettingsConnection *sett_conn, + NMConnection *new_connection, + NMSettingsConnectionPersistMode persist_mode, + NMSettingsConnectionIntFlags sett_flags, + NMSettingsConnectionIntFlags sett_mask, + NMSettingsConnectionUpdateReason update_reason, + const char *log_context_name, + GError **error); + +void nm_settings_delete_connection (NMSettings *self, + NMSettingsConnection *sett_conn, + gboolean allow_add_to_no_auto_default); + NMSettingsConnection *nm_settings_get_connection_by_path (NMSettings *settings, const char *path); NMSettingsConnection *nm_settings_get_connection_by_uuid (NMSettings *settings, const char *uuid); +const char *nm_settings_get_dbus_path_for_uuid (NMSettings *self, + const char *uuid); + gboolean nm_settings_has_connection (NMSettings *self, NMSettingsConnection *connection); const GSList *nm_settings_get_unmanaged_specs (NMSettings *self); @@ -114,4 +139,6 @@ void nm_settings_device_removed (NMSettings *self, NMDevice *device, gboolean qu const char *nm_settings_get_startup_complete_blocked_reason (NMSettings *self); +void nm_settings_kf_db_write (NMSettings *settings); + #endif /* __NM_SETTINGS_H__ */ diff --git a/src/settings/plugins/ibft/meson.build b/src/settings/plugins/ibft/meson.build deleted file mode 100644 index c33b24cc..00000000 --- a/src/settings/plugins/ibft/meson.build +++ /dev/null @@ -1,41 +0,0 @@ -name = 'nm-settings-plugin-ibft' - -libnms_ibft_core = static_library( - 'nms-ibft-core', - 'nms-ibft-reader.c', - dependencies: nm_dep, -) - -sources = files( - 'nms-ibft-connection.c', - 'nms-ibft-plugin.c', -) - -libnm_settings_plugin_ibft = shared_module( - name, - sources: sources, - dependencies: nm_dep, - link_with: libnms_ibft_core, - link_args: ldflags_linker_script_settings, - link_depends: linker_script_settings, - install: true, - install_dir: nm_plugindir, -) - -core_plugins += libnm_settings_plugin_ibft - -# FIXME: check_so_symbols replacement -''' -run_target( - 'check-local-symbols-settings-ibft', - command: [check_so_symbols, libnm_settings_plugin_ibft.full_path()], - depends: libnm_settings_plugin_ibft, -) - -check-local-symbols-settings-ibft: src/settings/plugins/ibft/libnm-settings-plugin-ibft.la - $(call check_so_symbols,$(builddir)/src/settings/plugins/ibft/.libs/libnm-settings-plugin-ibft.so) -''' - -if enable_tests - subdir('tests') -endif diff --git a/src/settings/plugins/ibft/nms-ibft-connection.c b/src/settings/plugins/ibft/nms-ibft-connection.c deleted file mode 100644 index a36d8a31..00000000 --- a/src/settings/plugins/ibft/nms-ibft-connection.c +++ /dev/null @@ -1,77 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager system settings service - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright 2014 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nms-ibft-connection.h" - -#include <net/ethernet.h> -#include <netinet/ether.h> -#include <glib/gstdio.h> - -#include "nms-ibft-reader.h" - -/*****************************************************************************/ - -struct _NMSIbftConnection { - NMSettingsConnection parent; -}; - -struct _NMSIbftConnectionClass { - NMSettingsConnectionClass parent; -}; - -G_DEFINE_TYPE (NMSIbftConnection, nms_ibft_connection, NM_TYPE_SETTINGS_CONNECTION) - -/*****************************************************************************/ - -static void -nms_ibft_connection_init (NMSIbftConnection *connection) -{ -} - -NMSIbftConnection * -nms_ibft_connection_new (const GPtrArray *block, GError **error) -{ - NMConnection *source; - GObject *object; - - source = nms_ibft_reader_get_connection_from_block (block, error); - if (!source) - return NULL; - - object = g_object_new (NMS_TYPE_IBFT_CONNECTION, NULL); - /* Update settings with what was read from iscsiadm */ - if (!nm_settings_connection_update (NM_SETTINGS_CONNECTION (object), - source, - NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP_SAVED, - NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, - NULL, - error)) - g_clear_object (&object); - - return (NMSIbftConnection *) object; -} - -static void -nms_ibft_connection_class_init (NMSIbftConnectionClass *ibft_connection_class) -{ -} - diff --git a/src/settings/plugins/ibft/nms-ibft-connection.h b/src/settings/plugins/ibft/nms-ibft-connection.h deleted file mode 100644 index de050662..00000000 --- a/src/settings/plugins/ibft/nms-ibft-connection.h +++ /dev/null @@ -1,41 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager system settings service - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright 2014 Red Hat, Inc. - */ - -#ifndef __NMS_IBFT_CONNECTION_H__ -#define __NMS_IBFT_CONNECTION_H__ - -#include "settings/nm-settings-connection.h" - -#define NMS_TYPE_IBFT_CONNECTION (nms_ibft_connection_get_type ()) -#define NMS_IBFT_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMS_TYPE_IBFT_CONNECTION, NMSIbftConnection)) -#define NMS_IBFT_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMS_TYPE_IBFT_CONNECTION, NMSIbftConnectionClass)) -#define NMS_IS_IBFT_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMS_TYPE_IBFT_CONNECTION)) -#define NMS_IS_IBFT_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMS_TYPE_IBFT_CONNECTION)) -#define NMS_IBFT_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMS_TYPE_IBFT_CONNECTION, NMSIbftConnectionClass)) - -typedef struct _NMSIbftConnection NMSIbftConnection; -typedef struct _NMSIbftConnectionClass NMSIbftConnectionClass; - -GType nms_ibft_connection_get_type (void); - -NMSIbftConnection *nms_ibft_connection_new (const GPtrArray *block, - GError **error); - -#endif /* __NMS_IBFT_CONNECTION_H__ */ diff --git a/src/settings/plugins/ibft/nms-ibft-plugin.c b/src/settings/plugins/ibft/nms-ibft-plugin.c deleted file mode 100644 index 47051995..00000000 --- a/src/settings/plugins/ibft/nms-ibft-plugin.c +++ /dev/null @@ -1,155 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager system settings service - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright 2014 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nms-ibft-plugin.h" - -#include <unistd.h> -#include <gmodule.h> - -#include "nm-setting-connection.h" -#include "settings/nm-settings-plugin.h" -#include "NetworkManagerUtils.h" - -#include "nms-ibft-reader.h" -#include "nms-ibft-connection.h" - -/*****************************************************************************/ - -typedef struct { - GHashTable *connections; /* uuid::connection */ - gboolean initialized; -} NMSIbftPluginPrivate; - -struct _NMSIbftPlugin { - NMSettingsPlugin parent; - NMSIbftPluginPrivate _priv; -}; - -struct _NMSIbftPluginClass { - NMSettingsPluginClass parent; -}; - -G_DEFINE_TYPE (NMSIbftPlugin, nms_ibft_plugin, NM_TYPE_SETTINGS_PLUGIN); - -#define NMS_IBFT_PLUGIN_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMSIbftPlugin, NMS_IS_IBFT_PLUGIN) - -/*****************************************************************************/ - -static NMSIbftPlugin *nms_ibft_plugin_get (void); - -NM_DEFINE_SINGLETON_GETTER (NMSIbftPlugin, nms_ibft_plugin_get, NMS_TYPE_IBFT_PLUGIN); - -/*****************************************************************************/ - -static void -read_connections (NMSIbftPlugin *self) -{ - NMSIbftPluginPrivate *priv = NMS_IBFT_PLUGIN_GET_PRIVATE (self); - nm_auto_free_ibft_blocks GSList *blocks = NULL; - GSList *iter; - gs_free_error GError *error = NULL; - NMSIbftConnection *connection; - - if (!nms_ibft_reader_load_blocks ("/sbin/iscsiadm", &blocks, &error)) { - nm_log_dbg (LOGD_SETTINGS, "ibft: failed to read iscsiadm records: %s", error->message); - return; - } - - for (iter = blocks; iter; iter = iter->next) { - connection = nms_ibft_connection_new (iter->data, &error); - if (!connection) { - nm_log_warn (LOGD_SETTINGS, "ibft: failed to read iscsiadm record: %s", error->message); - g_clear_error (&error); - continue; - } - - nm_log_info (LOGD_SETTINGS, "ibft: read connection '%s'", - nm_settings_connection_get_id (NM_SETTINGS_CONNECTION (connection))); - g_hash_table_insert (priv->connections, - g_strdup (nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (connection))), - connection); - } -} - -static GSList * -get_connections (NMSettingsPlugin *config) -{ - NMSIbftPlugin *self = NMS_IBFT_PLUGIN (config); - NMSIbftPluginPrivate *priv = NMS_IBFT_PLUGIN_GET_PRIVATE (self); - GSList *list = NULL; - GHashTableIter iter; - NMSIbftConnection *connection; - - if (!priv->initialized) { - read_connections (self); - priv->initialized = TRUE; - } - - g_hash_table_iter_init (&iter, priv->connections); - while (g_hash_table_iter_next (&iter, NULL, (gpointer) &connection)) - list = g_slist_prepend (list, connection); - - return list; -} - -/*****************************************************************************/ - -static void -nms_ibft_plugin_init (NMSIbftPlugin *self) -{ - NMSIbftPluginPrivate *priv = NMS_IBFT_PLUGIN_GET_PRIVATE (self); - - priv->connections = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_object_unref); -} - -static void -dispose (GObject *object) -{ - NMSIbftPlugin *self = NMS_IBFT_PLUGIN (object); - NMSIbftPluginPrivate *priv = NMS_IBFT_PLUGIN_GET_PRIVATE (self); - - if (priv->connections) { - g_hash_table_destroy (priv->connections); - priv->connections = NULL; - } - - G_OBJECT_CLASS (nms_ibft_plugin_parent_class)->dispose (object); -} - -static void -nms_ibft_plugin_class_init (NMSIbftPluginClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - NMSettingsPluginClass *plugin_class = NM_SETTINGS_PLUGIN_CLASS (klass); - - object_class->dispose = dispose; - - plugin_class->get_connections = get_connections; -} - -/*****************************************************************************/ - -G_MODULE_EXPORT NMSettingsPlugin * -nm_settings_plugin_factory (void) -{ - return NM_SETTINGS_PLUGIN (g_object_ref (nms_ibft_plugin_get ())); -} diff --git a/src/settings/plugins/ibft/nms-ibft-plugin.h b/src/settings/plugins/ibft/nms-ibft-plugin.h deleted file mode 100644 index 33a7620b..00000000 --- a/src/settings/plugins/ibft/nms-ibft-plugin.h +++ /dev/null @@ -1,36 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager system settings service - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright 2014 Red Hat, Inc. - */ - -#ifndef __NMS_IBFT_PLUGIN_H__ -#define __NMS_IBFT_PLUGIN_H__ - -#define NMS_TYPE_IBFT_PLUGIN (nms_ibft_plugin_get_type ()) -#define NMS_IBFT_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMS_TYPE_IBFT_PLUGIN, NMSIbftPlugin)) -#define NMS_IBFT_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMS_TYPE_IBFT_PLUGIN, NMSIbftPluginClass)) -#define NMS_IS_IBFT_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMS_TYPE_IBFT_PLUGIN)) -#define NMS_IS_IBFT_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMS_TYPE_IBFT_PLUGIN)) -#define NMS_IBFT_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMS_TYPE_IBFT_PLUGIN, NMSIbftPluginClass)) - -typedef struct _NMSIbftPlugin NMSIbftPlugin; -typedef struct _NMSIbftPluginClass NMSIbftPluginClass; - -GType nms_ibft_plugin_get_type (void); - -#endif /* __NMS_IBFT_PLUGIN_H__ */ diff --git a/src/settings/plugins/ibft/nms-ibft-reader.c b/src/settings/plugins/ibft/nms-ibft-reader.c deleted file mode 100644 index fde30383..00000000 --- a/src/settings/plugins/ibft/nms-ibft-reader.c +++ /dev/null @@ -1,535 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager system settings service - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright 2014 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nms-ibft-reader.h" - -#include <stdlib.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <arpa/inet.h> -#include <sys/wait.h> -#include <sys/inotify.h> -#include <sys/ioctl.h> -#include <unistd.h> - -#include "nm-core-internal.h" -#include "platform/nm-platform.h" -#include "NetworkManagerUtils.h" - -/*****************************************************************************/ - -#define PARSE_WARNING(msg...) nm_log_warn (LOGD_SETTINGS, " " msg) - -/* Removes trailing whitespace and whitespace before and immediately after the '=' */ -static char * -remove_most_whitespace (const char *src) -{ - char *s_new, *s2; - const char *svalue; - - src = nm_str_skip_leading_spaces (src); - - svalue = strchr (src, '='); - if (!svalue || svalue == src) - return NULL; - - s_new = g_new (char, strlen (src) + 1); - - memcpy (s_new, src, svalue - src); - s_new[svalue - src] = '\0'; - g_strchomp (s_new); - - svalue++; - while (*svalue && g_ascii_isspace (*svalue)) - svalue++; - - s2 = strchr (s_new, '\0'); - s2[0] = '='; - strcpy (++s2, svalue); - g_strchomp (s2); - - return s_new; -} - -#define TAG_BEGIN "# BEGIN RECORD" -#define TAG_END "# END RECORD" - -/** - * nms_ibft_reader_load_blocks: - * @iscsiadm_path: path to iscsiadm program - * @out_blocks: on return if successful, a #GSList of #GPtrArray, or %NULL on - * failure - * @error: location for an error on failure - * - * Parses iscsiadm output and returns a #GSList of #GPtrArray in the @out_blocks - * argument on success, otherwise @out_blocks is set to %NULL. Each #GPtrArray - * in @out_blocks contains the lines from an iscsiadm interface block. - * - * Returns: %TRUE on success, %FALSE on errors - */ -gboolean -nms_ibft_reader_load_blocks (const char *iscsiadm_path, - GSList **out_blocks, - GError **error) -{ - const char *argv[4] = { iscsiadm_path, "-m", "fw", NULL }; - const char *envp[1] = { NULL }; - nm_auto_free_ibft_blocks GSList *blocks = NULL; - gs_free char *out = NULL; - gs_free char *err = NULL; - gs_free const char **lines = NULL; - GPtrArray *block_lines = NULL; - gsize i; - int status = 0; - - g_return_val_if_fail (iscsiadm_path != NULL, FALSE); - g_return_val_if_fail (out_blocks != NULL && *out_blocks == NULL, FALSE); - - if (!g_spawn_sync ("/", (char **) argv, (char **) envp, 0, - NULL, NULL, &out, &err, &status, error)) - return FALSE; - - if (!WIFEXITED (status)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "iBFT: %s exited abnormally.", iscsiadm_path); - return FALSE; - } - - if (WEXITSTATUS (status) != 0) { - if (err) { - char *nl; - - /* the error message contains newlines. concatenate the lines with whitespace */ - for (nl = err; *nl; nl++) { - if (*nl == '\n') - *nl = ' '; - } - } - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "iBFT: %s exited with error %d. Message: '%s'", - iscsiadm_path, WEXITSTATUS (status), err ?: "(none)"); - return FALSE; - } - - nm_log_dbg (LOGD_SETTINGS, "iBFT records:\n%s", out); - - lines = nm_utils_strsplit_set (out, "\n\r"); - for (i = 0; lines && lines[i]; i++) { - const char *ss = lines[i]; - - if (!g_ascii_strncasecmp (ss, TAG_BEGIN, NM_STRLEN (TAG_BEGIN))) { - if (block_lines) { - PARSE_WARNING ("malformed iscsiadm record: missing END RECORD."); - nm_clear_pointer (&block_lines, g_ptr_array_unref); - } - /* Start new record */ - block_lines = g_ptr_array_new_full (15, g_free); - } else if (!g_ascii_strncasecmp (ss, TAG_END, NM_STRLEN (TAG_END))) { - if (block_lines) { - if (block_lines->len) - blocks = g_slist_prepend (blocks, g_steal_pointer (&block_lines)); - else - g_ptr_array_unref (g_steal_pointer (&block_lines)); - } - } else if (block_lines) { - char *s = remove_most_whitespace (ss); - - if (!s) { - PARSE_WARNING ("malformed iscsiadm record: no = in '%s'.", ss); - nm_clear_pointer (&block_lines, g_ptr_array_unref); - } else - g_ptr_array_add (block_lines, s); - } - } - - if (block_lines) { - PARSE_WARNING ("malformed iscsiadm record: missing # END RECORD."); - nm_clear_pointer (&block_lines, g_ptr_array_unref); - } - - *out_blocks = g_steal_pointer (&blocks); - return TRUE; -} - -#define ISCSI_HWADDR_TAG "iface.hwaddress" -#define ISCSI_BOOTPROTO_TAG "iface.bootproto" -#define ISCSI_IPADDR_TAG "iface.ipaddress" -#define ISCSI_SUBNET_TAG "iface.subnet_mask" -#define ISCSI_GATEWAY_TAG "iface.gateway" -#define ISCSI_DNS1_TAG "iface.primary_dns" -#define ISCSI_DNS2_TAG "iface.secondary_dns" -#define ISCSI_VLAN_ID_TAG "iface.vlan_id" -#define ISCSI_IFACE_TAG "iface.net_ifacename" - -static const char * -match_iscsiadm_tag (const char *line, const char *tag) -{ - gsize taglen = strlen (tag); - - if (g_ascii_strncasecmp (line, tag, taglen) != 0) - return NULL; - if (line[taglen] != '=') - return NULL; - return line + taglen + 1; -} - -/** - * nms_ibft_reader_parse_block: - * @block: an array of iscsiadm interface block lines - * @error: return location for errors - * @...: pairs of key (const char *) : location (const char **) indicating the - * key to look for and the location to store the retrieved value in - * - * Parses an iscsiadm interface block into variables requested by the caller. - * Callers should verify the returned data is complete and valid. Returned - * strings are owned by @block and should not be used after @block is freed. - * - * Returns: %TRUE if at least , %FALSE on failure - */ -gboolean -nms_ibft_reader_parse_block (const GPtrArray *block, GError **error, ...) -{ - gboolean success = FALSE; - const char **out_value, *p; - va_list ap; - const char *key; - guint i; - - g_return_val_if_fail (block != NULL, FALSE); - g_return_val_if_fail (block->len > 0, FALSE); - - /* Find requested keys and populate return values */ - va_start (ap, error); - while ((key = va_arg (ap, const char *))) { - out_value = va_arg (ap, const char **); - *out_value = NULL; - for (i = 0; i < block->len; i++) { - p = match_iscsiadm_tag (g_ptr_array_index (block, i), key); - if (p) { - *out_value = p; - success = TRUE; - break; - } - } - } - va_end (ap); - - if (!success) { - g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "iBFT: failed to match at least one iscsiadm block field"); - } - return success; -} - -static gboolean -ip4_setting_add_from_block (const GPtrArray *block, - NMConnection *connection, - GError **error) -{ - NMSettingIPConfig *s_ip4 = NULL; - NMIPAddress *addr; - const char *s_method = NULL; - const char *s_ipaddr = NULL; - const char *s_gateway = NULL; - const char *s_dns1 = NULL; - const char *s_dns2 = NULL; - const char *s_netmask = NULL; - guint32 netmask = 0; - guint32 prefix; - - g_assert (block); - - if (!nms_ibft_reader_parse_block (block, error, - ISCSI_BOOTPROTO_TAG, &s_method, - ISCSI_IPADDR_TAG, &s_ipaddr, - ISCSI_SUBNET_TAG, &s_netmask, - ISCSI_GATEWAY_TAG, &s_gateway, - ISCSI_DNS1_TAG, &s_dns1, - ISCSI_DNS2_TAG, &s_dns2, - NULL)) - goto error; - - if (!s_method) { - g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "iBFT: malformed iscsiadm record: missing " ISCSI_BOOTPROTO_TAG); - goto error; - } - - s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new (); - - if (!g_ascii_strcasecmp (s_method, "dhcp")) { - g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); - goto success; - } else if (g_ascii_strcasecmp (s_method, "static") != 0) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "iBFT: malformed iscsiadm record: unknown " ISCSI_BOOTPROTO_TAG " '%s'.", - s_method); - goto error; - } - - /* Static configuration stuff */ - g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, NULL); - - /* IP address */ - if (!s_ipaddr || !nm_utils_ipaddr_valid (AF_INET, s_ipaddr)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "iBFT: malformed iscsiadm record: invalid IP address '%s'.", - s_ipaddr); - goto error; - } - - /* Subnet/prefix */ - if (!s_netmask || inet_pton (AF_INET, s_netmask, &netmask) != 1) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "iBFT: malformed iscsiadm record: invalid subnet mask '%s'.", - s_netmask); - goto error; - } - prefix = nm_utils_ip4_netmask_to_prefix (netmask); - - if (s_gateway && !nm_utils_ipaddr_valid (AF_INET, s_gateway)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "iBFT: malformed iscsiadm record: invalid IP gateway '%s'.", - s_gateway); - goto error; - } - - if (s_dns1 && !nm_utils_ipaddr_valid (AF_INET, s_dns1)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "iBFT: malformed iscsiadm record: invalid DNS1 address '%s'.", - s_dns1); - goto error; - } - - if (s_dns2 && !nm_utils_ipaddr_valid (AF_INET, s_dns2)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "iBFT: malformed iscsiadm record: invalid DNS2 address '%s'.", - s_dns2); - goto error; - } - - addr = nm_ip_address_new (AF_INET, s_ipaddr, prefix, error); - if (!addr) { - g_prefix_error (error, "iBFT: malformed iscsiadm record: "); - goto error; - } - - nm_setting_ip_config_add_address (s_ip4, addr); - nm_ip_address_unref (addr); - - g_object_set (s_ip4, NM_SETTING_IP_CONFIG_GATEWAY, s_gateway, NULL); - - if (s_dns1) - nm_setting_ip_config_add_dns (s_ip4, s_dns1); - if (s_dns2) - nm_setting_ip_config_add_dns (s_ip4, s_dns2); - -success: - nm_connection_add_setting (connection, NM_SETTING (s_ip4)); - return TRUE; - -error: - g_clear_object (&s_ip4); - return FALSE; -} - -static gboolean -connection_setting_add (const GPtrArray *block, - NMConnection *connection, - const char *type, - const char *prefix, - const char *iface, - GError **error) -{ - NMSetting *s_con; - char *id, *uuid; - const char *s_hwaddr = NULL, *s_ip4addr = NULL, *s_vlanid; - - if (!nms_ibft_reader_parse_block (block, error, - ISCSI_VLAN_ID_TAG, &s_vlanid, - ISCSI_HWADDR_TAG, &s_hwaddr, - ISCSI_IPADDR_TAG, &s_ip4addr, - NULL)) - return FALSE; - if (!s_hwaddr) { - g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "iBFT: malformed iscsiadm record: missing " ISCSI_HWADDR_TAG); - return FALSE; - } - - id = g_strdup_printf ("iBFT%s%s %s", - prefix ? " " : "", - prefix ?: "", - iface); - - uuid = _nm_utils_uuid_generate_from_strings ("ibft", - s_hwaddr, - s_vlanid ? "V" : "v", - s_vlanid ?: "", - s_ip4addr ? "A" : "DHCP", - s_ip4addr ?: "", - NULL); - - s_con = nm_setting_connection_new (); - g_object_set (s_con, - NM_SETTING_CONNECTION_TYPE, type, - NM_SETTING_CONNECTION_UUID, uuid, - NM_SETTING_CONNECTION_ID, id, - NM_SETTING_CONNECTION_READ_ONLY, TRUE, - NULL); - - g_free (uuid); - g_free (id); - - nm_connection_add_setting (connection, NM_SETTING (s_con)); - return TRUE; -} - -static gboolean -is_ibft_vlan_device (const GPtrArray *block) -{ - char *s_vlan_id = NULL; - - if (nms_ibft_reader_parse_block (block, NULL, ISCSI_VLAN_ID_TAG, &s_vlan_id, NULL)) { - g_assert (s_vlan_id); - - /* VLAN 0 is normally a valid VLAN ID, but in the iBFT case it - * means "no VLAN". - */ - if (_nm_utils_ascii_str_to_int64 (s_vlan_id, 10, 1, 4095, -1) != -1) - return TRUE; - } - return FALSE; -} - -static gboolean -vlan_setting_add_from_block (const GPtrArray *block, - NMConnection *connection, - GError **error) -{ - NMSetting *s_vlan = NULL; - const char *vlan_id_str = NULL; - gint64 vlan_id = -1; - gboolean success; - - g_assert (block); - g_assert (connection); - - /* This won't fail since this function shouldn't be called unless the - * iBFT VLAN ID exists and is > 0. - */ - success = nms_ibft_reader_parse_block (block, NULL, ISCSI_VLAN_ID_TAG, &vlan_id_str, NULL); - g_assert (success); - g_assert (vlan_id_str); - - /* VLAN 0 is normally a valid VLAN ID, but in the iBFT case it means "no VLAN" */ - vlan_id = _nm_utils_ascii_str_to_int64 (vlan_id_str, 10, 1, 4095, -1); - if (vlan_id == -1) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Invalid VLAN_ID '%s'", vlan_id_str); - return FALSE; - } - - s_vlan = nm_setting_vlan_new (); - g_object_set (s_vlan, NM_SETTING_VLAN_ID, (guint32) vlan_id, NULL); - nm_connection_add_setting (connection, NM_SETTING (s_vlan)); - - return TRUE; -} - -static gboolean -wired_setting_add_from_block (const GPtrArray *block, - NMConnection *connection, - GError **error) -{ - NMSetting *s_wired = NULL; - const char *hwaddr = NULL; - - g_assert (block); - g_assert (connection); - - if (!nms_ibft_reader_parse_block (block, NULL, ISCSI_HWADDR_TAG, &hwaddr, NULL)) { - g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "iBFT: malformed iscsiadm record: missing " ISCSI_HWADDR_TAG); - return FALSE; - } - - if (!nm_utils_hwaddr_valid (hwaddr, ETH_ALEN)) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "iBFT: malformed iscsiadm record: invalid " ISCSI_HWADDR_TAG " '%s'.", - hwaddr); - return FALSE; - } - - s_wired = nm_setting_wired_new (); - g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, hwaddr, NULL); - - nm_connection_add_setting (connection, s_wired); - return TRUE; -} - -NMConnection * -nms_ibft_reader_get_connection_from_block (const GPtrArray *block, GError **error) -{ - NMConnection *connection = NULL; - gboolean is_vlan = FALSE; - const char *iface = NULL; - - g_assert (block); - - if (!nms_ibft_reader_parse_block (block, error, ISCSI_IFACE_TAG, &iface, NULL)) { - g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "iBFT: malformed iscsiadm record: missing " ISCSI_IFACE_TAG); - return NULL; - } - - connection = nm_simple_connection_new (); - - is_vlan = is_ibft_vlan_device (block); - if (is_vlan && !vlan_setting_add_from_block (block, connection, error)) - goto error; - - /* Always have a wired setting; for VLAN it defines the parent */ - if (!wired_setting_add_from_block (block, connection, error)) - goto error; - - if (!ip4_setting_add_from_block (block, connection, error)) - goto error; - - if (!connection_setting_add (block, - connection, - is_vlan ? NM_SETTING_VLAN_SETTING_NAME : NM_SETTING_WIRED_SETTING_NAME, - is_vlan ? "VLAN" : NULL, - iface, - error)) - goto error; - - if (!nm_connection_normalize (connection, NULL, NULL, error)) - goto error; - - return connection; - -error: - g_object_unref (connection); - return NULL; -} - diff --git a/src/settings/plugins/ibft/nms-ibft-reader.h b/src/settings/plugins/ibft/nms-ibft-reader.h deleted file mode 100644 index baa81e99..00000000 --- a/src/settings/plugins/ibft/nms-ibft-reader.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager system settings service - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright 2014 Red Hat, Inc. - */ - -#ifndef __NMS_IBFT_READER_H__ -#define __NMS_IBFT_READER_H__ - -#include "nm-connection.h" - -static inline void -_nm_auto_free_ibft_blocks (GSList **p_blocks) -{ - if (*p_blocks) - g_slist_free_full (*p_blocks, (GDestroyNotify) g_ptr_array_unref); -} -#define nm_auto_free_ibft_blocks nm_auto (_nm_auto_free_ibft_blocks) - -gboolean nms_ibft_reader_load_blocks (const char *iscsiadm_path, - GSList **out_blocks, - GError **error); - -NMConnection *nms_ibft_reader_get_connection_from_block (const GPtrArray *block, GError **error); - -gboolean nms_ibft_reader_parse_block (const GPtrArray *block, GError **error, ...) G_GNUC_NULL_TERMINATED; - -#endif /* __NMS_IBFT_READER_H__ */ diff --git a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns1 b/src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns1 deleted file mode 100755 index 54f02da6..00000000 --- a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns1 +++ /dev/null @@ -1,21 +0,0 @@ -#!/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 deleted file mode 100755 index ebd7a9ca..00000000 --- a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-dns2 +++ /dev/null @@ -1,21 +0,0 @@ -#!/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 deleted file mode 100755 index 4e326048..00000000 --- a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-entry +++ /dev/null @@ -1,20 +0,0 @@ -#!/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 deleted file mode 100755 index 5390a6c3..00000000 --- a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-gateway +++ /dev/null @@ -1,21 +0,0 @@ -#!/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 deleted file mode 100755 index b41cd1f1..00000000 --- a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-ipaddr +++ /dev/null @@ -1,21 +0,0 @@ -#!/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 deleted file mode 100755 index 22b34e6f..00000000 --- a/src/settings/plugins/ibft/tests/iscsiadm-test-bad-record +++ /dev/null @@ -1,18 +0,0 @@ -#!/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 deleted file mode 100755 index 556b0586..00000000 --- a/src/settings/plugins/ibft/tests/iscsiadm-test-dhcp +++ /dev/null @@ -1,33 +0,0 @@ -#!/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 deleted file mode 100755 index 51711480..00000000 --- a/src/settings/plugins/ibft/tests/iscsiadm-test-static +++ /dev/null @@ -1,35 +0,0 @@ -#!/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 deleted file mode 100755 index 59b80bd0..00000000 --- a/src/settings/plugins/ibft/tests/iscsiadm-test-vlan +++ /dev/null @@ -1,19 +0,0 @@ -#!/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 deleted file mode 100644 index e2f9ca7e..00000000 --- a/src/settings/plugins/ibft/tests/meson.build +++ /dev/null @@ -1,16 +0,0 @@ -test_unit = 'test-ibft' - -test_ibft_dir = meson.current_source_dir() - -exe = executable( - test_unit, - test_unit + '.c', - dependencies: test_nm_dep, - link_with: libnms_ibft_core, -) - -test( - 'ibft/' + test_unit, - test_script, - args: test_args + [exe.full_path()], -) diff --git a/src/settings/plugins/ibft/tests/test-ibft.c b/src/settings/plugins/ibft/tests/test-ibft.c deleted file mode 100644 index f5b584a1..00000000 --- a/src/settings/plugins/ibft/tests/test-ibft.c +++ /dev/null @@ -1,292 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager system settings service - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright 2014 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include <stdio.h> -#include <stdarg.h> -#include <unistd.h> -#include <netinet/ether.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#include <sys/socket.h> - -#include "nm-core-internal.h" -#include "NetworkManagerUtils.h" - -#include "settings/plugins/ibft/nms-ibft-reader.h" - -#include "nm-test-utils-core.h" - -#define TEST_DIR NM_BUILD_SRCDIR"/src/settings/plugins/ibft/tests" - -static GPtrArray * -read_block (const char *iscsiadm_path, const char *expected_mac) -{ - nm_auto_free_ibft_blocks GSList *blocks = NULL; - GSList *iter; - GPtrArray *block = NULL; - GError *error = NULL; - gboolean success; - - success = nms_ibft_reader_load_blocks (iscsiadm_path, &blocks, &error); - nmtst_assert_success (success, error); - - g_assert (blocks); - for (iter = blocks; iter; iter = iter->next) { - const char *s_hwaddr = NULL; - - if (!nms_ibft_reader_parse_block (iter->data, NULL, "iface.hwaddress", &s_hwaddr, NULL)) - continue; - g_assert (s_hwaddr); - if (nm_utils_hwaddr_matches (s_hwaddr, -1, expected_mac, -1)) { - block = g_ptr_array_ref (iter->data); - break; - } - } - g_assert (block); - - return block; -} - -static void -test_read_ibft_dhcp (void) -{ - NMConnection *connection; - NMSettingConnection *s_con; - NMSettingWired *s_wired; - NMSettingIPConfig *s_ip4; - GError *error = NULL; - const char *mac_address; - const char *expected_mac_address = "00:33:21:98:b9:f1"; - GPtrArray *block; - - block = read_block (TEST_DIR "/iscsiadm-test-dhcp", expected_mac_address); - - connection = nms_ibft_reader_get_connection_from_block (block, &error); - g_assert_no_error (error); - nmtst_assert_connection_verifies_without_normalization (connection); - - g_assert (!nm_connection_get_setting_vlan (connection)); - - /* ===== CONNECTION SETTING ===== */ - s_con = nm_connection_get_setting_connection (connection); - g_assert (s_con); - 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), ==, "iBFT eth1"); - g_assert_cmpint (nm_setting_connection_get_timestamp (s_con), ==, 0); - g_assert (nm_setting_connection_get_autoconnect (s_con)); - g_assert (nm_setting_connection_get_read_only (s_con)); - - /* ===== WIRED SETTING ===== */ - s_wired = nm_connection_get_setting_wired (connection); - g_assert (s_wired); - mac_address = nm_setting_wired_get_mac_address (s_wired); - g_assert (mac_address); - g_assert (nm_utils_hwaddr_matches (mac_address, -1, expected_mac_address, -1)); - g_assert_cmpint (nm_setting_wired_get_mtu (s_wired), ==, 0); - - /* ===== IPv4 SETTING ===== */ - s_ip4 = nm_connection_get_setting_ip4_config (connection); - g_assert (s_ip4); - g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_AUTO); - - g_object_unref (connection); - g_ptr_array_unref (block); -} - -static void -test_read_ibft_static (void) -{ - NMConnection *connection; - NMSettingConnection *s_con; - NMSettingWired *s_wired; - NMSettingIPConfig *s_ip4; - GError *error = NULL; - const char *mac_address; - const char *expected_mac_address = "00:33:21:98:b9:f0"; - NMIPAddress *ip4_addr; - GPtrArray *block; - - block = read_block (TEST_DIR "/iscsiadm-test-static", expected_mac_address); - - connection = nms_ibft_reader_get_connection_from_block (block, &error); - g_assert_no_error (error); - nmtst_assert_connection_verifies_without_normalization (connection); - - g_assert (!nm_connection_get_setting_vlan (connection)); - - /* ===== CONNECTION SETTING ===== */ - s_con = nm_connection_get_setting_connection (connection); - g_assert (s_con); - 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), ==, "iBFT eth0"); - g_assert_cmpint (nm_setting_connection_get_timestamp (s_con), ==, 0); - g_assert (nm_setting_connection_get_autoconnect (s_con)); - g_assert (nm_setting_connection_get_read_only (s_con)); - - /* ===== WIRED SETTING ===== */ - s_wired = nm_connection_get_setting_wired (connection); - g_assert (s_wired); - mac_address = nm_setting_wired_get_mac_address (s_wired); - g_assert (mac_address); - g_assert (nm_utils_hwaddr_matches (mac_address, -1, expected_mac_address, -1)); - g_assert_cmpint (nm_setting_wired_get_mtu (s_wired), ==, 0); - - /* ===== IPv4 SETTING ===== */ - s_ip4 = nm_connection_get_setting_ip4_config (connection); - g_assert (s_ip4); - g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_MANUAL); - - g_assert_cmpint (nm_setting_ip_config_get_num_dns (s_ip4), ==, 2); - g_assert_cmpstr (nm_setting_ip_config_get_dns (s_ip4, 0), ==, "10.16.255.2"); - g_assert_cmpstr (nm_setting_ip_config_get_dns (s_ip4, 1), ==, "10.16.255.3"); - - g_assert_cmpint (nm_setting_ip_config_get_num_addresses (s_ip4), ==, 1); - ip4_addr = nm_setting_ip_config_get_address (s_ip4, 0); - g_assert (ip4_addr); - g_assert_cmpstr (nm_ip_address_get_address (ip4_addr), ==, "192.168.32.72"); - g_assert_cmpint (nm_ip_address_get_prefix (ip4_addr), ==, 22); - - g_assert_cmpstr (nm_setting_ip_config_get_gateway (s_ip4), ==, "192.168.35.254"); - - g_object_unref (connection); - g_ptr_array_unref (block); -} - -static void -test_read_ibft_malformed (gconstpointer user_data) -{ - const char *iscsiadm_path = user_data; - nm_auto_free_ibft_blocks GSList *blocks = NULL; - GError *error = NULL; - gboolean success; - - g_assert (g_file_test (iscsiadm_path, G_FILE_TEST_EXISTS)); - - NMTST_EXPECT_NM_WARN ("*malformed iscsiadm record*"); - - success = nms_ibft_reader_load_blocks (iscsiadm_path, &blocks, &error); - nmtst_assert_success (success, error); - - g_assert (!blocks); - - g_test_assert_expected_messages (); -} - -static void -test_read_ibft_bad_address (gconstpointer user_data) -{ - const char *iscsiadm_path = user_data; - NMConnection *connection; - const char *expected_mac_address = "00:33:21:98:b9:f0"; - GPtrArray *block; - GError *error = NULL; - - g_assert (g_file_test (iscsiadm_path, G_FILE_TEST_EXISTS)); - - block = read_block (iscsiadm_path, expected_mac_address); - - connection = nms_ibft_reader_get_connection_from_block (block, &error); - g_assert_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION); - g_assert (strstr (error->message, "iBFT: malformed iscsiadm record: invalid")); - g_clear_error (&error); - g_assert (connection == NULL); - - g_ptr_array_unref (block); -} - -static void -test_read_ibft_vlan (void) -{ - NMConnection *connection; - NMSettingConnection *s_con; - NMSettingWired *s_wired; - NMSettingVlan *s_vlan; - NMSettingIPConfig *s_ip4; - const char *mac_address; - const char *expected_mac_address = "00:33:21:98:b9:f0"; - NMIPAddress *ip4_addr; - GError *error = NULL; - GPtrArray *block; - - block = read_block (TEST_DIR "/iscsiadm-test-vlan", expected_mac_address); - - connection = nms_ibft_reader_get_connection_from_block (block, &error); - g_assert_no_error (error); - nmtst_assert_connection_verifies_without_normalization (connection); - - s_con = nm_connection_get_setting_connection (connection); - g_assert (s_con); - g_assert_cmpstr (nm_setting_connection_get_connection_type (s_con), ==, NM_SETTING_VLAN_SETTING_NAME); - - /* ===== WIRED SETTING ===== */ - s_wired = nm_connection_get_setting_wired (connection); - g_assert (s_wired); - mac_address = nm_setting_wired_get_mac_address (s_wired); - g_assert (mac_address); - g_assert (nm_utils_hwaddr_matches (mac_address, -1, expected_mac_address, -1)); - - /* ===== VLAN SETTING ===== */ - s_vlan = nm_connection_get_setting_vlan (connection); - g_assert (s_vlan); - g_assert_cmpint (nm_setting_vlan_get_id (s_vlan), ==, 123); - g_assert_cmpstr (nm_setting_vlan_get_parent (s_vlan), ==, NULL); - - /* ===== IPv4 SETTING ===== */ - s_ip4 = nm_connection_get_setting_ip4_config (connection); - g_assert (s_ip4); - g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_MANUAL); - - g_assert_cmpint (nm_setting_ip_config_get_num_dns (s_ip4), ==, 0); - - g_assert_cmpint (nm_setting_ip_config_get_num_addresses (s_ip4), ==, 1); - ip4_addr = nm_setting_ip_config_get_address (s_ip4, 0); - g_assert (ip4_addr); - g_assert_cmpstr (nm_ip_address_get_address (ip4_addr), ==, "192.168.6.200"); - g_assert_cmpint (nm_ip_address_get_prefix (ip4_addr), ==, 24); - - g_assert_cmpstr (nm_setting_ip_config_get_gateway (s_ip4), ==, NULL); - - g_object_unref (connection); - g_ptr_array_unref (block); -} - -NMTST_DEFINE (); - -#define TPATH "/settings/plugins/ibft/" - -int main (int argc, char **argv) -{ - nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT"); - - g_test_add_func (TPATH "ibft/dhcp", test_read_ibft_dhcp); - g_test_add_func (TPATH "ibft/static", test_read_ibft_static); - g_test_add_func (TPATH "ibft/vlan", test_read_ibft_vlan); - g_test_add_data_func (TPATH "ibft/bad-record-read", TEST_DIR "/iscsiadm-test-bad-record", test_read_ibft_malformed); - g_test_add_data_func (TPATH "ibft/bad-entry-read", TEST_DIR "/iscsiadm-test-bad-entry", test_read_ibft_malformed); - g_test_add_data_func (TPATH "ibft/bad-ipaddr-read", TEST_DIR "/iscsiadm-test-bad-ipaddr", test_read_ibft_bad_address); - g_test_add_data_func (TPATH "ibft/bad-gateway-read", TEST_DIR "/iscsiadm-test-bad-gateway", test_read_ibft_bad_address); - g_test_add_data_func (TPATH "ibft/bad-dns1-read", TEST_DIR "/iscsiadm-test-bad-dns1", test_read_ibft_bad_address); - g_test_add_data_func (TPATH "ibft/bad-dns2-read", TEST_DIR "/iscsiadm-test-bad-dns2", test_read_ibft_bad_address); - - return g_test_run (); -} - diff --git a/src/settings/plugins/ifcfg-rh/meson.build b/src/settings/plugins/ifcfg-rh/meson.build index 9024782a..58acdcfc 100644 --- a/src/settings/plugins/ifcfg-rh/meson.build +++ b/src/settings/plugins/ifcfg-rh/meson.build @@ -19,7 +19,6 @@ libnmdbus_ifcfg_rh = static_library( ) core_sources = files( - 'nm-inotify-helper.c', 'nms-ifcfg-rh-reader.c', 'nms-ifcfg-rh-utils.c', 'nms-ifcfg-rh-writer.c', @@ -36,7 +35,7 @@ libnms_ifcfg_rh_core = static_library( dependencies: deps, ) -sources = [dbus_sources] + core_sources + files('nms-ifcfg-rh-connection.c', 'nms-ifcfg-rh-plugin.c') +sources = [dbus_sources] + core_sources + files('nms-ifcfg-rh-storage.c', 'nms-ifcfg-rh-plugin.c') libnm_settings_plugin_ifcfg_rh = shared_module( 'nm-settings-plugin-ifcfg-rh', diff --git a/src/settings/plugins/ifcfg-rh/nm-inotify-helper.c b/src/settings/plugins/ifcfg-rh/nm-inotify-helper.c deleted file mode 100644 index 04cbb5bc..00000000 --- a/src/settings/plugins/ifcfg-rh/nm-inotify-helper.c +++ /dev/null @@ -1,213 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager system settings service - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * (C) Copyright 2008 - 2017 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nm-inotify-helper.h" - -#include <unistd.h> -#include <sys/inotify.h> - -#include "NetworkManagerUtils.h" - -/* NOTE: this code should be killed once we depend on a new enough glib to - * include the patches from https://bugzilla.gnome.org/show_bug.cgi?id=532815 - */ - -/*****************************************************************************/ - -enum { - EVENT, - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL] = { 0 }; - -typedef struct { - int ifd; - guint inotify_id; - GHashTable *wd_refs; -} NMInotifyHelperPrivate; - -struct _NMInotifyHelper { - GObject parent; - NMInotifyHelperPrivate _priv; -}; - -struct _NMInotifyHelperClass { - GObjectClass parent; -}; - -G_DEFINE_TYPE (NMInotifyHelper, nm_inotify_helper, G_TYPE_OBJECT) - -#define NM_INOTIFY_HELPER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMInotifyHelper, NM_IS_INOTIFY_HELPER) - -/*****************************************************************************/ - -NM_DEFINE_SINGLETON_GETTER (NMInotifyHelper, nm_inotify_helper_get, NM_TYPE_INOTIFY_HELPER); - -/*****************************************************************************/ - -int -nm_inotify_helper_add_watch (NMInotifyHelper *self, const char *path) -{ - NMInotifyHelperPrivate *priv = NM_INOTIFY_HELPER_GET_PRIVATE (self); - int wd; - guint refcount; - - if (priv->ifd < 0) - return -1; - - /* We only care about modifications since we're just trying to get change - * notifications on hardlinks. - */ - - wd = inotify_add_watch (priv->ifd, path, IN_CLOSE_WRITE); - if (wd < 0) - return -1; - - refcount = GPOINTER_TO_UINT (g_hash_table_lookup (priv->wd_refs, GINT_TO_POINTER (wd))); - refcount++; - g_hash_table_replace (priv->wd_refs, GINT_TO_POINTER (wd), GUINT_TO_POINTER (refcount)); - - return wd; -} - -void -nm_inotify_helper_remove_watch (NMInotifyHelper *self, int wd) -{ - NMInotifyHelperPrivate *priv = NM_INOTIFY_HELPER_GET_PRIVATE (self); - guint refcount; - - if (priv->ifd < 0) - return; - - refcount = GPOINTER_TO_UINT (g_hash_table_lookup (priv->wd_refs, GINT_TO_POINTER (wd))); - if (!refcount) - return; - - refcount--; - if (!refcount) { - g_hash_table_remove (priv->wd_refs, GINT_TO_POINTER (wd)); - inotify_rm_watch (priv->ifd, wd); - } else - g_hash_table_replace (priv->wd_refs, GINT_TO_POINTER (wd), GUINT_TO_POINTER (refcount)); -} - -static gboolean -inotify_event_handler (GIOChannel *channel, GIOCondition cond, gpointer user_data) -{ - NMInotifyHelper *self = NM_INOTIFY_HELPER (user_data); - struct inotify_event evt; - - /* read the notifications from the watch descriptor */ - while (g_io_channel_read_chars (channel, (char *) &evt, sizeof (struct inotify_event), NULL, NULL) == G_IO_STATUS_NORMAL) { - char filename[PATH_MAX + 1]; - - filename[0] = '\0'; - if (evt.len > 0) { - g_io_channel_read_chars (channel, - filename, - evt.len > PATH_MAX ? PATH_MAX : evt.len, - NULL, NULL); - } - - if (!(evt.mask & IN_IGNORED)) - g_signal_emit (self, signals[EVENT], 0, &evt, &filename[0]); - } - - return TRUE; -} - -static gboolean -init_inotify (NMInotifyHelper *self) -{ - NMInotifyHelperPrivate *priv = NM_INOTIFY_HELPER_GET_PRIVATE (self); - GIOChannel *channel; - int errsv; - - priv->ifd = inotify_init1 (IN_CLOEXEC); - if (priv->ifd == -1) { - errsv = errno; - nm_log_warn (LOGD_SETTINGS, "couldn't initialize inotify: %s (%d)", nm_strerror_native (errsv), errsv); - return FALSE; - } - - /* Watch the inotify descriptor for file/directory change events */ - channel = g_io_channel_unix_new (priv->ifd); - g_io_channel_set_flags (channel, G_IO_FLAG_NONBLOCK, NULL); - g_io_channel_set_encoding (channel, NULL, NULL); - - priv->inotify_id = g_io_add_watch (channel, - G_IO_IN | G_IO_ERR, - (GIOFunc) inotify_event_handler, - (gpointer) self); - g_io_channel_unref (channel); - return TRUE; -} - -/*****************************************************************************/ - -static void -nm_inotify_helper_init (NMInotifyHelper *self) -{ - NMInotifyHelperPrivate *priv = NM_INOTIFY_HELPER_GET_PRIVATE (self); - - priv->wd_refs = g_hash_table_new (nm_direct_hash, NULL); -} - -static void -constructed (GObject *object) -{ - G_OBJECT_CLASS (nm_inotify_helper_parent_class)->constructed (object); - - init_inotify (NM_INOTIFY_HELPER (object)); -} - -static void -finalize (GObject *object) -{ - NMInotifyHelperPrivate *priv = NM_INOTIFY_HELPER_GET_PRIVATE ((NMInotifyHelper *) object); - - nm_clear_g_source (&priv->inotify_id); - nm_close (priv->ifd); - - g_hash_table_destroy (priv->wd_refs); - - G_OBJECT_CLASS (nm_inotify_helper_parent_class)->finalize (object); -} - -static void -nm_inotify_helper_class_init (NMInotifyHelperClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->constructed = constructed; - object_class->finalize = finalize; - - signals[EVENT] = - g_signal_new ("event", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, NULL, - G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_STRING); -} - diff --git a/src/settings/plugins/ifcfg-rh/nm-inotify-helper.h b/src/settings/plugins/ifcfg-rh/nm-inotify-helper.h deleted file mode 100644 index b887ae37..00000000 --- a/src/settings/plugins/ifcfg-rh/nm-inotify-helper.h +++ /dev/null @@ -1,59 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager system settings service - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * (C) Copyright 2008 Red Hat, Inc. - */ - -#ifndef __NM_INOTIFY_HELPER_H__ -#define __NM_INOTIFY_HELPER_H__ - -/* NOTE: this code should be killed once we depend on a new enough glib to - * include the patches from https://bugzilla.gnome.org/show_bug.cgi?id=532815 - */ - -#define NM_TYPE_INOTIFY_HELPER (nm_inotify_helper_get_type ()) -#define NM_INOTIFY_HELPER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_INOTIFY_HELPER, NMInotifyHelper)) -#define NM_INOTIFY_HELPER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_INOTIFY_HELPER, NMInotifyHelperClass)) -#define NM_IS_INOTIFY_HELPER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_INOTIFY_HELPER)) -#define NM_IS_INOTIFY_HELPER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_INOTIFY_HELPER)) -#define NM_INOTIFY_HELPER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_INOTIFY_HELPER, NMInotifyHelperClass)) - -typedef struct _NMInotifyHelper NMInotifyHelper; -typedef struct _NMInotifyHelperClass NMInotifyHelperClass; - -GType nm_inotify_helper_get_type (void); - -NMInotifyHelper * nm_inotify_helper_get (void); - -int nm_inotify_helper_add_watch (NMInotifyHelper *helper, const char *path); - -void nm_inotify_helper_remove_watch (NMInotifyHelper *helper, int wd); - -static inline gboolean -nm_inotify_helper_clear_watch (NMInotifyHelper *helper, int *wd) -{ - int x; - - if (wd && ((x = *wd) >= 0)) { - *wd = -1; - nm_inotify_helper_remove_watch (helper, x); - return TRUE; - } - return FALSE; -} - -#endif /* __NM_INOTIFY_HELPER_H__ */ diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-common.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-common.h index eaff2db4..ff01fc7a 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-common.h +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-common.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * This program is free software; you can redistribute it and/or modify diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c deleted file mode 100644 index 4f769c5f..00000000 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c +++ /dev/null @@ -1,511 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager system settings service - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 2008 - 2011 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nms-ifcfg-rh-connection.h" - -#include <sys/inotify.h> -#include <glib/gstdio.h> - -#include "nm-dbus-interface.h" -#include "nm-setting-connection.h" -#include "nm-setting-wired.h" -#include "nm-setting-wireless.h" -#include "nm-setting-gsm.h" -#include "nm-setting-cdma.h" -#include "nm-setting-pppoe.h" -#include "nm-setting-wireless-security.h" -#include "nm-setting-8021x.h" -#include "platform/nm-platform.h" -#include "nm-config.h" - -#include "nms-ifcfg-rh-common.h" -#include "nms-ifcfg-rh-reader.h" -#include "nms-ifcfg-rh-writer.h" -#include "nms-ifcfg-rh-utils.h" -#include "nm-inotify-helper.h" - -/*****************************************************************************/ - -NM_GOBJECT_PROPERTIES_DEFINE_BASE ( - PROP_UNMANAGED_SPEC, - PROP_UNRECOGNIZED_SPEC, -); - -enum { - IFCFG_CHANGED, - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL] = { 0 }; - -typedef struct { - gulong ih_event_id; - - int file_wd; - - char *keyfile; - int keyfile_wd; - - char *routefile; - int routefile_wd; - - char *route6file; - int route6file_wd; - - char *unmanaged_spec; - char *unrecognized_spec; - - gulong devtimeout_link_changed_handler; - guint devtimeout_timeout_id; - - NMInotifyHelper *inotify_helper; -} NMIfcfgConnectionPrivate; - -struct _NMIfcfgConnection { - NMSettingsConnection parent; - NMIfcfgConnectionPrivate _priv; -}; - -struct _NMIfcfgConnectionClass { - NMSettingsConnectionClass parent; -}; - -G_DEFINE_TYPE (NMIfcfgConnection, nm_ifcfg_connection, NM_TYPE_SETTINGS_CONNECTION) - -#define NM_IFCFG_CONNECTION_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMIfcfgConnection, NM_IS_IFCFG_CONNECTION) - -/*****************************************************************************/ - -static gboolean -devtimeout_ready (gpointer user_data) -{ - NMIfcfgConnection *self = user_data; - NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (self); - - priv->devtimeout_timeout_id = 0; - nm_settings_connection_set_ready (NM_SETTINGS_CONNECTION (self), TRUE); - return FALSE; -} - -static void -link_changed (NMPlatform *platform, int obj_type_i, int ifindex, const NMPlatformLink *link, - int change_type_i, - NMConnection *self) -{ - const NMPlatformSignalChangeType change_type = change_type_i; - NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE ((NMIfcfgConnection *) self); - const char *ifname; - - ifname = nm_connection_get_interface_name (self); - if (g_strcmp0 (link->name, ifname) != 0) - return; - - if (change_type == NM_PLATFORM_SIGNAL_REMOVED) - return; - - nm_log_info (LOGD_SETTINGS, "Device %s appeared; connection '%s' now ready", - ifname, nm_connection_get_id (self)); - - g_signal_handler_disconnect (platform, priv->devtimeout_link_changed_handler); - priv->devtimeout_link_changed_handler = 0; - g_source_remove (priv->devtimeout_timeout_id); - - /* Don't declare the connection ready right away, since NMManager may not have - * started processing the device yet. - */ - priv->devtimeout_timeout_id = g_idle_add (devtimeout_ready, self); -} - -static gboolean -devtimeout_expired (gpointer user_data) -{ - NMIfcfgConnection *self = user_data; - NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (self); - - nm_log_info (LOGD_SETTINGS, "Device for connection '%s' did not appear before timeout", - nm_settings_connection_get_id (NM_SETTINGS_CONNECTION (self))); - - g_signal_handler_disconnect (NM_PLATFORM_GET, priv->devtimeout_link_changed_handler); - priv->devtimeout_link_changed_handler = 0; - priv->devtimeout_timeout_id = 0; - - nm_settings_connection_set_ready (NM_SETTINGS_CONNECTION (self), TRUE); - return FALSE; -} - -static void -nm_ifcfg_connection_check_devtimeout (NMIfcfgConnection *self) -{ - NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (self); - NMSettingConnection *s_con; - const char *ifname; - const char *filename; - guint devtimeout; - const NMPlatformLink *pllink; - - s_con = nm_connection_get_setting_connection (nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (self))); - - if (!nm_setting_connection_get_autoconnect (s_con)) - return; - ifname = nm_setting_connection_get_interface_name (s_con); - if (!ifname) - return; - filename = nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (self)); - if (!filename) - return; - - pllink = nm_platform_link_get_by_ifname (NM_PLATFORM_GET, ifname); - if (pllink && pllink->initialized) - return; - - devtimeout = devtimeout_from_file (filename); - if (!devtimeout) - return; - - /* ONBOOT=yes, DEVICE and DEVTIMEOUT are set, but device is not present */ - nm_settings_connection_set_ready (NM_SETTINGS_CONNECTION (self), FALSE); - - nm_log_info (LOGD_SETTINGS, "Waiting %u seconds for %s to appear for connection '%s'", - devtimeout, ifname, nm_settings_connection_get_id (NM_SETTINGS_CONNECTION (self))); - - priv->devtimeout_link_changed_handler = - g_signal_connect (NM_PLATFORM_GET, NM_PLATFORM_SIGNAL_LINK_CHANGED, - G_CALLBACK (link_changed), self); - priv->devtimeout_timeout_id = g_timeout_add_seconds (devtimeout, devtimeout_expired, self); -} - -static void -files_changed_cb (NMInotifyHelper *ih, - struct inotify_event *evt, - const char *path, - gpointer user_data) -{ - NMIfcfgConnection *self = NM_IFCFG_CONNECTION (user_data); - NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (self); - - if ( (evt->wd != priv->file_wd) - && (evt->wd != priv->keyfile_wd) - && (evt->wd != priv->routefile_wd) - && (evt->wd != priv->route6file_wd)) - return; - - /* push the event up to the plugin */ - g_signal_emit (self, signals[IFCFG_CHANGED], 0); -} - -static void -path_watch_stop (NMIfcfgConnection *self) -{ - NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (self); - - nm_clear_g_signal_handler (priv->inotify_helper, &priv->ih_event_id); - - nm_inotify_helper_clear_watch (priv->inotify_helper, &priv->file_wd); - nm_inotify_helper_clear_watch (priv->inotify_helper, &priv->keyfile_wd); - nm_inotify_helper_clear_watch (priv->inotify_helper, &priv->routefile_wd); - nm_inotify_helper_clear_watch (priv->inotify_helper, &priv->route6file_wd); - - nm_clear_g_free (&priv->keyfile); - nm_clear_g_free (&priv->routefile); - nm_clear_g_free (&priv->route6file); -} - -static void -filename_changed (GObject *object, - GParamSpec *pspec, - gpointer user_data) -{ - NMIfcfgConnection *self = NM_IFCFG_CONNECTION (object); - NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (self); - const char *ifcfg_path; - - path_watch_stop (self); - - ifcfg_path = nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (self)); - if (!ifcfg_path) - return; - - priv->keyfile = utils_get_keys_path (ifcfg_path); - priv->routefile = utils_get_route_path (ifcfg_path); - priv->route6file = utils_get_route6_path (ifcfg_path); - - if (nm_config_get_monitor_connection_files (nm_config_get ())) { - NMInotifyHelper *ih; - - if (!priv->inotify_helper) - priv->inotify_helper = g_object_ref (nm_inotify_helper_get ()); - ih = priv->inotify_helper; - - priv->ih_event_id = g_signal_connect (ih, "event", G_CALLBACK (files_changed_cb), self); - priv->file_wd = nm_inotify_helper_add_watch (ih, ifcfg_path); - priv->keyfile_wd = nm_inotify_helper_add_watch (ih, priv->keyfile); - priv->routefile_wd = nm_inotify_helper_add_watch (ih, priv->routefile); - priv->route6file_wd = nm_inotify_helper_add_watch (ih, priv->route6file); - } -} - -const char * -nm_ifcfg_connection_get_unmanaged_spec (NMIfcfgConnection *self) -{ - g_return_val_if_fail (NM_IS_IFCFG_CONNECTION (self), NULL); - - return NM_IFCFG_CONNECTION_GET_PRIVATE (self)->unmanaged_spec; -} - -const char * -nm_ifcfg_connection_get_unrecognized_spec (NMIfcfgConnection *self) -{ - g_return_val_if_fail (NM_IS_IFCFG_CONNECTION (self), NULL); - - return NM_IFCFG_CONNECTION_GET_PRIVATE (self)->unrecognized_spec; -} - -static gboolean -commit_changes (NMSettingsConnection *connection, - NMConnection *new_connection, - NMSettingsConnectionCommitReason commit_reason, - NMConnection **out_reread_connection, - char **out_logmsg_change, - GError **error) -{ - const char *filename; - gs_unref_object NMConnection *reread = NULL; - gboolean reread_same = TRUE; - const char *operation_message; - gs_free char *ifcfg_path = NULL; - - nm_assert (out_reread_connection && !*out_reread_connection); - nm_assert (!out_logmsg_change || !*out_logmsg_change); - - filename = nm_settings_connection_get_filename (connection); - if (!nms_ifcfg_rh_writer_write_connection (new_connection, - IFCFG_DIR, - filename, - &ifcfg_path, - &reread, - &reread_same, - error)) - return FALSE; - - nm_assert ((!filename && ifcfg_path) || (filename && !ifcfg_path)); - if (ifcfg_path) { - nm_settings_connection_set_filename (connection, ifcfg_path); - operation_message = "persist"; - } else - operation_message = "update"; - - if (reread && !reread_same) - *out_reread_connection = g_steal_pointer (&reread); - - NM_SET_OUT (out_logmsg_change, - g_strdup_printf ("ifcfg-rh: %s %s", - operation_message, filename)); - return TRUE; -} - -static gboolean -delete (NMSettingsConnection *connection, - GError **error) -{ - NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE ((NMIfcfgConnection *) connection); - const char *filename; - - filename = nm_settings_connection_get_filename (connection); - if (filename) { - g_unlink (filename); - if (priv->keyfile) - g_unlink (priv->keyfile); - if (priv->routefile) - g_unlink (priv->routefile); - if (priv->route6file) - g_unlink (priv->route6file); - } - - return TRUE; -} - -/*****************************************************************************/ - -static void -get_property (GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) -{ - NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE ((NMIfcfgConnection *) object); - - switch (prop_id) { - case PROP_UNMANAGED_SPEC: - g_value_set_string (value, priv->unmanaged_spec); - break; - case PROP_UNRECOGNIZED_SPEC: - g_value_set_string (value, priv->unrecognized_spec); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) -{ - NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE ((NMIfcfgConnection *) object); - - switch (prop_id) { - case PROP_UNMANAGED_SPEC: - priv->unmanaged_spec = g_value_dup_string (value); - break; - case PROP_UNRECOGNIZED_SPEC: - priv->unrecognized_spec = g_value_dup_string (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -/*****************************************************************************/ - -static void -nm_ifcfg_connection_init (NMIfcfgConnection *connection) -{ - NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (connection); - - priv->file_wd = -1; - priv->keyfile_wd = -1; - priv->routefile_wd = -1; - priv->route6file_wd = -1; - - g_signal_connect (connection, "notify::" NM_SETTINGS_CONNECTION_FILENAME, - G_CALLBACK (filename_changed), NULL); -} - -NMIfcfgConnection * -nm_ifcfg_connection_new (NMConnection *source, - const char *full_path, - GError **error, - gboolean *out_ignore_error) -{ - GObject *object; - NMConnection *tmp; - char *unhandled_spec = NULL; - const char *unmanaged_spec = NULL, *unrecognized_spec = NULL; - - g_assert (source || full_path); - - if (out_ignore_error) - *out_ignore_error = FALSE; - - /* If we're given a connection already, prefer that instead of re-reading */ - if (source) - tmp = g_object_ref (source); - else { - tmp = connection_from_file (full_path, - &unhandled_spec, - error, - out_ignore_error); - if (!tmp) - return NULL; - } - - if (unhandled_spec && g_str_has_prefix (unhandled_spec, "unmanaged:")) - unmanaged_spec = unhandled_spec + strlen ("unmanaged:"); - else if (unhandled_spec && g_str_has_prefix (unhandled_spec, "unrecognized:")) - unrecognized_spec = unhandled_spec + strlen ("unrecognized:"); - - object = (GObject *) g_object_new (NM_TYPE_IFCFG_CONNECTION, - NM_SETTINGS_CONNECTION_FILENAME, full_path, - NM_IFCFG_CONNECTION_UNMANAGED_SPEC, unmanaged_spec, - NM_IFCFG_CONNECTION_UNRECOGNIZED_SPEC, unrecognized_spec, - NULL); - /* Update our settings with what was read from the file */ - if (nm_settings_connection_update (NM_SETTINGS_CONNECTION (object), - tmp, - full_path - ? NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP_SAVED - : NM_SETTINGS_CONNECTION_PERSIST_MODE_UNSAVED, - NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, - NULL, - error)) - nm_ifcfg_connection_check_devtimeout (NM_IFCFG_CONNECTION (object)); - else - g_clear_object (&object); - - g_object_unref (tmp); - g_free (unhandled_spec); - return (NMIfcfgConnection *) object; -} - -static void -dispose (GObject *object) -{ - NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE ((NMIfcfgConnection *) object); - - path_watch_stop (NM_IFCFG_CONNECTION (object)); - - nm_clear_g_signal_handler (NM_PLATFORM_GET, &priv->devtimeout_link_changed_handler); - nm_clear_g_source (&priv->devtimeout_timeout_id); - - g_clear_object (&priv->inotify_helper); - - g_clear_pointer (&priv->unmanaged_spec, g_free); - g_clear_pointer (&priv->unrecognized_spec, g_free); - - G_OBJECT_CLASS (nm_ifcfg_connection_parent_class)->dispose (object); -} - -static void -nm_ifcfg_connection_class_init (NMIfcfgConnectionClass *ifcfg_connection_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (ifcfg_connection_class); - NMSettingsConnectionClass *settings_class = NM_SETTINGS_CONNECTION_CLASS (ifcfg_connection_class); - - object_class->set_property = set_property; - object_class->get_property = get_property; - object_class->dispose = dispose; - - settings_class->delete = delete; - settings_class->commit_changes = commit_changes; - - obj_properties[PROP_UNMANAGED_SPEC] = - g_param_spec_string (NM_IFCFG_CONNECTION_UNMANAGED_SPEC, "", "", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_UNRECOGNIZED_SPEC] = - g_param_spec_string (NM_IFCFG_CONNECTION_UNRECOGNIZED_SPEC, "", "", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); - - signals[IFCFG_CHANGED] = - g_signal_new ("ifcfg-changed", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - 0, NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); -} - diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.h deleted file mode 100644 index fd68f2d3..00000000 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.h +++ /dev/null @@ -1,54 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager system settings service - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 2008 - 2011 Red Hat, Inc. - */ - -#ifndef __NETWORKMANAGER_IFCFG_CONNECTION_H__ -#define __NETWORKMANAGER_IFCFG_CONNECTION_H__ - -#include "nm-dbus-interface.h" -#include "settings/nm-settings-connection.h" - -#define NM_TYPE_IFCFG_CONNECTION (nm_ifcfg_connection_get_type ()) -#define NM_IFCFG_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_IFCFG_CONNECTION, NMIfcfgConnection)) -#define NM_IFCFG_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_IFCFG_CONNECTION, NMIfcfgConnectionClass)) -#define NM_IS_IFCFG_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_IFCFG_CONNECTION)) -#define NM_IS_IFCFG_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_IFCFG_CONNECTION)) -#define NM_IFCFG_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_IFCFG_CONNECTION, NMIfcfgConnectionClass)) - -#define NM_IFCFG_CONNECTION_UNMANAGED_SPEC "unmanaged-spec" -#define NM_IFCFG_CONNECTION_UNRECOGNIZED_SPEC "unrecognized-spec" - -typedef struct _NMIfcfgConnection NMIfcfgConnection; -typedef struct _NMIfcfgConnectionClass NMIfcfgConnectionClass; - -GType nm_ifcfg_connection_get_type (void); - -NMIfcfgConnection *nm_ifcfg_connection_new (NMConnection *source, - const char *full_path, - GError **error, - gboolean *out_ignore_error); - -const char *nm_ifcfg_connection_get_unmanaged_spec (NMIfcfgConnection *self); -const char *nm_ifcfg_connection_get_unrecognized_spec (NMIfcfgConnection *self); - -gboolean nm_ifcfg_connection_update (NMIfcfgConnection *self, - GHashTable *new_settings, - GError **error); - -#endif /* __NETWORKMANAGER_IFCFG_CONNECTION_H__ */ 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 5160dbf0..cc4fe4ce 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * Dan Williams <dcbw@redhat.com> @@ -25,23 +24,26 @@ #include "nms-ifcfg-rh-plugin.h" -#include <unistd.h> #include <sys/types.h> #include <sys/stat.h> -#include <gmodule.h> +#include <unistd.h> +#include "nm-std-aux/c-list-util.h" +#include "nm-glib-aux/nm-c-list.h" +#include "nm-glib-aux/nm-io-utils.h" #include "nm-std-aux/nm-dbus-compat.h" -#include "nm-setting-connection.h" -#include "settings/nm-settings-plugin.h" +#include "nm-utils.h" +#include "nm-core-internal.h" #include "nm-config.h" +#include "settings/nm-settings-plugin.h" +#include "settings/nm-settings-utils.h" #include "NetworkManagerUtils.h" -#include "nms-ifcfg-rh-connection.h" +#include "nms-ifcfg-rh-storage.h" #include "nms-ifcfg-rh-common.h" +#include "nms-ifcfg-rh-utils.h" #include "nms-ifcfg-rh-reader.h" #include "nms-ifcfg-rh-writer.h" -#include "nms-ifcfg-rh-utils.h" -#include "shvar.h" #define IFCFGRH1_BUS_NAME "com.redhat.ifcfgrh1" #define IFCFGRH1_OBJECT_PATH "/com/redhat/ifcfgrh1" @@ -60,31 +62,25 @@ typedef struct { guint regist_id; } dbus; - GHashTable *connections; /* uuid::connection */ - gboolean initialized; + NMSettUtilStorages storages; - GFileMonitor *ifcfg_monitor; - gulong ifcfg_monitor_id; -} SettingsPluginIfcfgPrivate; + GHashTable *unmanaged_specs; + GHashTable *unrecognized_specs; -struct _SettingsPluginIfcfg { +} NMSIfcfgRHPluginPrivate; + +struct _NMSIfcfgRHPlugin { NMSettingsPlugin parent; - SettingsPluginIfcfgPrivate _priv; + NMSIfcfgRHPluginPrivate _priv; }; -struct _SettingsPluginIfcfgClass { +struct _NMSIfcfgRHPluginClass { NMSettingsPluginClass parent; }; -G_DEFINE_TYPE (SettingsPluginIfcfg, settings_plugin_ifcfg, NM_TYPE_SETTINGS_PLUGIN) +G_DEFINE_TYPE (NMSIfcfgRHPlugin, nms_ifcfg_rh_plugin, NM_TYPE_SETTINGS_PLUGIN) -#define SETTINGS_PLUGIN_IFCFG_GET_PRIVATE(self) _NM_GET_PRIVATE (self, SettingsPluginIfcfg, SETTINGS_IS_PLUGIN_IFCFG) - -/*****************************************************************************/ - -static SettingsPluginIfcfg *settings_plugin_ifcfg_get (void); - -NM_DEFINE_SINGLETON_GETTER (SettingsPluginIfcfg, settings_plugin_ifcfg_get, SETTINGS_TYPE_PLUGIN_IFCFG); +#define NMS_IFCFG_RH_PLUGIN_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMSIfcfgRHPlugin, NMS_IS_IFCFG_RH_PLUGIN, NMSettingsPlugin) /*****************************************************************************/ @@ -99,610 +95,811 @@ NM_DEFINE_SINGLETON_GETTER (SettingsPluginIfcfg, settings_plugin_ifcfg_get, SETT /*****************************************************************************/ -static NMIfcfgConnection *update_connection (SettingsPluginIfcfg *plugin, - NMConnection *source, - const char *full_path, - NMIfcfgConnection *connection, - gboolean protect_existing_connection, - GHashTable *protected_connections, - GError **error); +static void _unhandled_specs_reset (NMSIfcfgRHPlugin *self); + +static void _unhandled_specs_merge_storages (NMSIfcfgRHPlugin *self, + NMSettUtilStorages *storages); /*****************************************************************************/ static void -connection_ifcfg_changed (NMIfcfgConnection *connection, gpointer user_data) +nm_assert_self (NMSIfcfgRHPlugin *self, gboolean unhandled_specs_consistent) { - SettingsPluginIfcfg *self = SETTINGS_PLUGIN_IFCFG (user_data); - SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self); - const char *path; + nm_assert (NMS_IS_IFCFG_RH_PLUGIN (self)); - path = nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection)); - g_return_if_fail (path != NULL); +#if NM_MORE_ASSERTS > 5 + { + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); + NMSIfcfgRHStorage *storage; + gsize n_uuid; + gs_unref_hashtable GHashTable *h_unmanaged = NULL; + gs_unref_hashtable GHashTable *h_unrecognized = NULL; - if (!priv->ifcfg_monitor) { - _LOGD ("connection_ifcfg_changed("NM_IFCFG_CONNECTION_LOG_FMTD"): %s", NM_IFCFG_CONNECTION_LOG_ARGD (connection), "ignore event"); - return; - } + nm_assert (g_hash_table_size (priv->storages.idx_by_filename) == c_list_length (&priv->storages._storage_lst_head)); + + h_unmanaged = g_hash_table_new (nm_str_hash, g_str_equal); + h_unrecognized = g_hash_table_new (nm_str_hash, g_str_equal); + + n_uuid = 0; + + c_list_for_each_entry (storage, &priv->storages._storage_lst_head, parent._storage_lst) { + const char *uuid; + const char *filename; + + filename = nms_ifcfg_rh_storage_get_filename (storage); + + nm_assert (filename && NM_STR_HAS_PREFIX (filename, IFCFG_DIR"/")); + + uuid = nms_ifcfg_rh_storage_get_uuid_opt (storage); + + nm_assert ((!!uuid) + (!!storage->unmanaged_spec) + (!!storage->unrecognized_spec) == 1); - _LOGD ("connection_ifcfg_changed("NM_IFCFG_CONNECTION_LOG_FMTD"): %s", NM_IFCFG_CONNECTION_LOG_ARGD (connection), "reload"); + nm_assert (storage == nm_sett_util_storages_lookup_by_filename (&priv->storages, filename)); + + if (uuid) { + NMSettUtilStorageByUuidHead *sbuh; + NMSettUtilStorageByUuidHead *sbuh2; + + if (storage->connection) + nm_assert (nm_streq0 (nm_connection_get_uuid (storage->connection), uuid)); + + if (!g_hash_table_lookup_extended (priv->storages.idx_by_uuid, &uuid, (gpointer *) &sbuh, (gpointer *) &sbuh2)) + nm_assert_not_reached (); + + nm_assert (sbuh); + nm_assert (nm_streq (uuid, sbuh->uuid)); + nm_assert (sbuh == sbuh2); + nm_assert (c_list_contains (&sbuh->_storage_by_uuid_lst_head, &storage->parent._storage_by_uuid_lst)); + + if (c_list_first (&sbuh->_storage_by_uuid_lst_head) == &storage->parent._storage_by_uuid_lst) + n_uuid++; + } else if (storage->unmanaged_spec) { + nm_assert (strlen (storage->unmanaged_spec) > 0); + g_hash_table_add (h_unmanaged, storage->unmanaged_spec); + } else if (storage->unrecognized_spec) { + nm_assert (strlen (storage->unrecognized_spec) > 0); + g_hash_table_add (h_unrecognized, storage->unrecognized_spec); + } else + nm_assert_not_reached (); + + nm_assert (!storage->connection); + } - update_connection (self, NULL, path, connection, TRUE, NULL, NULL); + nm_assert (g_hash_table_size (priv->storages.idx_by_uuid) == n_uuid); + + if (unhandled_specs_consistent) { + nm_assert (nm_utils_hashtable_same_keys (h_unmanaged, priv->unmanaged_specs)); + nm_assert (nm_utils_hashtable_same_keys (h_unrecognized, priv->unrecognized_specs)); + } + } +#endif } -static void -connection_removed_cb (NMSettingsConnection *obj, gpointer user_data) +/*****************************************************************************/ + +static NMSIfcfgRHStorage * +_load_file (NMSIfcfgRHPlugin *self, + const char *filename, + GError **error) { - g_hash_table_remove (SETTINGS_PLUGIN_IFCFG_GET_PRIVATE ((SettingsPluginIfcfg *) user_data)->connections, - nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (obj))); + gs_unref_object NMConnection *connection = NULL; + gs_free_error GError *load_error = NULL; + gs_free char *unhandled_spec = NULL; + gboolean load_error_ignore; + struct stat st; + + if (stat (filename, &st) != 0) { + int errsv = errno; + + if (error) { + nm_utils_error_set_errno (error, errsv, + "failure to stat file \%s\": %s", + filename); + } else + _LOGT ("load[%s]: failure to stat file: %s", filename, nm_strerror_native (errsv)); + return NULL; + } + + connection = connection_from_file (filename, + &unhandled_spec, + &load_error, + &load_error_ignore); + if (load_error) { + if (error) { + nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, + "failure to read file \"%s\": %s", + filename, load_error->message); + } else { + _NMLOG (load_error_ignore ? LOGL_TRACE : LOGL_WARN, + "load[%s]: failure to read file: %s", filename, load_error->message); + } + return NULL; + } + + if (unhandled_spec) { + const char *unmanaged_spec; + const char *unrecognized_spec; + + if (!nms_ifcfg_rh_util_parse_unhandled_spec (unhandled_spec, + &unmanaged_spec, + &unrecognized_spec)) { + nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, + "invalid unhandled spec \"%s\"", + unhandled_spec); + nm_assert_not_reached (); + return NULL; + } + return nms_ifcfg_rh_storage_new_unhandled (self, + filename, + unmanaged_spec, + unrecognized_spec); + } + + return nms_ifcfg_rh_storage_new_connection (self, + filename, + g_steal_pointer (&connection), + &st.st_mtim); } static void -remove_connection (SettingsPluginIfcfg *self, NMIfcfgConnection *connection) +_load_dir (NMSIfcfgRHPlugin *self, + NMSettUtilStorages *storages) { - SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self); - gboolean unmanaged, unrecognized; + gs_unref_hashtable GHashTable *dupl_filenames = NULL; + gs_free_error GError *local = NULL; + const char *f_filename; + GDir *dir; - g_return_if_fail (self != NULL); - g_return_if_fail (connection != NULL); + dir = g_dir_open (IFCFG_DIR, 0, &local); + if (!dir) { + _LOGT ("Could not read directory '%s': %s", IFCFG_DIR, local->message); + return; + } - _LOGI ("remove "NM_IFCFG_CONNECTION_LOG_FMT, NM_IFCFG_CONNECTION_LOG_ARG (connection)); + dupl_filenames = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_free); - unmanaged = !!nm_ifcfg_connection_get_unmanaged_spec (connection); - unrecognized = !!nm_ifcfg_connection_get_unrecognized_spec (connection); + while ((f_filename = g_dir_read_name (dir))) { + gs_free char *full_path = NULL; + NMSIfcfgRHStorage *storage; + char *full_filename; - g_object_ref (connection); - g_hash_table_remove (priv->connections, nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (connection))); - if (!unmanaged && !unrecognized) - nm_settings_connection_signal_remove (NM_SETTINGS_CONNECTION (connection)); - g_object_unref (connection); + full_path = g_build_filename (IFCFG_DIR, f_filename, NULL); + full_filename = utils_detect_ifcfg_path (full_path, TRUE); + if (!full_filename) + continue; - /* Emit changes _after_ removing the connection */ - if (unmanaged) - _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NM_SETTINGS_PLUGIN (self)); - if (unrecognized) - _nm_settings_plugin_emit_signal_unrecognized_specs_changed (NM_SETTINGS_PLUGIN (self)); + if (!g_hash_table_add (dupl_filenames, full_filename)) + continue; + + nm_assert (!nm_sett_util_storages_lookup_by_filename (storages, full_filename)); + + storage = _load_file (self, + full_filename, + NULL); + if (storage) + nm_sett_util_storages_add_take (storages, storage); + } + g_dir_close (dir); } -static NMIfcfgConnection * -find_by_path (SettingsPluginIfcfg *self, const char *path) +static void +_storages_consolidate (NMSIfcfgRHPlugin *self, + NMSettUtilStorages *storages_new, + gboolean replace_all, + GHashTable *storages_replaced, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data) { - SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self); - GHashTableIter iter; - NMSettingsConnection *candidate = NULL; + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); + CList lst_conn_info_deleted = C_LIST_INIT (lst_conn_info_deleted); + gs_unref_ptrarray GPtrArray *storages_modified = NULL; + CList storages_deleted; + NMSIfcfgRHStorage *storage_safe; + NMSIfcfgRHStorage *storage_new; + NMSIfcfgRHStorage *storage_old; + NMSIfcfgRHStorage *storage; + guint i; - g_return_val_if_fail (path != NULL, NULL); + /* when we reload all files, we must signal add/update/modify of profiles one-by-one. + * NMSettings then goes ahead and emits further signals and a lot of things happen. + * + * So, first, emit an update of the unmanaged/unrecognized specs that contains *all* + * the unmanaged/unrecognized devices from before and after. Since both unmanaged/unrecognized + * specs have the meaning of "not doing something", it makes sense that we temporarily + * disable that action for the sum of before and after. */ + _unhandled_specs_merge_storages (self, storages_new); + + storages_modified = g_ptr_array_new_with_free_func (g_object_unref); + c_list_init (&storages_deleted); + + c_list_for_each_entry (storage_old, &priv->storages._storage_lst_head, parent._storage_lst) + storage_old->dirty = TRUE; + + c_list_for_each_entry_safe (storage_new, storage_safe, &storages_new->_storage_lst_head, parent._storage_lst) { + storage_old = nm_sett_util_storages_lookup_by_filename (&priv->storages, nms_ifcfg_rh_storage_get_filename (storage_new)); + + nm_sett_util_storages_steal (storages_new, storage_new); + + if ( !storage_old + || !nms_ifcfg_rh_storage_equal_type (storage_new, storage_old)) { + if (storage_old) { + nm_sett_util_storages_steal (&priv->storages, storage_old); + if (nms_ifcfg_rh_storage_get_uuid_opt (storage_old)) + c_list_link_tail (&storages_deleted, &storage_old->parent._storage_lst); + else + nms_ifcfg_rh_storage_destroy (storage_old); + } + storage_new->dirty = FALSE; + nm_sett_util_storages_add_take (&priv->storages, storage_new); + g_ptr_array_add (storages_modified, g_object_ref (storage_new)); + continue; + } - g_hash_table_iter_init (&iter, priv->connections); - while (g_hash_table_iter_next (&iter, NULL, (gpointer) &candidate)) { - if (g_strcmp0 (path, nm_settings_connection_get_filename (candidate)) == 0) - return NM_IFCFG_CONNECTION (candidate); + storage_old->dirty = FALSE; + nms_ifcfg_rh_storage_copy_content (storage_old, storage_new); + nms_ifcfg_rh_storage_destroy (storage_new); + g_ptr_array_add (storages_modified, g_object_ref (storage_old)); } - return NULL; -} -static NMIfcfgConnection * -update_connection (SettingsPluginIfcfg *self, - NMConnection *source, - const char *full_path, - NMIfcfgConnection *connection, - gboolean protect_existing_connection, - GHashTable *protected_connections, - GError **error) -{ - SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self); - NMIfcfgConnection *connection_new; - NMIfcfgConnection *connection_by_uuid; - GError *local = NULL; - const char *new_unmanaged = NULL, *old_unmanaged = NULL; - const char *new_unrecognized = NULL, *old_unrecognized = NULL; - gboolean unmanaged_changed = FALSE, unrecognized_changed = FALSE; - const char *uuid; - gboolean ignore_error = FALSE; - - g_return_val_if_fail (!source || NM_IS_CONNECTION (source), NULL); - g_return_val_if_fail (full_path || source, NULL); - - if (full_path) - _LOGD ("loading from file \"%s\"...", full_path); - - /* Create a NMIfcfgConnection instance, either by reading from @full_path or - * based on @source. */ - connection_new = nm_ifcfg_connection_new (source, full_path, &local, &ignore_error); - if (!connection_new) { - /* Unexpected failure. Probably the file is invalid? */ - if ( connection - && !protect_existing_connection - && (!protected_connections || !g_hash_table_contains (protected_connections, connection))) - remove_connection (self, connection); - if (!source) { - _NMLOG (ignore_error ? LOGL_DEBUG : LOGL_WARN, - "loading \"%s\" fails: %s", full_path, local ? local->message : "(unknown reason)"); + c_list_for_each_entry_safe (storage_old, storage_safe, &priv->storages._storage_lst_head, parent._storage_lst) { + if (!storage_old->dirty) + continue; + if ( replace_all + || ( storages_replaced + && g_hash_table_contains (storages_replaced, storage_old))) { + nm_sett_util_storages_steal (&priv->storages, storage_old); + if (nms_ifcfg_rh_storage_get_uuid_opt (storage_old)) + c_list_link_tail (&storages_deleted, &storage_old->parent._storage_lst); + else + nms_ifcfg_rh_storage_destroy (storage_old); } - g_propagate_error (error, local); - return NULL; } - uuid = nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (connection_new)); - connection_by_uuid = g_hash_table_lookup (priv->connections, uuid); + /* raise events. */ - if ( connection - && connection != connection_by_uuid) { + for (i = 0; i < storages_modified->len; i++) { + storage = storages_modified->pdata[i]; + storage->dirty = TRUE; + } - if ( (protect_existing_connection && connection_by_uuid != NULL) - || (protected_connections && g_hash_table_contains (protected_connections, connection))) { - NMIfcfgConnection *conflicting = (protect_existing_connection && connection_by_uuid != NULL) ? connection_by_uuid : connection; + for (i = 0; i < storages_modified->len; i++) { + gs_unref_object NMConnection *connection = NULL; + storage = storages_modified->pdata[i]; - if (source) - _LOGW ("cannot update protected connection "NM_IFCFG_CONNECTION_LOG_FMT" due to conflicting UUID %s", NM_IFCFG_CONNECTION_LOG_ARG (conflicting), uuid); - else - _LOGW ("cannot load %s due to conflicting UUID for "NM_IFCFG_CONNECTION_LOG_FMT, full_path, NM_IFCFG_CONNECTION_LOG_ARG (conflicting)); - g_object_unref (connection_new); - g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Cannot update protected connection due to conflicting UUID"); - return NULL; + if (!storage->dirty) { + /* the entry is no longer dirty. In the meantime we already emited + * another signal for it. */ + continue; + } + storage->dirty = FALSE; + if (storage != nm_sett_util_storages_lookup_by_filename (&priv->storages, nms_ifcfg_rh_storage_get_filename (storage))) { + /* hm? The profile was deleted in the meantime? That is only possible + * if the signal handler called again into the plugin. In any case, the event + * was already emitted. Skip. */ + continue; } - /* The new connection has a different UUID then the original one that we - * are about to update. Remove @connection. */ - remove_connection (self, connection); + connection = nms_ifcfg_rh_storage_steal_connection (storage); + if (!connection) { + nm_assert (!nms_ifcfg_rh_storage_get_uuid_opt (storage)); + continue; + } + + nm_assert (NM_IS_CONNECTION (connection)); + nm_assert (nms_ifcfg_rh_storage_get_uuid_opt (storage)); + callback (NM_SETTINGS_PLUGIN (self), + NM_SETTINGS_STORAGE (storage), + connection, + user_data); } - /* Check if the found connection with the same UUID is not protected from updating. */ - if ( connection_by_uuid - && ( (!connection && protect_existing_connection) - || (protected_connections && g_hash_table_contains (protected_connections, connection_by_uuid)))) { - if (source) - _LOGW ("cannot update connection due to conflicting UUID for "NM_IFCFG_CONNECTION_LOG_FMT, NM_IFCFG_CONNECTION_LOG_ARG (connection_by_uuid)); - else - _LOGW ("cannot load %s due to conflicting UUID for "NM_IFCFG_CONNECTION_LOG_FMT, full_path, NM_IFCFG_CONNECTION_LOG_ARG (connection_by_uuid)); - g_object_unref (connection_new); - g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Skip updating protected connection during reload"); - return NULL; + while ((storage = c_list_first_entry (&storages_deleted, NMSIfcfgRHStorage, parent._storage_lst))) { + c_list_unlink (&storage->parent._storage_lst); + callback (NM_SETTINGS_PLUGIN (self), + NM_SETTINGS_STORAGE (storage), + NULL, + user_data); + nms_ifcfg_rh_storage_destroy (storage); } +} - /* Evaluate unmanaged/unrecognized flags. */ - if (connection_by_uuid) - old_unmanaged = nm_ifcfg_connection_get_unmanaged_spec (connection_by_uuid); - new_unmanaged = nm_ifcfg_connection_get_unmanaged_spec (connection_new); - unmanaged_changed = g_strcmp0 (old_unmanaged, new_unmanaged); - - if (connection_by_uuid) - old_unrecognized = nm_ifcfg_connection_get_unrecognized_spec (connection_by_uuid); - new_unrecognized = nm_ifcfg_connection_get_unrecognized_spec (connection_new); - unrecognized_changed = g_strcmp0 (old_unrecognized, new_unrecognized); - - if (connection_by_uuid) { - const char *old_path; - - old_path = nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection_by_uuid)); - - if ( !unmanaged_changed - && !unrecognized_changed - && nm_connection_compare (nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection_by_uuid)), - nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection_new)), - NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS | - NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)) { - if ( old_path - && !nm_streq0 (old_path, full_path)) { - _LOGI ("rename \"%s\" to "NM_IFCFG_CONNECTION_LOG_FMT" without other changes", - nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection_by_uuid)), - NM_IFCFG_CONNECTION_LOG_ARG (connection_new)); - } - } else { +/*****************************************************************************/ + +static void +load_connections (NMSettingsPlugin *plugin, + NMSettingsPluginConnectionLoadEntry *entries, + gsize n_entries, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data) +{ + NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (plugin); + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); + nm_auto_clear_sett_util_storages NMSettUtilStorages storages_new = NM_SETT_UTIL_STORAGES_INIT (storages_new, nms_ifcfg_rh_storage_destroy); + gs_unref_hashtable GHashTable *dupl_filenames = NULL; + gs_unref_hashtable GHashTable *storages_replaced = NULL; + gs_unref_hashtable GHashTable *loaded_uuids = NULL; + const char *loaded_uuid; + GHashTableIter h_iter; + gsize i; + + if (n_entries == 0) + return; - /******************************************************* - * UPDATE - *******************************************************/ + dupl_filenames = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL); - if (source) - _LOGI ("update "NM_IFCFG_CONNECTION_LOG_FMT" from %s", NM_IFCFG_CONNECTION_LOG_ARG (connection_new), NM_IFCFG_CONNECTION_LOG_PATH (old_path)); - else if (nm_streq0 (old_path, nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection_new)))) - _LOGI ("update "NM_IFCFG_CONNECTION_LOG_FMT, NM_IFCFG_CONNECTION_LOG_ARG (connection_new)); - else if (old_path) - _LOGI ("rename \"%s\" to "NM_IFCFG_CONNECTION_LOG_FMT, old_path, NM_IFCFG_CONNECTION_LOG_ARG (connection_new)); - else - _LOGI ("update and persist "NM_IFCFG_CONNECTION_LOG_FMT, NM_IFCFG_CONNECTION_LOG_ARG (connection_new)); - - g_object_set (connection_by_uuid, - NM_IFCFG_CONNECTION_UNMANAGED_SPEC, new_unmanaged, - NM_IFCFG_CONNECTION_UNRECOGNIZED_SPEC, new_unrecognized, - NULL); - - if (!nm_settings_connection_update (NM_SETTINGS_CONNECTION (connection_by_uuid), - nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection_new)), - NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP_SAVED, - NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, - "ifcfg-update", - &local)) { - /* Shouldn't ever get here as 'connection_new' was verified by the reader already - * and the UUID did not change. */ - g_assert_not_reached (); - } - g_assert_no_error (local); - - if (new_unmanaged || new_unrecognized) { - if (!old_unmanaged && !old_unrecognized) { - /* ref connection first, because we put it into priv->connections below. - * Emitting signal-removed might otherwise delete it. */ - g_object_ref (connection_by_uuid); - - /* Unexport the connection by telling the settings service it's - * been removed. - */ - nm_settings_connection_signal_remove (NM_SETTINGS_CONNECTION (connection_by_uuid)); - - /* signal_remove() will end up removing the connection from our hash, - * so add it back now. - */ - g_hash_table_insert (priv->connections, - g_strdup (nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (connection_by_uuid))), - connection_by_uuid /* we took reference above and pass it on */); - } - } else { - if (old_unmanaged /* && !new_unmanaged */) { - _LOGI ("Managing connection "NM_IFCFG_CONNECTION_LOG_FMT" and its device because NM_CONTROLLED was true.", - NM_IFCFG_CONNECTION_LOG_ARG (connection_new)); - _nm_settings_plugin_emit_signal_connection_added (NM_SETTINGS_PLUGIN (self), - NM_SETTINGS_CONNECTION (connection_by_uuid)); - } else if (old_unrecognized /* && !new_unrecognized */) { - _LOGI ("Managing connection "NM_IFCFG_CONNECTION_LOG_FMT" because it is now a recognized type.", - NM_IFCFG_CONNECTION_LOG_ARG (connection_new)); - _nm_settings_plugin_emit_signal_connection_added (NM_SETTINGS_PLUGIN (self), - NM_SETTINGS_CONNECTION (connection_by_uuid)); - } - } + loaded_uuids = g_hash_table_new (nm_str_hash, g_str_equal); + + storages_replaced = g_hash_table_new_full (nm_direct_hash, NULL, g_object_unref, NULL); + + for (i = 0; i < n_entries; i++) { + NMSettingsPluginConnectionLoadEntry *const entry = &entries[i]; + gs_free_error GError *local = NULL; + const char *full_filename; + const char *uuid; + gs_free char *full_filename_keep = NULL; + NMSettingsPluginConnectionLoadEntry *dupl_content_entry; + gs_unref_object NMSIfcfgRHStorage *storage = NULL; + + if (entry->handled) + continue; - if (unmanaged_changed) - _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NM_SETTINGS_PLUGIN (self)); - if (unrecognized_changed) - _nm_settings_plugin_emit_signal_unrecognized_specs_changed (NM_SETTINGS_PLUGIN (self)); + if (entry->filename[0] != '/') + continue; + + full_filename_keep = utils_detect_ifcfg_path (entry->filename, FALSE); + + if (!full_filename_keep) { + if (nm_utils_file_is_in_path (entry->filename, IFCFG_DIR)) { + nm_utils_error_set (&entry->error, + NM_UTILS_ERROR_UNKNOWN, + ("path is not a valid name for an ifcfg-rh file")); + entry->handled = TRUE; + } + continue; } - nm_settings_connection_set_filename (NM_SETTINGS_CONNECTION (connection_by_uuid), full_path); - g_object_unref (connection_new); - return connection_by_uuid; - } else { - /******************************************************* - * ADD - *******************************************************/ + if ((dupl_content_entry = g_hash_table_lookup (dupl_filenames, full_filename_keep))) { + /* we already visited this file. */ + entry->handled = dupl_content_entry->handled; + if (dupl_content_entry->error) { + g_set_error_literal (&entry->error, + dupl_content_entry->error->domain, + dupl_content_entry->error->code, + dupl_content_entry->error->message); + } + continue; + } - if (source) - _LOGI ("add connection "NM_IFCFG_CONNECTION_LOG_FMT, NM_IFCFG_CONNECTION_LOG_ARG (connection_new)); - else - _LOGI ("new connection "NM_IFCFG_CONNECTION_LOG_FMT, NM_IFCFG_CONNECTION_LOG_ARG (connection_new)); - g_hash_table_insert (priv->connections, - g_strdup (uuid), - connection_new /* take reference */); - - g_signal_connect (connection_new, NM_SETTINGS_CONNECTION_REMOVED, - G_CALLBACK (connection_removed_cb), - self); - - if (nm_ifcfg_connection_get_unmanaged_spec (connection_new)) { - _LOGI ("Ignoring connection "NM_IFCFG_CONNECTION_LOG_FMT" due to NM_CONTROLLED=no. Unmanaged: %s.", - NM_IFCFG_CONNECTION_LOG_ARG (connection_new), - nm_ifcfg_connection_get_unmanaged_spec (connection_new)); - } else if (nm_ifcfg_connection_get_unrecognized_spec (connection_new)) - _LOGW ("Ignoring connection "NM_IFCFG_CONNECTION_LOG_FMT" of unrecognized type.", NM_IFCFG_CONNECTION_LOG_ARG (connection_new)); - - /* watch changes of ifcfg hardlinks */ - g_signal_connect (G_OBJECT (connection_new), "ifcfg-changed", - G_CALLBACK (connection_ifcfg_changed), self); - - if (!source) { - /* Only raise the signal if we were called without source, i.e. if we read the connection from file. - * Otherwise, we were called by add_connection() which does not expect the signal. */ - if (nm_ifcfg_connection_get_unmanaged_spec (connection_new)) - _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NM_SETTINGS_PLUGIN (self)); - else if (nm_ifcfg_connection_get_unrecognized_spec (connection_new)) - _nm_settings_plugin_emit_signal_unrecognized_specs_changed (NM_SETTINGS_PLUGIN (self)); - else { - _nm_settings_plugin_emit_signal_connection_added (NM_SETTINGS_PLUGIN (self), - NM_SETTINGS_CONNECTION (connection_new)); + entry->handled = TRUE; + + full_filename = full_filename_keep; + if (!g_hash_table_insert (dupl_filenames, g_steal_pointer (&full_filename_keep), entry)) + nm_assert_not_reached (); + + storage = _load_file (self, + full_filename, + &local); + if (!storage) { + if (nm_utils_file_stat (full_filename, NULL) == -ENOENT) { + NMSIfcfgRHStorage *storage2; + + /* the file does not exist. We take that as indication to unload the file + * that was previously loaded... */ + storage2 = nm_sett_util_storages_lookup_by_filename (&priv->storages, full_filename); + if (storage2) + g_hash_table_add (storages_replaced, g_object_ref (storage2)); + continue; } + g_propagate_error (&entry->error, g_steal_pointer (&local)); + continue; } - return connection_new; + + uuid = nms_ifcfg_rh_storage_get_uuid_opt (storage); + if (uuid) + g_hash_table_add (loaded_uuids, (char *) uuid); + + nm_sett_util_storages_add_take (&storages_new, g_steal_pointer (&storage)); } -} -static void -ifcfg_dir_changed (GFileMonitor *monitor, - GFile *file, - GFile *other_file, - GFileMonitorEvent event_type, - gpointer user_data) -{ - SettingsPluginIfcfg *plugin = SETTINGS_PLUGIN_IFCFG (user_data); - char *path, *ifcfg_path; - NMIfcfgConnection *connection; - - path = g_file_get_path (file); - - ifcfg_path = utils_detect_ifcfg_path (path, FALSE); - _LOGD ("ifcfg_dir_changed(%s) = %d // %s", path, event_type, ifcfg_path ?: "(none)"); - if (ifcfg_path) { - connection = find_by_path (plugin, ifcfg_path); - switch (event_type) { - case G_FILE_MONITOR_EVENT_DELETED: - if (connection) - remove_connection (plugin, connection); - break; - case G_FILE_MONITOR_EVENT_CREATED: - case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT: - /* Update or new */ - update_connection (plugin, NULL, ifcfg_path, connection, TRUE, NULL, NULL); - break; - default: - break; + /* now we visit all UUIDs that are about to change... */ + g_hash_table_iter_init (&h_iter, loaded_uuids); + while (g_hash_table_iter_next (&h_iter, (gpointer *) &loaded_uuid, NULL)) { + NMSIfcfgRHStorage *storage; + NMSettUtilStorageByUuidHead *sbuh; + + sbuh = nm_sett_util_storages_lookup_by_uuid (&priv->storages, loaded_uuid); + if (!sbuh) + continue; + + c_list_for_each_entry (storage, &sbuh->_storage_by_uuid_lst_head, parent._storage_by_uuid_lst) { + const char *full_filename = nms_ifcfg_rh_storage_get_filename (storage); + gs_unref_object NMSIfcfgRHStorage *storage_new = NULL; + gs_free_error GError *local = NULL; + + if (g_hash_table_contains (dupl_filenames, full_filename)) { + /* already re-loaded. */ + continue; + } + + /* @storage has a UUID that was just loaded from disk, but we have an entry in cache. + * Reload that file too despite not being told to do so. The reason is to get + * the latest file timestamp so that we get the priorities right. */ + + storage_new = _load_file (self, + full_filename, + &local); + if ( storage_new + && !nm_streq0 (loaded_uuid, nms_ifcfg_rh_storage_get_uuid_opt (storage_new))) { + /* the file now references a different UUID. We are not told to reload + * that file, so this means the existing storage (with the previous + * filename and UUID tuple) is no longer valid. */ + g_clear_object (&storage_new); + } + + g_hash_table_add (storages_replaced, g_object_ref (storage)); + if (storage_new) + nm_sett_util_storages_add_take (&storages_new, g_steal_pointer (&storage_new)); } - g_free (ifcfg_path); } - g_free (path); + + nm_clear_pointer (&loaded_uuids, g_hash_table_destroy); + nm_clear_pointer (&dupl_filenames, g_hash_table_destroy); + + _storages_consolidate (self, + &storages_new, + FALSE, + storages_replaced, + callback, + user_data); } static void -setup_ifcfg_monitoring (SettingsPluginIfcfg *plugin) +reload_connections (NMSettingsPlugin *plugin, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data) { - SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (plugin); - GFile *file; - GFileMonitor *monitor; - - file = g_file_new_for_path (IFCFG_DIR "/"); - monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL); - g_object_unref (file); - - if (monitor) { - priv->ifcfg_monitor_id = g_signal_connect (monitor, "changed", - G_CALLBACK (ifcfg_dir_changed), plugin); - priv->ifcfg_monitor = monitor; - } -} + NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (plugin); + nm_auto_clear_sett_util_storages NMSettUtilStorages storages_new = NM_SETT_UTIL_STORAGES_INIT (storages_new, nms_ifcfg_rh_storage_destroy); -static GHashTable * -_paths_from_connections (GHashTable *connections) -{ - GHashTableIter iter; - NMIfcfgConnection *connection; - GHashTable *paths = g_hash_table_new (nm_str_hash, g_str_equal); + nm_assert_self (self, TRUE); - g_hash_table_iter_init (&iter, connections); - while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &connection)) { - const char *path = nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection)); + _load_dir (self, &storages_new); - if (path) - g_hash_table_add (paths, (void *) path); - } - return paths; + _storages_consolidate (self, + &storages_new, + TRUE, + NULL, + callback, + user_data); + + nm_assert_self (self, FALSE); } -static int -_sort_paths (const char **f1, const char **f2, GHashTable *paths) +static void +load_connections_done (NMSettingsPlugin *plugin) { - struct stat st; - gboolean c1, c2; - gint64 m1, m2; - - c1 = !!g_hash_table_contains (paths, *f1); - c2 = !!g_hash_table_contains (paths, *f2); - if (c1 != c2) - return c1 ? -1 : 1; + NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (plugin); - m1 = stat (*f1, &st) == 0 ? (gint64) st.st_mtime : G_MININT64; - m2 = stat (*f2, &st) == 0 ? (gint64) st.st_mtime : G_MININT64; - if (m1 != m2) - return m1 > m2 ? -1 : 1; + /* at the beginning of a load, we emit a change signal for unmanaged/unrecognized + * specs that contain the sum of before and after (_unhandled_specs_merge_storages()). + * + * The idea is that while we emit signals about changes to connection, we have + * the sum of all unmanaged/unrecognized devices from before and after. + * + * This if triggered at the end, to reset the specs. */ + _unhandled_specs_reset (self); - return strcmp (*f1, *f2); + nm_assert_self (self, TRUE); } -static void -read_connections (SettingsPluginIfcfg *plugin) -{ - SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (plugin); - GDir *dir; - GError *err = NULL; - const char *item; - GHashTable *alive_connections; - GHashTableIter iter; - NMIfcfgConnection *connection; - GPtrArray *dead_connections = NULL; - guint i; - GPtrArray *filenames; - GHashTable *paths; +/*****************************************************************************/ - dir = g_dir_open (IFCFG_DIR, 0, &err); - if (!dir) { - _LOGW ("Could not read directory '%s': %s", IFCFG_DIR, err->message); - g_error_free (err); - return; +static gboolean +add_connection (NMSettingsPlugin *plugin, + NMConnection *connection, + NMSettingsStorage **out_storage, + NMConnection **out_connection, + GError **error) +{ + NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (plugin); + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); + gs_unref_object NMSIfcfgRHStorage *storage = NULL; + gs_unref_object NMConnection *reread = NULL; + gs_free char *full_filename = NULL; + GError *local = NULL; + gboolean reread_same; + struct timespec mtime; + + nm_assert_self (self, TRUE); + nm_assert (NM_IS_CONNECTION (connection)); + nm_assert (out_storage && !*out_storage); + nm_assert (out_connection && !*out_connection); + + if (!nms_ifcfg_rh_writer_write_connection (connection, + IFCFG_DIR, + NULL, + nm_sett_util_allow_filename_cb, + NM_SETT_UTIL_ALLOW_FILENAME_DATA (&priv->storages, NULL), + &full_filename, + &reread, + &reread_same, + &local)) { + _LOGT ("commit: %s (%s): failed to add: %s", + nm_connection_get_uuid (connection), + nm_connection_get_id (connection), + local->message); + g_propagate_error (error, local); + return FALSE; } - alive_connections = g_hash_table_new (nm_direct_hash, NULL); + if ( !reread + || reread_same) + nm_g_object_ref_set (&reread, connection); - filenames = g_ptr_array_new_with_free_func (g_free); - while ((item = g_dir_read_name (dir))) { - char *full_path, *real_path; + nm_assert (full_filename && full_filename[0] == '/'); - full_path = g_build_filename (IFCFG_DIR, item, NULL); - real_path = utils_detect_ifcfg_path (full_path, TRUE); + _LOGT ("commit: %s (%s) added as \"%s\"", + nm_connection_get_uuid (reread), + nm_connection_get_id (reread), + full_filename); - if (real_path) - g_ptr_array_add (filenames, real_path); - g_free (full_path); - } - g_dir_close (dir); + storage = nms_ifcfg_rh_storage_new_connection (self, + full_filename, + g_steal_pointer (&reread), + nm_sett_util_stat_mtime (full_filename, FALSE, &mtime)); - /* While reloading, we don't replace connections that we already loaded while - * iterating over the files. - * - * To have sensible, reproducible behavior, sort the paths by last modification - * time preferring older files. - */ - paths = _paths_from_connections (priv->connections); - g_ptr_array_sort_with_data (filenames, (GCompareDataFunc) _sort_paths, paths); - g_hash_table_destroy (paths); - - for (i = 0; i < filenames->len; i++) { - connection = update_connection (plugin, NULL, filenames->pdata[i], NULL, FALSE, alive_connections, NULL); - if (connection) - g_hash_table_add (alive_connections, connection); - } - g_ptr_array_free (filenames, TRUE); - - g_hash_table_iter_init (&iter, priv->connections); - while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &connection)) { - if ( !g_hash_table_contains (alive_connections, connection) - && nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection))) { - if (!dead_connections) - dead_connections = g_ptr_array_new (); - g_ptr_array_add (dead_connections, connection); - } - } - g_hash_table_destroy (alive_connections); + nm_sett_util_storages_add_take (&priv->storages, g_object_ref (storage)); - if (dead_connections) { - for (i = 0; i < dead_connections->len; i++) - remove_connection (plugin, dead_connections->pdata[i]); - g_ptr_array_free (dead_connections, TRUE); - } + *out_connection = nms_ifcfg_rh_storage_steal_connection (storage); + *out_storage = NM_SETTINGS_STORAGE (g_steal_pointer (&storage)); + + nm_assert_self (self, TRUE); + + return TRUE; } -static GSList * -get_connections (NMSettingsPlugin *config) +static gboolean +update_connection (NMSettingsPlugin *plugin, + NMSettingsStorage *storage_x, + NMConnection *connection, + NMSettingsStorage **out_storage, + NMConnection **out_connection, + GError **error) { - SettingsPluginIfcfg *plugin = SETTINGS_PLUGIN_IFCFG (config); - SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (plugin); - GSList *list = NULL; - GHashTableIter iter; - NMIfcfgConnection *connection; - - if (!priv->initialized) { - if (nm_config_get_monitor_connection_files (nm_config_get ())) - setup_ifcfg_monitoring (plugin); - read_connections (plugin); - priv->initialized = TRUE; + NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (plugin); + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); + NMSIfcfgRHStorage *storage = NMS_IFCFG_RH_STORAGE (storage_x); + const char *full_filename; + const char *uuid; + GError *local = NULL; + gs_unref_object NMConnection *reread = NULL; + gboolean reread_same; + struct timespec mtime; + + nm_assert_self (self, TRUE); + nm_assert (NM_IS_CONNECTION (connection)); + nm_assert (NMS_IS_IFCFG_RH_STORAGE (storage)); + nm_assert (_nm_connection_verify (connection, NULL) == NM_SETTING_VERIFY_SUCCESS); + nm_assert (!error || !*error); + + uuid = nms_ifcfg_rh_storage_get_uuid_opt (storage); + + nm_assert (uuid && nm_streq0 (uuid, nm_connection_get_uuid (connection))); + + full_filename = nms_ifcfg_rh_storage_get_filename (storage); + + nm_assert (full_filename); + nm_assert (storage == nm_sett_util_storages_lookup_by_filename (&priv->storages, full_filename)); + + if (!nms_ifcfg_rh_writer_write_connection (connection, + IFCFG_DIR, + full_filename, + nm_sett_util_allow_filename_cb, + NM_SETT_UTIL_ALLOW_FILENAME_DATA (&priv->storages, full_filename), + NULL, + &reread, + &reread_same, + &local)) { + _LOGT ("commit: failure to write %s (%s) to \"%s\": %s", + nm_connection_get_uuid (connection), + nm_connection_get_id (connection), + full_filename, + local->message); + g_propagate_error (error, local); + return FALSE; } - g_hash_table_iter_init (&iter, priv->connections); - while (g_hash_table_iter_next (&iter, NULL, (gpointer) &connection)) { - if ( !nm_ifcfg_connection_get_unmanaged_spec (connection) - && !nm_ifcfg_connection_get_unrecognized_spec (connection)) - list = g_slist_prepend (list, connection); - } + if ( !reread + || reread_same) + nm_g_object_ref_set (&reread, connection); - return list; + _LOGT ("commit: \"%s\": profile %s (%s) written", + full_filename, + uuid, + nm_connection_get_id (connection)); + + storage->stat_mtime = *nm_sett_util_stat_mtime (full_filename, FALSE, &mtime); + + *out_storage = NM_SETTINGS_STORAGE (g_object_ref (storage)); + *out_connection = g_steal_pointer (&reread); + + nm_assert_self (self, TRUE); + + return TRUE; } static gboolean -load_connection (NMSettingsPlugin *config, - const char *filename) +delete_connection (NMSettingsPlugin *plugin, + NMSettingsStorage *storage_x, + GError **error) { - SettingsPluginIfcfg *plugin = SETTINGS_PLUGIN_IFCFG (config); - NMIfcfgConnection *connection; - char *ifcfg_path; + NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (plugin); + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); + NMSIfcfgRHStorage *storage = NMS_IFCFG_RH_STORAGE (storage_x); + const char *operation_message; + const char *full_filename; - if (!nm_utils_file_is_in_path (filename, IFCFG_DIR)) - return FALSE; + nm_assert_self (self, TRUE); + nm_assert (!error || !*error); + nm_assert (NMS_IS_IFCFG_RH_STORAGE (storage)); - /* get the real ifcfg-path. This allows us to properly - * handle load command using a route-* file etc. */ - ifcfg_path = utils_detect_ifcfg_path (filename, FALSE); - if (!ifcfg_path) - return FALSE; + full_filename = nms_ifcfg_rh_storage_get_filename (storage); + nm_assert (full_filename); + + nm_assert (nms_ifcfg_rh_storage_get_uuid_opt (storage)); + + nm_assert (storage == nm_sett_util_storages_lookup_by_filename (&priv->storages, full_filename)); + + { + gs_free char *keyfile = utils_get_keys_path (full_filename); + gs_free char *routefile = utils_get_route_path (full_filename); + gs_free char *route6file = utils_get_route6_path (full_filename); + const char *const files[] = { full_filename, keyfile, routefile, route6file }; + gboolean any_deleted = FALSE; + gboolean any_failure = FALSE; + int i; + + for (i = 0; i < G_N_ELEMENTS (files); i++) { + int errsv; + + if (unlink (files[i]) == 0) { + any_deleted = TRUE; + continue; + } + errsv = errno; + if (errsv == ENOENT) + continue; + + _LOGW ("commit: failure to delete file \"%s\": %s", + files[i], + nm_strerror_native (errsv)); + any_failure = TRUE; + } + if (any_failure) + operation_message = "failed to delete files from disk"; + else if (any_deleted) + operation_message = "deleted from disk"; + else + operation_message = "does not exist on disk"; + } + + _LOGT ("commit: deleted \"%s\", profile %s (%s)", + full_filename, + nms_ifcfg_rh_storage_get_uuid_opt (storage), + operation_message); - connection = find_by_path (plugin, ifcfg_path); - update_connection (plugin, NULL, ifcfg_path, connection, TRUE, NULL, NULL); - if (!connection) - connection = find_by_path (plugin, ifcfg_path); + nm_sett_util_storages_steal (&priv->storages, storage); + nms_ifcfg_rh_storage_destroy (storage); - g_free (ifcfg_path); - return (connection != NULL); + nm_assert_self (self, TRUE); + + return TRUE; } +/*****************************************************************************/ + static void -reload_connections (NMSettingsPlugin *config) +_unhandled_specs_reset (NMSIfcfgRHPlugin *self) { - SettingsPluginIfcfg *plugin = SETTINGS_PLUGIN_IFCFG (config); + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); + gs_unref_hashtable GHashTable *unmanaged_specs = NULL; + gs_unref_hashtable GHashTable *unrecognized_specs = NULL; + NMSIfcfgRHStorage *storage; + + unmanaged_specs = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL); + unrecognized_specs = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL); + + c_list_for_each_entry (storage, &priv->storages._storage_lst_head, parent._storage_lst) { + if (storage->unmanaged_spec) + g_hash_table_add (unmanaged_specs, g_strdup (storage->unmanaged_spec)); + if (storage->unrecognized_spec) + g_hash_table_add (unrecognized_specs, g_strdup (storage->unrecognized_spec)); + } - read_connections (plugin); + if (!nm_utils_hashtable_same_keys (unmanaged_specs, priv->unmanaged_specs)) { + g_hash_table_unref (priv->unmanaged_specs); + priv->unmanaged_specs = g_steal_pointer (&unmanaged_specs); + } + if (!nm_utils_hashtable_same_keys (unrecognized_specs, priv->unrecognized_specs)) { + g_hash_table_unref (priv->unrecognized_specs); + priv->unrecognized_specs = g_steal_pointer (&unrecognized_specs); + } + + if (!unmanaged_specs) + _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NM_SETTINGS_PLUGIN (self)); + if (!unrecognized_specs) + _nm_settings_plugin_emit_signal_unrecognized_specs_changed (NM_SETTINGS_PLUGIN (self)); } -static GSList * -get_unhandled_specs (NMSettingsPlugin *config, - const char *property) +static void +_unhandled_specs_merge_storages (NMSIfcfgRHPlugin *self, + NMSettUtilStorages *storages) { - SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE ((SettingsPluginIfcfg *) config); - GSList *list = NULL, *list_iter; - GHashTableIter iter; - gpointer connection; - char *spec; - gboolean found; - - g_hash_table_iter_init (&iter, priv->connections); - while (g_hash_table_iter_next (&iter, NULL, &connection)) { - g_object_get (connection, property, &spec, NULL); - if (spec) { - /* Ignore duplicates */ - for (list_iter = list, found = FALSE; list_iter; list_iter = g_slist_next (list_iter)) { - if (g_str_equal (list_iter->data, spec)) { - found = TRUE; - break; - } - } - if (found) - g_free (spec); - else - list = g_slist_prepend (list, spec); + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); + gboolean unmanaged_changed = FALSE; + gboolean unrecognized_changed = FALSE; + NMSIfcfgRHStorage *storage; + + c_list_for_each_entry (storage, &storages->_storage_lst_head, parent._storage_lst) { + if ( storage->unmanaged_spec + && !g_hash_table_contains (priv->unmanaged_specs, storage->unmanaged_spec)) { + unmanaged_changed = TRUE; + g_hash_table_add (priv->unmanaged_specs, g_strdup (storage->unmanaged_spec)); + } + if ( storage->unrecognized_spec + && !g_hash_table_contains (priv->unrecognized_specs, storage->unrecognized_spec)) { + unrecognized_changed = TRUE; + g_hash_table_add (priv->unrecognized_specs, g_strdup (storage->unrecognized_spec)); } } - return list; + + if (unmanaged_changed) + _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NM_SETTINGS_PLUGIN (self)); + if (unrecognized_changed) + _nm_settings_plugin_emit_signal_unrecognized_specs_changed (NM_SETTINGS_PLUGIN (self)); } static GSList * -get_unmanaged_specs (NMSettingsPlugin *config) +_unhandled_specs_from_hashtable (GHashTable *hash) { - return get_unhandled_specs (config, NM_IFCFG_CONNECTION_UNMANAGED_SPEC); + gs_free const char **keys = NULL; + GSList *list = NULL; + guint i, l; + + keys = nm_utils_strdict_get_keys (hash, TRUE, &l); + for (i = l; i > 0; ) { + i--; + list = g_slist_prepend (list, g_strdup (keys[i])); + } + return list; } static GSList * -get_unrecognized_specs (NMSettingsPlugin *config) +get_unmanaged_specs (NMSettingsPlugin *plugin) { - return get_unhandled_specs (config, NM_IFCFG_CONNECTION_UNRECOGNIZED_SPEC); + return _unhandled_specs_from_hashtable (NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (plugin)->unmanaged_specs); } -static NMSettingsConnection * -add_connection (NMSettingsPlugin *config, - NMConnection *connection, - gboolean save_to_disk, - GError **error) +static GSList * +get_unrecognized_specs (NMSettingsPlugin *plugin) { - SettingsPluginIfcfg *self = SETTINGS_PLUGIN_IFCFG (config); - gs_free char *path = NULL; - gs_unref_object NMConnection *reread = NULL; - - if (save_to_disk) { - if (!nms_ifcfg_rh_writer_write_connection (connection, IFCFG_DIR, NULL, &path, &reread, NULL, error)) - return NULL; - } else { - if (!nms_ifcfg_rh_writer_can_write_connection (connection, error)) - return NULL; - } - return NM_SETTINGS_CONNECTION (update_connection (self, reread ?: connection, path, NULL, FALSE, NULL, error)); + return _unhandled_specs_from_hashtable (NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (plugin)->unrecognized_specs); } +/*****************************************************************************/ + static void -impl_ifcfgrh_get_ifcfg_details (SettingsPluginIfcfg *plugin, +impl_ifcfgrh_get_ifcfg_details (NMSIfcfgRHPlugin *self, GDBusMethodInvocation *context, const char *in_ifcfg) { - NMIfcfgConnection *connection; - NMSettingConnection *s_con; + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); + gs_free char *ifcfg_path = NULL; + NMSIfcfgRHStorage *storage; const char *uuid; const char *path; - gs_free char *ifcfg_path = NULL; - if (!g_path_is_absolute (in_ifcfg)) { + if (in_ifcfg[0] != '/') { g_dbus_method_invocation_return_error (context, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, @@ -719,10 +916,8 @@ impl_ifcfgrh_get_ifcfg_details (SettingsPluginIfcfg *plugin, return; } - connection = find_by_path (plugin, ifcfg_path); - if ( !connection - || nm_ifcfg_connection_get_unmanaged_spec (connection) - || nm_ifcfg_connection_get_unrecognized_spec (connection)) { + storage = nm_sett_util_storages_lookup_by_filename (&priv->storages, ifcfg_path); + if (!storage) { g_dbus_method_invocation_return_error (context, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, @@ -730,25 +925,23 @@ impl_ifcfgrh_get_ifcfg_details (SettingsPluginIfcfg *plugin, return; } - s_con = nm_connection_get_setting_connection (nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection))); - if (!s_con) { - g_dbus_method_invocation_return_error (context, - NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_FAILED, - "unable to retrieve the connection setting"); - return; - } - - uuid = nm_setting_connection_get_uuid (s_con); + uuid = nms_ifcfg_rh_storage_get_uuid_opt (storage); if (!uuid) { g_dbus_method_invocation_return_error (context, NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_FAILED, - "unable to get the UUID"); + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "ifcfg file '%s' not managed by NetworkManager", in_ifcfg); return; } - path = nm_dbus_object_get_path (NM_DBUS_OBJECT (connection)); + /* It is ugly that the ifcfg-rh plugin needs to call back into NMSettings this + * way. + * There are alternatives (like invoking a signal), but they are all significant + * extra code (and performance overhead). So the quick and dirty solution here + * is likely to be simpler than getting this right (also from point of readability!). + */ + path = nm_settings_get_dbus_path_for_uuid (nm_settings_get (), uuid); + if (!path) { g_dbus_method_invocation_return_error (context, NM_SETTINGS_ERROR, @@ -764,9 +957,9 @@ impl_ifcfgrh_get_ifcfg_details (SettingsPluginIfcfg *plugin, /*****************************************************************************/ static void -_dbus_clear (SettingsPluginIfcfg *self) +_dbus_clear (NMSIfcfgRHPlugin *self) { - SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self); + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); guint id; nm_clear_g_signal_handler (priv->dbus.connection, &priv->dbus.signal_id); @@ -788,7 +981,7 @@ _dbus_connection_closed (GDBusConnection *connection, gpointer user_data) { _LOGW ("dbus: %s bus closed", IFCFGRH1_BUS_NAME); - _dbus_clear (SETTINGS_PLUGIN_IFCFG (user_data)); + _dbus_clear (NMS_IFCFG_RH_PLUGIN (user_data)); /* Retry or recover? */ } @@ -803,25 +996,27 @@ _method_call (GDBusConnection *connection, GDBusMethodInvocation *invocation, gpointer user_data) { - SettingsPluginIfcfg *self = SETTINGS_PLUGIN_IFCFG (user_data); - const char *ifcfg; - - if ( !nm_streq (interface_name, IFCFGRH1_IFACE1_NAME) - || !nm_streq (method_name, IFCFGRH1_IFACE1_METHOD_GET_IFCFG_DETAILS)) { - g_dbus_method_invocation_return_error (invocation, - G_DBUS_ERROR, - G_DBUS_ERROR_UNKNOWN_METHOD, - "Unknown method %s", - method_name); - return; + NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (user_data); + + if (nm_streq (interface_name, IFCFGRH1_IFACE1_NAME)) { + if (nm_streq (method_name, IFCFGRH1_IFACE1_METHOD_GET_IFCFG_DETAILS)) { + const char *ifcfg; + + g_variant_get (parameters, "(&s)", &ifcfg); + impl_ifcfgrh_get_ifcfg_details (self, invocation, ifcfg); + return; + } } - g_variant_get (parameters, "(&s)", &ifcfg); - impl_ifcfgrh_get_ifcfg_details (self, invocation, ifcfg); + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_UNKNOWN_METHOD, + "Unknown method %s", + method_name); } static GDBusInterfaceInfo *const interface_info = NM_DEFINE_GDBUS_INTERFACE_INFO ( - IFCFGRH1_BUS_NAME, + IFCFGRH1_IFACE1_NAME, .methods = NM_DEFINE_GDBUS_METHOD_INFOS ( NM_DEFINE_GDBUS_METHOD_INFO ( IFCFGRH1_IFACE1_METHOD_GET_IFCFG_DETAILS, @@ -842,8 +1037,8 @@ _dbus_request_name_done (GObject *source_object, gpointer user_data) { GDBusConnection *connection = G_DBUS_CONNECTION (source_object); - SettingsPluginIfcfg *self; - SettingsPluginIfcfgPrivate *priv; + NMSIfcfgRHPlugin *self; + NMSIfcfgRHPluginPrivate *priv; gs_free_error GError *error = NULL; gs_unref_variant GVariant *ret = NULL; guint32 result; @@ -852,8 +1047,8 @@ _dbus_request_name_done (GObject *source_object, if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) return; - self = SETTINGS_PLUGIN_IFCFG (user_data); - priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self); + self = NMS_IFCFG_RH_PLUGIN (user_data); + priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); g_clear_object (&priv->dbus.cancellable); @@ -900,8 +1095,8 @@ _dbus_create_done (GObject *source_object, GAsyncResult *res, gpointer user_data) { - SettingsPluginIfcfg *self; - SettingsPluginIfcfgPrivate *priv; + NMSIfcfgRHPlugin *self; + NMSIfcfgRHPluginPrivate *priv; gs_free_error GError *error = NULL; GDBusConnection *connection; @@ -909,8 +1104,8 @@ _dbus_create_done (GObject *source_object, if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) return; - self = SETTINGS_PLUGIN_IFCFG (user_data); - priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self); + self = NMS_IFCFG_RH_PLUGIN (user_data); + priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); g_clear_object (&priv->dbus.cancellable); @@ -944,9 +1139,9 @@ _dbus_create_done (GObject *source_object, } static void -_dbus_setup (SettingsPluginIfcfg *self) +_dbus_setup (NMSIfcfgRHPlugin *self) { - SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self); + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); gs_free char *address = NULL; gs_free_error GError *error = NULL; @@ -974,9 +1169,9 @@ config_changed_cb (NMConfig *config, NMConfigData *config_data, NMConfigChangeFlags changes, NMConfigData *old_data, - SettingsPluginIfcfg *self) + NMSIfcfgRHPlugin *self) { - SettingsPluginIfcfgPrivate *priv; + NMSIfcfgRHPluginPrivate *priv; /* If the dbus connection for some reason is borked the D-Bus service * won't be offered. @@ -988,7 +1183,7 @@ config_changed_cb (NMConfig *config, | NM_CONFIG_CHANGE_CAUSE_SIGUSR1)) return; - priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self); + priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); if ( !priv->dbus.connection && !priv->dbus.cancellable) _dbus_setup (self); @@ -997,23 +1192,26 @@ config_changed_cb (NMConfig *config, /*****************************************************************************/ static void -settings_plugin_ifcfg_init (SettingsPluginIfcfg *plugin) +nms_ifcfg_rh_plugin_init (NMSIfcfgRHPlugin *self) { - SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE ((SettingsPluginIfcfg *) plugin); + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); + + priv->config = g_object_ref (nm_config_get ()); + + priv->unmanaged_specs = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL); + priv->unrecognized_specs = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL); - priv->connections = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_object_unref); + priv->storages = (NMSettUtilStorages) NM_SETT_UTIL_STORAGES_INIT (priv->storages, nms_ifcfg_rh_storage_destroy); } static void constructed (GObject *object) { - SettingsPluginIfcfg *self = SETTINGS_PLUGIN_IFCFG (object); - SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self); + NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (object); + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); - G_OBJECT_CLASS (settings_plugin_ifcfg_parent_class)->constructed (object); + G_OBJECT_CLASS (nms_ifcfg_rh_plugin_parent_class)->constructed (object); - priv->config = nm_config_get (); - g_object_add_weak_pointer ((GObject *) priv->config, (gpointer *) &priv->config); g_signal_connect (priv->config, NM_CONFIG_SIGNAL_CONFIG_CHANGED, G_CALLBACK (config_changed_cb), @@ -1025,48 +1223,44 @@ constructed (GObject *object) static void dispose (GObject *object) { - SettingsPluginIfcfg *self = SETTINGS_PLUGIN_IFCFG (object); - SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE (self); + NMSIfcfgRHPlugin *self = NMS_IFCFG_RH_PLUGIN (object); + NMSIfcfgRHPluginPrivate *priv = NMS_IFCFG_RH_PLUGIN_GET_PRIVATE (self); - if (priv->config) { - g_object_remove_weak_pointer ((GObject *) priv->config, (gpointer *) &priv->config); + if (priv->config) g_signal_handlers_disconnect_by_func (priv->config, config_changed_cb, self); - priv->config = NULL; - } + /* FIXME(shutdown) we need a stop method so that we can unregistering the D-Bus service + * when NMSettings is shutting down, and not when the instance gets destroyed. */ _dbus_clear (self); - if (priv->connections) { - g_hash_table_destroy (priv->connections); - priv->connections = NULL; - } + nm_sett_util_storages_clear (&priv->storages); - if (priv->ifcfg_monitor) { - if (priv->ifcfg_monitor_id) - g_signal_handler_disconnect (priv->ifcfg_monitor, priv->ifcfg_monitor_id); + g_clear_object (&priv->config); - g_file_monitor_cancel (priv->ifcfg_monitor); - g_object_unref (priv->ifcfg_monitor); - } + G_OBJECT_CLASS (nms_ifcfg_rh_plugin_parent_class)->dispose (object); - G_OBJECT_CLASS (settings_plugin_ifcfg_parent_class)->dispose (object); + nm_clear_pointer (&priv->unmanaged_specs, g_hash_table_destroy); + nm_clear_pointer (&priv->unrecognized_specs, g_hash_table_destroy); } static void -settings_plugin_ifcfg_class_init (SettingsPluginIfcfgClass *klass) +nms_ifcfg_rh_plugin_class_init (NMSIfcfgRHPluginClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); NMSettingsPluginClass *plugin_class = NM_SETTINGS_PLUGIN_CLASS (klass); object_class->constructed = constructed; - object_class->dispose = dispose; + object_class->dispose = dispose; - plugin_class->get_connections = get_connections; - plugin_class->add_connection = add_connection; - plugin_class->load_connection = load_connection; - plugin_class->reload_connections = reload_connections; - plugin_class->get_unmanaged_specs = get_unmanaged_specs; + plugin_class->plugin_name = "ifcfg-rh"; + plugin_class->get_unmanaged_specs = get_unmanaged_specs; plugin_class->get_unrecognized_specs = get_unrecognized_specs; + plugin_class->reload_connections = reload_connections; + plugin_class->load_connections = load_connections; + plugin_class->load_connections_done = load_connections_done; + plugin_class->add_connection = add_connection; + plugin_class->update_connection = update_connection; + plugin_class->delete_connection = delete_connection; } /*****************************************************************************/ @@ -1074,5 +1268,5 @@ settings_plugin_ifcfg_class_init (SettingsPluginIfcfgClass *klass) G_MODULE_EXPORT NMSettingsPlugin * nm_settings_plugin_factory (void) { - return NM_SETTINGS_PLUGIN (g_object_ref (settings_plugin_ifcfg_get ())); + return g_object_new (NMS_TYPE_IFCFG_RH_PLUGIN, NULL); } diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.h index d815cc51..1db36083 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.h +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * Dan Williams <dcbw@redhat.com> @@ -21,19 +20,19 @@ * Copyright (C) 2007 - 2008 Red Hat, Inc. */ -#ifndef _PLUGIN_H_ -#define _PLUGIN_H_ +#ifndef __NMS_IFCFG_RH_PLUGIN_H__ +#define __NMS_IFCFG_RH_PLUGIN_H__ -#define SETTINGS_TYPE_PLUGIN_IFCFG (settings_plugin_ifcfg_get_type ()) -#define SETTINGS_PLUGIN_IFCFG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SETTINGS_TYPE_PLUGIN_IFCFG, SettingsPluginIfcfg)) -#define SETTINGS_PLUGIN_IFCFG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SETTINGS_TYPE_PLUGIN_IFCFG, SettingsPluginIfcfgClass)) -#define SETTINGS_IS_PLUGIN_IFCFG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SETTINGS_TYPE_PLUGIN_IFCFG)) -#define SETTINGS_IS_PLUGIN_IFCFG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SETTINGS_TYPE_PLUGIN_IFCFG)) -#define SETTINGS_PLUGIN_IFCFG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SETTINGS_TYPE_PLUGIN_IFCFG, SettingsPluginIfcfgClass)) +#define NMS_TYPE_IFCFG_RH_PLUGIN (nms_ifcfg_rh_plugin_get_type ()) +#define NMS_IFCFG_RH_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMS_TYPE_IFCFG_RH_PLUGIN, NMSIfcfgRHPlugin)) +#define NMS_IFCFG_RH_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMS_TYPE_IFCFG_RH_PLUGIN, NMSIfcfgRHPluginClass)) +#define NMS_IS_IFCFG_RH_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMS_TYPE_IFCFG_RH_PLUGIN)) +#define NMS_IS_IFCFG_RH_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMS_TYPE_IFCFG_RH_PLUGIN)) +#define NMS_IFCFG_RH_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMS_TYPE_IFCFG_RH_PLUGIN, NMSIfcfgRHPluginClass)) -typedef struct _SettingsPluginIfcfg SettingsPluginIfcfg; -typedef struct _SettingsPluginIfcfgClass SettingsPluginIfcfgClass; +typedef struct _NMSIfcfgRHPlugin NMSIfcfgRHPlugin; +typedef struct _NMSIfcfgRHPluginClass NMSIfcfgRHPluginClass; -GType settings_plugin_ifcfg_get_type (void); +GType nms_ifcfg_rh_plugin_get_type (void); -#endif /* _PLUGIN_H_ */ +#endif /* __NMS_IFCFG_RH_PLUGIN_H__ */ 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 e5423b18..900a3fc1 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * This program is free software; you can redistribute it and/or modify @@ -543,6 +542,37 @@ make_connection_setting (const char *file, vint64 = svGetValueInt64 (ifcfg, "AUTH_RETRIES", 10, -1, G_MAXINT32, -1); g_object_set (s_con, NM_SETTING_CONNECTION_AUTH_RETRIES, (int) vint64, NULL); + nm_clear_g_free (&value); + v = svGetValueStr (ifcfg, "DEVTIMEOUT", &value); + if (v) { + vint64 = _nm_utils_ascii_str_to_int64 (v, 10, 0, ((gint64) G_MAXINT32) / 1000, -1); + if (vint64 != -1) + vint64 *= 1000; + else { + char *endptr; + double d; + + d = g_ascii_strtod (v, &endptr); + if ( errno == 0 + && endptr[0] == '\0' + && d >= 0.0) { + d *= 1000.0; + + /* We round. Yes, this is not correct to round IEEE 754 floats in general, + * but sufficient for our case where we know that NetworkManager wrote the + * setting with up to 3 digits for the milliseconds. */ + d += 0.5; + if ( d >= 0.0 + && d <= (double) G_MAXINT32) + vint64 = (gint64) d; + } + } + if (vint64 == -1) + PARSE_WARNING ("invalid DEVTIMEOUT setting"); + else + g_object_set (s_con, NM_SETTING_CONNECTION_WAIT_DEVICE_TIMEOUT, (int) vint64, NULL); + } + i_val = NM_SETTING_CONNECTION_MDNS_DEFAULT; if (!svGetValueEnum (ifcfg, "MDNS", nm_setting_connection_mdns_get_type (), @@ -1906,7 +1936,10 @@ make_ip6_setting (shvarFile *ifcfg, gs_unref_object NMSettingIPConfig *s_ip6 = NULL; const char *v; gs_free char *value = NULL; - gboolean ipv6init, ipv6forwarding, dhcp6 = FALSE; + gboolean ipv6init; + gboolean ipv6forwarding; + gboolean disabled; + gboolean dhcp6 = FALSE; char *method = NM_SETTING_IP6_CONFIG_METHOD_MANUAL; const char *ipv6addr, *ipv6addr_secondaries; gs_free char *ipv6addr_to_free = NULL; @@ -1964,6 +1997,7 @@ make_ip6_setting (shvarFile *ifcfg, /* Find out method property */ /* Is IPV6 enabled? Set method to "ignored", when not enabled */ + disabled = svGetValueBoolean(ifcfg, "IPV6_DISABLED", FALSE); nm_clear_g_free (&value); v = svGetValueStr (ifcfg, "IPV6INIT", &value); ipv6init = svGetValueBoolean (ifcfg, "IPV6INIT", FALSE); @@ -1972,8 +2006,10 @@ make_ip6_setting (shvarFile *ifcfg, ipv6init = svGetValueBoolean (network_ifcfg, "IPV6INIT", FALSE); } - if (!ipv6init) - method = NM_SETTING_IP6_CONFIG_METHOD_IGNORE; /* IPv6 is disabled */ + if (disabled) + method = NM_SETTING_IP6_CONFIG_METHOD_DISABLED; + else if (!ipv6init) + method = NM_SETTING_IP6_CONFIG_METHOD_IGNORE; else { ipv6forwarding = svGetValueBoolean (ifcfg, "IPV6FORWARDING", FALSE); nm_clear_g_free (&value); @@ -2039,7 +2075,8 @@ make_ip6_setting (shvarFile *ifcfg, NULL); /* Don't bother to read IP, DNS and routes when IPv6 is disabled */ - if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0) + if (NM_IN_STRSET (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NM_SETTING_IP6_CONFIG_METHOD_DISABLED)) return NM_SETTING (g_steal_pointer (&s_ip6)); nm_clear_g_free (&value); @@ -3071,12 +3108,17 @@ eap_tls_reader (const char *eap_method, gs_unref_bytes GBytes *privkey = NULL; gs_unref_bytes GBytes *client_cert = NULL; gs_free char *identity_free = NULL; + gs_free char *value_to_free = NULL; + const char *client_cert_var; + const char *client_cert_prop; + NMSetting8021xCKFormat format; g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, svGetValueStr (ifcfg, "IEEE_8021X_IDENTITY", &identity_free), NULL); + /* CA certificate */ if (!_cert_set_from_ifcfg (s_8021x, ifcfg, phase2 ? "IEEE_8021X_INNER_CA_CERT" : "IEEE_8021X_CA_CERT", @@ -3090,6 +3132,7 @@ eap_tls_reader (const char *eap_method, phase2 ? "IEEE_8021X_INNER_CA_CERT_PASSWORD" : "IEEE_8021X_CA_CERT_PASSWORD", phase2 ? NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD : NM_SETTING_802_1X_CA_CERT_PASSWORD); + /* Private key */ if (!_cert_set_from_ifcfg (s_8021x, ifcfg, phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY" : "IEEE_8021X_PRIVATE_KEY", @@ -3102,35 +3145,39 @@ eap_tls_reader (const char *eap_method, keys_ifcfg, phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD" : "IEEE_8021X_PRIVATE_KEY_PASSWORD", phase2 ? NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD : NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD); - if (!privkey) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Missing %s for EAP method '%s'.", - phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY" : "IEEE_8021X_PRIVATE_KEY", - eap_method); - return FALSE; - } + /* Client certificate */ + client_cert_var = phase2 ? "IEEE_8021X_INNER_CLIENT_CERT" : "IEEE_8021X_CLIENT_CERT"; + client_cert_prop = phase2 ? NM_SETTING_802_1X_PHASE2_CLIENT_CERT : NM_SETTING_802_1X_CLIENT_CERT; if (!_cert_set_from_ifcfg (s_8021x, ifcfg, - phase2 ? "IEEE_8021X_INNER_CLIENT_CERT" : "IEEE_8021X_CLIENT_CERT", - phase2 ? NM_SETTING_802_1X_PHASE2_CLIENT_CERT : NM_SETTING_802_1X_CLIENT_CERT, + client_cert_var, + client_cert_prop, &client_cert, error)) return FALSE; - /* 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 seldom used feature so that it is not fixed yet. */ _secret_set_from_ifcfg (s_8021x, ifcfg, keys_ifcfg, phase2 ? "IEEE_8021X_INNER_CLIENT_CERT_PASSWORD" : "IEEE_8021X_CLIENT_CERT_PASSWORD", phase2 ? NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD : NM_SETTING_802_1X_CLIENT_CERT_PASSWORD); - if (!client_cert) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Missing certificate for EAP method '%s'.", - eap_method); - return FALSE; + + /* In the past when the private key and client certificate + * were the same PKCS #12 file we used to write only the + * private key variable. Still support that even if it means + * that we have to look into the file content, which makes + * the connection not self-contained. + */ + if ( !client_cert + && privkey + && !svGetValue (ifcfg, client_cert_var, &value_to_free)) { + if (phase2) + format = nm_setting_802_1x_get_phase2_private_key_format (s_8021x); + else + format = nm_setting_802_1x_get_private_key_format (s_8021x); + + if (format == NM_SETTING_802_1X_CK_FORMAT_PKCS12) + g_object_set (s_8021x, client_cert_prop, privkey, NULL); } return TRUE; @@ -4369,9 +4416,11 @@ parse_ethtool_options (shvarFile *ifcfg, NMConnection *connection) gboolean autoneg = FALSE; guint32 speed = 0; const char *duplex = NULL; + gboolean wired_found = FALSE; ethtool_opts = svGetValue (ifcfg, "ETHTOOL_OPTS", ðtool_opts_free); if (ethtool_opts) { + wired_found = TRUE; /* WAKE_ON_LAN_IGNORE is inferred from a specified but empty ETHTOOL_OPTS */ if (!ethtool_opts[0]) wol_flags = NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE; @@ -4395,7 +4444,9 @@ parse_ethtool_options (shvarFile *ifcfg, NMConnection *connection) } /* ETHTOOL_WAKE_ON_LAN = ignore overrides WoL settings in ETHTOOL_OPTS */ - tmp = svGetValueStr (ifcfg, "ETHTOOL_WAKE_ON_LAN", &wol_value_free); + tmp = svGetValue (ifcfg, "ETHTOOL_WAKE_ON_LAN", &wol_value_free); + if (tmp) + wired_found = TRUE; if (nm_streq0 (tmp, "ignore")) wol_flags = NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE; else if (tmp) @@ -4408,6 +4459,10 @@ parse_ethtool_options (shvarFile *ifcfg, NMConnection *connection) } s_wired = nm_connection_get_setting_wired (connection); + if (!s_wired && wired_found) { + s_wired = (NMSettingWired *) nm_setting_wired_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + } if (s_wired) { g_object_set (s_wired, NM_SETTING_WIRED_WAKE_ON_LAN, wol_flags, @@ -4433,86 +4488,100 @@ make_wired_setting (shvarFile *ifcfg, gs_unref_object NMSettingWired *s_wired = NULL; const char *cvalue; gs_free char *value = NULL; - char *nettype; + gboolean found = FALSE; s_wired = NM_SETTING_WIRED (nm_setting_wired_new ()); - value = svGetValueStr_cp (ifcfg, "MTU"); - if (value) { + cvalue = svGetValue (ifcfg, "MTU", &value); + if (cvalue) { int mtu; - mtu = _nm_utils_ascii_str_to_int64 (value, 0, 0, 65535, -1); + mtu = _nm_utils_ascii_str_to_int64 (cvalue, 0, 0, 65535, -1); if (mtu >= 0) g_object_set (s_wired, NM_SETTING_WIRED_MTU, (guint) mtu, NULL); else - PARSE_WARNING ("invalid MTU '%s'", value); + PARSE_WARNING ("invalid MTU '%s'", cvalue); nm_clear_g_free (&value); + found = TRUE; } - value = svGetValueStr_cp (ifcfg, "HWADDR"); + value = svGetValue_cp (ifcfg, "HWADDR"); if (value) { - value = g_strstrip (value); - g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, value, NULL); + if (value[0] != '\0') { + value = g_strstrip (value); + g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, value, NULL); + } nm_clear_g_free (&value); + found = TRUE; } - value = svGetValueStr_cp (ifcfg, "SUBCHANNELS"); - if (value) { - const char *p = value; - gboolean success = TRUE; - - /* basic sanity checks */ - while (*p) { - if (!g_ascii_isxdigit (*p) && (*p != ',') && (*p != '.')) { - PARSE_WARNING ("invalid SUBCHANNELS '%s'", value); - success = FALSE; - break; + cvalue = svGetValue (ifcfg, "SUBCHANNELS", &value); + if (cvalue) { + if (cvalue[0] != '\0') { + const char *p = cvalue; + gboolean success = TRUE; + + /* basic sanity checks */ + while (*p) { + if (!g_ascii_isxdigit (*p) && (*p != ',') && (*p != '.')) { + PARSE_WARNING ("invalid SUBCHANNELS '%s'", cvalue); + success = FALSE; + break; + } + p++; } - p++; - } - if (success) { - gs_free const char **chans = NULL; - guint32 num_chans; + if (success) { + gs_free const char **chans = NULL; + guint32 num_chans; - chans = nm_utils_strsplit_set (value, ","); - num_chans = NM_PTRARRAY_LEN (chans); - if (num_chans < 2 || num_chans > 3) { - PARSE_WARNING ("invalid SUBCHANNELS '%s' (%u channels, 2 or 3 expected)", - value, (unsigned) NM_PTRARRAY_LEN (chans)); - } else - g_object_set (s_wired, NM_SETTING_WIRED_S390_SUBCHANNELS, chans, NULL); + chans = nm_utils_strsplit_set (cvalue, ","); + num_chans = NM_PTRARRAY_LEN (chans); + if (num_chans < 2 || num_chans > 3) { + PARSE_WARNING ("invalid SUBCHANNELS '%s' (%u channels, 2 or 3 expected)", + cvalue, (unsigned) NM_PTRARRAY_LEN (chans)); + } else + g_object_set (s_wired, NM_SETTING_WIRED_S390_SUBCHANNELS, chans, NULL); + } } nm_clear_g_free (&value); + found = TRUE; } - value = svGetValueStr_cp (ifcfg, "PORTNAME"); - if (value) { - nm_setting_wired_add_s390_option (s_wired, "portname", value); + cvalue = svGetValue (ifcfg, "PORTNAME", &value); + if (cvalue) { + if (cvalue[0] != '\0') + nm_setting_wired_add_s390_option (s_wired, "portname", cvalue); + found = TRUE; nm_clear_g_free (&value); } - value = svGetValueStr_cp (ifcfg, "CTCPROT"); - if (value) { - nm_setting_wired_add_s390_option (s_wired, "ctcprot", value); + cvalue = svGetValue (ifcfg, "CTCPROT", &value); + if (cvalue) { + if (cvalue[0] != '\0') + nm_setting_wired_add_s390_option (s_wired, "ctcprot", cvalue); nm_clear_g_free (&value); + found = TRUE; } - nettype = svGetValueStr_cp (ifcfg, "NETTYPE"); - if (nettype) { - if (!strcmp (nettype, "qeth") || !strcmp (nettype, "lcs") || !strcmp (nettype, "ctc")) - g_object_set (s_wired, NM_SETTING_WIRED_S390_NETTYPE, nettype, NULL); + cvalue = svGetValue (ifcfg, "NETTYPE", &value); + if (cvalue) { + if (NM_IN_STRSET (cvalue, "qeth", "lcs", "ctc")) + g_object_set (s_wired, NM_SETTING_WIRED_S390_NETTYPE, cvalue, NULL); else - PARSE_WARNING ("unknown s390 NETTYPE '%s'", nettype); - g_free (nettype); + PARSE_WARNING ("unknown s390 NETTYPE '%s'", cvalue); + nm_clear_g_free (&value); + found = TRUE; } - value = svGetValueStr_cp (ifcfg, "OPTIONS"); - if (value) { + cvalue = svGetValue (ifcfg, "OPTIONS", &value); + if (cvalue) + found = TRUE; + if (cvalue && cvalue[0]) { gs_free const char **options = NULL; gsize i; - options = nm_utils_strsplit_set_with_empty (value, " "); + options = nm_utils_escaped_tokens_split (cvalue, NM_ASCII_SPACES); for (i = 0; options && options[i]; i++) { const char *line = options[i]; const char *equals; @@ -4526,20 +4595,33 @@ make_wired_setting (shvarFile *ifcfg, if (!valid) PARSE_WARNING ("invalid s390 OPTION '%s'", line); } - nm_clear_g_free (&value); + found = TRUE; } - - g_object_set (s_wired, - NM_SETTING_WIRED_CLONED_MAC_ADDRESS, - svGetValueStr (ifcfg, "MACADDR", &value), - NULL); nm_clear_g_free (&value); - g_object_set (s_wired, - NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK, - svGetValueStr (ifcfg, "GENERATE_MAC_ADDRESS_MASK", &value), - NULL); - nm_clear_g_free (&value); + cvalue = svGetValueStr (ifcfg, "MACADDR", &value); + if (cvalue) { + if (cvalue[0] != '\0') { + g_object_set (s_wired, + NM_SETTING_WIRED_CLONED_MAC_ADDRESS, + cvalue, + NULL); + } + nm_clear_g_free (&value); + found = TRUE; + } + + cvalue = svGetValueStr (ifcfg, "GENERATE_MAC_ADDRESS_MASK", &value); + if (cvalue) { + if (cvalue[0] != '\0') { + g_object_set (s_wired, + NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK, + cvalue, + NULL); + } + nm_clear_g_free (&value); + found = TRUE; + } cvalue = svGetValueStr (ifcfg, "HWADDR_BLACKLIST", &value); if (cvalue) { @@ -4548,20 +4630,31 @@ make_wired_setting (shvarFile *ifcfg, strv = transform_hwaddr_blacklist (cvalue); g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST, strv, NULL); nm_clear_g_free (&value); + found = TRUE; } - value = svGetValueStr_cp (ifcfg, "KEY_MGMT"); - if (value) { - if (!strcmp (value, "IEEE8021X")) { - *s_8021x = fill_8021x (ifcfg, file, value, FALSE, error); + cvalue = svGetValue (ifcfg, "KEY_MGMT", &value); + if (cvalue) + found = TRUE; + if (cvalue && cvalue[0] != '\0') { + if (!strcmp (cvalue, "IEEE8021X")) { + *s_8021x = fill_8021x (ifcfg, file, cvalue, FALSE, error); if (!*s_8021x) return NULL; } else { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Unknown wired KEY_MGMT type '%s'", value); + "Unknown wired KEY_MGMT type '%s'", cvalue); return NULL; } - nm_clear_g_free (&value); + } + nm_clear_g_free (&value); + + if (!found) { + g_set_error (error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_SETTING_MISSING, + "The setting is missing."); + return NULL; } return (NMSetting *) g_steal_pointer (&s_wired); @@ -4576,6 +4669,7 @@ wired_connection_from_ifcfg (const char *file, NMSetting *con_setting = NULL; NMSetting *wired_setting = NULL; NMSetting8021x *s_8021x = NULL; + GError *local = NULL; g_return_val_if_fail (file != NULL, NULL); g_return_val_if_fail (ifcfg != NULL, NULL); @@ -4591,12 +4685,16 @@ wired_connection_from_ifcfg (const char *file, } nm_connection_add_setting (connection, con_setting); - wired_setting = make_wired_setting (ifcfg, file, &s_8021x, error); - if (!wired_setting) { + wired_setting = make_wired_setting (ifcfg, file, &s_8021x, &local); + if (local && !g_error_matches (local, NM_UTILS_ERROR, NM_UTILS_ERROR_SETTING_MISSING)) { + g_propagate_error (error, local); g_object_unref (connection); return NULL; } - nm_connection_add_setting (connection, wired_setting); + g_clear_error (&local); + + if (wired_setting) + nm_connection_add_setting (connection, wired_setting); if (s_8021x) nm_connection_add_setting (connection, NM_SETTING (s_8021x)); @@ -4699,8 +4797,8 @@ make_infiniband_setting (shvarFile *ifcfg, g_object_set (s_infiniband, NM_SETTING_INFINIBAND_TRANSPORT_MODE, "datagram", NULL); if (svGetValueBoolean (ifcfg, "PKEY", FALSE)) { + gs_free char *parent = NULL; int p_key; - char *parent; if (!parse_infiniband_p_key (ifcfg, &p_key, &parent, error)) { g_object_unref (s_infiniband); @@ -4827,6 +4925,7 @@ bond_connection_from_ifcfg (const char *file, NMSetting *bond_setting = NULL; NMSetting *wired_setting = NULL; NMSetting8021x *s_8021x = NULL; + GError *local = NULL; g_return_val_if_fail (file != NULL, NULL); g_return_val_if_fail (ifcfg != NULL, NULL); @@ -4849,12 +4948,16 @@ bond_connection_from_ifcfg (const char *file, } nm_connection_add_setting (connection, bond_setting); - wired_setting = make_wired_setting (ifcfg, file, &s_8021x, error); - if (!wired_setting) { + wired_setting = make_wired_setting (ifcfg, file, &s_8021x, &local); + if (local && !g_error_matches (local, NM_UTILS_ERROR, NM_UTILS_ERROR_SETTING_MISSING)) { + g_propagate_error (error, local); g_object_unref (connection); return NULL; } - nm_connection_add_setting (connection, wired_setting); + g_clear_error (&local); + + if (wired_setting) + nm_connection_add_setting (connection, wired_setting); if (s_8021x) nm_connection_add_setting (connection, NM_SETTING (s_8021x)); @@ -4862,62 +4965,27 @@ bond_connection_from_ifcfg (const char *file, return connection; } -/* Check 'error' for errors. Missing config (NULL return value) is a valid case. */ -static char * -read_team_config (shvarFile *ifcfg, const char *key, GError **error) -{ - gs_free_error GError *local_error = NULL; - gs_free char *value = NULL; - size_t l; - - value = svGetValueStr_cp (ifcfg, key); - if (!value) - return NULL; - - l = strlen (value); - if (l > 1*1024*1024) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "%s too long (size %zd)", key, l); - return NULL; - } - - if (!nm_utils_is_json_object (value, &local_error)) { - PARSE_WARNING ("ignoring invalid team configuration: %s", local_error->message); - return NULL; - } - - return g_steal_pointer (&value); -} - static NMSetting * make_team_setting (shvarFile *ifcfg, const char *file, GError **error) { - NMSettingTeam *s_team; - char *value; - GError *local_err = NULL; + NMSetting *s_team; + gs_free char *value_device = NULL; + gs_free char *value = NULL; - value = svGetValueStr_cp (ifcfg, "DEVICE"); - if (!value) { + if (!svGetValueStr (ifcfg, "DEVICE", &value_device)) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "mandatory DEVICE keyword missing"); return NULL; } - g_free (value); - - value = read_team_config (ifcfg, "TEAM_CONFIG", &local_err); - if (local_err) { - g_propagate_error (error, local_err); - return NULL; - } - s_team = NM_SETTING_TEAM (nm_setting_team_new ()); - - g_object_set (s_team, NM_SETTING_TEAM_CONFIG, value, NULL); - g_free (value); - - return (NMSetting *) s_team; + s_team = nm_setting_team_new (); + g_object_set (s_team, + NM_SETTING_TEAM_CONFIG, + svGetValue (ifcfg, "TEAM_CONFIG", &value), + NULL); + return s_team; } static NMConnection * @@ -4930,6 +4998,7 @@ team_connection_from_ifcfg (const char *file, NMSetting *team_setting = NULL; NMSetting *wired_setting = NULL; NMSetting8021x *s_8021x = NULL; + GError *local = NULL; g_return_val_if_fail (file != NULL, NULL); g_return_val_if_fail (ifcfg != NULL, NULL); @@ -4952,12 +5021,16 @@ team_connection_from_ifcfg (const char *file, } nm_connection_add_setting (connection, team_setting); - wired_setting = make_wired_setting (ifcfg, file, &s_8021x, error); - if (!wired_setting) { + wired_setting = make_wired_setting (ifcfg, file, &s_8021x, &local); + if (local && !g_error_matches (local, NM_UTILS_ERROR, NM_UTILS_ERROR_SETTING_MISSING)) { + g_propagate_error (error, local); g_object_unref (connection); return NULL; } - nm_connection_add_setting (connection, wired_setting); + g_clear_error (&local); + + if (wired_setting) + nm_connection_add_setting (connection, wired_setting); if (s_8021x) nm_connection_add_setting (connection, NM_SETTING (s_8021x)); @@ -5207,6 +5280,7 @@ bridge_connection_from_ifcfg (const char *file, NMSetting *bridge_setting = NULL; NMSetting *wired_setting = NULL; NMSetting8021x *s_8021x = NULL; + GError *local = NULL; g_return_val_if_fail (file != NULL, NULL); g_return_val_if_fail (ifcfg != NULL, NULL); @@ -5229,12 +5303,16 @@ bridge_connection_from_ifcfg (const char *file, } nm_connection_add_setting (connection, bridge_setting); - wired_setting = make_wired_setting (ifcfg, file, &s_8021x, error); - if (!wired_setting) { + wired_setting = make_wired_setting (ifcfg, file, &s_8021x, &local); + if (local && !g_error_matches (local, NM_UTILS_ERROR, NM_UTILS_ERROR_SETTING_MISSING)) { + g_propagate_error (error, local); g_object_unref (connection); return NULL; } - nm_connection_add_setting (connection, wired_setting); + g_clear_error (&local); + + if (wired_setting) + nm_connection_add_setting (connection, wired_setting); if (s_8021x) nm_connection_add_setting (connection, NM_SETTING (s_8021x)); @@ -5276,20 +5354,18 @@ make_bridge_port_setting (shvarFile *ifcfg) static NMSetting * make_team_port_setting (shvarFile *ifcfg) { - NMSetting *s_port = NULL; - char *value; - GError *error = NULL; + NMSetting *s_port; + gs_free char *value = NULL; - value = read_team_config (ifcfg, "TEAM_PORT_CONFIG", &error); - if (value) { - s_port = nm_setting_team_port_new (); - g_object_set (s_port, NM_SETTING_TEAM_PORT_CONFIG, value, NULL); - g_free (value); - } else if (error) { - PARSE_WARNING ("%s", error->message); - g_error_free (error); - } + value = svGetValueStr_cp (ifcfg, "TEAM_PORT_CONFIG"); + if (!value) + return NULL; + s_port = nm_setting_team_port_new (); + g_object_set (s_port, + NM_SETTING_TEAM_PORT_CONFIG, + value, + NULL); return s_port; } @@ -5486,6 +5562,7 @@ vlan_connection_from_ifcfg (const char *file, NMSetting *wired_setting = NULL; NMSetting *vlan_setting = NULL; NMSetting8021x *s_8021x = NULL; + GError *local = NULL; g_return_val_if_fail (file != NULL, NULL); g_return_val_if_fail (ifcfg != NULL, NULL); @@ -5508,12 +5585,16 @@ vlan_connection_from_ifcfg (const char *file, } nm_connection_add_setting (connection, vlan_setting); - wired_setting = make_wired_setting (ifcfg, file, &s_8021x, error); - if (!wired_setting) { + wired_setting = make_wired_setting (ifcfg, file, &s_8021x, &local); + if (local && !g_error_matches (local, NM_UTILS_ERROR, NM_UTILS_ERROR_SETTING_MISSING)) { + g_propagate_error (error, local); g_object_unref (connection); return NULL; } - nm_connection_add_setting (connection, wired_setting); + g_clear_error (&local); + + if (wired_setting) + nm_connection_add_setting (connection, wired_setting); if (s_8021x) nm_connection_add_setting (connection, NM_SETTING (s_8021x)); @@ -5549,21 +5630,21 @@ create_unhandled_connection (const char *filename, shvarFile *ifcfg, if (v) { gs_free char *lower = g_ascii_strdown (v, -1); - *out_spec = g_strdup_printf ("%s:mac:%s", type, lower); + *out_spec = g_strdup_printf ("%s:"NM_MATCH_SPEC_MAC_TAG"%s", type, lower); return connection; } nm_clear_g_free (&value); v = svGetValueStr (ifcfg, "SUBCHANNELS", &value); if (v) { - *out_spec = g_strdup_printf ("%s:s390-subchannels:%s", type, v); + *out_spec = g_strdup_printf ("%s:"NM_MATCH_SPEC_S390_SUBCHANNELS_TAG"%s", type, v); return connection; } nm_clear_g_free (&value); v = svGetValueStr (ifcfg, "DEVICE", &value); if (v) { - *out_spec = g_strdup_printf ("%s:interface-name:%s", type, v); + *out_spec = g_strdup_printf ("%s:"NM_MATCH_SPEC_INTERFACE_NAME_TAG"=%s", type, v); return connection; } @@ -5654,7 +5735,7 @@ connection_from_file_full (const char *filename, return g_steal_pointer (&connection); } - /* iBFT is handled by the iBFT settings plugin */ + /* iBFT is handled by nm-initrd-generator during boot. */ bootproto = svGetValueStr_cp (main_ifcfg, "BOOTPROTO"); if (bootproto && !g_ascii_strcasecmp (bootproto, "ibft")) { NM_SET_OUT (out_ignore_error, TRUE); @@ -5902,12 +5983,10 @@ connection_from_file_full (const char *filename, if (s_match) nm_connection_add_setting (connection, s_match); - /* Bridge port? */ s_port = make_bridge_port_setting (main_ifcfg); if (s_port) nm_connection_add_setting (connection, s_port); - /* Team port? */ s_port = make_team_port_setting (main_ifcfg); if (s_port) nm_connection_add_setting (connection, s_port); @@ -5949,20 +6028,3 @@ nmtst_connection_from_file (const char *filename, error, NULL); } - -guint -devtimeout_from_file (const char *filename) -{ - shvarFile *ifcfg; - guint devtimeout; - - g_return_val_if_fail (filename != NULL, 0); - - ifcfg = svOpenFile (filename, NULL); - if (!ifcfg) - return 0; - - devtimeout = svGetValueInt64 (ifcfg, "DEVTIMEOUT", 10, 0, G_MAXUINT, 0); - svCloseFile (ifcfg); - return devtimeout; -} diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.h index a8937ac8..8008e052 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.h +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * This program is free software; you can redistribute it and/or modify @@ -18,8 +17,8 @@ * Copyright (C) 2008 Red Hat, Inc. */ -#ifndef __READER_H__ -#define __READER_H__ +#ifndef __NMS_IFCFG_RH_READER_H__ +#define __NMS_IFCFG_RH_READER_H__ #include "nm-connection.h" @@ -28,12 +27,10 @@ NMConnection *connection_from_file (const char *filename, GError **error, gboolean *out_ignore_error); -guint devtimeout_from_file (const char *filename); - NMConnection *nmtst_connection_from_file (const char *filename, const char *network_file, const char *test_type, char **out_unhandled, GError **error); -#endif /* __READER_H__ */ +#endif /* __NMS_IFCFG_RH_READER_H__ */ diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.c new file mode 100644 index 00000000..2841bedb --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.c @@ -0,0 +1,198 @@ +/* NetworkManager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2018 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nms-ifcfg-rh-storage.h" + +#include "nm-utils.h" +#include "nm-core-internal.h" +#include "nm-connection.h" +#include "nms-ifcfg-rh-plugin.h" + +/*****************************************************************************/ + +struct _NMSIfcfgRHStorageClass { + NMSettingsStorageClass parent; +}; + +G_DEFINE_TYPE (NMSIfcfgRHStorage, nms_ifcfg_rh_storage, NM_TYPE_SETTINGS_STORAGE) + +/*****************************************************************************/ + +gboolean +nms_ifcfg_rh_storage_equal_type (const NMSIfcfgRHStorage *self_a, + const NMSIfcfgRHStorage *self_b) +{ + return (self_a == self_b) + || ( self_a + && self_b + && nm_streq0 (nms_ifcfg_rh_storage_get_uuid_opt (self_a), + nms_ifcfg_rh_storage_get_uuid_opt (self_b)) + && nm_streq0 (self_a->unmanaged_spec, + self_b->unmanaged_spec) + && nm_streq0 (self_a->unrecognized_spec, + self_b->unrecognized_spec)); +} + +void +nms_ifcfg_rh_storage_copy_content (NMSIfcfgRHStorage *dst, + const NMSIfcfgRHStorage *src) +{ + nm_assert (src != dst); + nm_assert (src && dst); + nm_assert (nms_ifcfg_rh_storage_equal_type (dst, src)); + nm_assert ( nms_ifcfg_rh_storage_get_filename (dst) + && nm_streq (nms_ifcfg_rh_storage_get_filename (dst), + nms_ifcfg_rh_storage_get_filename (src))); + + nm_g_object_ref_set (&dst->connection, src->connection); + g_free (dst->unmanaged_spec); + g_free (dst->unrecognized_spec); + dst->unmanaged_spec = g_strdup (src->unmanaged_spec); + dst->unrecognized_spec = g_strdup (src->unrecognized_spec); + dst->stat_mtime = src->stat_mtime; +} + +NMConnection * +nms_ifcfg_rh_storage_steal_connection (NMSIfcfgRHStorage *self) +{ + nm_assert (NMS_IS_IFCFG_RH_STORAGE (self)); + + return g_steal_pointer (&self->connection); +} + +/*****************************************************************************/ + +static int +cmp_fcn (const NMSIfcfgRHStorage *a, + const NMSIfcfgRHStorage *b) +{ + nm_assert (NMS_IS_IFCFG_RH_STORAGE (a)); + nm_assert (NMS_IS_IFCFG_RH_STORAGE (b)); + nm_assert (a != b); + + /* newer files are more important. */ + NM_CMP_FIELD (a, b, stat_mtime.tv_sec); + NM_CMP_FIELD (a, b, stat_mtime.tv_nsec); + + NM_CMP_DIRECT_STRCMP (nms_ifcfg_rh_storage_get_filename (a), nms_ifcfg_rh_storage_get_filename (b)); + + return 0; +} + +/*****************************************************************************/ + +static void +nms_ifcfg_rh_storage_init (NMSIfcfgRHStorage *self) +{ +} + +static NMSIfcfgRHStorage * +_storage_new (NMSIfcfgRHPlugin *plugin, + const char *uuid, + const char *filename) +{ + nm_assert (NMS_IS_IFCFG_RH_PLUGIN (plugin)); + nm_assert (!uuid || nm_utils_is_uuid (uuid)); + nm_assert (filename && filename[0] == '/'); + + return g_object_new (NMS_TYPE_IFCFG_RH_STORAGE, + NM_SETTINGS_STORAGE_PLUGIN, plugin, + NM_SETTINGS_STORAGE_UUID, uuid, + NM_SETTINGS_STORAGE_FILENAME, filename, + NULL); +} + +NMSIfcfgRHStorage * +nms_ifcfg_rh_storage_new_connection (NMSIfcfgRHPlugin *plugin, + const char *filename, + NMConnection *connection_take, + const struct timespec *mtime) +{ + NMSIfcfgRHStorage *self; + + nm_assert (NM_IS_CONNECTION (connection_take)); + nm_assert (_nm_connection_verify (connection_take, NULL) == NM_SETTING_VERIFY_SUCCESS); + nmtst_connection_assert_unchanging (connection_take); + + self = _storage_new (plugin, + nm_connection_get_uuid (connection_take), + filename); + self->connection = connection_take; + if (mtime) + self->stat_mtime = *mtime; + return self; +} + +NMSIfcfgRHStorage * +nms_ifcfg_rh_storage_new_unhandled (NMSIfcfgRHPlugin *plugin, + const char *filename, + const char *unmanaged_spec, + const char *unrecognized_spec) +{ + NMSIfcfgRHStorage *self; + + nm_assert (unmanaged_spec || unrecognized_spec); + + self = _storage_new (plugin, + NULL, + filename); + self->unmanaged_spec = g_strdup (unmanaged_spec); + self->unrecognized_spec = g_strdup (unrecognized_spec); + return self; +} + +static void +_storage_clear (NMSIfcfgRHStorage *self) +{ + c_list_unlink (&self->parent._storage_lst); + c_list_unlink (&self->parent._storage_by_uuid_lst); + nm_clear_g_free (&self->unmanaged_spec); + nm_clear_g_free (&self->unrecognized_spec); + g_clear_object (&self->connection); +} + +static void +dispose (GObject *object) +{ + NMSIfcfgRHStorage *self = NMS_IFCFG_RH_STORAGE (object); + + _storage_clear (self); + + G_OBJECT_CLASS (nms_ifcfg_rh_storage_parent_class)->dispose (object); +} + +void +nms_ifcfg_rh_storage_destroy (NMSIfcfgRHStorage *self) +{ + _storage_clear (self); + g_object_unref (self); +} + +static void +nms_ifcfg_rh_storage_class_init (NMSIfcfgRHStorageClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMSettingsStorageClass *storage_class = NM_SETTINGS_STORAGE_CLASS (klass); + + object_class->dispose = dispose; + + storage_class->cmp_fcn = (int (*) (NMSettingsStorage *, NMSettingsStorage *)) cmp_fcn; +} diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.h new file mode 100644 index 00000000..e1165f50 --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-storage.h @@ -0,0 +1,93 @@ +/* NetworkManager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2019 Red Hat, Inc. + */ + +#ifndef __NMS_IFCFG_RH_STORAGE_H__ +#define __NMS_IFCFG_RH_STORAGE_H__ + +#include "c-list/src/c-list.h" +#include "settings/nm-settings-storage.h" + +/*****************************************************************************/ + +#define NMS_TYPE_IFCFG_RH_STORAGE (nms_ifcfg_rh_storage_get_type ()) +#define NMS_IFCFG_RH_STORAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMS_TYPE_IFCFG_RH_STORAGE, NMSIfcfgRHStorage)) +#define NMS_IFCFG_RH_STORAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMS_TYPE_IFCFG_RH_STORAGE, NMSIfcfgRHStorageClass)) +#define NMS_IS_IFCFG_RH_STORAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMS_TYPE_IFCFG_RH_STORAGE)) +#define NMS_IS_IFCFG_RH_STORAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMS_TYPE_IFCFG_RH_STORAGE)) +#define NMS_IFCFG_RH_STORAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMS_TYPE_IFCFG_RH_STORAGE, NMSIfcfgRHStorageClass)) + +typedef struct { + NMSettingsStorage parent; + + NMConnection *connection; + + char *unmanaged_spec; + char *unrecognized_spec; + + /* The timestamp (stat's mtime) of the file. Newer files have + * higher priority. */ + struct timespec stat_mtime; + + bool dirty:1; + +} NMSIfcfgRHStorage; + +typedef struct _NMSIfcfgRHStorageClass NMSIfcfgRHStorageClass; + +GType nms_ifcfg_rh_storage_get_type (void); + +struct _NMSIfcfgRHPlugin; + +NMSIfcfgRHStorage *nms_ifcfg_rh_storage_new_connection (struct _NMSIfcfgRHPlugin *plugin, + const char *filename, + NMConnection *connection_take, + const struct timespec *mtime); + +NMSIfcfgRHStorage *nms_ifcfg_rh_storage_new_unhandled (struct _NMSIfcfgRHPlugin *plugin, + const char *filename, + const char *unmanaged_spec, + const char *unrecognized_spec); + +void nms_ifcfg_rh_storage_destroy (NMSIfcfgRHStorage *self); + +/*****************************************************************************/ + +gboolean nms_ifcfg_rh_storage_equal_type (const NMSIfcfgRHStorage *self_a, + const NMSIfcfgRHStorage *self_b); + +void nms_ifcfg_rh_storage_copy_content (NMSIfcfgRHStorage *dst, + const NMSIfcfgRHStorage *src); + +NMConnection *nms_ifcfg_rh_storage_steal_connection (NMSIfcfgRHStorage *self); + +/*****************************************************************************/ + +static inline const char * +nms_ifcfg_rh_storage_get_uuid_opt (const NMSIfcfgRHStorage *self) +{ + return nm_settings_storage_get_uuid_opt ((const NMSettingsStorage *) self); +} + +static inline const char * +nms_ifcfg_rh_storage_get_filename (const NMSIfcfgRHStorage *self) +{ + return nm_settings_storage_get_filename ((const NMSettingsStorage *) self); +} + +#endif /* __NMS_IFCFG_RH_STORAGE_H__ */ diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c index 22c9061b..cb1fc23a 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * This program is free software; you can redistribute it and/or modify @@ -29,6 +28,32 @@ #include "nms-ifcfg-rh-common.h" +/*****************************************************************************/ + +gboolean +nms_ifcfg_rh_util_parse_unhandled_spec (const char *unhandled_spec, + const char **out_unmanaged_spec, + const char **out_unrecognized_spec) +{ + if (unhandled_spec) { + if (NM_STR_HAS_PREFIX (unhandled_spec, "unmanaged:")) { + NM_SET_OUT (out_unmanaged_spec, &unhandled_spec[NM_STRLEN ("unmanaged:")]); + NM_SET_OUT (out_unrecognized_spec, NULL); + return TRUE; + } + if (NM_STR_HAS_PREFIX (unhandled_spec, "unrecognized:")) { + NM_SET_OUT (out_unmanaged_spec, NULL); + NM_SET_OUT (out_unrecognized_spec, &unhandled_spec[NM_STRLEN ("unrecognized:")]); + return TRUE; + } + } + NM_SET_OUT (out_unmanaged_spec, NULL); + NM_SET_OUT (out_unrecognized_spec, NULL); + return FALSE; +} + +/*****************************************************************************/ + /* * Check ';[a-fA-F0-9]{8}' file suffix used for temporary files by rpm when * installing packages. @@ -317,36 +342,41 @@ utils_is_ifcfg_alias_file (const char *alias, const char *ifcfg) char * utils_detect_ifcfg_path (const char *path, gboolean only_ifcfg) { - gs_free char *base = NULL; - char *ptr, *ifcfg = NULL; + const char *base; g_return_val_if_fail (path != NULL, NULL); if (utils_should_ignore_file (path, only_ifcfg)) return NULL; - base = g_path_get_basename (path); + base = strrchr (path, '/'); + if (!base) + base = path; + else + base += 1; - if (strncmp (base, IFCFG_TAG, NM_STRLEN (IFCFG_TAG)) == 0) { + if (NM_STR_HAS_PREFIX (base, IFCFG_TAG)) { if (base[NM_STRLEN (IFCFG_TAG)] == '\0') return NULL; if (utils_is_ifcfg_alias_file (base, NULL)) { + gs_free char *ifcfg = NULL; + char *ptr; + ifcfg = g_strdup (path); ptr = strrchr (ifcfg, ':'); - if (ptr && ptr > ifcfg) { + if ( ptr + && ptr > ifcfg + && !strchr (ptr, '/')) { *ptr = '\0'; if (g_file_test (ifcfg, G_FILE_TEST_EXISTS)) { /* the file has a colon, so it is probably an alias. * To be ~more~ certain that this is an alias file, * check whether a corresponding base file exists. */ - if (only_ifcfg) { - g_free (ifcfg); + if (only_ifcfg) return NULL; - } - return ifcfg; + return g_steal_pointer (&ifcfg); } } - g_free (ifcfg); } return g_strdup (path); } diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h index c7729df5..20d6f72d 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * This program is free software; you can redistribute it and/or modify @@ -26,6 +25,10 @@ #include "shvar.h" +gboolean nms_ifcfg_rh_util_parse_unhandled_spec (const char *unhandled_spec, + const char **out_unmanaged_spec, + const char **out_unrecognized_spec); + #define NM_IFCFG_CONNECTION_LOG_PATH(path) ((path) ?: "in-memory") #define NM_IFCFG_CONNECTION_LOG_FMT "%s (%s,\"%s\")" #define NM_IFCFG_CONNECTION_LOG_ARG(con) NM_IFCFG_CONNECTION_LOG_PATH (nm_settings_connection_get_filename ((NMSettingsConnection *) (con))), nm_settings_connection_get_uuid ((NMSettingsConnection *) (con)), nm_settings_connection_get_id ((NMSettingsConnection *) (con)) 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 80b1bffe..38dc5c8d 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service - keyfile plugin * * This program is free software; you can redistribute it and/or modify @@ -177,30 +176,18 @@ typedef struct { } Setting8021xSchemeVtable; static const Setting8021xSchemeVtable setting_8021x_scheme_vtable[] = { - [NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT] = { - .vtable = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT], - .ifcfg_rh_key = "IEEE_8021X_CA_CERT", - }, - [NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT] = { - .vtable = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT], - .ifcfg_rh_key = "IEEE_8021X_INNER_CA_CERT", - }, - [NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT] = { - .vtable = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT], - .ifcfg_rh_key = "IEEE_8021X_CLIENT_CERT", - }, - [NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT] = { - .vtable = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT], - .ifcfg_rh_key = "IEEE_8021X_INNER_CLIENT_CERT", - }, - [NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY] = { - .vtable = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY], - .ifcfg_rh_key = "IEEE_8021X_PRIVATE_KEY", - }, - [NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY] = { - .vtable = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY], - .ifcfg_rh_key = "IEEE_8021X_INNER_PRIVATE_KEY", - }, +#define _D(_scheme_type, _ifcfg_rh_key) \ + [(_scheme_type)] = { \ + .vtable = &nm_setting_8021x_scheme_vtable[(_scheme_type)], \ + .ifcfg_rh_key = ""_ifcfg_rh_key"", \ + } + _D (NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT, "IEEE_8021X_CA_CERT"), + _D (NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT, "IEEE_8021X_INNER_CA_CERT"), + _D (NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT, "IEEE_8021X_CLIENT_CERT"), + _D (NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT, "IEEE_8021X_INNER_CLIENT_CERT"), + _D (NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY, "IEEE_8021X_PRIVATE_KEY"), + _D (NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY, "IEEE_8021X_INNER_PRIVATE_KEY"), +#undef _D }; static gboolean @@ -209,6 +196,7 @@ write_object (NMSetting8021x *s_8021x, GHashTable *secrets, GHashTable *blobs, const Setting8021xSchemeVtable *objtype, + gboolean force_write, GError **error) { NMSetting8021xCKScheme scheme; @@ -216,7 +204,8 @@ write_object (NMSetting8021x *s_8021x, GBytes *blob = NULL; const char *password = NULL; NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE; - char *secret_name, *secret_flags; + char secret_name[100]; + char secret_flags[sizeof (secret_name) + NM_STRLEN ("_FLAGS")]; const char *extension; char *standard_file; @@ -243,13 +232,11 @@ write_object (NMSetting8021x *s_8021x, } /* Set the password for certificate/private key. */ - secret_name = g_strdup_printf ("%s_PASSWORD", objtype->ifcfg_rh_key); - secret_flags = g_strdup_printf ("%s_PASSWORD_FLAGS", objtype->ifcfg_rh_key); + nm_sprintf_buf (secret_name, "%s_PASSWORD", objtype->ifcfg_rh_key); + nm_sprintf_buf (secret_flags, "%s_PASSWORD_FLAGS", objtype->ifcfg_rh_key); password = (*(objtype->vtable->passwd_func))(s_8021x); flags = (*(objtype->vtable->pwflag_func))(s_8021x); set_secret (ifcfg, secrets, secret_name, password, secret_flags, flags); - g_free (secret_name); - g_free (secret_flags); if (!objtype->vtable->format_func) extension = "der"; @@ -287,7 +274,7 @@ write_object (NMSetting8021x *s_8021x, */ standard_file = utils_cert_path (svFileGetName (ifcfg), objtype->vtable->file_suffix, extension); g_hash_table_replace (blobs, standard_file, NULL); - svUnsetValue (ifcfg, objtype->ifcfg_rh_key); + svSetValue (ifcfg, objtype->ifcfg_rh_key, force_write ? "" : NULL); return TRUE; } @@ -338,43 +325,41 @@ write_8021x_certs (NMSetting8021x *s_8021x, shvarFile *ifcfg, GError **error) { - const Setting8021xSchemeVtable *otype = NULL; + const Setting8021xSchemeVtable *pk_otype = NULL; + gs_free char *value_to_free = NULL; /* CA certificate */ if (!write_object (s_8021x, ifcfg, secrets, blobs, phase2 ? &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT] : &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT], + FALSE, error)) return FALSE; /* Private key */ if (phase2) - otype = &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY]; + pk_otype = &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY]; else - otype = &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY]; + pk_otype = &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY]; /* Save the private key */ - if (!write_object (s_8021x, ifcfg, secrets, blobs, otype, error)) + if (!write_object (s_8021x, ifcfg, secrets, blobs, pk_otype, FALSE, error)) return FALSE; - /* Client certificate */ - if (otype->vtable->format_func (s_8021x) == NM_SETTING_802_1X_CK_FORMAT_PKCS12) { - /* Don't need a client certificate with PKCS#12 since the file is both - * the client certificate and the private key in one file. - */ - svSetValueStr (ifcfg, - phase2 ? "IEEE_8021X_INNER_CLIENT_CERT" : "IEEE_8021X_CLIENT_CERT", - NULL); - } else { - /* Save the client certificate */ - if (!write_object (s_8021x, ifcfg, secrets, blobs, - phase2 - ? &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT] - : &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT], - error)) - return FALSE; - } + /* Save the client certificate. + * If there is a private key, always write a property for the + * client certificate even if it is empty, so that the reader + * doesn't have to read the private key file to determine if it + * is a PKCS #12 one which serves also as client certificate. + */ + if (!write_object (s_8021x, ifcfg, secrets, blobs, + phase2 + ? &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT] + : &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT], + !!svGetValue (ifcfg, pk_otype->ifcfg_rh_key, &value_to_free), + error)) + return FALSE; return TRUE; } @@ -1118,14 +1103,22 @@ write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) nm_setting_wired_get_s390_option (s_wired, i, &s390_key, &s390_val); /* portname is handled separately */ - if (!strcmp (s390_key, "portname") || !strcmp (s390_key, "ctcprot")) + if (NM_IN_STRSET (s390_key, "portname", "ctcprot")) continue; + if (strchr (s390_key, '=')) { + /* this key cannot be expressed. But after all, it's not valid anyway + * and the connection shouldn't even verify. */ + continue; + } + if (!tmp) tmp = g_string_sized_new (30); else g_string_append_c (tmp, ' '); - g_string_append_printf (tmp, "%s=%s", s390_key, s390_val); + nm_utils_escaped_tokens_escape_gstr (s390_key, NM_ASCII_SPACES, tmp); + g_string_append_c (tmp, '='); + nm_utils_escaped_tokens_escape_gstr (s390_val, NM_ASCII_SPACES, tmp); } if (tmp) svSetValueStr (ifcfg, "OPTIONS", tmp->str); @@ -1302,7 +1295,7 @@ write_wired_for_virtual (NMConnection *connection, shvarFile *ifcfg) has_wired = TRUE; device_mac = nm_setting_wired_get_mac_address (s_wired); - svSetValueStr (ifcfg, "HWADDR", device_mac); + svSetValue (ifcfg, "HWADDR", device_mac ?: ""); cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired); svSetValueStr (ifcfg, "MACADDR", cloned_mac); @@ -1856,6 +1849,7 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg) GString *str; const char *master, *master_iface = NULL, *type; int vint; + gint32 vint32; NMSettingConnectionMdns mdns; NMSettingConnectionLlmnr llmnr; guint32 vuint32; @@ -2022,6 +2016,19 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg) vint = nm_setting_connection_get_auth_retries (s_con); svSetValueInt64_cond (ifcfg, "AUTH_RETRIES", vint >= 0, vint); + vint32 = nm_setting_connection_get_wait_device_timeout (s_con); + if (vint32 == -1) + svUnsetValue (ifcfg, "DEVTIMEOUT"); + else if ((vint32 % 1000) == 0) + svSetValueInt64 (ifcfg, "DEVTIMEOUT", vint32 / 1000); + else { + char b[100]; + + svSetValueStr (ifcfg, + "DEVTIMEOUT", + nm_sprintf_buf (b, "%.3f", ((double) vint) / 1000.0)); + } + mdns = nm_setting_connection_get_mdns (s_con); if (mdns != NM_SETTING_CONNECTION_MDNS_DEFAULT) { svSetValueEnum (ifcfg, "MDNS", nm_setting_connection_mdns_get_type (), @@ -2797,10 +2804,17 @@ write_ip6_setting (NMConnection *connection, value = nm_setting_ip_config_get_method (s_ip6); g_assert (value); + svUnsetValue (ifcfg, "IPV6_DISABLED"); if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) { svSetValueStr (ifcfg, "IPV6INIT", "no"); svUnsetValue (ifcfg, "DHCPV6C"); return TRUE; + } else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_DISABLED)) { + svSetValueStr (ifcfg, "IPV6_DISABLED", "yes"); + svSetValueStr (ifcfg, "IPV6INIT", "no"); + svUnsetValue (ifcfg, "DHCPV6C"); + svUnsetValue (ifcfg, "IPV6_AUTOCONF"); + return TRUE; } else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) { svSetValueStr (ifcfg, "IPV6INIT", "yes"); svSetValueStr (ifcfg, "IPV6_AUTOCONF", "yes"); @@ -3024,6 +3038,8 @@ static gboolean do_write_construct (NMConnection *connection, const char *ifcfg_dir, const char *filename, + NMSIfcfgRHWriterAllowFilenameCb allow_filename_cb, + gpointer allow_filename_user_data, shvarFile **out_ifcfg, GHashTable **out_blobs, GHashTable **out_secrets, @@ -3067,30 +3083,31 @@ do_write_construct (NMConnection *connection, ifcfg_name = g_strdup (filename); } else if (ifcfg_dir) { - char *escaped; + gs_free char *escaped = NULL; + int i_path; escaped = escape_id (nm_setting_connection_get_id (s_con)); - ifcfg_name = g_strdup_printf ("%s/ifcfg-%s", ifcfg_dir, escaped); - /* If a file with this path already exists then we need another name. - * Multiple connections can have the same ID (ie if two connections with - * the same ID are visible to different users) but of course can't have - * the same path. - */ - if (g_file_test (ifcfg_name, G_FILE_TEST_EXISTS)) { - guint32 idx = 0; - - nm_clear_g_free (&ifcfg_name); - while (idx++ < 500) { - ifcfg_name = g_strdup_printf ("%s/ifcfg-%s-%u", ifcfg_dir, escaped, idx); - if (g_file_test (ifcfg_name, G_FILE_TEST_EXISTS) == FALSE) - break; - nm_clear_g_free (&ifcfg_name); - } + for (i_path = 0; i_path < 10000; i_path++) { + gs_free char *path_candidate = NULL; + + if (i_path == 0) + path_candidate = g_strdup_printf ("%s/ifcfg-%s", ifcfg_dir, escaped); + else + path_candidate = g_strdup_printf ("%s/ifcfg-%s-%d", ifcfg_dir, escaped, i_path); + + if ( allow_filename_cb + && !allow_filename_cb (path_candidate, allow_filename_user_data)) + continue; + + if (g_file_test (path_candidate, G_FILE_TEST_EXISTS)) + continue; + + ifcfg_name = g_steal_pointer (&path_candidate); + break; } - g_free (escaped); - if (ifcfg_name == NULL) { + if (!ifcfg_name) { g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, "Failed to find usable ifcfg file name"); return FALSE; @@ -3329,54 +3346,12 @@ do_write_to_disk (NMConnection *connection, return TRUE; } -static gboolean -do_write_reread (NMConnection *connection, - const char *ifcfg_name, - NMConnection **out_reread, - gboolean *out_reread_same, - GError **error) -{ - gs_unref_object NMConnection *reread = NULL; - gs_free_error GError *local = NULL; - gs_free char *unhandled = NULL; - gboolean reread_same = FALSE; - - nm_assert (!out_reread || !*out_reread); - - reread = connection_from_file (ifcfg_name, &unhandled, &local, NULL); - - if (!reread) { - g_propagate_error (error, local); - local = NULL; - return FALSE; - } - if (unhandled) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "connection is unhandled"); - return FALSE; - } - if (out_reread_same) { - if (nm_connection_compare (reread, connection, NM_SETTING_COMPARE_FLAG_EXACT)) - reread_same = TRUE; - - nm_assert (reread_same == nm_connection_compare (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT)); - nm_assert (reread_same == ({ - gs_unref_hashtable GHashTable *_settings = NULL; - - ( nm_connection_diff (reread, connection, NM_SETTING_COMPARE_FLAG_EXACT, &_settings) - && !_settings); - })); - } - - NM_SET_OUT (out_reread, g_steal_pointer (&reread)); - NM_SET_OUT (out_reread_same, reread_same); - return TRUE; -} - gboolean nms_ifcfg_rh_writer_write_connection (NMConnection *connection, const char *ifcfg_dir, const char *filename, + NMSIfcfgRHWriterAllowFilenameCb allow_filename_cb, + gpointer allow_filename_user_data, char **out_filename, NMConnection **out_reread, gboolean *out_reread_same, @@ -3389,13 +3364,14 @@ nms_ifcfg_rh_writer_write_connection (NMConnection *connection, nm_auto_free_gstring GString *route6_content = NULL; gs_unref_hashtable GHashTable *secrets = NULL; gs_unref_hashtable GHashTable *blobs = NULL; - GError *local = NULL; nm_assert (!out_reread || !*out_reread); if (!do_write_construct (connection, ifcfg_dir, filename, + allow_filename_cb, + allow_filename_user_data, &ifcfg, &blobs, &secrets, @@ -3424,28 +3400,46 @@ nms_ifcfg_rh_writer_write_connection (NMConnection *connection, /* Note that we just wrote the connection to disk, and re-read it from there. * That is racy if somebody else modifies the connection. + * That race is why we must not tread a failure to re-read the profile + * as an error. * - * A better solution might be, to re-read the connection only based on the - * in-memory representation of what we collected above. But the reader + * FIXME: a much better solution might be, to re-read the connection only based + * on the in-memory representation of what we collected above. But the reader * does not yet allow to inject the configuration. */ - if (out_reread || out_reread_same) { - if (!do_write_reread (connection, - svFileGetName (ifcfg), - out_reread, - out_reread_same, - &local)) { + if ( out_reread + || out_reread_same) { + gs_unref_object NMConnection *reread = NULL; + gboolean reread_same = FALSE; + gs_free_error GError *local = NULL; + gs_free char *unhandled = NULL; + + reread = connection_from_file (svFileGetName (ifcfg), + &unhandled, + &local, + NULL); + nm_assert ((NM_IS_CONNECTION (reread) && !local) || (!reread && local)); + + if (!reread) { _LOGW ("write: failure to re-read connection \"%s\": %s", svFileGetName (ifcfg), local->message); - g_clear_error (&local); + } else if (unhandled) { + g_clear_object (&reread); + _LOGW ("write: failure to re-read connection \"%s\": %s", + svFileGetName (ifcfg), "connection is unhandled"); } else { - if ( out_reread_same - && !*out_reread_same) { - _LOGD ("write: connection %s (%s) was modified by persisting it to \"%s\" ", - nm_connection_get_id (connection), - nm_connection_get_uuid (connection), - svFileGetName (ifcfg)); + if (out_reread_same) { + reread_same = nm_connection_compare (reread, connection, NM_SETTING_COMPARE_FLAG_EXACT); + if (!reread_same) { + _LOGD ("write: connection %s (%s) was modified by persisting it to \"%s\" ", + nm_connection_get_id (connection), + nm_connection_get_uuid (connection), + svFileGetName (ifcfg)); + } } } + + NM_SET_OUT (out_reread, g_steal_pointer (&reread)); + NM_SET_OUT (out_reread_same, reread_same); } /* Only return the filename if this was a newly written ifcfg */ diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.h index d7a255a9..0902daee 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.h +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service - keyfile plugin * * This program is free software; you can redistribute it and/or modify @@ -23,12 +22,18 @@ #include "nm-connection.h" + +typedef gboolean (*NMSIfcfgRHWriterAllowFilenameCb) (const char *check_filename, + gpointer allow_filename_user_data); + gboolean nms_ifcfg_rh_writer_can_write_connection (NMConnection *connection, GError **error); gboolean nms_ifcfg_rh_writer_write_connection (NMConnection *connection, const char *ifcfg_dir, const char *filename, + NMSIfcfgRHWriterAllowFilenameCb allow_filename_cb, + gpointer allow_filename_user_data, char **out_filename, NMConnection **out_reread, gboolean *out_reread_same, diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c index b399a17f..94e31aac 100644 --- a/src/settings/plugins/ifcfg-rh/shvar.c +++ b/src/settings/plugins/ifcfg-rh/shvar.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* * shvar.c * diff --git a/src/settings/plugins/ifcfg-rh/shvar.h b/src/settings/plugins/ifcfg-rh/shvar.h index b38a8557..67fb5404 100644 --- a/src/settings/plugins/ifcfg-rh/shvar.h +++ b/src/settings/plugins/ifcfg-rh/shvar.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* * shvar.h * diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected index 5d81dfef..36df7712 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected @@ -1,6 +1,7 @@ BONDING_OPTS="downdelay=5 miimon=100 mode=balance-rr updelay=10" TYPE=Bond BONDING_MASTER=yes +HWADDR= PROXY_METHOD=none BROWSER_ONLY=no BOOTPROTO=none diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_VLAN_reorder_hdr.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_VLAN_reorder_hdr.cexpected index 339f8107..9c2a1ff0 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_VLAN_reorder_hdr.cexpected +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_VLAN_reorder_hdr.cexpected @@ -5,6 +5,7 @@ VLAN_ID=444 REORDER_HDR=yes GVRP=no MVRP=no +HWADDR= PROXY_METHOD=none BROWSER_ONLY=no BOOTPROTO=dhcp diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Vlan_test-vlan-interface.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Vlan_test-vlan-interface.cexpected index 793713ea..44eb777c 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Vlan_test-vlan-interface.cexpected +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Vlan_test-vlan-interface.cexpected @@ -8,6 +8,7 @@ VLAN_FLAGS=LOOSE_BINDING MVRP=no VLAN_INGRESS_PRIORITY_MAP=0:1,2:5 VLAN_EGRESS_PRIORITY_MAP=3:1,12:3,14:7 +HWADDR= PROXY_METHOD=none BROWSER_ONLY=no BOOTPROTO=none diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-team-slave-enp31s0f1-142.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-team-slave-enp31s0f1-142.cexpected index 87980dc4..b01372af 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-team-slave-enp31s0f1-142.cexpected +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-team-slave-enp31s0f1-142.cexpected @@ -4,6 +4,7 @@ VLAN_ID=142 REORDER_HDR=yes GVRP=no MVRP=no +HWADDR= NAME=team-slave-enp31s0f1-142 UUID=74f435bb-ede4-415a-9d48-f580b60eba04 DEVICE=enp31s0f1-142 diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-ip6-disabled.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-ip6-disabled.cexpected new file mode 100644 index 00000000..cae51b89 --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-ip6-disabled.cexpected @@ -0,0 +1,11 @@ +TYPE=Ethernet +PROXY_METHOD=none +BROWSER_ONLY=no +BOOTPROTO=dhcp +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6_DISABLED=yes +IPV6INIT=no +NAME="Test Write Wired Disabled IP6" +UUID=${UUID} +ONBOOT=yes diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-tls-p12-no-client-cert b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-tls-p12-no-client-cert new file mode 100644 index 00000000..24397473 --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-8021x-tls-p12-no-client-cert @@ -0,0 +1,13 @@ +# Intel Corporation 82540EP Gigabit Ethernet Controller (Mobile) +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=dhcp +ONBOOT=yes +NM_CONTROLLED=yes +KEY_MGMT=IEEE8021X +IEEE_8021X_EAP_METHODS=TLS +IEEE_8021X_IDENTITY="David Smith" +IEEE_8021X_CA_CERT=test_ca_cert.pem +IEEE_8021X_PRIVATE_KEY=test_client.p12 +IEEE_8021X_PRIVATE_KEY_PASSWORD="test1" diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/test_client.p12 b/src/settings/plugins/ifcfg-rh/tests/network-scripts/test_client.p12 new file mode 100644 index 00000000..edc2af75 --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/test_client.p12 Binary files differdiff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index 49ab04d4..45e90b91 100644 --- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service - keyfile plugin * * This program is free software; you can redistribute it and/or modify @@ -239,7 +238,7 @@ _assert_expected_content (NMConnection *connection, const char *filename, const g_assert (_ifcfg_dir && _ifcfg_dir[0]); \ g_assert (_filename && _filename[0]); \ \ - _success = nms_ifcfg_rh_writer_write_connection (_connection, _ifcfg_dir, _filename, NULL, _out_reread, _out_reread_same, &_error); \ + _success = nms_ifcfg_rh_writer_write_connection (_connection, _ifcfg_dir, _filename, NULL, NULL, NULL, _out_reread, _out_reread_same, &_error); \ nmtst_assert_success (_success, _error); \ _assert_expected_content (_connection, _filename, _expected); \ } G_STMT_END @@ -310,7 +309,7 @@ _writer_new_connection_reread (NMConnection *connection, char *filename = NULL; gs_unref_object NMConnection *con_verified = NULL; gs_unref_object NMConnection *reread_copy = NULL; - NMConnection **reread = out_reread ?: ((nmtst_get_rand_int () % 2) ? &reread_copy : NULL); + NMConnection **reread = out_reread ?: ((nmtst_get_rand_uint32 () % 2) ? &reread_copy : NULL); g_assert (NM_IS_CONNECTION (connection)); g_assert (ifcfg_dir); @@ -320,6 +319,8 @@ _writer_new_connection_reread (NMConnection *connection, success = nms_ifcfg_rh_writer_write_connection (con_verified, ifcfg_dir, NULL, + NULL, + NULL, &filename, reread, out_reread_same, @@ -395,6 +396,8 @@ _writer_new_connection_fail (NMConnection *connection, success = nms_ifcfg_rh_writer_write_connection (connection_normalized, ifcfg_dir, NULL, + NULL, + NULL, &filename, &reread, NULL, @@ -718,7 +721,7 @@ test_read_unmanaged_unrecognized (void) connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-nm-controlled-unrecognized", NULL, NULL, &unhandled_spec); - g_assert_cmpstr (unhandled_spec, ==, "unmanaged:interface-name:ipoac0"); + g_assert_cmpstr (unhandled_spec, ==, "unmanaged:interface-name:=ipoac0"); /* ===== CONNECTION SETTING ===== */ s_con = nm_connection_get_setting_connection (connection); @@ -1974,6 +1977,27 @@ test_read_802_1x_ttls_eapgtc (void) } static void +test_read_802_1x_tls_p12_no_client_cert (void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSetting8021x *s_8021x; + const char *path; + + connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wired-8021x-tls-p12-no-client-cert", + NULL, TYPE_ETHERNET, NULL); + + s_8021x = nm_connection_get_setting_802_1x (connection); + g_assert (s_8021x); + + g_assert_cmpint (nm_setting_802_1x_get_private_key_scheme (s_8021x), ==, NM_SETTING_802_1X_CK_SCHEME_PATH); + path = nm_setting_802_1x_get_private_key_path (s_8021x); + g_assert (path); + + g_assert_cmpint (nm_setting_802_1x_get_client_cert_scheme (s_8021x), ==, NM_SETTING_802_1X_CK_SCHEME_PATH); + g_assert_cmpstr (path, ==, nm_setting_802_1x_get_client_cert_path (s_8021x)); +} + +static void test_read_write_802_1x_password_raw (void) { nmtst_auto_unlinkfile char *testfile = NULL; @@ -4768,6 +4792,48 @@ test_write_wired_static_ip6_only (void) nmtst_assert_connection_equals (connection, TRUE, reread, FALSE); } +static void +test_write_ip6_disabled (void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection *s_con; + NMSettingWired *s_wired; + NMSettingIPConfig *s_ip4; + NMSettingIPConfig *s_ip6; + + connection = nmtst_create_minimal_connection ("Test Write Wired Disabled IP6", + NULL, + NM_SETTING_WIRED_SETTING_NAME, + &s_con); + + s_wired = (NMSettingWired *) nm_setting_wired_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new (); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + g_object_set (s_ip4, + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NULL); + + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new (); + nm_connection_add_setting (connection, NM_SETTING (s_ip6)); + g_object_set (s_ip6, + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_DISABLED, + NULL); + + nmtst_assert_connection_verifies (connection); + + _writer_new_connec_exp (connection, + TEST_SCRATCH_DIR_TMP, + TEST_IFCFG_DIR"/ifcfg-test-ip6-disabled.cexpected", + &testfile); + + reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL); + nmtst_assert_connection_equals (connection, TRUE, reread, FALSE); +} + /* Test writing an IPv6 config with varying gateway address. * For missing gateway (::), we expect no IPV6_DEFAULTGW to be written * to ifcfg-rh. @@ -8938,25 +9004,16 @@ static void test_read_team_master_invalid (gconstpointer user_data) { const char *const PATH_NAME = user_data; - NMConnection *connection; - NMSettingConnection *s_con; - NMSettingTeam *s_team; - - NMTST_EXPECT_NM_WARN ("*ignoring invalid team configuration*"); - connection = _connection_from_file (PATH_NAME, NULL, TYPE_ETHERNET, NULL); - g_test_assert_expected_messages (); - - g_assert_cmpstr (nm_connection_get_interface_name (connection), ==, "team0"); - - s_con = nm_connection_get_setting_connection (connection); - g_assert (s_con); - g_assert_cmpstr (nm_setting_connection_get_connection_type (s_con), ==, NM_SETTING_TEAM_SETTING_NAME); + gs_free_error GError *error = NULL; + gs_unref_object NMConnection *connection = NULL; - s_team = nm_connection_get_setting_team (connection); - g_assert (s_team); - g_assert (nm_setting_team_get_config (s_team) == NULL); + if (WITH_JSON_VALIDATION) { + _connection_from_file_fail (PATH_NAME, NULL, TYPE_ETHERNET, &error); - g_object_unref (connection); + g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY); + g_assert (strstr (error->message, _("invalid json"))); + } else + connection = _connection_from_file (PATH_NAME, NULL, TYPE_ETHERNET, NULL); } static void @@ -9237,19 +9294,19 @@ test_team_reread_slave (void) nmtst_assert_connection_equals (connection_1, FALSE, connection_2, FALSE); - _writer_new_connection_reread ((nmtst_get_rand_int () % 2) ? connection_1 : connection_2, + _writer_new_connection_reread ((nmtst_get_rand_uint32 () % 2) ? connection_1 : connection_2, TEST_SCRATCH_DIR, &testfile, TEST_IFCFG_DIR"/ifcfg-team-slave-enp31s0f1-142.cexpected", &reread, &reread_same); - _assert_reread_same ((nmtst_get_rand_int () % 2) ? connection_1 : connection_2, reread); + _assert_reread_same ((nmtst_get_rand_uint32 () % 2) ? connection_1 : connection_2, reread); g_assert (reread_same); g_clear_object (&reread); reread = _connection_from_file (testfile, NULL, TYPE_VLAN, NULL); - nmtst_assert_connection_equals ((nmtst_get_rand_int () % 2) ? connection_1 : connection_2, FALSE, + nmtst_assert_connection_equals ((nmtst_get_rand_uint32 () % 2) ? connection_1 : connection_2, FALSE, reread, FALSE); } @@ -9414,7 +9471,7 @@ do_svUnescape_combine_ansi (GString *str_val, GString *str_exp, const UnescapeTe g_string_append (str_val, "$'"); if (idx < 0) { for (i = -idx; i > 0; i--) { - j = nmtst_get_rand_int () % data_len; + j = nmtst_get_rand_uint32 () % data_len; if (!data_ansi[j].can_concat) { i++; continue; @@ -9613,7 +9670,7 @@ test_svUnescape (void) /* different values can be just concatenated... */ for (i = 0; i < 200; i++) { - gsize num_concat = (nmtst_get_rand_int () % 5) + 2; + gsize num_concat = (nmtst_get_rand_uint32 () % 5) + 2; g_string_set_size (str_val, 0); g_string_set_size (str_exp, 0); @@ -9621,12 +9678,12 @@ test_svUnescape (void) while (num_concat > 0) { gsize idx; - if ((nmtst_get_rand_int () % 3 == 0)) { - do_svUnescape_combine_ansi (str_val2, str_exp2, data_ansi, G_N_ELEMENTS (data_ansi), -((int) ((nmtst_get_rand_int () % 5) + 1))); + if ((nmtst_get_rand_uint32 () % 3 == 0)) { + do_svUnescape_combine_ansi (str_val2, str_exp2, data_ansi, G_N_ELEMENTS (data_ansi), -((int) ((nmtst_get_rand_uint32 () % 5) + 1))); continue; } - idx = nmtst_get_rand_int () % G_N_ELEMENTS (data_full); + idx = nmtst_get_rand_uint32 () % G_N_ELEMENTS (data_full); if (!data_full[idx].can_concat) continue; g_string_append (str_val, data_full[idx].val); @@ -9634,7 +9691,7 @@ test_svUnescape (void) num_concat--; } - switch (nmtst_get_rand_int () % 3) { + switch (nmtst_get_rand_uint32 () % 3) { case 0: g_string_append (str_val, " "); break; @@ -9642,7 +9699,7 @@ test_svUnescape (void) g_string_append (str_val, " "); break; } - switch (nmtst_get_rand_int () % 3) { + switch (nmtst_get_rand_uint32 () % 3) { case 0: g_string_append (str_val, " #"); break; @@ -10177,6 +10234,7 @@ int main (int argc, char **argv) g_test_add_data_func (TPATH "static-ip6-only-gw/::", "::", test_write_wired_static_ip6_only_gw); g_test_add_data_func (TPATH "static-ip6-only-gw/2001:db8:8:4::2", "2001:db8:8:4::2", test_write_wired_static_ip6_only_gw); g_test_add_data_func (TPATH "static-ip6-only-gw/::ffff:255.255.255.255", "::ffff:255.255.255.255", test_write_wired_static_ip6_only_gw); + g_test_add_func (TPATH "ip6/disabled", test_write_ip6_disabled); g_test_add_func (TPATH "read-dns-options", test_read_dns_options); g_test_add_func (TPATH "clear-master", test_clear_master); @@ -10223,6 +10281,8 @@ int main (int argc, char **argv) g_test_add_func (TPATH "802-1x/subj-matches", test_read_write_802_1X_subj_matches); g_test_add_func (TPATH "802-1x/ttls-eapgtc", test_read_802_1x_ttls_eapgtc); g_test_add_func (TPATH "802-1x/password_raw", test_read_write_802_1x_password_raw); + g_test_add_func (TPATH "802-1x/tls-p12-no-client-cert", test_read_802_1x_tls_p12_no_client_cert); + g_test_add_data_func (TPATH "wired/read/aliases/good/0", GINT_TO_POINTER (0), test_read_wired_aliases_good); g_test_add_data_func (TPATH "wired/read/aliases/good/3", GINT_TO_POINTER (3), test_read_wired_aliases_good); g_test_add_func (TPATH "wired/read/aliases/bad1", test_read_wired_aliases_bad_1); diff --git a/src/settings/plugins/ifupdown/meson.build b/src/settings/plugins/ifupdown/meson.build index 42edd438..365ae1a9 100644 --- a/src/settings/plugins/ifupdown/meson.build +++ b/src/settings/plugins/ifupdown/meson.build @@ -15,7 +15,6 @@ libnms_ifupdown_core = static_library( ) sources = files( - 'nms-ifupdown-connection.c', 'nms-ifupdown-plugin.c', ) diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-connection.c b/src/settings/plugins/ifupdown/nms-ifupdown-connection.c deleted file mode 100644 index d06078a9..00000000 --- a/src/settings/plugins/ifupdown/nms-ifupdown-connection.c +++ /dev/null @@ -1,106 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager system settings service (ifupdown) - * - * Alexander Sack <asac@ubuntu.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * (C) Copyright 2007,2008 Canonical Ltd. - */ - -#include "nm-default.h" - -#include "nms-ifupdown-connection.h" - -#include <glib/gstdio.h> - -#include "nm-dbus-interface.h" -#include "nm-utils.h" -#include "nm-setting-wireless-security.h" -#include "settings/nm-settings-connection.h" -#include "settings/nm-settings-plugin.h" - -#include "nms-ifupdown-parser.h" - -/*****************************************************************************/ - -struct _NMIfupdownConnection { - NMSettingsConnection parent; -}; - -struct _NMIfupdownConnectionClass { - NMSettingsConnectionClass parent; -}; - -G_DEFINE_TYPE (NMIfupdownConnection, nm_ifupdown_connection, NM_TYPE_SETTINGS_CONNECTION) - -/*****************************************************************************/ - -#define _NMLOG_PREFIX_NAME "ifupdown" -#define _NMLOG_DOMAIN LOGD_SETTINGS -#define _NMLOG(level, ...) \ - nm_log ((level), _NMLOG_DOMAIN, NULL, NULL, \ - "%s" _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ - _NMLOG_PREFIX_NAME": " \ - _NM_UTILS_MACRO_REST (__VA_ARGS__)) - -/*****************************************************************************/ - -static gboolean -supports_secrets (NMSettingsConnection *connection, const char *setting_name) -{ - _LOGI ("supports_secrets() for setting_name: '%s'", setting_name); - - return (strcmp (setting_name, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME) == 0); -} - -/*****************************************************************************/ - -static void -nm_ifupdown_connection_init (NMIfupdownConnection *connection) -{ -} - -NMIfupdownConnection * -nm_ifupdown_connection_new (if_block *block) -{ - NMIfupdownConnection *connection; - GError *error = NULL; - - g_return_val_if_fail (block != NULL, NULL); - - connection = g_object_new (NM_TYPE_IFUPDOWN_CONNECTION, NULL); - - /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */ - if (!ifupdown_update_connection_from_if_block (nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection)), - block, - &error)) { - _LOGW ("invalid connection read from /etc/network/interfaces: %s", - error->message); - g_object_unref (connection); - return NULL; - } - - return connection; -} - -static void -nm_ifupdown_connection_class_init (NMIfupdownConnectionClass *ifupdown_connection_class) -{ - NMSettingsConnectionClass *connection_class = NM_SETTINGS_CONNECTION_CLASS (ifupdown_connection_class); - - connection_class->supports_secrets = supports_secrets; -} - diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-connection.h b/src/settings/plugins/ifupdown/nms-ifupdown-connection.h deleted file mode 100644 index 7536c23e..00000000 --- a/src/settings/plugins/ifupdown/nms-ifupdown-connection.h +++ /dev/null @@ -1,45 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ - -/* NetworkManager system settings service (ifupdown) - * - * Alexander Sack <asac@ubuntu.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * (C) Copyright 2008 Canonical Ltd. - */ - -#ifndef __NETWORKMANAGER_IFUPDOWN_CONNECTION_H__ -#define __NETWORKMANAGER_IFUPDOWN_CONNECTION_H__ - -#include "settings/nm-settings-connection.h" - -#include "nms-ifupdown-interface-parser.h" - -#define NM_TYPE_IFUPDOWN_CONNECTION (nm_ifupdown_connection_get_type ()) -#define NM_IFUPDOWN_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_IFUPDOWN_CONNECTION, NMIfupdownConnection)) -#define NM_IFUPDOWN_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_IFUPDOWN_CONNECTION, NMIfupdownConnectionClass)) -#define NM_IS_IFUPDOWN_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_IFUPDOWN_CONNECTION)) -#define NM_IS_IFUPDOWN_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_IFUPDOWN_CONNECTION)) -#define NM_IFUPDOWN_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_IFUPDOWN_CONNECTION, NMIfupdownConnectionClass)) - -typedef struct _NMIfupdownConnection NMIfupdownConnection; -typedef struct _NMIfupdownConnectionClass NMIfupdownConnectionClass; - -GType nm_ifupdown_connection_get_type (void); - -NMIfupdownConnection *nm_ifupdown_connection_new (if_block *block); - -#endif /* __NETWORKMANAGER_IFUPDOWN_CONNECTION_H__ */ diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c b/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c index 6587fc84..75f29878 100644 --- a/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c +++ b/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * Tom Parker <palfrey@tevp.net> diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.h b/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.h index f367f626..308228a4 100644 --- a/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.h +++ b/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * Tom Parker <palfrey@tevp.net> diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-parser.c b/src/settings/plugins/ifupdown/nms-ifupdown-parser.c index 239f6415..41b20850 100644 --- a/src/settings/plugins/ifupdown/nms-ifupdown-parser.c +++ b/src/settings/plugins/ifupdown/nms-ifupdown-parser.c @@ -1,5 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ - /* NetworkManager system settings service (ifupdown) * * Alexander Sack <asac@ubuntu.com> @@ -653,22 +651,21 @@ update_ip6_setting_from_if_block (NMConnection *connection, return TRUE; } -gboolean -ifupdown_update_connection_from_if_block (NMConnection *connection, - if_block *block, - GError **error) +NMConnection * +ifupdown_new_connection_from_if_block (if_block *block, + gboolean autoconnect, + GError **error) { + gs_unref_object NMConnection *connection = NULL; const char *type; gs_free char *idstr = NULL; gs_free char *uuid = NULL; NMSettingConnection *s_con; - gboolean success = FALSE; - s_con = nm_connection_get_setting_connection (connection); - if (!s_con) { - s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); - nm_connection_add_setting (connection, NM_SETTING (s_con)); - } + connection = nm_simple_connection_new (); + + s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); + nm_connection_add_setting (connection, NM_SETTING (s_con)); type = _ifupdownplugin_guess_connection_type (block); idstr = g_strconcat ("Ifupdown (", block->name, ")", NULL); @@ -679,11 +676,10 @@ ifupdown_update_connection_from_if_block (NMConnection *connection, NM_SETTING_CONNECTION_INTERFACE_NAME, block->name, NM_SETTING_CONNECTION_ID, idstr, NM_SETTING_CONNECTION_UUID, uuid, - NM_SETTING_CONNECTION_READ_ONLY, TRUE, - NM_SETTING_CONNECTION_AUTOCONNECT, FALSE, + NM_SETTING_CONNECTION_AUTOCONNECT, (gboolean) (!!autoconnect), NULL); - _LOGI ("update_connection_setting_from_if_block: name:%s, type:%s, id:%s, uuid: %s", + _LOGD ("update_connection_setting_from_if_block: name:%s, type:%s, id:%s, uuid: %s", block->name, type, idstr, nm_setting_connection_get_uuid (s_con)); if (nm_streq (type, NM_SETTING_WIRED_SETTING_NAME)) @@ -693,13 +689,16 @@ ifupdown_update_connection_from_if_block (NMConnection *connection, update_wireless_security_setting_from_if_block (connection, block); } - if (ifparser_haskey (block, "inet6")) - success = update_ip6_setting_from_if_block (connection, block, error); - else - success = update_ip4_setting_from_if_block (connection, block, error); + if (ifparser_haskey (block, "inet6")) { + if (!update_ip6_setting_from_if_block (connection, block, error)) + return FALSE; + } else { + if (!update_ip4_setting_from_if_block (connection, block, error)) + return FALSE; + } - if (success == TRUE) - success = nm_connection_verify (connection, error); + if (!nm_connection_normalize (connection, NULL, NULL, error)) + return NULL; - return success; + return g_steal_pointer (&connection); } diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-parser.h b/src/settings/plugins/ifupdown/nms-ifupdown-parser.h index 6a86bf86..7569648f 100644 --- a/src/settings/plugins/ifupdown/nms-ifupdown-parser.h +++ b/src/settings/plugins/ifupdown/nms-ifupdown-parser.h @@ -1,5 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ - /* NetworkManager system settings service (ifupdown) * * Alexander Sack <asac@ubuntu.com> @@ -21,16 +19,14 @@ * (C) Copyright 2008 Canonical Ltd. */ -#ifndef __PARSER_H__ -#define __PARSER_H__ +#ifndef __NMS_IFUPDOWN_PARSER_H__ +#define __NMS_IFUPDOWN_PARSER_H__ #include "nm-connection.h" - #include "nms-ifupdown-interface-parser.h" -gboolean -ifupdown_update_connection_from_if_block (NMConnection *connection, - if_block *block, - GError **error); +NMConnection *ifupdown_new_connection_from_if_block (if_block *block, + gboolean autoconnect, + GError **error); -#endif /* __PARSER_H__ */ +#endif /* __NMS_IFUPDOWN_PARSER_H__ */ diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c index 04281f00..93f1813c 100644 --- a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c +++ b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c @@ -1,5 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ - /* NetworkManager system settings service (ifupdown) * * Alexander Sack <asac@ubuntu.com> @@ -26,23 +24,13 @@ #include "nms-ifupdown-plugin.h" -#include <arpa/inet.h> -#include <gmodule.h> - -#include "nm-setting-connection.h" -#include "nm-dbus-interface.h" -#include "settings/nm-settings-plugin.h" -#include "nm-setting-ip4-config.h" -#include "nm-setting-wireless.h" -#include "nm-setting-wired.h" -#include "nm-setting-ppp.h" -#include "nm-utils.h" #include "nm-core-internal.h" -#include "NetworkManagerUtils.h" +#include "nm-core-utils.h" #include "nm-config.h" +#include "settings/nm-settings-plugin.h" +#include "settings/nm-settings-storage.h" #include "nms-ifupdown-interface-parser.h" -#include "nms-ifupdown-connection.h" #include "nms-ifupdown-parser.h" #define ENI_INTERFACES_FILE "/etc/network/interfaces" @@ -52,31 +40,35 @@ /*****************************************************************************/ typedef struct { + NMConnection *connection; + NMSettingsStorage *storage; +} StorageData; + +typedef struct { /* Stores an entry for blocks/interfaces read from /e/n/i and (if exists) - * the NMIfupdownConnection associated with the block. + * the StorageData associated with the block. */ GHashTable *eni_ifaces; - bool ifupdown_managed; -} SettingsPluginIfupdownPrivate; + bool ifupdown_managed:1; -struct _SettingsPluginIfupdown { + bool initialized:1; + + bool already_reloaded:1; +} NMSIfupdownPluginPrivate; + +struct _NMSIfupdownPlugin { NMSettingsPlugin parent; - SettingsPluginIfupdownPrivate _priv; + NMSIfupdownPluginPrivate _priv; }; -struct _SettingsPluginIfupdownClass { +struct _NMSIfupdownPluginClass { NMSettingsPluginClass parent; }; -G_DEFINE_TYPE (SettingsPluginIfupdown, settings_plugin_ifupdown, NM_TYPE_SETTINGS_PLUGIN) - -#define SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE(self) _NM_GET_PRIVATE (self, SettingsPluginIfupdown, SETTINGS_IS_PLUGIN_IFUPDOWN) +G_DEFINE_TYPE (NMSIfupdownPlugin, nms_ifupdown_plugin, NM_TYPE_SETTINGS_PLUGIN) -/*****************************************************************************/ - -static SettingsPluginIfupdown *settings_plugin_ifupdown_get (void); -NM_DEFINE_SINGLETON_GETTER (SettingsPluginIfupdown, settings_plugin_ifupdown_get, SETTINGS_TYPE_PLUGIN_IFUPDOWN); +#define NMS_IFUPDOWN_PLUGIN_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMSIfupdownPlugin, NMS_IS_IFUPDOWN_PLUGIN) /*****************************************************************************/ @@ -90,46 +82,146 @@ NM_DEFINE_SINGLETON_GETTER (SettingsPluginIfupdown, settings_plugin_ifupdown_get /*****************************************************************************/ -/* Returns the plugins currently known list of connections. The returned - * list is freed by the system settings service. - */ -static GSList* -get_connections (NMSettingsPlugin *plugin) +static GHashTable *load_eni_ifaces (NMSIfupdownPlugin *self); + +/*****************************************************************************/ + +static void +_storage_data_destroy (StorageData *sd) +{ + nm_g_object_unref (sd->connection); + nm_g_object_unref (sd->storage); + g_slice_free (StorageData, sd); +} + +/*****************************************************************************/ + +static void +initialize (NMSIfupdownPlugin *self) { - SettingsPluginIfupdown *self = SETTINGS_PLUGIN_IFUPDOWN (plugin); - SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (self); - GSList *list = NULL; + NMSIfupdownPluginPrivate *priv = NMS_IFUPDOWN_PLUGIN_GET_PRIVATE (self); + gboolean ifupdown_managed; + + nm_assert (!priv->initialized); + + priv->initialized = TRUE; + + ifupdown_managed = nm_config_data_get_value_boolean (NM_CONFIG_GET_DATA_ORIG, + NM_CONFIG_KEYFILE_GROUP_IFUPDOWN, + NM_CONFIG_KEYFILE_KEY_IFUPDOWN_MANAGED, + !IFUPDOWN_UNMANAGE_WELL_KNOWN_DEFAULT); + _LOGI ("management mode: %s", ifupdown_managed ? "managed" : "unmanaged"); + priv->ifupdown_managed = ifupdown_managed; + + priv->eni_ifaces = load_eni_ifaces (self); +} + +static void +reload_connections (NMSettingsPlugin *plugin, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data) +{ + NMSIfupdownPlugin *self = NMS_IFUPDOWN_PLUGIN (plugin); + NMSIfupdownPluginPrivate *priv = NMS_IFUPDOWN_PLUGIN_GET_PRIVATE (self); + gs_unref_hashtable GHashTable *eni_ifaces_old = NULL; GHashTableIter iter; - void *value; + StorageData *sd; + StorageData *sd2; + const char *block_name; - if (!priv->ifupdown_managed) { - _LOGD ("get_connections: not connections due to managed=false"); - return NULL; + if (!priv->initialized) + initialize (self); + else if (!priv->already_reloaded) { + /* This is the first call to reload, but we are already initialized. + * + * This happens because during start NMSettings first queries unmanaged-specs, + * and then issues a reload call right away. + * + * On future reloads, we really want to load /e/n/i again. */ + priv->already_reloaded = TRUE; + } else { + eni_ifaces_old = priv->eni_ifaces; + priv->eni_ifaces = load_eni_ifaces (self); + + g_hash_table_iter_init (&iter, eni_ifaces_old); + while (g_hash_table_iter_next (&iter, (gpointer *) &block_name, (gpointer *) &sd)) { + if (!sd) + continue; + + sd2 = g_hash_table_lookup (priv->eni_ifaces, block_name); + if (!sd2) + continue; + + nm_assert (nm_streq (nm_settings_storage_get_uuid (sd->storage), nm_settings_storage_get_uuid (sd2->storage))); + nm_g_object_ref_set (&sd2->storage, sd->storage); + g_hash_table_iter_remove (&iter); + } } + if (!priv->ifupdown_managed) + _LOGD ("load: no connections due to managed=false"); + g_hash_table_iter_init (&iter, priv->eni_ifaces); - while (g_hash_table_iter_next (&iter, NULL, &value)) { - if (value) - list = g_slist_prepend (list, value); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &sd)) { + gs_unref_object NMConnection *connection = NULL; + + if (!sd) + continue; + + connection = g_steal_pointer (&sd->connection); + + if (!priv->ifupdown_managed) + continue; + + _LOGD ("load: %s (%s)", + nm_settings_storage_get_uuid (sd->storage), + nm_connection_get_id (connection)); + callback (plugin, + sd->storage, + connection, + user_data); } + if ( eni_ifaces_old + && priv->ifupdown_managed) { + g_hash_table_iter_init (&iter, eni_ifaces_old); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &sd)) { + if (!sd) + continue; + _LOGD ("unload: %s", + nm_settings_storage_get_uuid (sd->storage)); + callback (plugin, + sd->storage, + NULL, + user_data); + } + } +} + +/*****************************************************************************/ - _LOGD ("get_connections: %u connections", g_slist_length (list)); - return list; +static GSList * +_unmanaged_specs (GHashTable *eni_ifaces) +{ + gs_free const char **keys = NULL; + GSList *specs = NULL; + guint i, len; + + keys = nm_utils_strdict_get_keys (eni_ifaces, TRUE, &len); + for (i = len; i > 0; ) { + i--; + specs = g_slist_prepend (specs, g_strdup_printf (NM_MATCH_SPEC_INTERFACE_NAME_TAG"=%s", keys[i])); + } + return specs; } -/* - * Return a list of device specifications which NetworkManager should not - * manage. Returned list will be freed by the system settings service, and - * each element must be allocated using g_malloc() or its variants. - */ static GSList* get_unmanaged_specs (NMSettingsPlugin *plugin) { - SettingsPluginIfupdown *self = SETTINGS_PLUGIN_IFUPDOWN (plugin); - SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (self); - GSList *specs = NULL; - GHashTableIter iter; - const char *iface; + NMSIfupdownPlugin *self = NMS_IFUPDOWN_PLUGIN (plugin); + NMSIfupdownPluginPrivate *priv = NMS_IFUPDOWN_PLUGIN_GET_PRIVATE (self); + + if (G_UNLIKELY (!priv->initialized)) + initialize (self); if (priv->ifupdown_managed) return NULL; @@ -137,38 +229,46 @@ get_unmanaged_specs (NMSettingsPlugin *plugin) _LOGD ("unmanaged-specs: unmanaged devices count %u", g_hash_table_size (priv->eni_ifaces)); - g_hash_table_iter_init (&iter, priv->eni_ifaces); - while (g_hash_table_iter_next (&iter, (gpointer) &iface, NULL)) - specs = g_slist_append (specs, g_strdup_printf ("interface-name:=%s", iface)); - return specs; + return _unmanaged_specs (priv->eni_ifaces); } /*****************************************************************************/ -static void -initialize (NMSettingsPlugin *plugin) +static GHashTable * +load_eni_ifaces (NMSIfupdownPlugin *self) { - SettingsPluginIfupdown *self = SETTINGS_PLUGIN_IFUPDOWN (plugin); - SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (self); + gs_unref_hashtable GHashTable *eni_ifaces = NULL; gs_unref_hashtable GHashTable *auto_ifaces = NULL; nm_auto_ifparser if_parser *parser = NULL; if_block *block; - GHashTableIter con_iter; - const char *block_name; - NMIfupdownConnection *conn; + StorageData *sd; + + eni_ifaces = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, (GDestroyNotify) _storage_data_destroy); parser = ifparser_parse (ENI_INTERFACES_FILE, 0); c_list_for_each_entry (block, &parser->block_lst_head, block_lst) { - - if (NM_IN_STRSET (block->type, "auto", "allow-hotplug")) { + if (NM_IN_STRSET (block->type, "auto", + "allow-hotplug")) { if (!auto_ifaces) - auto_ifaces = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL); - g_hash_table_add (auto_ifaces, g_strdup (block->name)); - continue; + auto_ifaces = g_hash_table_new (nm_str_hash, g_str_equal); + g_hash_table_add (auto_ifaces, (char *) block->name); } + } + + c_list_for_each_entry (block, &parser->block_lst_head, block_lst) { + + if (NM_IN_STRSET (block->type, "auto", + "allow-hotplug")) + continue; if (nm_streq (block->type, "iface")) { + gs_free_error GError *local = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMSettingsStorage *storage = NULL; + const char *uuid = NULL; + StorageData *sd_repl; + /* Bridge configuration */ if (g_str_has_prefix (block->name, "br")) { /* Try to find bridge ports */ @@ -201,13 +301,13 @@ initialize (NMSettingsPlugin *plugin) if (nm_streq (token, "none")) continue; if (state == 0) { - conn = g_hash_table_lookup (priv->eni_ifaces, block->name); - if (!conn) { + sd = g_hash_table_lookup (eni_ifaces, block->name); + if (!sd) { _LOGD ("parse: adding bridge port \"%s\"", token); - g_hash_table_insert (priv->eni_ifaces, g_strdup (token), NULL); + g_hash_table_insert (eni_ifaces, g_strdup (token), NULL); } else { _LOGD ("parse: adding bridge port \"%s\" (have connection %s)", token, - nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (conn))); + nm_settings_storage_get_uuid (sd->storage)); } } } @@ -219,107 +319,91 @@ initialize (NMSettingsPlugin *plugin) if (nm_streq (block->name, "lo")) continue; - /* Remove any connection for this block that was previously found */ - conn = g_hash_table_lookup (priv->eni_ifaces, block->name); - if (conn) { + sd_repl = g_hash_table_lookup (eni_ifaces, block->name); + if (sd_repl) { + storage = g_steal_pointer (&sd_repl->storage); _LOGD ("parse: replace connection \"%s\" (%s)", block->name, - nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (conn))); - nm_settings_connection_delete (NM_SETTINGS_CONNECTION (conn), NULL); - g_hash_table_remove (priv->eni_ifaces, block->name); + nm_settings_storage_get_uuid (sd_repl->storage)); + g_hash_table_remove (eni_ifaces, block->name); + } + + connection = ifupdown_new_connection_from_if_block (block, + auto_ifaces + && g_hash_table_contains (auto_ifaces, block->name), + &local); + + if (!connection) { + _LOGD ("parse: adding place holder for \"%s\"%s%s%s", + block->name, + NM_PRINT_FMT_QUOTED (local, " (", local->message, ")", "")); + sd = NULL; + } else { + + nmtst_connection_assert_unchanging (connection); + uuid = nm_connection_get_uuid (connection); + + if (!storage) + storage = nm_settings_storage_new (NM_SETTINGS_PLUGIN (self), uuid, NULL); + + sd = g_slice_new (StorageData); + *sd = (StorageData) { + .connection = g_steal_pointer (&connection), + .storage = g_steal_pointer (&storage), + }; + _LOGD ("parse: adding connection \"%s\" (%s)", block->name, uuid); } - /* add the new connection */ - conn = nm_ifupdown_connection_new (block); - if (conn) { - _LOGD ("parse: adding connection \"%s\" (%s)", block->name, - nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (conn))); - } else - _LOGD ("parse: adding place holder for connection \"%s\"", block->name); - g_hash_table_insert (priv->eni_ifaces, g_strdup (block->name), conn); + g_hash_table_replace (eni_ifaces, g_strdup (block->name), sd); continue; } if (nm_streq (block->type, "mapping")) { - conn = g_hash_table_lookup (priv->eni_ifaces, block->name); - if (!conn) { + sd = g_hash_table_lookup (eni_ifaces, block->name); + if (!sd) { _LOGD ("parse: adding mapping \"%s\"", block->name); - g_hash_table_insert (priv->eni_ifaces, g_strdup (block->name), NULL); + g_hash_table_insert (eni_ifaces, g_strdup (block->name), NULL); } else { _LOGD ("parse: adding mapping \"%s\" (have connection %s)", block->name, - nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (conn))); + nm_settings_storage_get_uuid (sd->storage)); } continue; } } - /* Make 'auto' interfaces autoconnect=TRUE */ - g_hash_table_iter_init (&con_iter, priv->eni_ifaces); - while (g_hash_table_iter_next (&con_iter, (gpointer) &block_name, (gpointer) &conn)) { - NMSettingConnection *setting; + nm_clear_pointer (&auto_ifaces, g_hash_table_destroy); - if ( !conn - || !auto_ifaces - || !g_hash_table_contains (auto_ifaces, block_name)) - continue; - - /* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */ - setting = nm_connection_get_setting_connection (nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (conn))); - g_object_set (setting, NM_SETTING_CONNECTION_AUTOCONNECT, TRUE, NULL); - } - - /* Check the config file to find out whether to manage interfaces */ - priv->ifupdown_managed = nm_config_data_get_value_boolean (NM_CONFIG_GET_DATA_ORIG, - NM_CONFIG_KEYFILE_GROUP_IFUPDOWN, - NM_CONFIG_KEYFILE_KEY_IFUPDOWN_MANAGED, - !IFUPDOWN_UNMANAGE_WELL_KNOWN_DEFAULT); - _LOGI ("management mode: %s", priv->ifupdown_managed ? "managed" : "unmanaged"); - - /* Now if we're running in managed mode, let NM know there are new connections */ - if (priv->ifupdown_managed) { - GHashTableIter iter; - - g_hash_table_iter_init (&iter, priv->eni_ifaces); - while (g_hash_table_iter_next (&iter, NULL, (gpointer) &conn)) { - if (conn) { - _nm_settings_plugin_emit_signal_connection_added (NM_SETTINGS_PLUGIN (self), - NM_SETTINGS_CONNECTION (conn)); - } - } - } + return g_steal_pointer (&eni_ifaces); } /*****************************************************************************/ static void -settings_plugin_ifupdown_init (SettingsPluginIfupdown *self) +nms_ifupdown_plugin_init (NMSIfupdownPlugin *self) { - SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (self); - - priv->eni_ifaces = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_object_unref); } static void dispose (GObject *object) { - SettingsPluginIfupdown *plugin = SETTINGS_PLUGIN_IFUPDOWN (object); - SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (plugin); + NMSIfupdownPlugin *plugin = NMS_IFUPDOWN_PLUGIN (object); + NMSIfupdownPluginPrivate *priv = NMS_IFUPDOWN_PLUGIN_GET_PRIVATE (plugin); g_clear_pointer (&priv->eni_ifaces, g_hash_table_destroy); - G_OBJECT_CLASS (settings_plugin_ifupdown_parent_class)->dispose (object); + G_OBJECT_CLASS (nms_ifupdown_plugin_parent_class)->dispose (object); } static void -settings_plugin_ifupdown_class_init (SettingsPluginIfupdownClass *klass) +nms_ifupdown_plugin_class_init (NMSIfupdownPluginClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); NMSettingsPluginClass *plugin_class = NM_SETTINGS_PLUGIN_CLASS (klass); object_class->dispose = dispose; - plugin_class->initialize = initialize; - plugin_class->get_connections = get_connections; + plugin_class->plugin_name = "ifupdown"; + plugin_class->reload_connections = reload_connections; plugin_class->get_unmanaged_specs = get_unmanaged_specs; } @@ -328,5 +412,5 @@ settings_plugin_ifupdown_class_init (SettingsPluginIfupdownClass *klass) G_MODULE_EXPORT NMSettingsPlugin * nm_settings_plugin_factory (void) { - return NM_SETTINGS_PLUGIN (g_object_ref (settings_plugin_ifupdown_get ())); + return g_object_new (NMS_TYPE_IFUPDOWN_PLUGIN, NULL); } diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.h b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.h index c2a3639c..10ea2be4 100644 --- a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.h +++ b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.h @@ -1,5 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ - /* NetworkManager system settings service (ifupdown) * * Alexander Sack <asac@ubuntu.com> @@ -21,21 +19,21 @@ * (C) Copyright 2008 Canonical Ltd. */ -#ifndef _PLUGIN_H_ -#define _PLUGIN_H_ +#ifndef __NMS_IFUPDOWN_PLUGIN_H__ +#define __NMS_IFUPDOWN_PLUGIN_H__ #define PLUGIN_NAME "ifupdown" -#define SETTINGS_TYPE_PLUGIN_IFUPDOWN (settings_plugin_ifupdown_get_type ()) -#define SETTINGS_PLUGIN_IFUPDOWN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SETTINGS_TYPE_PLUGIN_IFUPDOWN, SettingsPluginIfupdown)) -#define SETTINGS_PLUGIN_IFUPDOWN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SETTINGS_TYPE_PLUGIN_IFUPDOWN, SettingsPluginIfupdownClass)) -#define SETTINGS_IS_PLUGIN_IFUPDOWN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SETTINGS_TYPE_PLUGIN_IFUPDOWN)) -#define SETTINGS_IS_PLUGIN_IFUPDOWN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SETTINGS_TYPE_PLUGIN_IFUPDOWN)) -#define SETTINGS_PLUGIN_IFUPDOWN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SETTINGS_TYPE_PLUGIN_IFUPDOWN, SettingsPluginIfupdownClass)) +#define NMS_TYPE_IFUPDOWN_PLUGIN (nms_ifupdown_plugin_get_type ()) +#define NMS_IFUPDOWN_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMS_TYPE_IFUPDOWN_PLUGIN, NMSIfupdownPlugin)) +#define NMS_IFUPDOWN_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMS_TYPE_IFUPDOWN_PLUGIN, NMSIfupdownPluginClass)) +#define NMS_IS_IFUPDOWN_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMS_TYPE_IFUPDOWN_PLUGIN)) +#define NMS_IS_IFUPDOWN_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMS_TYPE_IFUPDOWN_PLUGIN)) +#define NMS_IFUPDOWN_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMS_TYPE_IFUPDOWN_PLUGIN, NMSIfupdownPluginClass)) -typedef struct _SettingsPluginIfupdown SettingsPluginIfupdown; -typedef struct _SettingsPluginIfupdownClass SettingsPluginIfupdownClass; +typedef struct _NMSIfupdownPlugin NMSIfupdownPlugin; +typedef struct _NMSIfupdownPluginClass NMSIfupdownPluginClass; -GType settings_plugin_ifupdown_get_type (void); +GType nms_ifupdown_plugin_get_type (void); -#endif /* _PLUGIN_H_ */ +#endif /* __NMS_IFUPDOWN_PLUGIN_H__ */ diff --git a/src/settings/plugins/ifupdown/tests/test-ifupdown.c b/src/settings/plugins/ifupdown/tests/test-ifupdown.c index 674cb19c..4adcf085 100644 --- a/src/settings/plugins/ifupdown/tests/test-ifupdown.c +++ b/src/settings/plugins/ifupdown/tests/test-ifupdown.c @@ -1,4 +1,3 @@ -/* -*- 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 @@ -31,6 +30,29 @@ /*****************************************************************************/ +#define _connection_from_if_block(block) \ + ({ \ + NMConnection *_con; \ + if_block *_block = (block); \ + GError *_local = NULL; \ + \ + g_assert (_block); \ + _con = ifupdown_new_connection_from_if_block (_block, FALSE, &_local); \ + nmtst_assert_success (NM_IS_CONNECTION (_con), _local); \ + nmtst_assert_connection_verifies_without_normalization (_con); \ + _con; \ + }) + +#define _connection_first_from_parser(parser) \ + ({ \ + if_parser *_parser = (parser); \ + \ + g_assert (_parser); \ + _connection_from_if_block (ifparser_getfirst (_parser)); \ + }) + +/*****************************************************************************/ + typedef struct { char *key; char *data; @@ -453,26 +475,14 @@ test16_missing_newline (void) static void test17_read_static_ipv4 (void) { - NMConnection *connection; + gs_unref_object NMConnection *connection = NULL; NMSettingConnection *s_con; NMSettingIPConfig *s_ip4; NMSettingWired *s_wired; - GError *error = NULL; - gboolean success; NMIPAddress *ip4_addr; - if_block *block = NULL; nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test17-wired-static-verify-ip4"); - block = ifparser_getfirst (parser); - connection = nm_simple_connection_new(); - g_assert (connection); - - ifupdown_update_connection_from_if_block (connection, block, &error); - g_assert_no_error (error); - - success = nm_connection_verify (connection, &error); - g_assert_no_error (error); - g_assert (success); + connection = _connection_first_from_parser (parser); /* ===== CONNECTION SETTING ===== */ s_con = nm_connection_get_setting_connection (connection); @@ -501,32 +511,19 @@ test17_read_static_ipv4 (void) g_assert_cmpint (nm_setting_ip_config_get_num_dns_searches (s_ip4), ==, 2); g_assert_cmpstr (nm_setting_ip_config_get_dns_search (s_ip4, 0), ==, "example.com"); g_assert_cmpstr (nm_setting_ip_config_get_dns_search (s_ip4, 1), ==, "foo.example.com"); - - g_object_unref (connection); } static void test18_read_static_ipv6 (void) { - NMConnection *connection; + gs_unref_object NMConnection *connection = NULL; NMSettingConnection *s_con; NMSettingIPConfig *s_ip6; NMSettingWired *s_wired; - GError *error = NULL; - gboolean success; NMIPAddress *ip6_addr; - if_block *block = NULL; nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test18-wired-static-verify-ip6"); - block = ifparser_getfirst (parser); - connection = nm_simple_connection_new(); - g_assert (connection); - ifupdown_update_connection_from_if_block (connection, block, &error); - g_assert_no_error (error); - - success = nm_connection_verify (connection, &error); - g_assert_no_error (error); - g_assert (success); + connection = _connection_first_from_parser (parser); /* ===== CONNECTION SETTING ===== */ s_con = nm_connection_get_setting_connection (connection); @@ -555,30 +552,17 @@ test18_read_static_ipv6 (void) g_assert_cmpint (nm_setting_ip_config_get_num_dns_searches (s_ip6), ==, 2); g_assert_cmpstr (nm_setting_ip_config_get_dns_search (s_ip6, 0), ==, "example.com"); g_assert_cmpstr (nm_setting_ip_config_get_dns_search (s_ip6, 1), ==, "foo.example.com"); - - g_object_unref (connection); } static void test19_read_static_ipv4_plen (void) { - NMConnection *connection; + gs_unref_object NMConnection *connection = NULL; NMSettingIPConfig *s_ip4; - GError *error = NULL; NMIPAddress *ip4_addr; - if_block *block = NULL; - gboolean success; nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test19-wired-static-verify-ip4-plen"); - block = ifparser_getfirst (parser); - connection = nm_simple_connection_new(); - g_assert (connection); - ifupdown_update_connection_from_if_block (connection, block, &error); - g_assert_no_error (error); - - success = nm_connection_verify (connection, &error); - g_assert_no_error (error); - g_assert (success); + connection = _connection_first_from_parser (parser); /* ===== IPv4 SETTING ===== */ s_ip4 = nm_connection_get_setting_ip4_config (connection); @@ -589,8 +573,6 @@ test19_read_static_ipv4_plen (void) g_assert (ip4_addr != NULL); g_assert_cmpstr (nm_ip_address_get_address (ip4_addr), ==, "10.0.0.3"); g_assert_cmpint (nm_ip_address_get_prefix (ip4_addr), ==, 8); - - g_object_unref (connection); } static void @@ -671,4 +653,3 @@ main (int argc, char **argv) return g_test_run (); } - diff --git a/src/settings/plugins/keyfile/nms-keyfile-connection.c b/src/settings/plugins/keyfile/nms-keyfile-connection.c deleted file mode 100644 index 3b362978..00000000 --- a/src/settings/plugins/keyfile/nms-keyfile-connection.c +++ /dev/null @@ -1,186 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager system settings service - keyfile plugin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 2008 Novell, Inc. - * Copyright (C) 2008 - 2012 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nms-keyfile-connection.h" - -#include <glib/gstdio.h> - -#include "nm-dbus-interface.h" -#include "nm-setting-connection.h" -#include "nm-utils.h" - -#include "settings/nm-settings-plugin.h" - -#include "nms-keyfile-reader.h" -#include "nms-keyfile-writer.h" -#include "nms-keyfile-utils.h" - -/*****************************************************************************/ - -struct _NMSKeyfileConnection { - NMSettingsConnection parent; -}; - -struct _NMSKeyfileConnectionClass { - NMSettingsConnectionClass parent; -}; - -G_DEFINE_TYPE (NMSKeyfileConnection, nms_keyfile_connection, NM_TYPE_SETTINGS_CONNECTION) - -/*****************************************************************************/ - -static gboolean -commit_changes (NMSettingsConnection *connection, - NMConnection *new_connection, - NMSettingsConnectionCommitReason commit_reason, - NMConnection **out_reread_connection, - char **out_logmsg_change, - GError **error) -{ - gs_free char *path = NULL; - gs_unref_object NMConnection *reread = NULL; - gboolean reread_same = FALSE; - - nm_assert (out_reread_connection && !*out_reread_connection); - nm_assert (!out_logmsg_change || !*out_logmsg_change); - - if (!nms_keyfile_writer_connection (new_connection, - TRUE, - nm_settings_connection_get_filename (connection), - NM_FLAGS_ALL (commit_reason, NM_SETTINGS_CONNECTION_COMMIT_REASON_USER_ACTION - | NM_SETTINGS_CONNECTION_COMMIT_REASON_ID_CHANGED), - &path, - &reread, - &reread_same, - error)) - return FALSE; - - if (!nm_streq0 (path, nm_settings_connection_get_filename (connection))) { - gs_free char *old_path = g_strdup (nm_settings_connection_get_filename (connection)); - - nm_settings_connection_set_filename (connection, path); - if (old_path) { - NM_SET_OUT (out_logmsg_change, - g_strdup_printf ("keyfile: update "NMS_KEYFILE_CONNECTION_LOG_FMT" and rename from \"%s\"", - NMS_KEYFILE_CONNECTION_LOG_ARG (connection), - old_path)); - } else { - NM_SET_OUT (out_logmsg_change, - g_strdup_printf ("keyfile: update "NMS_KEYFILE_CONNECTION_LOG_FMT" and persist connection", - NMS_KEYFILE_CONNECTION_LOG_ARG (connection))); - } - } else { - NM_SET_OUT (out_logmsg_change, - g_strdup_printf ("keyfile: update "NMS_KEYFILE_CONNECTION_LOG_FMT, - NMS_KEYFILE_CONNECTION_LOG_ARG (connection))); - } - - if (reread && !reread_same) - *out_reread_connection = g_steal_pointer (&reread); - - return TRUE; -} - -static gboolean -delete (NMSettingsConnection *connection, - GError **error) -{ - const char *path; - - path = nm_settings_connection_get_filename (connection); - if (path) - g_unlink (path); - return TRUE; -} - -/*****************************************************************************/ - -static void -nms_keyfile_connection_init (NMSKeyfileConnection *connection) -{ -} - -NMSKeyfileConnection * -nms_keyfile_connection_new (NMConnection *source, - const char *full_path, - const char *profile_dir, - GError **error) -{ - GObject *object; - NMConnection *tmp; - const char *uuid; - gboolean update_unsaved = TRUE; - - nm_assert (source || full_path); - nm_assert (!full_path || full_path[0] == '/'); - nm_assert (!profile_dir || profile_dir[0] == '/'); - - /* If we're given a connection already, prefer that instead of re-reading */ - if (source) - tmp = g_object_ref (source); - else { - tmp = nms_keyfile_reader_from_file (full_path, profile_dir, error); - if (!tmp) - return NULL; - - uuid = nm_connection_get_uuid (tmp); - if (!uuid) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Connection in file %s had no UUID", full_path); - g_object_unref (tmp); - return NULL; - } - - /* If we just read the connection from disk, it's clearly not Unsaved */ - update_unsaved = FALSE; - } - - object = g_object_new (NMS_TYPE_KEYFILE_CONNECTION, - NM_SETTINGS_CONNECTION_FILENAME, full_path, - NULL); - - /* Update our settings with what was read from the file */ - if (!nm_settings_connection_update (NM_SETTINGS_CONNECTION (object), - tmp, - update_unsaved - ? NM_SETTINGS_CONNECTION_PERSIST_MODE_UNSAVED - : NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP_SAVED, - NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, - NULL, - error)) { - g_object_unref (object); - object = NULL; - } - - g_object_unref (tmp); - return (NMSKeyfileConnection *) object; -} - -static void -nms_keyfile_connection_class_init (NMSKeyfileConnectionClass *keyfile_connection_class) -{ - NMSettingsConnectionClass *settings_class = NM_SETTINGS_CONNECTION_CLASS (keyfile_connection_class); - - settings_class->commit_changes = commit_changes; - settings_class->delete = delete; -} diff --git a/src/settings/plugins/keyfile/nms-keyfile-connection.h b/src/settings/plugins/keyfile/nms-keyfile-connection.h deleted file mode 100644 index 0773ced0..00000000 --- a/src/settings/plugins/keyfile/nms-keyfile-connection.h +++ /dev/null @@ -1,44 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager system settings service - keyfile plugin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 2008 Novell, Inc. - * Copyright (C) 2008 - 2012 Red Hat, Inc. - */ - -#ifndef __NMS_KEYFILE_CONNECTION_H__ -#define __NMS_KEYFILE_CONNECTION_H__ - -#include "settings/nm-settings-connection.h" - -#define NMS_TYPE_KEYFILE_CONNECTION (nms_keyfile_connection_get_type ()) -#define NMS_KEYFILE_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMS_TYPE_KEYFILE_CONNECTION, NMSKeyfileConnection)) -#define NMS_KEYFILE_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMS_TYPE_KEYFILE_CONNECTION, NMSKeyfileConnectionClass)) -#define NMS_IS_KEYFILE_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMS_TYPE_KEYFILE_CONNECTION)) -#define NMS_IS_KEYFILE_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMS_TYPE_KEYFILE_CONNECTION)) -#define NMS_KEYFILE_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMS_TYPE_KEYFILE_CONNECTION, NMSKeyfileConnectionClass)) - -typedef struct _NMSKeyfileConnection NMSKeyfileConnection; -typedef struct _NMSKeyfileConnectionClass NMSKeyfileConnectionClass; - -GType nms_keyfile_connection_get_type (void); - -NMSKeyfileConnection *nms_keyfile_connection_new (NMConnection *source, - const char *full_path, - const char *profile_dir, - GError **error); - -#endif /* __NMS_KEYFILE_CONNECTION_H__ */ diff --git a/src/settings/plugins/keyfile/nms-keyfile-plugin.c b/src/settings/plugins/keyfile/nms-keyfile-plugin.c index c13cc1ff..fbe70ef4 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-plugin.c +++ b/src/settings/plugins/keyfile/nms-keyfile-plugin.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service - keyfile plugin * * This program is free software; you can redistribute it and/or modify @@ -16,7 +15,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Copyright (C) 2008 Novell, Inc. - * Copyright (C) 2008 - 2013 Red Hat, Inc. + * Copyright (C) 2008 - 2018 Red Hat, Inc. */ #include "nm-default.h" @@ -26,7 +25,11 @@ #include <sys/stat.h> #include <unistd.h> #include <sys/types.h> -#include <glib/gstdio.h> +#include <sys/time.h> + +#include "nm-std-aux/c-list-util.h" +#include "nm-glib-aux/nm-c-list.h" +#include "nm-glib-aux/nm-io-utils.h" #include "nm-connection.h" #include "nm-setting.h" @@ -36,22 +39,42 @@ #include "nm-core-internal.h" #include "nm-keyfile-internal.h" +#include "systemd/nm-sd-utils-shared.h" + #include "settings/nm-settings-plugin.h" +#include "settings/nm-settings-storage.h" +#include "settings/nm-settings-utils.h" -#include "nms-keyfile-connection.h" +#include "nms-keyfile-storage.h" #include "nms-keyfile-writer.h" +#include "nms-keyfile-reader.h" #include "nms-keyfile-utils.h" /*****************************************************************************/ typedef struct { - GHashTable *connections; /* uuid::connection */ - - gboolean initialized; - GFileMonitor *monitor; - gulong monitor_id; NMConfig *config; + + /* there can/could be multiple read-only directories. For example, one + * could set dirname_libs to + * - /usr/lib/NetworkManager/profiles/ + * - /etc/NetworkManager/system-connections + * and leave dirname_etc unset. In this case, there would be multiple + * read-only directories. + * + * Directories that come later have higher priority and shadow profiles + * from earlier directories. + * + * Currently, this is only an array with zero or one elements. It could be + * easily extended to support multiple read-only directories. + */ + char *dirname_libs[2]; + char *dirname_etc; + char *dirname_run; + + NMSettUtilStorages storages; + } NMSKeyfilePluginPrivate; struct _NMSKeyfilePlugin { @@ -65,7 +88,7 @@ struct _NMSKeyfilePluginClass { G_DEFINE_TYPE (NMSKeyfilePlugin, nms_keyfile_plugin, NM_TYPE_SETTINGS_PLUGIN) -#define NMS_KEYFILE_PLUGIN_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMSKeyfilePlugin, NMS_IS_KEYFILE_PLUGIN) +#define NMS_KEYFILE_PLUGIN_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMSKeyfilePlugin, NMS_IS_KEYFILE_PLUGIN, NMSettingsPlugin) /*****************************************************************************/ @@ -79,494 +102,1112 @@ G_DEFINE_TYPE (NMSKeyfilePlugin, nms_keyfile_plugin, NM_TYPE_SETTINGS_PLUGIN) /*****************************************************************************/ -static void -connection_removed_cb (NMSettingsConnection *sett_conn, NMSKeyfilePlugin *self) +static const char * +_extra_flags_to_string (char *str, gsize str_len, gboolean is_nm_generated, gboolean is_volatile) { - g_hash_table_remove (NMS_KEYFILE_PLUGIN_GET_PRIVATE (self)->connections, - nm_settings_connection_get_uuid (sett_conn)); + const char *str0 = str; + + if ( !is_nm_generated + && !is_volatile) + nm_utils_strbuf_append_str (&str, &str_len, ""); + else { + nm_utils_strbuf_append_str (&str, &str_len, " ("); + if (is_nm_generated) { + nm_utils_strbuf_append_str (&str, &str_len, "nm-generated"); + if (is_volatile) + nm_utils_strbuf_append_c (&str, &str_len, ','); + } + if (is_volatile) + nm_utils_strbuf_append_str (&str, &str_len, "volatile"); + nm_utils_strbuf_append_c (&str, &str_len, ')'); + } + + return str0; } -/* Monitoring */ +static gboolean +_ignore_filename (NMSKeyfileStorageType storage_type, + const char *filename) +{ + /* for backward-compatibility, we don't require an extension for + * files under "/etc/...". */ + return nm_keyfile_utils_ignore_filename (filename, + (storage_type != NMS_KEYFILE_STORAGE_TYPE_ETC)); +} -static void -remove_connection (NMSKeyfilePlugin *self, NMSKeyfileConnection *connection) +static const char * +_get_plugin_dir (NMSKeyfilePluginPrivate *priv) { - gboolean removed; + /* the plugin dir is only needed to generate connection.uuid value via + * nm_keyfile_read_ensure_uuid(). This is either the configured /etc + * directory, of the compile-time default (in case the /etc directory + * is disabled). */ + return priv->dirname_etc ?: NM_KEYFILE_PATH_NAME_ETC_DEFAULT; +} - g_return_if_fail (connection != NULL); +static gboolean +_path_detect_storage_type (const char *full_filename, + const char *const*dirname_libs, + const char *dirname_etc, + const char *dirname_run, + NMSKeyfileStorageType *out_storage_type, + const char **out_dirname, + const char **out_filename, + gboolean *out_is_nmmeta_file, + gboolean *out_failed_due_to_invalid_filename) +{ + NMSKeyfileStorageType storage_type; + const char *filename = NULL; + const char *dirname = NULL; + guint i; + gboolean is_nmmeta_file = FALSE; + + NM_SET_OUT (out_failed_due_to_invalid_filename, FALSE); + + if (full_filename[0] != '/') + return FALSE; + + if ( dirname_run + && (filename = nm_utils_file_is_in_path (full_filename, dirname_run))) { + storage_type = NMS_KEYFILE_STORAGE_TYPE_RUN; + dirname = dirname_run; + } else if ( dirname_etc + && (filename = nm_utils_file_is_in_path (full_filename, dirname_etc))) { + storage_type = NMS_KEYFILE_STORAGE_TYPE_ETC; + dirname = dirname_etc; + } else { + for (i = 0; dirname_libs && dirname_libs[i]; i++) { + if ((filename = nm_utils_file_is_in_path (full_filename, dirname_libs[i]))) { + storage_type = NMS_KEYFILE_STORAGE_TYPE_LIB (i); + dirname = dirname_libs[i]; + break; + } + } + if (!dirname) + return FALSE; + } + + if (_ignore_filename (storage_type, filename)) { - _LOGI ("removed " NMS_KEYFILE_CONNECTION_LOG_FMT, NMS_KEYFILE_CONNECTION_LOG_ARG (connection)); + /* we accept nmmeta files, but only in /etc and /run directories. */ - /* Removing from the hash table should drop the last reference */ - g_object_ref (connection); - g_signal_handlers_disconnect_by_func (connection, connection_removed_cb, self); - removed = g_hash_table_remove (NMS_KEYFILE_PLUGIN_GET_PRIVATE (self)->connections, - nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (connection))); - nm_settings_connection_signal_remove (NM_SETTINGS_CONNECTION (connection)); - g_object_unref (connection); + if ( !NM_IN_SET (storage_type, NMS_KEYFILE_STORAGE_TYPE_RUN, + NMS_KEYFILE_STORAGE_TYPE_ETC) + || !nms_keyfile_nmmeta_check_filename (filename, NULL)) { + NM_SET_OUT (out_failed_due_to_invalid_filename, TRUE); + return FALSE; + } + + is_nmmeta_file = TRUE; + } - g_return_if_fail (removed); + NM_SET_OUT (out_storage_type, storage_type); + NM_SET_OUT (out_dirname, dirname); + NM_SET_OUT (out_filename, filename); + NM_SET_OUT (out_is_nmmeta_file, is_nmmeta_file); + return TRUE; } -static NMSKeyfileConnection * -find_by_path (NMSKeyfilePlugin *self, const char *path) +/*****************************************************************************/ + +static NMConnection * +_read_from_file (const char *full_filename, + const char *plugin_dir, + struct stat *out_stat, + NMTernary *out_is_nm_generated, + NMTernary *out_is_volatile, + char **out_shadowed_storage, + NMTernary *out_shadowed_owned, + GError **error) { - NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); - GHashTableIter iter; - NMSettingsConnection *candidate = NULL; + NMConnection *connection; - g_return_val_if_fail (path != NULL, NULL); + nm_assert (full_filename && full_filename[0] == '/'); - g_hash_table_iter_init (&iter, priv->connections); - while (g_hash_table_iter_next (&iter, NULL, (gpointer) &candidate)) { - if (g_strcmp0 (path, nm_settings_connection_get_filename (candidate)) == 0) - return NMS_KEYFILE_CONNECTION (candidate); - } - return NULL; + connection = nms_keyfile_reader_from_file (full_filename, + plugin_dir, + out_stat, + out_is_nm_generated, + out_is_volatile, + out_shadowed_storage, + out_shadowed_owned, + error); + + nm_assert (!connection || (_nm_connection_verify (connection, NULL) == NM_SETTING_VERIFY_SUCCESS)); + nm_assert (!connection || nm_utils_is_uuid (nm_connection_get_uuid (connection))); + + return connection; } -/* update_connection: - * @self: the plugin instance - * @source: if %NULL, this re-reads the connection from @full_path - * and updates it. When passing @source, this adds a connection from - * memory. - * @full_path: the filename of the keyfile to be loaded - * @connection: an existing connection that might be updated. - * If given, @connection must be an existing connection that is currently - * owned by the plugin. - * @protect_existing_connection: if %TRUE, and !@connection, we don't allow updating - * an existing connection with the same UUID. - * If %TRUE and @connection, allow updating only if the reload would modify - * @connection (without changing its UUID) or if we would create a new connection. - * In other words, if this parameter is %TRUE, we only allow creating a - * new connection (with an unseen UUID) or updating the passed in @connection - * (whereas the UUID cannot change). - * Note, that this allows for @connection to be replaced by a new connection. - * @protected_connections: (allow-none): if given, we only update an - * existing connection if it is not contained in this hash. - * @error: error in case of failure - * - * Loads a connection from file @full_path. This can both be used to - * load a connection initially or to update an existing connection. - * - * If you pass in an existing connection and the reloaded file happens - * to have a different UUID, the connection is deleted. - * Beware, that means that after the function, you have a dangling pointer - * if the returned connection is different from @connection. - * - * Returns: the updated connection. - * */ -static NMSKeyfileConnection * -update_connection (NMSKeyfilePlugin *self, - NMConnection *source, - const char *full_path, - NMSKeyfileConnection *connection, - gboolean protect_existing_connection, - GHashTable *protected_connections, - GError **error) +/*****************************************************************************/ + +static void +_nm_assert_storage (gpointer plugin /* NMSKeyfilePlugin */, + gpointer storage /* NMSKeyfileStorage */, + gboolean tracked) { - NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); - NMSKeyfileConnection *connection_new; - NMSKeyfileConnection *connection_by_uuid; - GError *local = NULL; +#if NM_MORE_ASSERTS + NMSettUtilStorageByUuidHead *sbuh; const char *uuid; - g_return_val_if_fail (!source || NM_IS_CONNECTION (source), NULL); - g_return_val_if_fail (full_path || source, NULL); + nm_assert (!plugin || NMS_IS_KEYFILE_PLUGIN (plugin)); + nm_assert (NMS_IS_KEYFILE_STORAGE (storage)); + nm_assert (!plugin || plugin == nm_settings_storage_get_plugin (storage)); - if (full_path) - _LOGD ("loading from file \"%s\"...", full_path); + nm_assert (({ + const char *f = nms_keyfile_storage_get_filename (storage); + f && f[0] == '/'; + })); - 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; - } + uuid = nms_keyfile_storage_get_uuid (storage); - connection_new = nms_keyfile_connection_new (source, full_path, nms_keyfile_utils_get_path (), &local); - if (!connection_new) { - /* Error; remove the connection */ - if (source) - _LOGW ("error creating connection %s: %s", nm_connection_get_uuid (source), local->message); - else - _LOGW ("error loading connection from file %s: %s", full_path, local->message); - if ( connection - && !protect_existing_connection - && (!protected_connections || !g_hash_table_contains (protected_connections, connection))) - remove_connection (self, connection); - g_propagate_error (error, local); - return NULL; + nm_assert (nm_utils_is_uuid (uuid)); + + nm_assert ( ((NMSKeyfileStorage *) storage)->is_meta_data + || !(((NMSKeyfileStorage *) storage)->u.conn_data.connection) + || ( NM_IS_CONNECTION ((((NMSKeyfileStorage *) storage)->u.conn_data.connection)) + && nm_streq0 (uuid, nm_connection_get_uuid ((((NMSKeyfileStorage *) storage)->u.conn_data.connection))))); + + nm_assert ( !tracked + || !plugin + || c_list_contains (&NMS_KEYFILE_PLUGIN_GET_PRIVATE (plugin)->storages._storage_lst_head, + &NMS_KEYFILE_STORAGE (storage)->parent._storage_lst)); + + nm_assert ( !tracked + || !plugin + || storage == g_hash_table_lookup (NMS_KEYFILE_PLUGIN_GET_PRIVATE (plugin)->storages.idx_by_filename, + nms_keyfile_storage_get_filename (storage))); + + if ( tracked + && plugin) { + sbuh = g_hash_table_lookup (NMS_KEYFILE_PLUGIN_GET_PRIVATE (plugin)->storages.idx_by_uuid, &uuid); + nm_assert (sbuh); + nm_assert (c_list_contains (&sbuh->_storage_by_uuid_lst_head, &((NMSKeyfileStorage *) storage)->parent._storage_by_uuid_lst)); } +#endif +} - uuid = nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (connection_new)); - connection_by_uuid = g_hash_table_lookup (priv->connections, uuid); +/*****************************************************************************/ - if ( connection - && connection != connection_by_uuid) { +static NMSKeyfileStorage * +_load_file (NMSKeyfilePlugin *self, + const char *dirname, + const char *filename, + NMSKeyfileStorageType storage_type, + GError **error) +{ + NMSKeyfilePluginPrivate *priv; + gs_unref_object NMConnection *connection = NULL; + NMTernary is_volatile_opt; + NMTernary is_nm_generated_opt; + NMTernary shadowed_owned_opt; + gs_free char *shadowed_storage = NULL; + gs_free_error GError *local = NULL; + gs_free char *full_filename = NULL; + struct stat st; - if ( (protect_existing_connection && connection_by_uuid != NULL) - || (protected_connections && g_hash_table_contains (protected_connections, connection))) { - NMSKeyfileConnection *conflicting = (protect_existing_connection && connection_by_uuid != NULL) ? connection_by_uuid : connection; + if (_ignore_filename (storage_type, filename)) { + gs_free char *nmmeta = NULL; + gs_free char *loaded_path = NULL; + gs_free char *shadowed_storage_filename = NULL; - if (source) - _LOGW ("cannot update protected "NMS_KEYFILE_CONNECTION_LOG_FMT" connection due to conflicting UUID %s", NMS_KEYFILE_CONNECTION_LOG_ARG (conflicting), uuid); + if (!nms_keyfile_nmmeta_check_filename (filename, NULL)) { + if (error) + nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, "skip due to invalid filename"); + else + _LOGT ("load: \"%s/%s\": skip file due to invalid filename", dirname, filename); + return NULL; + } + if (!nms_keyfile_nmmeta_read (dirname, + filename, + &full_filename, + &nmmeta, + &loaded_path, + &shadowed_storage_filename, + NULL)) { + if (error) + nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, "skip unreadable nmmeta file"); else - _LOGW ("cannot load %s due to conflicting UUID for "NMS_KEYFILE_CONNECTION_LOG_FMT, full_path, NMS_KEYFILE_CONNECTION_LOG_ARG (conflicting)); - g_object_unref (connection_new); - g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Cannot update protected connection due to conflicting UUID"); + _LOGT ("load: \"%s/%s\": skip unreadable nmmeta file", dirname, filename); + return NULL; + } + nm_assert (loaded_path); + if (!NM_IN_SET (storage_type, NMS_KEYFILE_STORAGE_TYPE_RUN, + NMS_KEYFILE_STORAGE_TYPE_ETC)) { + if (error) + nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, "skip nmmeta file from read-only directory"); + else + _LOGT ("load: \"%s/%s\": skip nmmeta file from read-only directory", dirname, filename); + return NULL; + } + if (!nm_streq (loaded_path, NM_KEYFILE_PATH_NMMETA_SYMLINK_NULL)) { + if (error) + nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, "skip nmmeta file not symlinking %s", NM_KEYFILE_PATH_NMMETA_SYMLINK_NULL); + else + _LOGT ("load: \"%s/%s\": skip nmmeta file not symlinking to %s", dirname, filename, NM_KEYFILE_PATH_NMMETA_SYMLINK_NULL); return NULL; } - /* The new connection has a different UUID then the original one. - * Remove @connection. */ - remove_connection (self, connection); + return nms_keyfile_storage_new_tombstone (self, + nmmeta, + full_filename, + storage_type, + shadowed_storage_filename); } - if ( connection_by_uuid - && ( (!connection && protect_existing_connection) - || (protected_connections && g_hash_table_contains (protected_connections, connection_by_uuid)))) { - if (source) - _LOGW ("cannot update connection due to conflicting UUID for "NMS_KEYFILE_CONNECTION_LOG_FMT, NMS_KEYFILE_CONNECTION_LOG_ARG (connection_by_uuid)); + full_filename = g_build_filename (dirname, filename, NULL); + + priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); + + connection = _read_from_file (full_filename, + _get_plugin_dir (priv), + &st, + &is_nm_generated_opt, + &is_volatile_opt, + &shadowed_storage, + &shadowed_owned_opt, + &local); + if (!connection) { + if (error) + g_propagate_error (error, local); else - _LOGW ("cannot load %s due to conflicting UUID for "NMS_KEYFILE_CONNECTION_LOG_FMT, full_path, NMS_KEYFILE_CONNECTION_LOG_ARG (connection_by_uuid)); - g_object_unref (connection_new); - g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "Skip updating protected connection during reload"); + _LOGW ("load: \"%s\": failed to load connection: %s", full_filename, local->message); return NULL; } - if (connection_by_uuid) { - const char *old_path; - - old_path = nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection_by_uuid)); - - if (nm_connection_compare (nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection_by_uuid)), - nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection_new)), - NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS | - NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)) { - /* Nothing to do... except updating the path. */ - if (old_path && g_strcmp0 (old_path, full_path) != 0) - _LOGI ("rename \"%s\" to "NMS_KEYFILE_CONNECTION_LOG_FMT" without other changes", old_path, NMS_KEYFILE_CONNECTION_LOG_ARG (connection_new)); - } else { - /* An existing connection changed. */ - if (source) - _LOGI ("update "NMS_KEYFILE_CONNECTION_LOG_FMT" from %s", NMS_KEYFILE_CONNECTION_LOG_ARG (connection_new), NMS_KEYFILE_CONNECTION_LOG_PATH (old_path)); - else if (!g_strcmp0 (old_path, nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection_new)))) - _LOGI ("update "NMS_KEYFILE_CONNECTION_LOG_FMT, NMS_KEYFILE_CONNECTION_LOG_ARG (connection_new)); - else if (old_path) - _LOGI ("rename \"%s\" to "NMS_KEYFILE_CONNECTION_LOG_FMT, old_path, NMS_KEYFILE_CONNECTION_LOG_ARG (connection_new)); - else - _LOGI ("update and persist "NMS_KEYFILE_CONNECTION_LOG_FMT, NMS_KEYFILE_CONNECTION_LOG_ARG (connection_new)); - - if (!nm_settings_connection_update (NM_SETTINGS_CONNECTION (connection_by_uuid), - nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection_new)), - NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP_SAVED, - NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE, - "keyfile-update", - &local)) { - /* Shouldn't ever get here as 'connection_new' was verified by the reader already - * and the UUID did not change. */ - g_assert_not_reached (); - } - g_assert_no_error (local); - } - nm_settings_connection_set_filename (NM_SETTINGS_CONNECTION (connection_by_uuid), full_path); - g_object_unref (connection_new); - return connection_by_uuid; - } else { - if (source) - _LOGI ("add connection "NMS_KEYFILE_CONNECTION_LOG_FMT, NMS_KEYFILE_CONNECTION_LOG_ARG (connection_new)); - else - _LOGI ("new connection "NMS_KEYFILE_CONNECTION_LOG_FMT, NMS_KEYFILE_CONNECTION_LOG_ARG (connection_new)); - g_hash_table_insert (priv->connections, g_strdup (uuid), connection_new); - - g_signal_connect (connection_new, NM_SETTINGS_CONNECTION_REMOVED, - G_CALLBACK (connection_removed_cb), - self); - - if (!source) { - /* Only raise the signal if we were called without source, i.e. if we read the connection from file. - * Otherwise, we were called by add_connection() which does not expect the signal. */ - _nm_settings_plugin_emit_signal_connection_added (NM_SETTINGS_PLUGIN (self), - NM_SETTINGS_CONNECTION (connection_new)); - } + return nms_keyfile_storage_new_connection (self, + g_steal_pointer (&connection), + full_filename, + storage_type, + is_nm_generated_opt, + is_volatile_opt, + shadowed_storage, + shadowed_owned_opt, + &st.st_mtim); +} - return connection_new; - } +static NMSKeyfileStorage * +_load_file_from_path (NMSKeyfilePlugin *self, + const char *full_filename, + NMSKeyfileStorageType storage_type, + GError **error) +{ + gs_free char *f_dirname_free = NULL; + const char *f_filename; + const char *f_dirname; + + nm_assert (full_filename && full_filename[0] == '/'); + + f_filename = strrchr (full_filename, '/'); + f_dirname = nm_strndup_a (300, full_filename, f_filename - full_filename, &f_dirname_free); + f_filename++; + return _load_file (self, + f_dirname, + f_filename, + storage_type, + error); } static void -dir_changed (GFileMonitor *monitor, - GFile *file, - GFile *other_file, - GFileMonitorEvent event_type, - gpointer user_data) +_load_dir (NMSKeyfilePlugin *self, + NMSKeyfileStorageType storage_type, + const char *dirname, + NMSettUtilStorages *storages) { - NMSettingsPlugin *config = NM_SETTINGS_PLUGIN (user_data); - NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN (config); - NMSKeyfileConnection *connection; - char *full_path; - gboolean exists; - - full_path = g_file_get_path (file); - if (nm_keyfile_utils_ignore_filename (full_path, FALSE)) { - g_free (full_path); + const char *filename; + GDir *dir; + gs_unref_hashtable GHashTable *dupl_filenames = NULL; + + dir = g_dir_open (dirname, 0, NULL); + if (!dir) return; - } - exists = g_file_test (full_path, G_FILE_TEST_EXISTS); - - _LOGD ("dir_changed(%s) = %d; file %s", full_path, event_type, exists ? "exists" : "does not exist"); - - connection = find_by_path (self, full_path); - - switch (event_type) { - case G_FILE_MONITOR_EVENT_DELETED: - if (!exists && connection) - remove_connection (NMS_KEYFILE_PLUGIN (config), connection); - break; - case G_FILE_MONITOR_EVENT_CREATED: - case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT: - if (exists) - update_connection (NMS_KEYFILE_PLUGIN (config), NULL, full_path, connection, TRUE, NULL, NULL); - break; - default: - break; + + dupl_filenames = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_free); + + while ((filename = g_dir_read_name (dir))) { + gs_unref_object NMSKeyfileStorage *storage = NULL; + + filename = g_strdup (filename); + if (!g_hash_table_add (dupl_filenames, (char *) filename)) + continue; + + storage = _load_file (self, + dirname, + filename, + storage_type, + NULL); + if (!storage) + continue; + + nm_sett_util_storages_add_take (storages, g_steal_pointer (&storage)); } - g_free (full_path); + g_dir_close (dir); + +#if NM_MORE_ASSERTS + { + NMSKeyfileStorage *storage; + + c_list_for_each_entry (storage, &storages->_storage_lst_head, parent._storage_lst) + nm_assert (NMS_IS_KEYFILE_STORAGE (storage)); + } +#endif } +/*****************************************************************************/ + static void -config_changed_cb (NMConfig *config, - NMConfigData *config_data, - NMConfigChangeFlags changes, - NMConfigData *old_data, - NMSKeyfilePlugin *self) +_storages_consolidate (NMSKeyfilePlugin *self, + NMSettUtilStorages *storages_new, + gboolean replace_all, + GHashTable *storages_replaced, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data) { - gs_free char *old_value = NULL; - gs_free char *new_value = NULL; + NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); + CList lst_conn_info_deleted = C_LIST_INIT (lst_conn_info_deleted); + gs_unref_ptrarray GPtrArray *storages_modified = NULL; + CList storages_deleted; + NMSKeyfileStorage *storage_safe; + NMSKeyfileStorage *storage_new; + NMSKeyfileStorage *storage_old; + NMSKeyfileStorage *storage; + guint i; - old_value = nm_config_data_get_value (old_data, NM_CONFIG_KEYFILE_GROUP_KEYFILE, NM_CONFIG_KEYFILE_KEY_KEYFILE_UNMANAGED_DEVICES, NM_CONFIG_GET_VALUE_TYPE_SPEC); - new_value = nm_config_data_get_value (config_data, NM_CONFIG_KEYFILE_GROUP_KEYFILE, NM_CONFIG_KEYFILE_KEY_KEYFILE_UNMANAGED_DEVICES, NM_CONFIG_GET_VALUE_TYPE_SPEC); + storages_modified = g_ptr_array_new_with_free_func (g_object_unref); + c_list_init (&storages_deleted); - if (!nm_streq0 (old_value, new_value)) - _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NM_SETTINGS_PLUGIN (self)); -} + c_list_for_each_entry (storage_old, &priv->storages._storage_lst_head, parent._storage_lst) + storage_old->is_dirty = TRUE; -static void -setup_monitoring (NMSettingsPlugin *config) -{ - NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE ((NMSKeyfilePlugin *) config); - GFile *file; - GFileMonitor *monitor; - - if (nm_config_get_monitor_connection_files (priv->config)) { - file = g_file_new_for_path (nms_keyfile_utils_get_path ()); - monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL); - g_object_unref (file); - - if (monitor) { - priv->monitor_id = g_signal_connect (monitor, "changed", G_CALLBACK (dir_changed), config); - priv->monitor = monitor; + c_list_for_each_entry_safe (storage_new, storage_safe, &storages_new->_storage_lst_head, parent._storage_lst) { + storage_old = nm_sett_util_storages_lookup_by_filename (&priv->storages, nms_keyfile_storage_get_filename (storage_new)); + + nm_sett_util_storages_steal (storages_new, storage_new); + + if ( !storage_old + || !nm_streq (nms_keyfile_storage_get_uuid (storage_new), nms_keyfile_storage_get_uuid (storage_old))) { + if (storage_old) { + nm_sett_util_storages_steal (&priv->storages, storage_old); + c_list_link_tail (&storages_deleted, &storage_old->parent._storage_by_uuid_lst); + } + storage_new->is_dirty = FALSE; + nm_sett_util_storages_add_take (&priv->storages, storage_new); + g_ptr_array_add (storages_modified, g_object_ref (storage_new)); + continue; } - } - g_signal_connect (G_OBJECT (priv->config), - NM_CONFIG_SIGNAL_CONFIG_CHANGED, - G_CALLBACK (config_changed_cb), - config); -} + storage_old->is_dirty = FALSE; + nms_keyfile_storage_copy_content (storage_old, storage_new); + nms_keyfile_storage_destroy (storage_new); + g_ptr_array_add (storages_modified, g_object_ref (storage_old)); + } -static GHashTable * -_paths_from_connections (GHashTable *connections) -{ - GHashTableIter iter; - NMSKeyfileConnection *connection; - GHashTable *paths = g_hash_table_new (nm_str_hash, g_str_equal); + c_list_for_each_entry_safe (storage_old, storage_safe, &priv->storages._storage_lst_head, parent._storage_lst) { + if (!storage_old->is_dirty) + continue; + if ( replace_all + || ( storages_replaced + && g_hash_table_contains (storages_replaced, storage_old))) { + nm_sett_util_storages_steal (&priv->storages, storage_old); + c_list_link_tail (&storages_deleted, &storage_old->parent._storage_by_uuid_lst); + } + } - g_hash_table_iter_init (&iter, connections); - while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &connection)) { - const char *path = nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection)); + /* raise events. */ - if (path) - g_hash_table_add (paths, (void *) path); + for (i = 0; i < storages_modified->len; i++) { + storage = storages_modified->pdata[i]; + storage->is_dirty = TRUE; } - return paths; -} -static int -_sort_paths (const char **f1, const char **f2, GHashTable *paths) -{ - struct stat st; - gboolean c1, c2; - gint64 m1, m2; + for (i = 0; i < storages_modified->len; i++) { + gs_unref_object NMConnection *connection = NULL; - c1 = !!g_hash_table_contains (paths, *f1); - c2 = !!g_hash_table_contains (paths, *f2); - if (c1 != c2) - return c1 ? -1 : 1; + storage = storages_modified->pdata[i]; - m1 = stat (*f1, &st) == 0 ? (gint64) st.st_mtime : G_MININT64; - m2 = stat (*f2, &st) == 0 ? (gint64) st.st_mtime : G_MININT64; - if (m1 != m2) - return m1 > m2 ? -1 : 1; + if (!storage->is_dirty) { + /* the entry is no longer is_dirty. In the meantime we already emited + * another signal for it. */ + continue; + } + storage->is_dirty = FALSE; - return strcmp (*f1, *f2); -} + if (c_list_is_empty (&storage->parent._storage_lst)) { + /* hm? The profile was deleted in the meantime? That is only possible + * if the signal handler called again into the plugin. In any case, the event + * was already emitted. Skip. */ + continue; + } -static void -_read_dir (GPtrArray *filenames, - const char *path, - gboolean require_extension) -{ - GDir *dir; - const char *item; - GError *error = NULL; + nm_assert (storage == nm_sett_util_storages_lookup_by_filename (&priv->storages, nms_keyfile_storage_get_filename (storage))); - dir = g_dir_open (path, 0, &error); - if (!dir) { - _LOGD ("cannot read directory '%s': %s", path, error->message); - g_clear_error (&error); - return; + connection = nms_keyfile_storage_steal_connection (storage); + + callback (NM_SETTINGS_PLUGIN (self), + NM_SETTINGS_STORAGE (storage), + connection, + user_data); } - while ((item = g_dir_read_name (dir))) { - if (nm_keyfile_utils_ignore_filename (item, require_extension)) - continue; - g_ptr_array_add (filenames, g_build_filename (path, item, NULL)); + while ((storage = c_list_first_entry (&storages_deleted, NMSKeyfileStorage, parent._storage_by_uuid_lst))) { + c_list_unlink (&storage->parent._storage_by_uuid_lst); + callback (NM_SETTINGS_PLUGIN (self), + NM_SETTINGS_STORAGE (storage), + NULL, + user_data); + nms_keyfile_storage_destroy (storage); } - g_dir_close (dir); } +static void +reload_connections (NMSettingsPlugin *plugin, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data) +{ + NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN (plugin); + NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); + nm_auto_clear_sett_util_storages NMSettUtilStorages storages_new = NM_SETT_UTIL_STORAGES_INIT (storages_new, nms_keyfile_storage_destroy); + int i; + + _load_dir (self, NMS_KEYFILE_STORAGE_TYPE_RUN, priv->dirname_run, &storages_new); + if (priv->dirname_etc) + _load_dir (self, NMS_KEYFILE_STORAGE_TYPE_ETC, priv->dirname_etc, &storages_new); + for (i = 0; priv->dirname_libs[i]; i++) + _load_dir (self, NMS_KEYFILE_STORAGE_TYPE_LIB (i), priv->dirname_libs[i], &storages_new); + + _storages_consolidate (self, + &storages_new, + TRUE, + NULL, + callback, + user_data); +} static void -read_connections (NMSettingsPlugin *config) +load_connections (NMSettingsPlugin *plugin, + NMSettingsPluginConnectionLoadEntry *entries, + gsize n_entries, + NMSettingsPluginConnectionLoadCallback callback, + gpointer user_data) { - NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN (config); + NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN (plugin); NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); - GHashTable *alive_connections; - GHashTableIter iter; - NMSKeyfileConnection *connection; - GPtrArray *dead_connections = NULL; - guint i; - GPtrArray *filenames; - GHashTable *paths; + nm_auto_clear_sett_util_storages NMSettUtilStorages storages_new = NM_SETT_UTIL_STORAGES_INIT (storages_new, nms_keyfile_storage_destroy); + gs_unref_hashtable GHashTable *dupl_filenames = NULL; + gs_unref_hashtable GHashTable *storages_replaced = NULL; + gs_unref_hashtable GHashTable *loaded_uuids = NULL; + const char *loaded_uuid; + GHashTableIter h_iter; + gsize i; + + if (n_entries == 0) + return; - filenames = g_ptr_array_new_with_free_func (g_free); + dupl_filenames = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL); - _read_dir (filenames, NM_KEYFILE_PATH_NAME_RUN, TRUE); - _read_dir (filenames, nms_keyfile_utils_get_path (), FALSE); + loaded_uuids = g_hash_table_new (nm_str_hash, g_str_equal); - alive_connections = g_hash_table_new (nm_direct_hash, NULL); + storages_replaced = g_hash_table_new_full (nm_direct_hash, NULL, g_object_unref, NULL); - /* While reloading, we don't replace connections that we already loaded while - * iterating over the files. - * - * To have sensible, reproducible behavior, sort the paths by last modification - * time preferring older files. - */ - paths = _paths_from_connections (priv->connections); - g_ptr_array_sort_with_data (filenames, (GCompareDataFunc) _sort_paths, paths); - g_hash_table_destroy (paths); - - for (i = 0; i < filenames->len; i++) { - connection = update_connection (self, NULL, filenames->pdata[i], NULL, FALSE, alive_connections, NULL); - if (connection) - g_hash_table_add (alive_connections, connection); - } - g_ptr_array_free (filenames, TRUE); - - g_hash_table_iter_init (&iter, priv->connections); - while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &connection)) { - if ( !g_hash_table_contains (alive_connections, connection) - && nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection))) { - if (!dead_connections) - dead_connections = g_ptr_array_new (); - g_ptr_array_add (dead_connections, connection); + for (i = 0; i < n_entries; i++) { + NMSettingsPluginConnectionLoadEntry *const entry = &entries[i]; + NMSKeyfileStorageType storage_type; + gs_free_error GError *local = NULL; + const char *f_filename; + const char *f_dirname; + const char *full_filename; + gs_free char *full_filename_keep = NULL; + gboolean is_nmmeta_file; + NMSettingsPluginConnectionLoadEntry *dupl_content_entry; + gboolean failed_due_to_invalid_filename; + gs_unref_object NMSKeyfileStorage *storage = NULL; + + if (entry->handled) + continue; + + if (!_path_detect_storage_type (entry->filename, + (const char *const*) priv->dirname_libs, + priv->dirname_etc, + priv->dirname_run, + &storage_type, + &f_dirname, + &f_filename, + &is_nmmeta_file, + &failed_due_to_invalid_filename)) { + if (failed_due_to_invalid_filename) { + entry->handled = TRUE; + nm_utils_error_set (&entry->error, NM_UTILS_ERROR_UNKNOWN, "filename is not valid for a keyfile"); + } + continue; } - } - g_hash_table_destroy (alive_connections); - if (dead_connections) { - for (i = 0; i < dead_connections->len; i++) - remove_connection (self, dead_connections->pdata[i]); - g_ptr_array_free (dead_connections, TRUE); + full_filename_keep = g_build_filename (f_dirname, f_filename, NULL); + + if ((dupl_content_entry = g_hash_table_lookup (dupl_filenames, full_filename_keep))) { + /* we already visited this file. */ + entry->handled = dupl_content_entry->handled; + if (dupl_content_entry->error) { + g_set_error_literal (&entry->error, + dupl_content_entry->error->domain, + dupl_content_entry->error->code, + dupl_content_entry->error->message); + } + continue; + } + + entry->handled = TRUE; + + full_filename = full_filename_keep; + if (!g_hash_table_insert (dupl_filenames, g_steal_pointer (&full_filename_keep), entry)) + nm_assert_not_reached (); + + storage = _load_file (self, + f_dirname, + f_filename, + storage_type, + &local); + if (!storage) { + if (nm_utils_file_stat (full_filename, NULL) == -ENOENT) { + NMSKeyfileStorage *storage2; + + /* the file does not exist. We take that as indication to unload the file + * that was previously loaded... */ + storage2 = nm_sett_util_storages_lookup_by_filename (&priv->storages, full_filename); + if (storage2) + g_hash_table_add (storages_replaced, g_object_ref (storage2)); + continue; + } + g_propagate_error (&entry->error, g_steal_pointer (&local)); + continue; + } + + g_hash_table_add (loaded_uuids, (char *) nms_keyfile_storage_get_uuid (storage)); + + nm_sett_util_storages_add_take (&storages_new, g_steal_pointer (&storage)); } -} -/*****************************************************************************/ + /* now we visit all UUIDs that are about to change... */ + g_hash_table_iter_init (&h_iter, loaded_uuids); + while (g_hash_table_iter_next (&h_iter, (gpointer *) &loaded_uuid, NULL)) { + NMSKeyfileStorage *storage; + NMSettUtilStorageByUuidHead *sbuh; -static GSList * -get_connections (NMSettingsPlugin *config) -{ - NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE ((NMSKeyfilePlugin *) config); + sbuh = nm_sett_util_storages_lookup_by_uuid (&priv->storages, loaded_uuid); + if (!sbuh) + continue; + + c_list_for_each_entry (storage, &sbuh->_storage_by_uuid_lst_head, parent._storage_by_uuid_lst) { + const char *full_filename = nms_keyfile_storage_get_filename (storage); + gs_unref_object NMSKeyfileStorage *storage_new = NULL; + gs_free_error GError *local = NULL; + + if (g_hash_table_contains (dupl_filenames, full_filename)) { + /* already re-loaded. */ + continue; + } + + /* @storage has a UUID that was just loaded from disk, but we have an entry in cache. + * Reload that file too despite not being told to do so. The reason is to get + * the latest file timestamp so that we get the priorities right. */ + + storage_new = _load_file_from_path (self, + full_filename, + storage->storage_type, + &local); + if ( storage_new + && !nm_streq (loaded_uuid, nms_keyfile_storage_get_uuid (storage_new))) { + /* the file now references a different UUID. We are not told to reload + * that file, so this means the existing storage (with the previous + * filename and UUID tuple) is no longer valid. */ + g_clear_object (&storage_new); + } - if (!priv->initialized) { - setup_monitoring (config); - read_connections (config); - priv->initialized = TRUE; + g_hash_table_add (storages_replaced, g_object_ref (storage)); + if (storage_new) + nm_sett_util_storages_add_take (&storages_new, g_steal_pointer (&storage_new)); + } } - return _nm_utils_hash_values_to_slist (priv->connections); + + nm_clear_pointer (&loaded_uuids, g_hash_table_destroy); + nm_clear_pointer (&dupl_filenames, g_hash_table_destroy); + + _storages_consolidate (self, + &storages_new, + FALSE, + storages_replaced, + callback, + user_data); } -static gboolean -load_connection (NMSettingsPlugin *config, - const char *filename) +gboolean +nms_keyfile_plugin_add_connection (NMSKeyfilePlugin *self, + NMConnection *connection, + gboolean in_memory, + gboolean is_nm_generated, + gboolean is_volatile, + const char *shadowed_storage, + gboolean shadowed_owned, + NMSettingsStorage **out_storage, + NMConnection **out_connection, + GError **error) { - NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN ((NMSKeyfilePlugin *) config); - NMSKeyfileConnection *connection; - gboolean require_extension; - - if (nm_utils_file_is_in_path (filename, nms_keyfile_utils_get_path ())) - require_extension = FALSE; - else if (nm_utils_file_is_in_path (filename, NM_KEYFILE_PATH_NAME_RUN)) - require_extension = TRUE; - else - return FALSE; + NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); + gs_unref_object NMConnection *reread = NULL; + gs_free char *full_filename = NULL; + NMSKeyfileStorageType storage_type; + gs_unref_object NMSKeyfileStorage *storage = NULL; + GError *local = NULL; + const char *uuid; + gboolean reread_same; + struct timespec mtime; + char strbuf[100]; + + nm_assert (NM_IS_CONNECTION (connection)); + nm_assert (out_storage && !*out_storage); + nm_assert (out_connection && !*out_connection); + + nm_assert ( in_memory + || ( !is_nm_generated + && !is_volatile + && !shadowed_storage + && !shadowed_owned)); - if (nm_keyfile_utils_ignore_filename (filename, require_extension)) + uuid = nm_connection_get_uuid (connection); + + /* Note that even if the caller requests persistent storage, we may switch to in-memory, if + * no /etc directory is configured. */ + storage_type = !in_memory && priv->dirname_etc + ? NMS_KEYFILE_STORAGE_TYPE_ETC + : NMS_KEYFILE_STORAGE_TYPE_RUN; + + if (!nms_keyfile_writer_connection (connection, + is_nm_generated, + is_volatile, + shadowed_storage, + shadowed_owned, + storage_type == NMS_KEYFILE_STORAGE_TYPE_ETC + ? priv->dirname_etc + : priv->dirname_run, + _get_plugin_dir (priv), + NULL, + FALSE, + FALSE, + nm_sett_util_allow_filename_cb, + NM_SETT_UTIL_ALLOW_FILENAME_DATA (&priv->storages, NULL), + &full_filename, + &reread, + &reread_same, + &local)) { + _LOGT ("commit: %s (%s) failed to add: %s", + nm_connection_get_uuid (connection), + nm_connection_get_id (connection), + local->message); + g_propagate_error (error, local); return FALSE; + } - connection = update_connection (self, NULL, filename, find_by_path (self, filename), TRUE, NULL, NULL); + if ( !reread + || reread_same) + nm_g_object_ref_set (&reread, connection); - return (connection != NULL); -} + nm_assert (_nm_connection_verify (reread, NULL) == NM_SETTING_VERIFY_SUCCESS); + nm_assert (nm_streq0 (nm_connection_get_uuid (connection), nm_connection_get_uuid (reread))); -static void -reload_connections (NMSettingsPlugin *config) -{ - read_connections (config); + nm_assert (full_filename && full_filename[0] == '/'); + nm_assert (!nm_sett_util_storages_lookup_by_filename (&priv->storages, full_filename)); + + _LOGT ("commit: %s (%s) added as \"%s\"%s%s%s%s", + uuid, + nm_connection_get_id (connection), + full_filename, + _extra_flags_to_string (strbuf, sizeof (strbuf), is_nm_generated, is_volatile), + NM_PRINT_FMT_QUOTED (shadowed_storage, " (shadows \"", shadowed_storage, shadowed_owned ? "\", owned)" : "\")", "")); + + storage = nms_keyfile_storage_new_connection (self, + g_steal_pointer (&reread), + full_filename, + storage_type, + is_nm_generated ? NM_TERNARY_TRUE : NM_TERNARY_FALSE, + is_volatile ? NM_TERNARY_TRUE : NM_TERNARY_FALSE, + shadowed_storage, + shadowed_owned ? NM_TERNARY_TRUE : NM_TERNARY_FALSE, + nm_sett_util_stat_mtime (full_filename, FALSE, &mtime)); + + nm_sett_util_storages_add_take (&priv->storages, g_object_ref (storage)); + + *out_connection = nms_keyfile_storage_steal_connection (storage); + *out_storage = NM_SETTINGS_STORAGE (g_steal_pointer (&storage)); + + return TRUE; } -static NMSettingsConnection * -add_connection (NMSettingsPlugin *config, +static gboolean +add_connection (NMSettingsPlugin *plugin, NMConnection *connection, - gboolean save_to_disk, + NMSettingsStorage **out_storage, + NMConnection **out_connection, GError **error) { - NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN (config); - gs_free char *path = NULL; + return nms_keyfile_plugin_add_connection (NMS_KEYFILE_PLUGIN (plugin), + connection, + FALSE, + FALSE, + FALSE, + NULL, + FALSE, + out_storage, + out_connection, + error); +} + +gboolean +nms_keyfile_plugin_update_connection (NMSKeyfilePlugin *self, + NMSettingsStorage *storage_x, + NMConnection *connection, + gboolean is_nm_generated, + gboolean is_volatile, + const char *shadowed_storage, + gboolean shadowed_owned, + gboolean force_rename, + NMSettingsStorage **out_storage, + NMConnection **out_connection, + GError **error) +{ + NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); + NMSKeyfileStorage *storage = NMS_KEYFILE_STORAGE (storage_x); + gs_unref_object NMConnection *connection_clone = NULL; gs_unref_object NMConnection *reread = NULL; + gs_free char *full_filename = NULL; + gs_free_error GError *local = NULL; + struct timespec mtime; + const char *previous_filename; + gboolean reread_same; + const char *uuid; + char strbuf[100]; + + _nm_assert_storage (self, storage, TRUE); + nm_assert (NM_IS_CONNECTION (connection)); + nm_assert (_nm_connection_verify (connection, NULL) == NM_SETTING_VERIFY_SUCCESS); + nm_assert (nm_streq (nms_keyfile_storage_get_uuid (storage), nm_connection_get_uuid (connection))); + nm_assert (!error || !*error); + nm_assert (NM_IN_SET (storage->storage_type, NMS_KEYFILE_STORAGE_TYPE_ETC, + NMS_KEYFILE_STORAGE_TYPE_RUN)); + nm_assert (!storage->is_meta_data); + nm_assert ( storage->storage_type == NMS_KEYFILE_STORAGE_TYPE_RUN + || ( !is_nm_generated + && !is_volatile + && !shadowed_storage + && !shadowed_owned)); + nm_assert (!shadowed_owned || shadowed_storage); + nm_assert ( priv->dirname_etc + || storage->storage_type != NMS_KEYFILE_STORAGE_TYPE_ETC); + + previous_filename = nms_keyfile_storage_get_filename (storage); + uuid = nms_keyfile_storage_get_uuid (storage); if (!nms_keyfile_writer_connection (connection, - save_to_disk, - NULL, + is_nm_generated, + is_volatile, + shadowed_storage, + shadowed_owned, + storage->storage_type == NMS_KEYFILE_STORAGE_TYPE_ETC + ? priv->dirname_etc + : priv->dirname_run, + _get_plugin_dir (priv), + previous_filename, + FALSE, FALSE, - &path, + nm_sett_util_allow_filename_cb, + NM_SETT_UTIL_ALLOW_FILENAME_DATA (&priv->storages, previous_filename), + &full_filename, &reread, - NULL, - error)) - return NULL; + &reread_same, + &local)) { + _LOGW ("commit: failure to write %s (%s) to \"%s\": %s", + uuid, + nm_connection_get_id (connection_clone), + previous_filename, + local->message); + g_propagate_error (error, g_steal_pointer (&local)); + return FALSE; + } + + nm_assert ( full_filename + && nm_streq (full_filename, previous_filename)); + + if ( !reread + || reread_same) + nm_g_object_ref_set (&reread, connection); - return NM_SETTINGS_CONNECTION (update_connection (self, reread ?: connection, path, NULL, FALSE, NULL, error)); + nm_assert (_nm_connection_verify (reread, NULL) == NM_SETTING_VERIFY_SUCCESS); + nm_assert (nm_streq (nm_connection_get_uuid (reread), uuid)); + + _LOGT ("commit: \"%s\": profile %s (%s) written%s%s%s%s", + full_filename, + uuid, + nm_connection_get_id (connection), + _extra_flags_to_string (strbuf, sizeof (strbuf), is_nm_generated, is_volatile), + NM_PRINT_FMT_QUOTED (shadowed_storage, shadowed_owned ? " (owns \"" : " (shadows \"", shadowed_storage, "\")", "")); + + storage->u.conn_data.is_nm_generated = is_nm_generated; + storage->u.conn_data.is_volatile = is_volatile; + storage->u.conn_data.stat_mtime = *nm_sett_util_stat_mtime (full_filename, FALSE, &mtime); + storage->u.conn_data.shadowed_owned = shadowed_owned; + + *out_storage = g_object_ref (NM_SETTINGS_STORAGE (storage)); + *out_connection = g_steal_pointer (&reread); + return TRUE; +} + +static gboolean +update_connection (NMSettingsPlugin *plugin, + NMSettingsStorage *storage, + NMConnection *connection, + NMSettingsStorage **out_storage, + NMConnection **out_connection, + GError **error) +{ + return nms_keyfile_plugin_update_connection (NMS_KEYFILE_PLUGIN (plugin), + storage, + connection, + FALSE, + FALSE, + NULL, + FALSE, + FALSE, + out_storage, + out_connection, + error); +} + +static gboolean +delete_connection (NMSettingsPlugin *plugin, + NMSettingsStorage *storage_x, + GError **error) +{ + NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN (plugin); + NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); + gs_unref_object NMSKeyfileStorage *storage = g_object_ref (NMS_KEYFILE_STORAGE (storage_x)); + const char *remove_from_disk_errmsg = NULL; + const char *operation_message; + const char *previous_filename; + const char *uuid; + gboolean success = TRUE; + + _nm_assert_storage (self, storage, TRUE); + nm_assert (!error || !*error); + + previous_filename = nms_keyfile_storage_get_filename (storage); + uuid = nms_keyfile_storage_get_uuid (storage); + + if (!NM_IN_SET (storage->storage_type, NMS_KEYFILE_STORAGE_TYPE_ETC, + NMS_KEYFILE_STORAGE_TYPE_RUN)) { + nm_utils_error_set (error, + NM_UTILS_ERROR_UNKNOWN, + "profile in read-only storage cannot be deleted"); + success = FALSE; + operation_message = "dropped readonly file from memory"; + } else if (unlink (previous_filename) != 0) { + int errsv; + + errsv = errno; + if (errsv != ENOENT) { + remove_from_disk_errmsg = nm_strerror_native (errsv); + operation_message = "failed to delete from disk"; + success = FALSE; + nm_utils_error_set_errno (error, + errsv, + "failure to delete \"%s\": %s", + previous_filename); + } else + operation_message = "does not exist on disk"; + } else + operation_message = "deleted from disk"; + + _LOGT ("commit: deleted \"%s\", %s %s (%s%s%s%s)", + previous_filename, + storage->is_meta_data ? "meta-data" : "profile", + uuid, + operation_message, + NM_PRINT_FMT_QUOTED (remove_from_disk_errmsg, ": ", remove_from_disk_errmsg, "", "")); + + if (success) { + nm_sett_util_storages_steal (&priv->storages, storage); + nms_keyfile_storage_destroy (storage); + } + + return success; +} + +/** + * nms_keyfile_plugin_set_nmmeta_tombstone: + * @self: the #NMSKeyfilePlugin instance + * @simulate: if %TRUE, don't do anything on the filename but just pretend + * that the loaded UUID file gets tracked/untracked. In this mode, the function + * cannot fail (except on hard-failure, see below). + * The idea is that you first try without simulate to write to disk. + * If that fails, you might still want to forcefully pretend (in-memory + * only) that this uuid is marked as tombstone (or not), as desired. + * So you repeate the call with @simulate %TRUE. + * @uuid: the UUID for which to write/delete the nmmeta file + * @in_memory: the storage type, either /etc or /run. Note that if @self + * has no /etc directory configured, this results in a hard failure. + * @set: if %TRUE, write the symlink to point to /dev/null. If %FALSE, + * delete the nmmeta file (if it exists). + * @shadowed_storage: a tombstone can also shadow an existing storage. + * In combination with @set and @in_memory, this is allowed to store + * the shadowed storage filename. + * @out_storage: (transfer full) (allow-none): the storage element that changes, or + * NULL if nothing changed. Note that the file on disk is already as + * we want to write it, then this still counts as a change. No change only + * means if we try to delete a storage (@set %FALSE) that did not + * exist previously. + * @out_hard_failure: (allow-none): on failure, indicate that this is a hard failure. + * + * The function writes or deletes nmmeta files to/from filesystem. In this case, + * the nmmeta files can only be symlinks to /dev/null (to indicate tombstones). + * + * A hard failure can only happen if @self has no /etc directory configured + * and @in_memory is FALSE. In such case even @simulate call fails (which + * otherwise would always succeed). + * Also, if you get a hard-failure (with @simulate %FALSE) there is no point + * in retrying with @simulate %TRUE (contrary to all other cases!). + * + * Returns: %TRUE on success. + */ +gboolean +nms_keyfile_plugin_set_nmmeta_tombstone (NMSKeyfilePlugin *self, + gboolean simulate, + const char *uuid, + gboolean in_memory, + gboolean set, + const char *shadowed_storage, + NMSettingsStorage **out_storage, + gboolean *out_hard_failure) +{ + NMSKeyfilePluginPrivate *priv; + gboolean hard_failure = FALSE; + NMSKeyfileStorage *storage; + gs_unref_object NMSKeyfileStorage *storage_result = NULL; + gboolean nmmeta_success = FALSE; + gs_free char *nmmeta_filename = NULL; + NMSKeyfileStorageType storage_type; + const char *loaded_path; + const char *dirname; + + nm_assert (NMS_IS_KEYFILE_PLUGIN (self)); + nm_assert (nm_utils_is_uuid (uuid)); + nm_assert (!out_storage || !*out_storage); + nm_assert (!shadowed_storage || (set && in_memory)); + + priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); + + loaded_path = set + ? NM_KEYFILE_PATH_NMMETA_SYMLINK_NULL + : NULL; + + if (in_memory) { + storage_type = NMS_KEYFILE_STORAGE_TYPE_RUN; + dirname = priv->dirname_run; + } else { + if (!priv->dirname_etc) { + _LOGT ("commit: cannot %s%s nmmeta file for %s as there is no /etc directory", + simulate ? "simulate " : "", + loaded_path ? "write" : "delete", + uuid); + hard_failure = TRUE; + goto out; + } + storage_type = NMS_KEYFILE_STORAGE_TYPE_ETC; + dirname = priv->dirname_etc; + } + + if (simulate) { + nmmeta_success = TRUE; + nmmeta_filename = nms_keyfile_nmmeta_filename (dirname, uuid, FALSE); + } else { + nmmeta_success = nms_keyfile_nmmeta_write (dirname, + uuid, + loaded_path, + FALSE, + shadowed_storage, + &nmmeta_filename); + } + + _LOGT ("commit: %s nmmeta file \"%s\"%s%s%s%s%s%s %s", + loaded_path ? "writing" : "deleting", + nmmeta_filename, + NM_PRINT_FMT_QUOTED (loaded_path, " (pointing to \"", loaded_path, "\")", ""), + NM_PRINT_FMT_QUOTED (shadowed_storage, " (shadows \"", shadowed_storage, "\")", ""), + simulate + ? "simulated" + : ( nmmeta_success + ? "succeeded" + : "failed")); + + if (!nmmeta_success) + goto out; + + storage = nm_sett_util_storages_lookup_by_filename (&priv->storages, nmmeta_filename); + + nm_assert ( !storage + || ( storage->is_meta_data + && storage->storage_type == storage_type + && nm_streq (nms_keyfile_storage_get_uuid (storage), uuid))); + + if (loaded_path) { + + if (!storage) { + storage = nms_keyfile_storage_new_tombstone (self, + uuid, + nmmeta_filename, + storage_type, + shadowed_storage); + nm_sett_util_storages_add_take (&priv->storages, storage); + } else { + g_free (storage->u.meta_data.shadowed_storage); + storage->u.meta_data.shadowed_storage = g_strdup (shadowed_storage); + } + + storage_result = g_object_ref (storage); + } else { + if (storage) + storage_result = nm_sett_util_storages_steal (&priv->storages, storage); + } + +out: + nm_assert (!nmmeta_success || !hard_failure); + nm_assert (nmmeta_success || !storage_result); + + NM_SET_OUT (out_hard_failure, hard_failure); + NM_SET_OUT (out_storage, (NMSettingsStorage *) g_steal_pointer (&storage_result)); + return nmmeta_success; +} + +/*****************************************************************************/ + +static void +config_changed_cb (NMConfig *config, + NMConfigData *config_data, + NMConfigChangeFlags changes, + NMConfigData *old_data, + NMSKeyfilePlugin *self) +{ + gs_free char *old_value = NULL; + gs_free char *new_value = NULL; + + old_value = nm_config_data_get_value (old_data, NM_CONFIG_KEYFILE_GROUP_KEYFILE, NM_CONFIG_KEYFILE_KEY_KEYFILE_UNMANAGED_DEVICES, NM_CONFIG_GET_VALUE_TYPE_SPEC); + new_value = nm_config_data_get_value (config_data, NM_CONFIG_KEYFILE_GROUP_KEYFILE, NM_CONFIG_KEYFILE_KEY_KEYFILE_UNMANAGED_DEVICES, NM_CONFIG_GET_VALUE_TYPE_SPEC); + + if (!nm_streq0 (old_value, new_value)) + _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NM_SETTINGS_PLUGIN (self)); } static GSList * get_unmanaged_specs (NMSettingsPlugin *config) { - NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE ((NMSKeyfilePlugin *) config); + NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (config); gs_free char *value = NULL; value = nm_config_data_get_value (nm_config_get_data (priv->config), @@ -584,13 +1225,48 @@ nms_keyfile_plugin_init (NMSKeyfilePlugin *plugin) NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (plugin); priv->config = g_object_ref (nm_config_get ()); - priv->connections = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_object_unref); + + priv->storages = (NMSettUtilStorages) NM_SETT_UTIL_STORAGES_INIT (priv->storages, nms_keyfile_storage_destroy); + + /* dirname_libs are a set of read-only directories with lower priority than /etc or /run. + * There is nothing complicated about having multiple of such directories, so dirname_libs + * is a list (which currently only has at most one directory). */ + priv->dirname_libs[0] = nm_sd_utils_path_simplify (g_strdup (NM_KEYFILE_PATH_NAME_LIB), FALSE); + priv->dirname_libs[1] = NULL; + priv->dirname_run = nm_sd_utils_path_simplify (g_strdup (NM_KEYFILE_PATH_NAME_RUN), FALSE); + priv->dirname_etc = nm_config_data_get_value (NM_CONFIG_GET_DATA_ORIG, + NM_CONFIG_KEYFILE_GROUP_KEYFILE, + NM_CONFIG_KEYFILE_KEY_KEYFILE_PATH, + NM_CONFIG_GET_VALUE_STRIP); + if (priv->dirname_etc && priv->dirname_etc[0] == '\0') { + /* special case: configure an empty keyfile path so that NM has no writable keyfile + * directory. In this case, NM will only honor dirname_libs and dirname_run, meaning + * it cannot persist profile to non-volatile memory. */ + nm_clear_g_free (&priv->dirname_etc); + } else if (!priv->dirname_etc || priv->dirname_etc[0] != '/') { + /* either invalid path or unspecified. Use the default. */ + g_free (priv->dirname_etc); + priv->dirname_etc = nm_sd_utils_path_simplify (g_strdup (NM_KEYFILE_PATH_NAME_ETC_DEFAULT), FALSE); + } else + nm_sd_utils_path_simplify (priv->dirname_etc, FALSE); + + /* no duplicates */ + if (NM_IN_STRSET (priv->dirname_libs[0], priv->dirname_etc, + priv->dirname_run)) + nm_clear_g_free (&priv->dirname_libs[0]); + if (NM_IN_STRSET (priv->dirname_etc, priv->dirname_run)) + nm_clear_g_free (&priv->dirname_etc); + + nm_assert (!priv->dirname_libs[0] || priv->dirname_libs[0][0] == '/'); + nm_assert (!priv->dirname_etc || priv->dirname_etc[0] == '/'); + nm_assert ( priv->dirname_run && priv->dirname_run[0] == '/'); } static void constructed (GObject *object) { - NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE ((NMSKeyfilePlugin *) object); + NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN (object); + NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); G_OBJECT_CLASS (nms_keyfile_plugin_parent_class)->constructed (object); @@ -599,6 +1275,17 @@ constructed (GObject *object) NM_CONFIG_KEYFILE_KEY_KEYFILE_HOSTNAME, NM_CONFIG_GET_VALUE_RAW)) _LOGW ("'hostname' option is deprecated and has no effect"); + + if (nm_config_data_has_value (nm_config_get_data_orig (priv->config), + NM_CONFIG_KEYFILE_GROUP_MAIN, + NM_CONFIG_KEYFILE_KEY_MAIN_MONITOR_CONNECTION_FILES, + NM_CONFIG_GET_VALUE_RAW)) + _LOGW ("'monitor-connection-files' option is deprecated and has no effect"); + + g_signal_connect (G_OBJECT (priv->config), + NM_CONFIG_SIGNAL_CONFIG_CHANGED, + G_CALLBACK (config_changed_cb), + self); } NMSKeyfilePlugin * @@ -610,24 +1297,19 @@ nms_keyfile_plugin_new (void) static void dispose (GObject *object) { - NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE ((NMSKeyfilePlugin *) object); + NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN (object); + NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE (self); - if (priv->monitor) { - nm_clear_g_signal_handler (priv->monitor, &priv->monitor_id); + if (priv->config) + g_signal_handlers_disconnect_by_func (priv->config, config_changed_cb, object); - g_file_monitor_cancel (priv->monitor); - g_clear_object (&priv->monitor); - } + nm_sett_util_storages_clear (&priv->storages); - if (priv->connections) { - g_hash_table_destroy (priv->connections); - priv->connections = NULL; - } + nm_clear_g_free (&priv->dirname_libs[0]); + nm_clear_g_free (&priv->dirname_etc); + nm_clear_g_free (&priv->dirname_run); - if (priv->config) { - g_signal_handlers_disconnect_by_func (priv->config, config_changed_cb, object); - g_clear_object (&priv->config); - } + g_clear_object (&priv->config); G_OBJECT_CLASS (nms_keyfile_plugin_parent_class)->dispose (object); } @@ -641,9 +1323,11 @@ nms_keyfile_plugin_class_init (NMSKeyfilePluginClass *klass) object_class->constructed = constructed; object_class->dispose = dispose; - plugin_class->get_connections = get_connections; - plugin_class->load_connection = load_connection; + plugin_class->plugin_name = "keyfile"; + plugin_class->get_unmanaged_specs = get_unmanaged_specs; plugin_class->reload_connections = reload_connections; + plugin_class->load_connections = load_connections; plugin_class->add_connection = add_connection; - plugin_class->get_unmanaged_specs = get_unmanaged_specs; + plugin_class->update_connection = update_connection; + plugin_class->delete_connection = delete_connection; } diff --git a/src/settings/plugins/keyfile/nms-keyfile-plugin.h b/src/settings/plugins/keyfile/nms-keyfile-plugin.h index 41f47aac..48440964 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-plugin.h +++ b/src/settings/plugins/keyfile/nms-keyfile-plugin.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service - keyfile plugin * * This program is free software; you can redistribute it and/or modify @@ -22,6 +21,11 @@ #ifndef __NMS_KEYFILE_PLUGIN_H__ #define __NMS_KEYFILE_PLUGIN_H__ +#include "settings/nm-settings-plugin.h" +#include "settings/nm-settings-storage.h" + +#include "nms-keyfile-utils.h" + #define NMS_TYPE_KEYFILE_PLUGIN (nms_keyfile_plugin_get_type ()) #define NMS_KEYFILE_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMS_TYPE_KEYFILE_PLUGIN, NMSKeyfilePlugin)) #define NMS_KEYFILE_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMS_TYPE_KEYFILE_PLUGIN, NMSKeyfilePluginClass)) @@ -36,4 +40,36 @@ GType nms_keyfile_plugin_get_type (void); NMSKeyfilePlugin *nms_keyfile_plugin_new (void); +gboolean nms_keyfile_plugin_add_connection (NMSKeyfilePlugin *self, + NMConnection *connection, + gboolean in_memory, + gboolean is_nm_generated, + gboolean is_volatile, + const char *shadowed_storage, + gboolean shadowed_owned, + NMSettingsStorage **out_storage, + NMConnection **out_connection, + GError **error); + +gboolean nms_keyfile_plugin_update_connection (NMSKeyfilePlugin *self, + NMSettingsStorage *storage, + NMConnection *connection, + gboolean is_nm_generated, + gboolean is_volatile, + const char *shadowed_storage, + gboolean shadowed_owned, + gboolean force_rename, + NMSettingsStorage **out_storage, + NMConnection **out_connection, + GError **error); + +gboolean nms_keyfile_plugin_set_nmmeta_tombstone (NMSKeyfilePlugin *self, + gboolean simulate, + const char *uuid, + gboolean in_memory, + gboolean set, + const char *shadowed_storage, + NMSettingsStorage **out_storage, + gboolean *out_hard_failure); + #endif /* __NMS_KEYFILE_PLUGIN_H__ */ diff --git a/src/settings/plugins/keyfile/nms-keyfile-reader.c b/src/settings/plugins/keyfile/nms-keyfile-reader.c index 5778f13c..8d1f5599 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-reader.c +++ b/src/settings/plugins/keyfile/nms-keyfile-reader.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service - keyfile plugin * * This program is free software; you can redistribute it and/or modify @@ -162,6 +161,11 @@ nms_keyfile_reader_from_keyfile (GKeyFile *key_file, NMConnection * nms_keyfile_reader_from_file (const char *full_filename, const char *profile_dir, + struct stat *out_stat, + NMTernary *out_is_nm_generated, + NMTernary *out_is_volatile, + char **out_shadowed_storage, + NMTernary *out_shadowed_owned, GError **error) { gs_unref_keyfile GKeyFile *key_file = NULL; @@ -171,9 +175,12 @@ nms_keyfile_reader_from_file (const char *full_filename, nm_assert (full_filename && full_filename[0] == '/'); nm_assert (!profile_dir || profile_dir[0] == '/'); + NM_SET_OUT (out_is_nm_generated, NM_TERNARY_DEFAULT); + NM_SET_OUT (out_is_volatile, NM_TERNARY_DEFAULT); + if (!nms_keyfile_utils_check_file_permissions (NMS_KEYFILE_FILETYPE_KEYFILE, full_filename, - NULL, + out_stat, error)) return NULL; @@ -195,6 +202,26 @@ nms_keyfile_reader_from_file (const char *full_filename, connection = NULL; } + NM_SET_OUT (out_is_nm_generated, nm_key_file_get_boolean (key_file, + NM_KEYFILE_GROUP_NMMETA, + NM_KEYFILE_KEY_NMMETA_NM_GENERATED, + NM_TERNARY_DEFAULT)); + + NM_SET_OUT (out_is_volatile, nm_key_file_get_boolean (key_file, + NM_KEYFILE_GROUP_NMMETA, + NM_KEYFILE_KEY_NMMETA_VOLATILE, + NM_TERNARY_DEFAULT)); + + NM_SET_OUT (out_shadowed_storage, g_key_file_get_string (key_file, + NM_KEYFILE_GROUP_NMMETA, + NM_KEYFILE_KEY_NMMETA_SHADOWED_STORAGE, + NULL)); + + NM_SET_OUT (out_shadowed_owned, nm_key_file_get_boolean (key_file, + NM_KEYFILE_GROUP_NMMETA, + NM_KEYFILE_KEY_NMMETA_SHADOWED_OWNED, + NM_TERNARY_DEFAULT)); + return connection; } diff --git a/src/settings/plugins/keyfile/nms-keyfile-reader.h b/src/settings/plugins/keyfile/nms-keyfile-reader.h index c0fb06d1..f20e6d93 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-reader.h +++ b/src/settings/plugins/keyfile/nms-keyfile-reader.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service - keyfile plugin * * This program is free software; you can redistribute it and/or modify @@ -31,8 +30,15 @@ NMConnection *nms_keyfile_reader_from_keyfile (GKeyFile *key_file, gboolean verbose, GError **error); +struct stat; + NMConnection *nms_keyfile_reader_from_file (const char *full_filename, const char *profile_dir, + struct stat *out_stat, + NMTernary *out_is_nm_generated, + NMTernary *out_is_volatile, + char **out_shadowed_storage, + NMTernary *out_shadowed_owned, GError **error); #endif /* __NMS_KEYFILE_READER_H__ */ diff --git a/src/settings/plugins/keyfile/nms-keyfile-storage.c b/src/settings/plugins/keyfile/nms-keyfile-storage.c new file mode 100644 index 00000000..d68d60c8 --- /dev/null +++ b/src/settings/plugins/keyfile/nms-keyfile-storage.c @@ -0,0 +1,277 @@ +/* NetworkManager system settings service - keyfile plugin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2018 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nms-keyfile-storage.h" + +#include "nm-utils.h" +#include "nm-core-internal.h" +#include "nms-keyfile-plugin.h" + +/*****************************************************************************/ + +struct _NMSKeyfileStorageClass { + NMSettingsStorageClass parent; +}; + +G_DEFINE_TYPE (NMSKeyfileStorage, nms_keyfile_storage, NM_TYPE_SETTINGS_STORAGE) + +/*****************************************************************************/ + +void +nms_keyfile_storage_copy_content (NMSKeyfileStorage *dst, + const NMSKeyfileStorage *src) +{ + nm_assert (src != dst); + nm_assert (nm_streq (nms_keyfile_storage_get_uuid (dst), nms_keyfile_storage_get_uuid (src))); + nm_assert ( nms_keyfile_storage_get_filename (dst) + && nm_streq (nms_keyfile_storage_get_filename (dst), nms_keyfile_storage_get_filename (src))); + nm_assert (dst->storage_type == src->storage_type); + nm_assert (dst->is_meta_data == src->is_meta_data); + + if (dst->is_meta_data) { + gs_free char *shadowed_storage_to_free = NULL; + + shadowed_storage_to_free = g_steal_pointer (&dst->u.meta_data.shadowed_storage); + dst->u.meta_data = src->u.meta_data; + dst->u.meta_data.shadowed_storage = g_strdup (dst->u.meta_data.shadowed_storage); + } else { + gs_unref_object NMConnection *connection_to_free = NULL; + gs_free char *shadowed_storage_to_free = NULL; + + connection_to_free = g_steal_pointer (&dst->u.conn_data.connection); + shadowed_storage_to_free = g_steal_pointer (&dst->u.conn_data.shadowed_storage); + dst->u.conn_data = src->u.conn_data; + nm_g_object_ref (dst->u.conn_data.connection); + dst->u.conn_data.shadowed_storage = g_strdup (dst->u.conn_data.shadowed_storage); + } +} + +NMConnection * +nms_keyfile_storage_steal_connection (NMSKeyfileStorage *self) +{ + nm_assert (NMS_IS_KEYFILE_STORAGE (self)); + nm_assert ( self->is_meta_data + || NM_IS_CONNECTION (self->u.conn_data.connection)); + + return self->is_meta_data + ? NULL + : g_steal_pointer (&self->u.conn_data.connection); +} + +/*****************************************************************************/ + +static int +cmp_fcn (const NMSKeyfileStorage *a, + const NMSKeyfileStorage *b) +{ + nm_assert (NMS_IS_KEYFILE_STORAGE (a)); + nm_assert (NMS_IS_KEYFILE_STORAGE (b)); + nm_assert (a != b); + + /* sort by storage-type, which also has a numeric value according to their + * (inverse) priority. */ + NM_CMP_FIELD_UNSAFE (b, a, storage_type); + + /* meta-data is more important. */ + NM_CMP_FIELD_UNSAFE (a, b, is_meta_data); + + if (a->is_meta_data) { + nm_assert (nm_streq (nms_keyfile_storage_get_filename (a), nms_keyfile_storage_get_filename (b))); + NM_CMP_FIELD_UNSAFE (a, b, u.meta_data.is_tombstone); + } else { + /* newer files are more important. */ + NM_CMP_FIELD (a, b, u.conn_data.stat_mtime.tv_sec); + NM_CMP_FIELD (a, b, u.conn_data.stat_mtime.tv_nsec); + + NM_CMP_DIRECT_STRCMP (nms_keyfile_storage_get_filename (a), nms_keyfile_storage_get_filename (b)); + } + + return 0; +} + +/*****************************************************************************/ + +static void +nms_keyfile_storage_init (NMSKeyfileStorage *self) +{ +} + +static NMSKeyfileStorage * +_storage_new (NMSKeyfilePlugin *plugin, + const char *uuid, + const char *filename, + gboolean is_meta_data, + NMSKeyfileStorageType storage_type) + +{ + NMSKeyfileStorage *self; + + nm_assert (NMS_IS_KEYFILE_PLUGIN (plugin)); + nm_assert (nm_utils_is_uuid (uuid)); + nm_assert (filename && filename[0] == '/'); + + self = g_object_new (NMS_TYPE_KEYFILE_STORAGE, + NM_SETTINGS_STORAGE_PLUGIN, plugin, + NM_SETTINGS_STORAGE_UUID, uuid, + NM_SETTINGS_STORAGE_FILENAME, filename, + NULL); + + *((bool *) &self->is_meta_data) = is_meta_data; + *((NMSKeyfileStorageType *) &self->storage_type) = storage_type; + + return self; +} + +NMSKeyfileStorage * +nms_keyfile_storage_new_tombstone (NMSKeyfilePlugin *plugin, + const char *uuid, + const char *filename, + NMSKeyfileStorageType storage_type, + const char *shadowed_storage) +{ + NMSKeyfileStorage *self; + + nm_assert (nm_utils_is_uuid (uuid)); + nm_assert (filename && filename[0] == '/'); + nm_assert (nms_keyfile_nmmeta_check_filename (filename, NULL)); + nm_assert (NM_IN_SET (storage_type, NMS_KEYFILE_STORAGE_TYPE_ETC, + NMS_KEYFILE_STORAGE_TYPE_RUN)); + + self = _storage_new (plugin, uuid, filename, TRUE, storage_type); + self->u.meta_data.is_tombstone = TRUE; + if (storage_type == NMS_KEYFILE_STORAGE_TYPE_RUN) + self->u.meta_data.shadowed_storage = g_strdup (shadowed_storage); + return self; +} + +NMSKeyfileStorage * +nms_keyfile_storage_new_connection (NMSKeyfilePlugin *plugin, + NMConnection *connection_take /* pass reference */, + const char *filename, + NMSKeyfileStorageType storage_type, + NMTernary is_nm_generated_opt, + NMTernary is_volatile_opt, + const char *shadowed_storage, + NMTernary shadowed_owned_opt, + const struct timespec *stat_mtime) +{ + NMSKeyfileStorage *self; + + nm_assert (NMS_IS_KEYFILE_PLUGIN (plugin)); + nm_assert (NM_IS_CONNECTION (connection_take)); + nm_assert (_nm_connection_verify (connection_take, NULL) == NM_SETTING_VERIFY_SUCCESS); + nm_assert (filename && filename[0] == '/'); + nm_assert ( storage_type >= NMS_KEYFILE_STORAGE_TYPE_RUN + && storage_type <= _NMS_KEYFILE_STORAGE_TYPE_LIB_LAST); + nmtst_connection_assert_unchanging (connection_take); + + self = _storage_new (plugin, nm_connection_get_uuid (connection_take), filename, FALSE, storage_type); + + self->u.conn_data.connection = connection_take; /* take reference. */ + + self->u.conn_data.shadowed_storage = g_strdup (shadowed_storage); + + if (stat_mtime) + self->u.conn_data.stat_mtime = *stat_mtime; + + if (storage_type == NMS_KEYFILE_STORAGE_TYPE_RUN) { + self->u.conn_data.is_nm_generated = (is_nm_generated_opt == NM_TERNARY_TRUE); + self->u.conn_data.is_volatile = (is_volatile_opt == NM_TERNARY_TRUE); + self->u.conn_data.shadowed_owned = shadowed_storage + && (shadowed_owned_opt == NM_TERNARY_TRUE); + } + + return self; +} + +static void +_storage_clear (NMSKeyfileStorage *self) +{ + c_list_unlink (&self->parent._storage_lst); + c_list_unlink (&self->parent._storage_by_uuid_lst); + if (self->is_meta_data) + nm_clear_g_free (&self->u.meta_data.shadowed_storage); + else { + g_clear_object (&self->u.conn_data.connection); + nm_clear_g_free (&self->u.conn_data.shadowed_storage); + self->u.conn_data.shadowed_owned = FALSE; + } +} + +static void +dispose (GObject *object) +{ + NMSKeyfileStorage *self = NMS_KEYFILE_STORAGE (object); + + _storage_clear (self); + + G_OBJECT_CLASS (nms_keyfile_storage_parent_class)->dispose (object); +} + +void +nms_keyfile_storage_destroy (NMSKeyfileStorage *self) +{ + _storage_clear (self); + g_object_unref (self); +} + +static void +nms_keyfile_storage_class_init (NMSKeyfileStorageClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMSettingsStorageClass *storage_class = NM_SETTINGS_STORAGE_CLASS (klass); + + object_class->dispose = dispose; + + storage_class->cmp_fcn = (int (*) (NMSettingsStorage *, NMSettingsStorage *)) cmp_fcn; +} + +/*****************************************************************************/ + +#include "settings/nm-settings-connection.h" + +void +nm_settings_storage_load_sett_flags (NMSettingsStorage *self, + NMSettingsConnectionIntFlags *sett_flags, + NMSettingsConnectionIntFlags *sett_mask) +{ + NMSKeyfileStorage *s; + + *sett_flags = NM_SETTINGS_CONNECTION_INT_FLAGS_NONE; + *sett_mask = NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED + | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE; + + if (!NMS_IS_KEYFILE_STORAGE (self)) + return; + + s = NMS_KEYFILE_STORAGE (self); + + if (s->is_meta_data) + return; + if (s->storage_type != NMS_KEYFILE_STORAGE_TYPE_RUN) + return; + + if (s->u.conn_data.is_nm_generated) + *sett_flags |= NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED; + + if (s->u.conn_data.is_volatile) + *sett_flags |= NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE; +} diff --git a/src/settings/plugins/keyfile/nms-keyfile-storage.h b/src/settings/plugins/keyfile/nms-keyfile-storage.h new file mode 100644 index 00000000..2252b47b --- /dev/null +++ b/src/settings/plugins/keyfile/nms-keyfile-storage.h @@ -0,0 +1,264 @@ +/* NetworkManager system settings service - keyfile plugin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2018 Red Hat, Inc. + */ + +#ifndef __NMS_KEYFILE_STORAGE_H__ +#define __NMS_KEYFILE_STORAGE_H__ + +#include "c-list/src/c-list.h" +#include "settings/nm-settings-storage.h" +#include "nms-keyfile-utils.h" + +/*****************************************************************************/ + +#define NMS_TYPE_KEYFILE_STORAGE (nms_keyfile_storage_get_type ()) +#define NMS_KEYFILE_STORAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMS_TYPE_KEYFILE_STORAGE, NMSKeyfileStorage)) +#define NMS_KEYFILE_STORAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMS_TYPE_KEYFILE_STORAGE, NMSKeyfileStorageClass)) +#define NMS_IS_KEYFILE_STORAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMS_TYPE_KEYFILE_STORAGE)) +#define NMS_IS_KEYFILE_STORAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMS_TYPE_KEYFILE_STORAGE)) +#define NMS_KEYFILE_STORAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMS_TYPE_KEYFILE_STORAGE, NMSKeyfileStorageClass)) + +typedef struct { + /* whether this is a tombstone to hide a UUID (via symlink to /dev/null). */ + char *shadowed_storage; + bool is_tombstone:1; +} NMSettingsMetaData; + +typedef struct { + NMSettingsStorage parent; + + /* The connection. Note that there are tombstones (loaded-uuid files to /dev/null) + * that don't have a connection. + * + * Also, we don't actually remember the loaded connection after returning it + * to NMSettings. So, also for regular storages (non-tombstones) this field + * is often cleared. */ + union { + struct { + NMConnection *connection; + + /* when we move a profile from permanent storage to unsaved (/run), then + * we may leave the profile on disk (depending on options for Update2()). + * + * Later, when we save the profile again to disk, we want to re-use that filename. + * Likewise, we delete the (now in-memory) profile, we may want to also delete + * the original filename. + * + * This is the original filename, and we store it inside [.nmmeta] in the + * keyfile in /run. Note that we don't store this in the .nmmeta file, because + * the information is tied to the particular keyfile in /run, not to all UUIDs + * in general. */ + char *shadowed_storage; + + /* the timestamp (stat's mtime) of the keyfile. For meta-data this + * is irrelevant. The purpose is that if the same storage type (directory) has + * multiple files with the same UUID, then the newer file gets preferred. */ + struct timespec stat_mtime; + + /* these flags are only relevant for storages with %NMS_KEYFILE_STORAGE_TYPE_RUN + * (and non-metadata). This is to persist and reload these settings flags to + * /run. + * + * Note that these flags are not stored in as meta-data. The reason is that meta-data + * is per UUID. But these flags are only relevant for a particular keyfile on disk. + * That is, it must be tied to the actual keyfile, and not to the UUID. */ + bool is_nm_generated:1; + bool is_volatile:1; + + /* if shadowed_storage is set, then this flag indicates whether the file + * is owned. The difference comes into play when deleting the in-memory, + * shadowing profile: a owned profile will also be deleted. */ + bool shadowed_owned:1; + + } conn_data; + + /* the content from the .nmmeta file. Note that the nmmeta file has the UUID + * in the filename, that means there can be only two variants of this file: + * in /etc and in /run. As such, this is really meta-data about the entire profile + * (the UUID), and not about the individual keyfile. */ + NMSettingsMetaData meta_data; + + } u; + + /* The storage type. This is directly related to the filename. Since + * the filename cannot change, this value is unchanging. */ + const NMSKeyfileStorageType storage_type; + + /* whether union "u" has meta_data or conn_data. Since the type of the storage + * depends on the (immutable) filename, this is also const. */ + const bool is_meta_data; + + /* this flag is only used during reload to mark and prune old entries. */ + bool is_dirty:1; + +} NMSKeyfileStorage; + +typedef struct _NMSKeyfileStorageClass NMSKeyfileStorageClass; + +GType nms_keyfile_storage_get_type (void); + +struct _NMSKeyfilePlugin; + +NMSKeyfileStorage *nms_keyfile_storage_new_tombstone (struct _NMSKeyfilePlugin *self, + const char *uuid, + const char *filename, + NMSKeyfileStorageType storage_type, + const char *shadowed_storage); + +NMSKeyfileStorage *nms_keyfile_storage_new_connection (struct _NMSKeyfilePlugin *self, + NMConnection *connection_take /* pass reference */, + const char *filename, + NMSKeyfileStorageType storage_type, + NMTernary is_nm_generated_opt, + NMTernary is_volatile_opt, + const char *shadowed_storage, + NMTernary shadowed_owned_opt, + const struct timespec *stat_mtime); + +void nms_keyfile_storage_destroy (NMSKeyfileStorage *storage); + +/*****************************************************************************/ + +void nms_keyfile_storage_copy_content (NMSKeyfileStorage *dst, + const NMSKeyfileStorage *src); + +NMConnection *nms_keyfile_storage_steal_connection (NMSKeyfileStorage *storage); + +/*****************************************************************************/ + +static inline const char * +nms_keyfile_storage_get_uuid (const NMSKeyfileStorage *self) +{ + return nm_settings_storage_get_uuid ((const NMSettingsStorage *) self); +} + +static inline const char * +nms_keyfile_storage_get_filename (const NMSKeyfileStorage *self) +{ + return nm_settings_storage_get_filename ((const NMSettingsStorage *) self); +} + +/*****************************************************************************/ + +static inline gboolean +nm_settings_storage_is_keyfile_run (const NMSettingsStorage *self) +{ + return NMS_IS_KEYFILE_STORAGE (self) + && (((NMSKeyfileStorage *) self)->storage_type == NMS_KEYFILE_STORAGE_TYPE_RUN); +} + +static inline gboolean +nm_settings_storage_is_keyfile_lib (const NMSettingsStorage *self) +{ + return NMS_IS_KEYFILE_STORAGE (self) + && (((NMSKeyfileStorage *) self)->storage_type >= NMS_KEYFILE_STORAGE_TYPE_LIB_BASE); +} + +static inline const NMSettingsMetaData * +nm_settings_storage_is_meta_data (const NMSettingsStorage *storage) +{ + const NMSKeyfileStorage *self; + + if (!NMS_IS_KEYFILE_STORAGE (storage)) + return NULL; + + self = (NMSKeyfileStorage *) storage; + + if (!self->is_meta_data) + return NULL; + + return &self->u.meta_data; +} + +static inline const NMSettingsMetaData * +nm_settings_storage_is_meta_data_alive (const NMSettingsStorage *storage) +{ + const NMSettingsMetaData *meta_data; + + meta_data = nm_settings_storage_is_meta_data (storage); + + if (!meta_data) + return NULL; + + /* Regular (all other) storages are alive as long as they report a NMConnection, and + * they will be dropped, once they have no more connection. + * + * Meta-data storages are special: they never report a NMConnection. + * So, a meta-data storage is alive as long as it is tracked by the + * settings plugin. + * + * This function is used to ckeck for that. */ + + if (c_list_is_empty (&storage->_storage_lst)) + return NULL; + + return meta_data; +} + +static inline const char * +nm_settings_storage_get_shadowed_storage (const NMSettingsStorage *storage, + gboolean *out_shadowed_owned) +{ + if (NMS_IS_KEYFILE_STORAGE (storage)) { + const NMSKeyfileStorage *self = (const NMSKeyfileStorage *) storage; + + if (self->storage_type == NMS_KEYFILE_STORAGE_TYPE_RUN) { + if (!self->is_meta_data) { + if (self->u.conn_data.shadowed_storage) { + NM_SET_OUT (out_shadowed_owned, self->u.conn_data.shadowed_owned); + return self->u.conn_data.shadowed_storage; + } + } else { + NM_SET_OUT (out_shadowed_owned, FALSE); + return self->u.meta_data.shadowed_storage; + } + } + } + + NM_SET_OUT (out_shadowed_owned, FALSE); + return NULL; +} + +static inline const char * +nm_settings_storage_get_filename_for_shadowed_storage (const NMSettingsStorage *storage) +{ + g_return_val_if_fail (NM_IS_SETTINGS_STORAGE (storage), NULL); + + if (!storage->_filename) + return NULL; + + if (NMS_IS_KEYFILE_STORAGE (storage)) { + const NMSKeyfileStorage *self = (const NMSKeyfileStorage *) storage; + + if ( self->is_meta_data + || self->storage_type != NMS_KEYFILE_STORAGE_TYPE_ETC) + return NULL; + } + + return storage->_filename; +} + +/*****************************************************************************/ + +enum _NMSettingsConnectionIntFlags; + +void nm_settings_storage_load_sett_flags (NMSettingsStorage *self, + enum _NMSettingsConnectionIntFlags *sett_flags, + enum _NMSettingsConnectionIntFlags *sett_mask); + +#endif /* __NMS_KEYFILE_STORAGE_H__ */ diff --git a/src/settings/plugins/keyfile/nms-keyfile-utils.c b/src/settings/plugins/keyfile/nms-keyfile-utils.c index 3c4b0288..ea03e1b6 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-utils.c +++ b/src/settings/plugins/keyfile/nms-keyfile-utils.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * This program is free software; you can redistribute it and/or modify @@ -15,7 +14,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * (C) Copyright 2010 Red Hat, Inc. + * (C) Copyright 2010 - 2018 Red Hat, Inc. */ #include "nm-default.h" @@ -25,6 +24,7 @@ #include <stdlib.h> #include <sys/stat.h> +#include "nm-glib-aux/nm-io-utils.h" #include "nm-keyfile-internal.h" #include "nm-utils.h" #include "nm-setting-wired.h" @@ -34,108 +34,155 @@ /*****************************************************************************/ +#define NMMETA_KF_GROUP_NAME_NMMETA "nmmeta" +#define NMMETA_KF_KEY_NAME_NMMETA_UUID "uuid" +#define NMMETA_KF_KEY_NAME_NMMETA_LOADED_PATH "loaded-path" +#define NMMETA_KF_KEY_NAME_NMMETA_SHADOWED_STORAGE "shadowed-storage" + +/*****************************************************************************/ + +const char * +nms_keyfile_nmmeta_check_filename (const char *filename, + guint *out_uuid_len) +{ + const char *uuid; + const char *s; + gsize len; + + s = strrchr (filename, '/'); + if (s) + filename = &s[1]; + + len = strlen (filename); + if ( len <= NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMMETA) + || memcmp (&filename[len - NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMMETA)], + NM_KEYFILE_PATH_SUFFIX_NMMETA, + NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMMETA)) != 0) { + /* the filename does not have the right suffix. */ + return NULL; + } + + len -= NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMMETA); + + 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 NULL; + } + + uuid = nm_strndup_a (100, filename, len, NULL); + if (!nm_utils_is_uuid (uuid)) + return NULL; + + NM_SET_OUT (out_uuid_len, len); + return filename; +} + char * -nms_keyfile_loaded_uuid_filename (const char *dirname, - const char *uuid, - gboolean temporary) +nms_keyfile_nmmeta_filename (const char *dirname, + const char *uuid, + gboolean temporary) { char filename[250]; + char *s; nm_assert (dirname && dirname[0] == '/'); - nm_assert (uuid && nm_utils_is_uuid (uuid) && !strchr (uuid, '/')); + nm_assert ( nm_utils_is_uuid (uuid) + && !strchr (uuid, '/')); if (g_snprintf (filename, sizeof (filename), - "%s%s%s%s", - NM_KEYFILE_PATH_PREFIX_NMLOADED, + "%s%s%s", uuid, - NM_KEYFILE_PATH_SUFFIX_NMCONNECTION, + NM_KEYFILE_PATH_SUFFIX_NMMETA, temporary ? "~" : "") >= sizeof (filename)) { - /* valid uuids are limited in length. The buffer should always be large - * enough. */ + /* valid uuids are limited in length (nm_utils_is_uuid). The buffer should always + * be large enough. */ nm_assert_not_reached (); - return NULL; } - return g_build_filename (dirname, filename, NULL); + s = g_build_filename (dirname, filename, NULL); + + nm_assert (nm_keyfile_utils_ignore_filename (s, FALSE)); + + return s; } gboolean -nms_keyfile_loaded_uuid_read (const char *dirname, - const char *filename, - char **out_full_filename, - char **out_uuid, - char **out_loaded_path) +nms_keyfile_nmmeta_read (const char *dirname, + const char *filename, + char **out_full_filename, + char **out_uuid, + char **out_loaded_path, + char **out_shadowed_storage, + struct stat *out_st) { const char *uuid; - const char *tmp; - gsize len; + guint uuid_len; gs_free char *full_filename = NULL; - gs_free char *ln = NULL; + gs_free char *loaded_path = NULL; + gs_free char *shadowed_storage = NULL; + struct stat st_stack; + struct stat *st = out_st ?: &st_stack; 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(). */ + uuid = nms_keyfile_nmmeta_check_filename (filename, &uuid_len); + if (!uuid) 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. */ + full_filename = g_build_filename (dirname, filename, NULL); + + if (!nms_keyfile_utils_check_file_permissions (NMS_KEYFILE_FILETYPE_NMMETA, + full_filename, + st, + NULL)) return FALSE; - } - tmp = &filename[NM_STRLEN (NM_KEYFILE_PATH_PREFIX_NMLOADED)]; - len -= NM_STRLEN (NM_KEYFILE_PATH_PREFIX_NMLOADED); + if (S_ISREG (st->st_mode)) { + gs_unref_keyfile GKeyFile *kf = NULL; + gs_free char *v_uuid = NULL; - 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); + kf = g_key_file_new (); - 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; - } + if (!g_key_file_load_from_file (kf, full_filename, G_KEY_FILE_NONE, NULL)) + return FALSE; - uuid = nm_strndup_a (100, tmp, len, NULL); - if (!nm_utils_is_uuid (uuid)) - return FALSE; + v_uuid = g_key_file_get_string (kf, NMMETA_KF_GROUP_NAME_NMMETA, NMMETA_KF_KEY_NAME_NMMETA_UUID, NULL); + if (!nm_streq0 (v_uuid, uuid)) + return FALSE; - full_filename = g_build_filename (dirname, filename, NULL); + loaded_path = g_key_file_get_string (kf, NMMETA_KF_GROUP_NAME_NMMETA, NMMETA_KF_KEY_NAME_NMMETA_LOADED_PATH, NULL); + shadowed_storage = g_key_file_get_string (kf, NMMETA_KF_GROUP_NAME_NMMETA, NMMETA_KF_KEY_NAME_NMMETA_SHADOWED_STORAGE, NULL); - if (!nms_keyfile_utils_check_file_permissions (NMS_KEYFILE_FILETYPE_NMLOADED, - full_filename, - NULL, - NULL)) - return FALSE; + if ( !loaded_path + && !shadowed_storage) { + /* if there is no useful information in the file, it is the same as if + * the file is not present. Signal failure. */ + return FALSE; + } - ln = nm_utils_read_link_absolute (full_filename, NULL); - if (!ln) - return FALSE; + } else { + loaded_path = nm_utils_read_link_absolute (full_filename, NULL); + if (!loaded_path) + return FALSE; + } - NM_SET_OUT (out_uuid, g_strdup (uuid)); + NM_SET_OUT (out_uuid, g_strndup (uuid, uuid_len)); NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename)); - NM_SET_OUT (out_loaded_path, g_steal_pointer (&ln)); + NM_SET_OUT (out_loaded_path, g_steal_pointer (&loaded_path)); + NM_SET_OUT (out_shadowed_storage, g_steal_pointer (&shadowed_storage)); return TRUE; } 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) +nms_keyfile_nmmeta_read_from_file (const char *full_filename, + char **out_dirname, + char **out_filename, + char **out_uuid, + char **out_loaded_path, + char **out_shadowed_storage) { gs_free char *dirname = NULL; gs_free char *filename = NULL; @@ -145,11 +192,13 @@ nms_keyfile_loaded_uuid_read_from_file (const char *full_filename, filename = g_path_get_basename (full_filename); dirname = g_path_get_dirname (full_filename); - if (!nms_keyfile_loaded_uuid_read (dirname, - filename, - NULL, - out_uuid, - out_loaded_path)) + if (!nms_keyfile_nmmeta_read (dirname, + filename, + NULL, + out_uuid, + out_loaded_path, + out_shadowed_storage, + NULL)) return FALSE; NM_SET_OUT (out_dirname, g_steal_pointer (&dirname)); @@ -158,20 +207,23 @@ nms_keyfile_loaded_uuid_read_from_file (const char *full_filename, } gboolean -nms_keyfile_loaded_uuid_write (const char *dirname, - const char *uuid, - const char *loaded_path, - gboolean allow_relative, - char **out_full_filename) +nms_keyfile_nmmeta_write (const char *dirname, + const char *uuid, + const char *loaded_path, + gboolean loaded_path_allow_relative, + const char *shadowed_storage, + char **out_full_filename) { 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 ( nm_utils_is_uuid (uuid) + && !strchr (uuid, '/')); nm_assert (!loaded_path || loaded_path[0] == '/'); + nm_assert (!shadowed_storage || loaded_path); - full_filename_tmp = nms_keyfile_loaded_uuid_filename (dirname, uuid, TRUE); + full_filename_tmp = nms_keyfile_nmmeta_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)); @@ -188,7 +240,7 @@ nms_keyfile_loaded_uuid_write (const char *dirname, return success; } - if (allow_relative) { + if (loaded_path_allow_relative) { const char *f; f = nm_utils_file_is_in_path (loaded_path, dirname); @@ -199,18 +251,40 @@ nms_keyfile_loaded_uuid_write (const char *dirname, } } - 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; - } + full_filename = g_strndup (full_filename_tmp, strlen (full_filename_tmp) - 1); - 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; + if (shadowed_storage) { + gs_unref_keyfile GKeyFile *kf = NULL; + gs_free char *contents = NULL; + gsize length; + + kf = g_key_file_new (); + + g_key_file_set_string (kf, NMMETA_KF_GROUP_NAME_NMMETA, NMMETA_KF_KEY_NAME_NMMETA_UUID, uuid); + g_key_file_set_string (kf, NMMETA_KF_GROUP_NAME_NMMETA, NMMETA_KF_KEY_NAME_NMMETA_LOADED_PATH, loaded_path); + g_key_file_set_string (kf, NMMETA_KF_GROUP_NAME_NMMETA, NMMETA_KF_KEY_NAME_NMMETA_SHADOWED_STORAGE, shadowed_storage); + + contents = g_key_file_to_data (kf, &length, NULL); + + if (!nm_utils_file_set_contents (full_filename, contents, length, 0600, NULL)) { + NM_SET_OUT (out_full_filename, g_steal_pointer (&full_filename_tmp)); + return FALSE; + } + } else { + /* we only have the "loaded_path" to store. That is commonly used for the tombstones to + * link to /dev/null. A symlink is sufficient to store that ammount of information. + * No need to bother with a keyfile. */ + 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; + } + + 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)); @@ -232,10 +306,11 @@ nms_keyfile_utils_check_file_permissions_stat (NMSKeyfileFiletype filetype, "file is not a regular file"); return FALSE; } - } else if (filetype == NMS_KEYFILE_FILETYPE_NMLOADED) { - if (!S_ISLNK (st->st_mode)) { + } else if (filetype == NMS_KEYFILE_FILETYPE_NMMETA) { + if ( !S_ISLNK (st->st_mode) + && !S_ISREG (st->st_mode)) { g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "file is not a slink"); + "file is neither a symlink nor a regular file"); return FALSE; } } else @@ -249,7 +324,7 @@ nms_keyfile_utils_check_file_permissions_stat (NMSKeyfileFiletype filetype, return FALSE; } - if ( filetype == NMS_KEYFILE_FILETYPE_KEYFILE + if ( S_ISREG (st->st_mode) && (st->st_mode & 0077)) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "File permissions (%03o) are insecure", @@ -279,7 +354,7 @@ nms_keyfile_utils_check_file_permissions (NMSKeyfileFiletype filetype, "cannot access file: %s", nm_strerror_native (errsv)); return FALSE; } - } else if (filetype == NMS_KEYFILE_FILETYPE_NMLOADED) { + } else if (filetype == NMS_KEYFILE_FILETYPE_NMMETA) { if (lstat (filename, &st) != 0) { errsv = errno; g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, @@ -295,22 +370,3 @@ nms_keyfile_utils_check_file_permissions (NMSKeyfileFiletype filetype, NM_SET_OUT (out_st, st); return TRUE; } - -/*****************************************************************************/ - -const char * -nms_keyfile_utils_get_path (void) -{ - static char *path = NULL; - - if (G_UNLIKELY (!path)) { - path = nm_config_data_get_value (NM_CONFIG_GET_DATA_ORIG, - NM_CONFIG_KEYFILE_GROUP_KEYFILE, - NM_CONFIG_KEYFILE_KEY_KEYFILE_PATH, - NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY); - if (!path) - 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 bc601dad..723c4436 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-utils.h +++ b/src/settings/plugins/keyfile/nms-keyfile-utils.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service * * This program is free software; you can redistribute it and/or modify @@ -15,7 +14,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * (C) Copyright 2010-2016 Red Hat, Inc. + * (C) Copyright 2010 - 2018 Red Hat, Inc. */ #ifndef __NMS_KEYFILE_UTILS_H__ @@ -23,42 +22,56 @@ #include "NetworkManagerUtils.h" -#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) - typedef enum { NMS_KEYFILE_FILETYPE_KEYFILE, - NMS_KEYFILE_FILETYPE_NMLOADED, + NMS_KEYFILE_FILETYPE_NMMETA, } NMSKeyfileFiletype; -const char *nms_keyfile_utils_get_path (void); +typedef enum { + NMS_KEYFILE_STORAGE_TYPE_RUN = 1, /* read-write, runtime only, e.g. /run */ + NMS_KEYFILE_STORAGE_TYPE_ETC = 2, /* read-write, persistent, e.g. /etc */ + NMS_KEYFILE_STORAGE_TYPE_LIB_BASE = 3, /* read-only, e.g. /usr/lib */ + + _NMS_KEYFILE_STORAGE_TYPE_LIB_LAST = 1000, +} NMSKeyfileStorageType; + +static inline NMSKeyfileStorageType +NMS_KEYFILE_STORAGE_TYPE_LIB (guint run_idx) +{ + nm_assert (run_idx <= (_NMS_KEYFILE_STORAGE_TYPE_LIB_LAST - NMS_KEYFILE_STORAGE_TYPE_LIB_BASE)); + return NMS_KEYFILE_STORAGE_TYPE_LIB_BASE + run_idx; +} /*****************************************************************************/ -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); +const char *nms_keyfile_nmmeta_check_filename (const char *filename, + guint *out_uuid_len); + +char *nms_keyfile_nmmeta_filename (const char *dirname, + const char *uuid, + gboolean temporary); + +gboolean nms_keyfile_nmmeta_read (const char *dirname, + const char *filename, + char **out_full_filename, + char **out_uuid, + char **out_loaded_path, + char **out_shadowed_storage, + struct stat *out_st); + +gboolean nms_keyfile_nmmeta_read_from_file (const char *full_filename, + char **out_dirname, + char **out_filename, + char **out_uuid, + char **out_loaded_path, + char **out_shadowed_storage); + +gboolean nms_keyfile_nmmeta_write (const char *dirname, + const char *uuid, + const char *loaded_path, + gboolean loaded_path_allow_relative, + const char *shadowed_storage, + char **out_full_filename); /*****************************************************************************/ diff --git a/src/settings/plugins/keyfile/nms-keyfile-writer.c b/src/settings/plugins/keyfile/nms-keyfile-writer.c index 2e06aeb9..5fbbb7a1 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-writer.c +++ b/src/settings/plugins/keyfile/nms-keyfile-writer.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service - keyfile plugin * * This program is free software; you can redistribute it and/or modify @@ -169,6 +168,10 @@ _handler_write (NMConnection *connection, static gboolean _internal_write_connection (NMConnection *connection, + gboolean is_nm_generated, + gboolean is_volatile, + const char *shadowed_storage, + gboolean shadowed_owned, const char *keyfile_dir, const char *profile_dir, gboolean with_extension, @@ -177,6 +180,8 @@ _internal_write_connection (NMConnection *connection, const char *existing_path, gboolean existing_path_read_only, gboolean force_rename, + NMSKeyfileWriterAllowFilenameCb allow_filename_cb, + gpointer allow_filename_user_data, char **out_path, NMConnection **out_reread, gboolean *out_reread_same, @@ -191,33 +196,57 @@ _internal_write_connection (NMConnection *connection, GError *local_err = NULL; int errsv; gboolean rename; + int i_path; g_return_val_if_fail (!out_path || !*out_path, FALSE); g_return_val_if_fail (keyfile_dir && keyfile_dir[0] == '/', FALSE); + nm_assert (_nm_connection_verify (connection, NULL) == NM_SETTING_VERIFY_SUCCESS); + + nm_assert (!shadowed_owned || shadowed_storage); + rename = force_rename || existing_path_read_only || ( existing_path && !nm_utils_file_is_in_path (existing_path, keyfile_dir)); - switch (_nm_connection_verify (connection, error)) { - case NM_SETTING_VERIFY_NORMALIZABLE: - nm_assert_not_reached (); - /* fall-through */ - case NM_SETTING_VERIFY_SUCCESS: - break; - default: - g_return_val_if_reached (FALSE); - } - id = nm_connection_get_id (connection); - g_assert (id && *id); + nm_assert (id && *id); info.keyfile_dir = keyfile_dir; kf_file = nm_keyfile_write (connection, _handler_write, &info, error); if (!kf_file) return FALSE; + + if (is_nm_generated) { + g_key_file_set_boolean (kf_file, + NM_KEYFILE_GROUP_NMMETA, + NM_KEYFILE_KEY_NMMETA_NM_GENERATED, + TRUE); + } + + if (is_volatile) { + g_key_file_set_boolean (kf_file, + NM_KEYFILE_GROUP_NMMETA, + NM_KEYFILE_KEY_NMMETA_VOLATILE, + TRUE); + } + + if (shadowed_storage) { + g_key_file_set_string (kf_file, + NM_KEYFILE_GROUP_NMMETA, + NM_KEYFILE_KEY_NMMETA_SHADOWED_STORAGE, + shadowed_storage); + } + + if (shadowed_owned) { + g_key_file_set_boolean (kf_file, + NM_KEYFILE_GROUP_NMMETA, + NM_KEYFILE_KEY_NMMETA_SHADOWED_OWNED, + TRUE); + } + kf_content_buf = g_key_file_to_data (kf_file, &kf_content_len, error); if (!kf_content_buf) return FALSE; @@ -225,64 +254,59 @@ _internal_write_connection (NMConnection *connection, if (!g_file_test (keyfile_dir, G_FILE_TEST_IS_DIR)) (void) g_mkdir_with_parents (keyfile_dir, 0755); - /* If we have existing file path, use it. Else generate one from - * connection's ID. - */ - if ( existing_path - && !rename) - path = g_strdup (existing_path); - else { - gs_free char *filename_escaped = NULL; + for (i_path = -2; i_path < 10000; i_path++) { + gs_free char *path_candidate = NULL; + gboolean is_existing_path; - filename_escaped = nm_keyfile_utils_create_filename (id, with_extension); - path = g_build_filename (keyfile_dir, filename_escaped, NULL); - } - - /* If a file with this path already exists (but isn't the existing path - * of the connection) then we need another name. Multiple connections - * can have the same ID (ie if two connections with the same ID are visible - * to different users) but of course can't have the same path. Yeah, - * there's a race here, but there's not a lot we can do about it, and - * we shouldn't get more than one connection with the same UUID either. - */ - if ( !nm_streq0 (path, existing_path) - && g_file_test (path, G_FILE_TEST_EXISTS)) { - guint i; - gboolean name_found = FALSE; + if (i_path == -2) { + if ( !existing_path + || rename) + continue; + path_candidate = g_strdup (existing_path); + } else if (i_path == -1) { + gs_free char *filename_escaped = NULL; - /* A keyfile with this connection's ID already exists. Pick another name. */ - for (i = 0; i < 100; i++) { + filename_escaped = nm_keyfile_utils_create_filename (id, with_extension); + path_candidate = g_build_filename (keyfile_dir, filename_escaped, NULL); + } else { gs_free char *filename_escaped = NULL; gs_free char *filename = NULL; - if (i == 0) + if (i_path == 0) filename = g_strdup_printf ("%s-%s", id, nm_connection_get_uuid (connection)); else - filename = g_strdup_printf ("%s-%s-%u", id, nm_connection_get_uuid (connection), i); + filename = g_strdup_printf ("%s-%s-%d", id, nm_connection_get_uuid (connection), i_path); filename_escaped = nm_keyfile_utils_create_filename (filename, with_extension); - g_free (path); - path = g_strdup_printf ("%s/%s", keyfile_dir, filename_escaped); - - if ( nm_streq0 (path, existing_path) - || !g_file_test (path, G_FILE_TEST_EXISTS)) { - name_found = TRUE; - break; - } + path_candidate = g_strdup_printf ("%s/%s", keyfile_dir, filename_escaped); } - if (!name_found) { - if (existing_path_read_only || !existing_path) { - /* this really should not happen, we tried hard to find an unused name... bail out. */ - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, - "could not find suitable keyfile file name (%s already used)", path); - return FALSE; - } - /* Both our preferred path based on connection id and id-uuid are taken. - * Fallback to @existing_path */ - g_free (path); - path = g_strdup (existing_path); + + is_existing_path = existing_path + && nm_streq (existing_path, path_candidate); + + if ( is_existing_path + && rename) + continue; + + if ( allow_filename_cb + && !allow_filename_cb (path_candidate, allow_filename_user_data)) + continue; + + if (!is_existing_path) { + if (g_file_test (path_candidate, G_FILE_TEST_EXISTS)) + continue; } + + path = g_steal_pointer (&path_candidate); + break; + } + + if (!path) { + /* this really should not happen, we tried hard to find an unused name... bail out. */ + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, + "could not find suitable keyfile file name (%s already used)", path); + return FALSE; } nm_utils_file_set_contents (path, kf_content_buf, kf_content_len, 0600, &local_err); @@ -348,30 +372,37 @@ _internal_write_connection (NMConnection *connection, gboolean nms_keyfile_writer_connection (NMConnection *connection, - gboolean save_to_disk, + gboolean is_nm_generated, + gboolean is_volatile, + const char *shadowed_storage, + gboolean shadowed_owned, + const char *keyfile_dir, + const char *profile_dir, const char *existing_path, + gboolean existing_path_read_only, gboolean force_rename, + NMSKeyfileWriterAllowFilenameCb allow_filename_cb, + gpointer allow_filename_user_data, char **out_path, NMConnection **out_reread, gboolean *out_reread_same, GError **error) { - const char *keyfile_dir; - - if (save_to_disk) - keyfile_dir = nms_keyfile_utils_get_path (); - else - keyfile_dir = NM_KEYFILE_PATH_NAME_RUN; - return _internal_write_connection (connection, + is_nm_generated, + is_volatile, + shadowed_storage, + shadowed_owned, keyfile_dir, - nms_keyfile_utils_get_path (), + profile_dir, TRUE, 0, 0, existing_path, - FALSE, + existing_path_read_only, force_rename, + allow_filename_cb, + allow_filename_user_data, out_path, out_reread, out_reread_same, @@ -389,6 +420,10 @@ nms_keyfile_writer_test_connection (NMConnection *connection, GError **error) { return _internal_write_connection (connection, + FALSE, + FALSE, + NULL, + FALSE, keyfile_dir, keyfile_dir, FALSE, @@ -397,6 +432,8 @@ nms_keyfile_writer_test_connection (NMConnection *connection, NULL, FALSE, FALSE, + NULL, + NULL, out_path, out_reread, out_reread_same, diff --git a/src/settings/plugins/keyfile/nms-keyfile-writer.h b/src/settings/plugins/keyfile/nms-keyfile-writer.h index 030fb7cc..99e86025 100644 --- a/src/settings/plugins/keyfile/nms-keyfile-writer.h +++ b/src/settings/plugins/keyfile/nms-keyfile-writer.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service - keyfile plugin * * This program is free software; you can redistribute it and/or modify @@ -24,10 +23,21 @@ #include "nm-connection.h" +typedef gboolean (*NMSKeyfileWriterAllowFilenameCb) (const char *check_filename, + gpointer allow_filename_user_data); + gboolean nms_keyfile_writer_connection (NMConnection *connection, - gboolean save_to_disk, + gboolean is_nm_generated, + gboolean is_volatile, + const char *shadowed_storage, + gboolean shadowed_owned, + const char *keyfile_dir, + const char *profile_dir, const char *existing_path, + gboolean existing_path_read_only, gboolean force_rename, + NMSKeyfileWriterAllowFilenameCb allow_filename_cb, + gpointer allow_filename_user_data, char **out_path, NMConnection **out_reread, gboolean *out_reread_same, diff --git a/src/settings/plugins/keyfile/tests/meson.build b/src/settings/plugins/keyfile/tests/meson.build index 752b6d7b..f1e96bdf 100644 --- a/src/settings/plugins/keyfile/tests/meson.build +++ b/src/settings/plugins/keyfile/tests/meson.build @@ -1,6 +1,4 @@ -test_unit = 'test-keyfile' - -test_keyfiles_dir = join_paths(meson.current_source_dir(), 'keyfiles') +test_unit = 'test-keyfile-settings' exe = executable( test_unit, @@ -9,7 +7,7 @@ exe = executable( ) test( - 'keyfile/' + test_unit, + test_unit, test_script, args: test_args + [exe.full_path()], timeout: default_test_timeout, diff --git a/src/settings/plugins/keyfile/tests/test-keyfile.c b/src/settings/plugins/keyfile/tests/test-keyfile-settings.c index baecac13..f96111a2 100644 --- a/src/settings/plugins/keyfile/tests/test-keyfile.c +++ b/src/settings/plugins/keyfile/tests/test-keyfile-settings.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager system settings service - keyfile plugin * * This program is free software; you can redistribute it and/or modify @@ -73,7 +72,12 @@ check_ip_route (NMSettingIPConfig *config, int idx, const char *destination, int \ _connection = nms_keyfile_reader_from_file (full_filename, \ NULL, \ - (nmtst_get_rand_int () % 2) ? &_error : NULL); \ + NULL, \ + NULL, \ + NULL, \ + NULL, \ + NULL, \ + (nmtst_get_rand_uint32 () % 2) ? &_error : NULL); \ nmtst_assert_success (_connection, _error); \ nmtst_assert_connection_verifies_without_normalization (_connection); \ \ @@ -128,7 +132,7 @@ write_test_connection_reread (NMConnection *connection, gid_t owner_grp; gboolean success; GError *error = NULL; - GError **p_error = (nmtst_get_rand_int () % 2) ? &error : NULL; + GError **p_error = (nmtst_get_rand_uint32 () % 2) ? &error : NULL; gs_unref_object NMConnection *connection_normalized = NULL; g_assert (NM_IS_CONNECTION (connection)); @@ -2508,14 +2512,14 @@ test_nm_keyfile_plugin_utils_escape_filename (void) /*****************************************************************************/ 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) +_assert_keyfile_nmmeta (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; @@ -2534,12 +2538,12 @@ _assert_keyfile_loaded_uuid (const char *dirname, filename = g_path_get_basename (exp_full_filename); - full_filename = nms_keyfile_loaded_uuid_filename (dirname, uuid, FALSE); + full_filename = nms_keyfile_nmmeta_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 (nms_keyfile_nmmeta_write (dirname, uuid, loaded_path, allow_relative, NULL, &full_filename)); g_assert_cmpstr (full_filename, ==, exp_full_filename); nm_clear_g_free (&full_filename); @@ -2551,7 +2555,7 @@ _assert_keyfile_loaded_uuid (const char *dirname, g_assert_cmpstr (symlink_target, ==, exp_symlink_target); - success = nms_keyfile_loaded_uuid_read (dirname, filename, &full_filename, &uuid2, &loaded_path2); + success = nms_keyfile_nmmeta_read (dirname, filename, &full_filename, &uuid2, &loaded_path2, NULL, NULL); g_assert_cmpint (!!exp_uuid, ==, success); if (success) g_assert_cmpstr (full_filename, ==, exp_full_filename); @@ -2562,7 +2566,7 @@ _assert_keyfile_loaded_uuid (const char *dirname, g_assert_cmpstr (loaded_path2, ==, exp_loaded_path); - success = nms_keyfile_loaded_uuid_read_from_file (exp_full_filename, &dirname3, &filename3, &uuid3, &loaded_path3); + success = nms_keyfile_nmmeta_read_from_file (exp_full_filename, &dirname3, &filename3, &uuid3, &loaded_path3, NULL); g_assert_cmpint (!!exp_uuid, ==, success); if (success) { g_assert_cmpstr (dirname3, ==, dirname); @@ -2576,34 +2580,33 @@ _assert_keyfile_loaded_uuid (const char *dirname, } static void -test_loaded_uuid (void) +test_nmmeta (void) { const char *uuid = "3c03fd17-ddc3-4100-a954-88b6fafff959"; - gs_free char *filename = g_strdup_printf ("%s%s%s", - NM_KEYFILE_PATH_PREFIX_NMLOADED, + gs_free char *filename = g_strdup_printf ("%s%s", uuid, - NM_KEYFILE_PATH_SUFFIX_NMCONNECTION); + NM_KEYFILE_PATH_SUFFIX_NMMETA); 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_path0 = NM_KEYFILE_PATH_NMMETA_SYMLINK_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_nmmeta (TEST_SCRATCH_DIR, uuid, NULL, FALSE, full_filename, NULL, NULL, NULL); + _assert_keyfile_nmmeta (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_nmmeta (TEST_SCRATCH_DIR, uuid, loaded_path0, FALSE, full_filename, uuid, loaded_path0, loaded_path0); + _assert_keyfile_nmmeta (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_nmmeta (TEST_SCRATCH_DIR, uuid, loaded_path1, FALSE, full_filename, uuid, loaded_path1, loaded_path1); + _assert_keyfile_nmmeta (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); + _assert_keyfile_nmmeta (TEST_SCRATCH_DIR, uuid, loaded_path2, FALSE, full_filename, uuid, loaded_path2, loaded_path2); + _assert_keyfile_nmmeta (TEST_SCRATCH_DIR, uuid, loaded_path2, TRUE, full_filename, uuid, filename2, loaded_path2); (void) unlink (full_filename); } @@ -2697,7 +2700,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); + g_test_add_func ("/keyfile/test_nmmeta", test_nmmeta); return g_test_run (); } diff --git a/src/settings/plugins/meson.build b/src/settings/plugins/meson.build index a1aa7823..83981aab 100644 --- a/src/settings/plugins/meson.build +++ b/src/settings/plugins/meson.build @@ -1,7 +1,3 @@ -if enable_ibft - subdir('ibft') -endif - if enable_ifcfg_rh subdir('ifcfg-rh') endif diff --git a/src/supplicant/nm-supplicant-config.c b/src/supplicant/nm-supplicant-config.c index 7708224b..806c087c 100644 --- a/src/supplicant/nm-supplicant-config.c +++ b/src/supplicant/nm-supplicant-config.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -50,6 +49,8 @@ typedef struct { gboolean dispose_has_run; gboolean support_pmf; gboolean support_fils; + gboolean support_ft; + gboolean support_sha384; } NMSupplicantConfigPrivate; struct _NMSupplicantConfig { @@ -68,7 +69,8 @@ G_DEFINE_TYPE (NMSupplicantConfig, nm_supplicant_config, G_TYPE_OBJECT) /*****************************************************************************/ NMSupplicantConfig * -nm_supplicant_config_new (gboolean support_pmf, gboolean support_fils) +nm_supplicant_config_new (gboolean support_pmf, gboolean support_fils, + gboolean support_ft, gboolean support_sha384) { NMSupplicantConfigPrivate *priv; NMSupplicantConfig *self; @@ -78,6 +80,8 @@ nm_supplicant_config_new (gboolean support_pmf, gboolean support_fils) priv->support_pmf = support_pmf; priv->support_fils = support_fils; + priv->support_ft = support_ft; + priv->support_sha384 = support_sha384; return self; } @@ -454,7 +458,7 @@ nm_supplicant_config_add_setting_wireless (NMSupplicantConfig * self, GError **error) { NMSupplicantConfigPrivate *priv; - gboolean is_adhoc, is_ap; + gboolean is_adhoc, is_ap, is_mesh; const char *mode, *band; guint32 channel; GBytes *ssid; @@ -469,6 +473,7 @@ nm_supplicant_config_add_setting_wireless (NMSupplicantConfig * self, mode = nm_setting_wireless_get_mode (setting); is_adhoc = (mode && !strcmp (mode, "adhoc")) ? TRUE : FALSE; is_ap = (mode && !strcmp (mode, "ap")) ? TRUE : FALSE; + is_mesh = (mode && !strcmp (mode, "mesh")) ? TRUE : FALSE; if (is_adhoc || is_ap) priv->ap_scan = 2; else @@ -498,7 +503,12 @@ nm_supplicant_config_add_setting_wireless (NMSupplicantConfig * self, return FALSE; } - if ((is_adhoc || is_ap) && fixed_freq) { + if (is_mesh) { + if (!nm_supplicant_config_add_option (self, "mode", "5", -1, NULL, error)) + return FALSE; + } + + if ((is_adhoc || is_ap || is_mesh) && fixed_freq) { gs_free char *str_freq = NULL; str_freq = g_strdup_printf ("%u", fixed_freq); @@ -506,10 +516,10 @@ nm_supplicant_config_add_setting_wireless (NMSupplicantConfig * self, return FALSE; } - /* Except for Ad-Hoc and Hotspot, request that the driver probe for the + /* Except for Ad-Hoc, Hotspot and Mesh, request that the driver probe for the * specific SSID we want to associate with. */ - if (!(is_adhoc || is_ap)) { + if (!(is_adhoc || is_ap || is_mesh)) { if (!nm_supplicant_config_add_option (self, "scan_ssid", "1", -1, NULL, error)) return FALSE; } @@ -755,7 +765,8 @@ nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self, GError **error) { NMSupplicantConfigPrivate *priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE (self); - const char *key_mgmt, *key_mgmt_conf, *auth_alg; + nm_auto_free_gstring GString *key_mgmt_conf = NULL; + const char *key_mgmt, *auth_alg; const char *psk; gboolean set_pmf; @@ -774,28 +785,43 @@ nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self, fils = NM_SETTING_WIRELESS_SECURITY_FILS_DISABLE; } - key_mgmt = key_mgmt_conf = nm_setting_wireless_security_get_key_mgmt (setting); + key_mgmt = nm_setting_wireless_security_get_key_mgmt (setting); + key_mgmt_conf = g_string_new (key_mgmt); if (nm_streq (key_mgmt, "wpa-psk")) { if (priv->support_pmf) - key_mgmt_conf = "wpa-psk wpa-psk-sha256"; + g_string_append (key_mgmt_conf, " wpa-psk-sha256"); + if (priv->support_ft) + g_string_append (key_mgmt_conf, " ft-psk"); } else if (nm_streq (key_mgmt, "wpa-eap")) { + if (priv->support_pmf) + g_string_append (key_mgmt_conf, " wpa-eap-sha256"); + if (priv->support_ft) + g_string_append (key_mgmt_conf, " ft-eap"); + if (priv->support_ft && priv->support_sha384) + g_string_append (key_mgmt_conf, " ft-eap-sha384"); switch (fils) { - case NM_SETTING_WIRELESS_SECURITY_FILS_OPTIONAL: - key_mgmt_conf = priv->support_pmf - ? "wpa-eap wpa-eap-sha256 fils-sha256 fils-sha384" - : "wpa-eap fils-sha256 fils-sha384"; - break; case NM_SETTING_WIRELESS_SECURITY_FILS_REQUIRED: - key_mgmt_conf = "fils-sha256 fils-sha384"; + g_string_truncate (key_mgmt_conf, 0); + if (!priv->support_pmf) + g_string_assign (key_mgmt_conf, "fils-sha256 fils-sha384"); + /* fall-through */ + case NM_SETTING_WIRELESS_SECURITY_FILS_OPTIONAL: + if (priv->support_pmf) + g_string_append (key_mgmt_conf, " fils-sha256 fils-sha384"); + if (priv->support_pmf && priv->support_ft) + g_string_append (key_mgmt_conf, " ft-fils-sha256"); + if (priv->support_pmf && priv->support_ft & priv->support_sha384) + g_string_append (key_mgmt_conf, " ft-fils-sha384"); break; default: - if (priv->support_pmf) - key_mgmt_conf = "wpa-eap wpa-eap-sha256"; break; } + } else if (nm_streq (key_mgmt, "sae")) { + if (priv->support_ft) + g_string_append (key_mgmt_conf, " ft-sae"); } - if (!add_string_val (self, key_mgmt_conf, "key_mgmt", TRUE, NULL, error)) + if (!add_string_val (self, key_mgmt_conf->str, "key_mgmt", TRUE, NULL, error)) return FALSE; auth_alg = nm_setting_wireless_security_get_auth_alg (setting); diff --git a/src/supplicant/nm-supplicant-config.h b/src/supplicant/nm-supplicant-config.h index f6c845a3..c4e7310d 100644 --- a/src/supplicant/nm-supplicant-config.h +++ b/src/supplicant/nm-supplicant-config.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -40,7 +39,8 @@ typedef struct _NMSupplicantConfigClass NMSupplicantConfigClass; GType nm_supplicant_config_get_type (void); -NMSupplicantConfig *nm_supplicant_config_new (gboolean support_pmf, gboolean support_fils); +NMSupplicantConfig *nm_supplicant_config_new (gboolean support_pmf, gboolean support_fils, + gboolean support_ft, gboolean support_sha384); guint32 nm_supplicant_config_get_ap_scan (NMSupplicantConfig *self); diff --git a/src/supplicant/nm-supplicant-interface.c b/src/supplicant/nm-supplicant-interface.c index 12d21aba..079afca3 100644 --- a/src/supplicant/nm-supplicant-interface.c +++ b/src/supplicant/nm-supplicant-interface.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -81,6 +80,12 @@ typedef struct _AddNetworkData { AssocData *assoc_data; } AddNetworkData; +typedef struct { + NMSupplicantInterface *self; + NMSupplicantInterfaceDisconnectCb callback; + gpointer user_data; +} DisconnectData; + enum { STATE, /* change in the interface's state */ REMOVED, /* interface was removed by the supplicant */ @@ -113,7 +118,10 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMSupplicantInterface, PROP_PMF_SUPPORT, PROP_FILS_SUPPORT, PROP_P2P_SUPPORT, + PROP_MESH_SUPPORT, PROP_WFD_SUPPORT, + PROP_FT_SUPPORT, + PROP_SHA384_SUPPORT, ); typedef struct { @@ -125,7 +133,10 @@ typedef struct { NMSupplicantFeature pmf_support; NMSupplicantFeature fils_support; NMSupplicantFeature p2p_support; + NMSupplicantFeature mesh_support; NMSupplicantFeature wfd_support; + NMSupplicantFeature ft_support; + NMSupplicantFeature sha384_support; guint32 max_scan_ssids; guint32 ready_count; @@ -782,11 +793,29 @@ nm_supplicant_interface_get_p2p_support (NMSupplicantInterface *self) } NMSupplicantFeature +nm_supplicant_interface_get_mesh_support (NMSupplicantInterface *self) +{ + return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->mesh_support; +} + +NMSupplicantFeature nm_supplicant_interface_get_wfd_support (NMSupplicantInterface *self) { return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->wfd_support; } +NMSupplicantFeature +nm_supplicant_interface_get_ft_support (NMSupplicantInterface *self) +{ + return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->ft_support; +} + +NMSupplicantFeature +nm_supplicant_interface_get_sha384_support (NMSupplicantInterface *self) +{ + return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->sha384_support; +} + void nm_supplicant_interface_set_ap_support (NMSupplicantInterface *self, NMSupplicantFeature ap_support) @@ -837,6 +866,15 @@ nm_supplicant_interface_set_p2p_support (NMSupplicantInterface *self, } void +nm_supplicant_interface_set_mesh_support (NMSupplicantInterface *self, + NMSupplicantFeature mesh_support) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + priv->mesh_support = mesh_support; +} + +void nm_supplicant_interface_set_wfd_support (NMSupplicantInterface *self, NMSupplicantFeature wfd_support) { @@ -845,6 +883,24 @@ nm_supplicant_interface_set_wfd_support (NMSupplicantInterface *self, priv->wfd_support = wfd_support; } +void +nm_supplicant_interface_set_ft_support (NMSupplicantInterface *self, + NMSupplicantFeature ft_support) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + priv->ft_support = ft_support; +} + +void +nm_supplicant_interface_set_sha384_support (NMSupplicantInterface *self, + NMSupplicantFeature sha384_support) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + priv->sha384_support = sha384_support; +} + /*****************************************************************************/ static void @@ -2118,6 +2174,60 @@ nm_supplicant_interface_disconnect (NMSupplicantInterface * self) } static void +disconnect_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) +{ + DisconnectData *disconnect_data = user_data; + gs_unref_object NMSupplicantInterface *self = disconnect_data->self; + gs_unref_variant GVariant *reply = NULL; + gs_free_error GError *error = NULL; + + reply = g_dbus_proxy_call_finish (proxy, result, &error); + + /* an already disconnected interface is not an error*/ + if ( !reply + && !strstr (error->message, "fi.w1.wpa_supplicant1.NotConnected")) { + g_clear_error(&error); + } + + disconnect_data->callback(self, error, disconnect_data->user_data); + g_slice_free (DisconnectData, disconnect_data); +} + +void +nm_supplicant_interface_disconnect_async ( NMSupplicantInterface * self, + GCancellable * cancellable, + NMSupplicantInterfaceDisconnectCb callback, + gpointer user_data) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + DisconnectData *disconnect_data; + + /* Don't do anything if there is no connection to the supplicant yet. */ + if (!priv->iface_proxy) + return; + + g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (self)); + g_return_if_fail (NULL != callback); + + disconnect_data = g_slice_new0(DisconnectData); + + /* Keep interface alive until disconnect finishes */ + disconnect_data->self = g_object_ref (self); + disconnect_data->callback = callback; + disconnect_data->user_data = user_data; + + /* Disconnect the interface */ + g_dbus_proxy_call (priv->iface_proxy, + "Disconnect", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + (GAsyncReadyCallback) disconnect_cb, + disconnect_data); +} + +static void assoc_select_network_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) { NMSupplicantInterface *self; @@ -2681,10 +2791,22 @@ set_property (GObject *object, /* construct-only */ priv->p2p_support = g_value_get_int (value); break; + case PROP_MESH_SUPPORT: + /* construct-only */ + priv->mesh_support = g_value_get_int (value); + break; case PROP_WFD_SUPPORT: /* construct-only */ priv->wfd_support = g_value_get_int (value); break; + case PROP_FT_SUPPORT: + /* construct-only */ + priv->ft_support = g_value_get_int (value); + break; + case PROP_SHA384_SUPPORT: + /* construct-only */ + priv->sha384_support = g_value_get_int (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -2710,7 +2832,10 @@ nm_supplicant_interface_new (const char *ifname, NMSupplicantFeature pmf_support, NMSupplicantFeature fils_support, NMSupplicantFeature p2p_support, - NMSupplicantFeature wfd_support) + NMSupplicantFeature mesh_support, + NMSupplicantFeature wfd_support, + NMSupplicantFeature ft_support, + NMSupplicantFeature sha384_support) { /* One of ifname or path need to be set */ g_return_val_if_fail (ifname != NULL || object_path != NULL, NULL); @@ -2725,7 +2850,10 @@ nm_supplicant_interface_new (const char *ifname, 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_MESH_SUPPORT, (int) mesh_support, NM_SUPPLICANT_INTERFACE_WFD_SUPPORT, (int) wfd_support, + NM_SUPPLICANT_INTERFACE_FT_SUPPORT, (int) ft_support, + NM_SUPPLICANT_INTERFACE_SHA384_SUPPORT, (int) sha384_support, NULL); } @@ -2876,6 +3004,14 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass) G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_MESH_SUPPORT] = + g_param_spec_int (NM_SUPPLICANT_INTERFACE_MESH_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, @@ -2884,6 +3020,22 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass) G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_FT_SUPPORT] = + g_param_spec_int (NM_SUPPLICANT_INTERFACE_FT_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_SHA384_SUPPORT] = + g_param_spec_int (NM_SUPPLICANT_INTERFACE_SHA384_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); diff --git a/src/supplicant/nm-supplicant-interface.h b/src/supplicant/nm-supplicant-interface.h index 0aa7732a..653830da 100644 --- a/src/supplicant/nm-supplicant-interface.h +++ b/src/supplicant/nm-supplicant-interface.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -68,7 +67,10 @@ typedef enum { #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_MESH_SUPPORT "mesh-support" #define NM_SUPPLICANT_INTERFACE_WFD_SUPPORT "wfd-support" +#define NM_SUPPLICANT_INTERFACE_FT_SUPPORT "ft-support" +#define NM_SUPPLICANT_INTERFACE_SHA384_SUPPORT "sha384-support" /* Signals */ #define NM_SUPPLICANT_INTERFACE_STATE "state" @@ -96,7 +98,10 @@ NMSupplicantInterface * nm_supplicant_interface_new (const char *ifname, NMSupplicantFeature pmf_support, NMSupplicantFeature fils_support, NMSupplicantFeature p2p_support, - NMSupplicantFeature wfd_support); + NMSupplicantFeature mesh_support, + NMSupplicantFeature wfd_support, + NMSupplicantFeature ft_support, + NMSupplicantFeature sha384_support); void nm_supplicant_interface_set_supplicant_available (NMSupplicantInterface *self, gboolean available); @@ -113,6 +118,16 @@ nm_supplicant_interface_assoc (NMSupplicantInterface *self, void nm_supplicant_interface_disconnect (NMSupplicantInterface * iface); +typedef void (*NMSupplicantInterfaceDisconnectCb) (NMSupplicantInterface *iface, + GError *error, + gpointer user_data); + +void +nm_supplicant_interface_disconnect_async (NMSupplicantInterface * self, + GCancellable * cancellable, + NMSupplicantInterfaceDisconnectCb callback, + gpointer user_data); + const char *nm_supplicant_interface_get_object_path (NMSupplicantInterface * iface); void nm_supplicant_interface_request_scan (NMSupplicantInterface *self, @@ -161,7 +176,10 @@ NMSupplicantFeature nm_supplicant_interface_get_ap_support (NMSupplicantInterfac 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_mesh_support (NMSupplicantInterface *self); NMSupplicantFeature nm_supplicant_interface_get_wfd_support (NMSupplicantInterface *self); +NMSupplicantFeature nm_supplicant_interface_get_ft_support (NMSupplicantInterface *self); +NMSupplicantFeature nm_supplicant_interface_get_sha384_support (NMSupplicantInterface *self); void nm_supplicant_interface_set_ap_support (NMSupplicantInterface *self, NMSupplicantFeature apmode); @@ -178,9 +196,18 @@ void nm_supplicant_interface_set_fils_support (NMSupplicantInterface *self, void nm_supplicant_interface_set_p2p_support (NMSupplicantInterface *self, NMSupplicantFeature p2p_support); +void nm_supplicant_interface_set_mesh_support (NMSupplicantInterface *self, + NMSupplicantFeature mesh_support); + void nm_supplicant_interface_set_wfd_support (NMSupplicantInterface *self, NMSupplicantFeature wfd_support); +void nm_supplicant_interface_set_ft_support (NMSupplicantInterface *self, + NMSupplicantFeature ft_support); + +void nm_supplicant_interface_set_sha384_support (NMSupplicantInterface *self, + NMSupplicantFeature sha384_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 104aeee7..df641c51 100644 --- a/src/supplicant/nm-supplicant-manager.c +++ b/src/supplicant/nm-supplicant-manager.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -40,7 +39,10 @@ typedef struct { NMSupplicantFeature pmf_support; NMSupplicantFeature fils_support; NMSupplicantFeature p2p_support; + NMSupplicantFeature mesh_support; NMSupplicantFeature wfd_support; + NMSupplicantFeature ft_support; + NMSupplicantFeature sha384_support; guint die_count_reset_id; guint die_count; } NMSupplicantManagerPrivate; @@ -232,7 +234,10 @@ nm_supplicant_manager_create_interface (NMSupplicantManager *self, priv->pmf_support, priv->fils_support, priv->p2p_support, - priv->wfd_support); + priv->mesh_support, + priv->wfd_support, + priv->ft_support, + priv->sha384_support); priv->ifaces = g_slist_prepend (priv->ifaces, iface); g_object_add_toggle_ref ((GObject *) iface, _sup_iface_last_ref, self); @@ -289,7 +294,10 @@ nm_supplicant_manager_create_interface_from_path (NMSupplicantManager *self, priv->pmf_support, priv->fils_support, priv->p2p_support, - priv->wfd_support); + priv->mesh_support, + priv->wfd_support, + priv->ft_support, + priv->sha384_support); priv->ifaces = g_slist_prepend (priv->ifaces, iface); g_object_add_toggle_ref ((GObject *) iface, _sup_iface_last_ref, self); @@ -325,8 +333,11 @@ 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 */ + /* Support for the following is newer than the capabilities property */ priv->p2p_support = NM_SUPPLICANT_FEATURE_NO; + priv->ft_support = NM_SUPPLICANT_FEATURE_NO; + priv->sha384_support = NM_SUPPLICANT_FEATURE_NO; + priv->mesh_support = NM_SUPPLICANT_FEATURE_NO; value = g_dbus_proxy_get_cached_property (priv->proxy, "Capabilities"); if (value) { @@ -336,6 +347,9 @@ update_capabilities (NMSupplicantManager *self) priv->pmf_support = NM_SUPPLICANT_FEATURE_NO; priv->fils_support = NM_SUPPLICANT_FEATURE_NO; priv->p2p_support = NM_SUPPLICANT_FEATURE_NO; + priv->ft_support = NM_SUPPLICANT_FEATURE_NO; + priv->sha384_support = NM_SUPPLICANT_FEATURE_NO; + priv->mesh_support = NM_SUPPLICANT_FEATURE_NO; if (array) { if (g_strv_contains (array, "ap")) priv->ap_support = NM_SUPPLICANT_FEATURE_YES; @@ -345,18 +359,27 @@ update_capabilities (NMSupplicantManager *self) priv->fils_support = NM_SUPPLICANT_FEATURE_YES; if (g_strv_contains (array, "p2p")) priv->p2p_support = NM_SUPPLICANT_FEATURE_YES; + if (g_strv_contains (array, "ft")) + priv->ft_support = NM_SUPPLICANT_FEATURE_YES; + if (g_strv_contains (array, "sha384")) + priv->sha384_support = NM_SUPPLICANT_FEATURE_YES; + if (g_strv_contains (array, "mesh")) + priv->mesh_support = NM_SUPPLICANT_FEATURE_YES; g_free (array); } } g_variant_unref (value); } - /* Tell all interfaces about results of the AP/PMF/FILS/P2P check */ + /* Tell all interfaces about results of the AP/PMF/FILS/P2P/FT/SHA384 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); + nm_supplicant_interface_set_ft_support (ifaces->data, priv->ft_support); + nm_supplicant_interface_set_sha384_support (ifaces->data, priv->sha384_support); + nm_supplicant_interface_set_mesh_support (ifaces->data, priv->mesh_support); } _LOGD ("AP mode is %ssupported", @@ -371,6 +394,15 @@ update_capabilities (NMSupplicantManager *self) _LOGD ("P2P is %ssupported", (priv->p2p_support == NM_SUPPLICANT_FEATURE_YES) ? "" : (priv->p2p_support == NM_SUPPLICANT_FEATURE_NO) ? "not " : "possibly "); + _LOGD ("FT is %ssupported", + (priv->ft_support == NM_SUPPLICANT_FEATURE_YES) ? "" : + (priv->ft_support == NM_SUPPLICANT_FEATURE_NO) ? "not " : "possibly "); + _LOGD ("SHA384 is %ssupported", + (priv->sha384_support == NM_SUPPLICANT_FEATURE_YES) ? "" : + (priv->sha384_support == NM_SUPPLICANT_FEATURE_NO) ? "not " : "possibly "); + _LOGD ("Mesh is %ssupported", + (priv->mesh_support == NM_SUPPLICANT_FEATURE_YES) ? "" : + (priv->mesh_support == NM_SUPPLICANT_FEATURE_NO) ? "not " : "possibly "); /* EAP-FAST */ priv->fast_support = NM_SUPPLICANT_FEATURE_NO; @@ -509,6 +541,8 @@ name_owner_cb (GDBusProxy *proxy, GParamSpec *pspec, gpointer user_data) priv->fast_support = NM_SUPPLICANT_FEATURE_UNKNOWN; priv->pmf_support = NM_SUPPLICANT_FEATURE_UNKNOWN; priv->fils_support = NM_SUPPLICANT_FEATURE_UNKNOWN; + priv->ft_support = NM_SUPPLICANT_FEATURE_UNKNOWN; + priv->sha384_support = NM_SUPPLICANT_FEATURE_UNKNOWN; set_running (self, FALSE); } diff --git a/src/supplicant/nm-supplicant-manager.h b/src/supplicant/nm-supplicant-manager.h index 058745fb..a1f23f53 100644 --- a/src/supplicant/nm-supplicant-manager.h +++ b/src/supplicant/nm-supplicant-manager.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/supplicant/nm-supplicant-settings-verify.c b/src/supplicant/nm-supplicant-settings-verify.c index f10bbb04..1bd71320 100644 --- a/src/supplicant/nm-supplicant-settings-verify.c +++ b/src/supplicant/nm-supplicant-settings-verify.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -67,8 +66,8 @@ static const struct validate_entry validate_table[] = { const char * pairwise_allowed[] = { "CCMP", "TKIP", "NONE", NULL }; const char * group_allowed[] = { "CCMP", "TKIP", "WEP104", "WEP40", NULL }; const char * proto_allowed[] = { "WPA", "RSN", NULL }; -const char * key_mgmt_allowed[] = { "WPA-PSK", "WPA-PSK-SHA256", - "WPA-EAP", "WPA-EAP-SHA256", +const char * key_mgmt_allowed[] = { "WPA-PSK", "WPA-PSK-SHA256", "FT-PSK", + "WPA-EAP", "WPA-EAP-SHA256", "FT-EAP", "FT-EAP-SHA384", "FILS-SHA256", "FILS-SHA384", "IEEE8021X", "WPA-NONE", "SAE", "NONE", NULL }; @@ -94,7 +93,6 @@ static const struct Opt opt_table[] = { { "ssid", TYPE_BYTES, 0, 32,FALSE, NULL }, { "bssid", TYPE_KEYWORD, 0, 0, FALSE, NULL }, { "scan_ssid", TYPE_INT, 0, 1, FALSE, NULL }, - { "mode", TYPE_INT, 0, 2, FALSE, NULL }, { "frequency", TYPE_INT, 2412, 5825, FALSE, NULL }, { "auth_alg", TYPE_KEYWORD, 0, 0, FALSE, auth_alg_allowed }, { "psk", TYPE_BYTES, 0, 0, FALSE, NULL }, @@ -255,6 +253,12 @@ nm_supplicant_settings_verify_setting (const char * key, g_return_val_if_fail (key != NULL, FALSE); g_return_val_if_fail (value != NULL, FALSE); + if (strcmp (key, "mode") == 0) { + if (strcmp (value, "1") && strcmp (value, "2") && strcmp (value, "5")) + return TYPE_INVALID; + return TYPE_INT; + } + for (i = 0; i < opt_count; i++) { if (strcmp (opt_table[i].key, key) != 0) continue; diff --git a/src/supplicant/nm-supplicant-settings-verify.h b/src/supplicant/nm-supplicant-settings-verify.h index 1eec136d..ea2482d3 100644 --- a/src/supplicant/nm-supplicant-settings-verify.h +++ b/src/supplicant/nm-supplicant-settings-verify.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/supplicant/nm-supplicant-types.h b/src/supplicant/nm-supplicant-types.h index 747cf152..b28a1efa 100644 --- a/src/supplicant/nm-supplicant-types.h +++ b/src/supplicant/nm-supplicant-types.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/supplicant/tests/test-supplicant-config.c b/src/supplicant/tests/test-supplicant-config.c index 2c7a71a3..819256fb 100644 --- a/src/supplicant/tests/test-supplicant-config.c +++ b/src/supplicant/tests/test-supplicant-config.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager * * This program is free software; you can redistribute it and/or modify @@ -111,7 +110,7 @@ build_supplicant_config (NMConnection *connection, NMSetting8021x *s_8021x; gboolean success; - config = nm_supplicant_config_new (support_pmf, support_fils); + config = nm_supplicant_config_new (support_pmf, support_fils, FALSE, FALSE); s_wifi = nm_connection_get_setting_wireless (connection); g_assert (s_wifi); diff --git a/src/systemd/meson.build b/src/systemd/meson.build index 257a9462..af1d0c8b 100644 --- a/src/systemd/meson.build +++ b/src/systemd/meson.build @@ -23,9 +23,9 @@ libnm_systemd_core = static_library( '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', + 'nm-sd-utils-dhcp.c', ), include_directories: [ src_inc, @@ -33,7 +33,6 @@ libnm_systemd_core = static_library( 'sd-adapt-core', 'src/libsystemd-network', 'src/libsystemd/sd-event', - 'src/shared', 'src/systemd', ) ], diff --git a/src/systemd/nm-sd-utils-dhcp.c b/src/systemd/nm-sd-utils-dhcp.c new file mode 100644 index 00000000..49a924b6 --- /dev/null +++ b/src/systemd/nm-sd-utils-dhcp.c @@ -0,0 +1,54 @@ +/* + * 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. + * + * (C) Copyright 2019 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-sd-utils-dhcp.h" + +#include "sd-adapt-core/nm-sd-adapt-core.h" +#include "src/libsystemd-network/dhcp-lease-internal.h" + +int +nm_sd_dhcp_lease_get_private_options (sd_dhcp_lease *lease, nm_sd_dhcp_option **out_options) +{ + struct sd_dhcp_raw_option *raw_option; + int cnt = 0; + + g_return_val_if_fail (lease, -EINVAL); + g_return_val_if_fail (out_options, -EINVAL); + g_return_val_if_fail (*out_options == NULL, -EINVAL); + + if (lease->private_options == NULL) + return -ENODATA; + + LIST_FOREACH (options, raw_option, lease->private_options) + cnt++; + + *out_options = g_new (nm_sd_dhcp_option, cnt); + cnt = 0; + + LIST_FOREACH (options, raw_option, lease->private_options) { + (*out_options)[cnt].code = raw_option->tag; + (*out_options)[cnt].data = raw_option->data; + (*out_options)[cnt].data_len = raw_option->length; + cnt++; + } + + return cnt; +} diff --git a/src/systemd/nm-sd-utils-dhcp.h b/src/systemd/nm-sd-utils-dhcp.h new file mode 100644 index 00000000..496bf374 --- /dev/null +++ b/src/systemd/nm-sd-utils-dhcp.h @@ -0,0 +1,34 @@ +/* + * 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. + * + * (C) Copyright 2019 Red Hat, Inc. + */ + +#ifndef __NETWORKMANAGER_DHCP_SYSTEMD_UTILS_H__ +#define __NETWORKMANAGER_DHCP_SYSTEMD_UTILS_H__ + +#include "nm-sd.h" + +typedef struct { + uint8_t code; + uint8_t data_len; + void *data; +} nm_sd_dhcp_option; + +int +nm_sd_dhcp_lease_get_private_options (sd_dhcp_lease *lease, nm_sd_dhcp_option **out_options); + +#endif /* __NETWORKMANAGER_DHCP_SYSTEMD_UTILS_H__ */ diff --git a/src/systemd/nm-sd.c b/src/systemd/nm-sd.c index 4009c617..bcab328f 100644 --- a/src/systemd/nm-sd.c +++ b/src/systemd/nm-sd.c @@ -1,4 +1,3 @@ -/* -*- 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) @@ -133,10 +132,6 @@ 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-core.h" diff --git a/src/systemd/nm-sd.h b/src/systemd/nm-sd.h index f988dfd0..07fc2ad4 100644 --- a/src/systemd/nm-sd.h +++ b/src/systemd/nm-sd.h @@ -1,4 +1,3 @@ -/* -*- 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) diff --git a/src/systemd/sd-adapt-core/nm-sd-adapt-core.c b/src/systemd/sd-adapt-core/nm-sd-adapt-core.c index d7ed687e..c461752e 100644 --- a/src/systemd/sd-adapt-core/nm-sd-adapt-core.c +++ b/src/systemd/sd-adapt-core/nm-sd-adapt-core.c @@ -1,4 +1,3 @@ -/* -*- 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) diff --git a/src/systemd/sd-adapt-core/nm-sd-adapt-core.h b/src/systemd/sd-adapt-core/nm-sd-adapt-core.h index 8c07c53a..bd71dd18 100644 --- a/src/systemd/sd-adapt-core/nm-sd-adapt-core.h +++ b/src/systemd/sd-adapt-core/nm-sd-adapt-core.h @@ -50,8 +50,6 @@ #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 diff --git a/src/systemd/src/libsystemd-network/arp-util.c b/src/systemd/src/libsystemd-network/arp-util.c index 154ba6b7..e48e4bfe 100644 --- a/src/systemd/src/libsystemd-network/arp-util.c +++ b/src/systemd/src/libsystemd-network/arp-util.c @@ -5,8 +5,9 @@ #include "nm-sd-adapt-core.h" -#include <linux/filter.h> #include <arpa/inet.h> +#include <linux/filter.h> +#include <netinet/if_ether.h> #include "arp-util.h" #include "fd-util.h" diff --git a/src/systemd/src/libsystemd-network/arp-util.h b/src/systemd/src/libsystemd-network/arp-util.h index 10c68486..9a4427e8 100644 --- a/src/systemd/src/libsystemd-network/arp-util.h +++ b/src/systemd/src/libsystemd-network/arp-util.h @@ -5,7 +5,7 @@ Copyright © 2014 Axis Communications AB. All rights reserved. ***/ -#include <netinet/if_ether.h> +#include <net/ethernet.h> #include "socket-util.h" #include "sparse-endian.h" diff --git a/src/systemd/src/libsystemd-network/dhcp-identifier.c b/src/systemd/src/libsystemd-network/dhcp-identifier.c index e3af362d..b28e0ba0 100644 --- a/src/systemd/src/libsystemd-network/dhcp-identifier.c +++ b/src/systemd/src/libsystemd-network/dhcp-identifier.c @@ -197,7 +197,7 @@ int dhcp_identifier_set_iaid( /* device is under renaming */ return -EBUSY; - name = net_get_name(device); + name = net_get_name_persistent(device); } } diff --git a/src/systemd/src/libsystemd-network/dhcp-internal.h b/src/systemd/src/libsystemd-network/dhcp-internal.h index a0f9c229..e0269b54 100644 --- a/src/systemd/src/libsystemd-network/dhcp-internal.h +++ b/src/systemd/src/libsystemd-network/dhcp-internal.h @@ -51,5 +51,5 @@ int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum, ui #define DHCP_CLIENT_DONT_DESTROY(client) \ _cleanup_(sd_dhcp_client_unrefp) _unused_ sd_dhcp_client *_dont_destroy_##client = sd_dhcp_client_ref(client) -#define log_dhcp_client_errno(client, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "DHCP CLIENT (0x%x): " fmt, client->xid, ##__VA_ARGS__) +#define log_dhcp_client_errno(client, error, fmt, ...) log_internal(LOG_DEBUG, error, PROJECT_FILE, __LINE__, __func__, "DHCP CLIENT (0x%x): " fmt, client->xid, ##__VA_ARGS__) #define log_dhcp_client(client, fmt, ...) log_dhcp_client_errno(client, 0, fmt, ##__VA_ARGS__) diff --git a/src/systemd/src/libsystemd-network/dhcp-network.c b/src/systemd/src/libsystemd-network/dhcp-network.c index 45d2ed89..858fe57c 100644 --- a/src/systemd/src/libsystemd-network/dhcp-network.c +++ b/src/systemd/src/libsystemd-network/dhcp-network.c @@ -5,6 +5,7 @@ #include "nm-sd-adapt-core.h" +#include <errno.h> #include <net/ethernet.h> #include <net/if.h> #include <net/if_arp.h> diff --git a/src/systemd/src/libsystemd-network/dhcp-option.c b/src/systemd/src/libsystemd-network/dhcp-option.c index eaf044bf..50238b0a 100644 --- a/src/systemd/src/libsystemd-network/dhcp-option.c +++ b/src/systemd/src/libsystemd-network/dhcp-option.c @@ -5,6 +5,7 @@ #include "nm-sd-adapt-core.h" +#include <errno.h> #include <stdint.h> #include <stdio.h> #include <string.h> @@ -199,7 +200,7 @@ static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overlo if (memchr(option, 0, len - 1)) return -EINVAL; - string = strndup((const char *) option, len); + string = memdup_suffix0((const char *) option, len); if (!string) return -ENOMEM; diff --git a/src/systemd/src/libsystemd-network/dhcp-packet.c b/src/systemd/src/libsystemd-network/dhcp-packet.c index 91e8a54e..9e565e28 100644 --- a/src/systemd/src/libsystemd-network/dhcp-packet.c +++ b/src/systemd/src/libsystemd-network/dhcp-packet.c @@ -5,6 +5,7 @@ #include "nm-sd-adapt-core.h" +#include <errno.h> #include <net/ethernet.h> #include <net/if_arp.h> #include <string.h> diff --git a/src/systemd/src/libsystemd-network/dhcp6-internal.h b/src/systemd/src/libsystemd-network/dhcp6-internal.h index 157fc0aa..f28ba68d 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-internal.h +++ b/src/systemd/src/libsystemd-network/dhcp6-internal.h @@ -79,7 +79,7 @@ struct DHCP6IA { typedef struct DHCP6IA DHCP6IA; -#define log_dhcp6_client_errno(p, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "DHCPv6 CLIENT: " fmt, ##__VA_ARGS__) +#define log_dhcp6_client_errno(p, error, fmt, ...) log_internal(LOG_DEBUG, error, PROJECT_FILE, __LINE__, __func__, "DHCPv6 CLIENT: " fmt, ##__VA_ARGS__) #define log_dhcp6_client(p, fmt, ...) log_dhcp6_client_errno(p, 0, fmt, ##__VA_ARGS__) int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, diff --git a/src/systemd/src/libsystemd-network/dhcp6-network.c b/src/systemd/src/libsystemd-network/dhcp6-network.c index d786756e..73c195a7 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-network.c +++ b/src/systemd/src/libsystemd-network/dhcp6-network.c @@ -5,6 +5,7 @@ #include "nm-sd-adapt-core.h" +#include <errno.h> #include <netinet/in.h> #include <netinet/ip6.h> #include <stdio.h> diff --git a/src/systemd/src/libsystemd-network/dhcp6-option.c b/src/systemd/src/libsystemd-network/dhcp6-option.c index 6172c368..562a34b5 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-option.c +++ b/src/systemd/src/libsystemd-network/dhcp6-option.c @@ -5,6 +5,7 @@ #include "nm-sd-adapt-core.h" +#include <errno.h> #include <netinet/in.h> #include <string.h> diff --git a/src/systemd/src/libsystemd-network/lldp-internal.h b/src/systemd/src/libsystemd-network/lldp-internal.h index 88b54933..9598438d 100644 --- a/src/systemd/src/libsystemd-network/lldp-internal.h +++ b/src/systemd/src/libsystemd-network/lldp-internal.h @@ -32,7 +32,7 @@ struct sd_lldp { struct ether_addr filter_address; }; -#define log_lldp_errno(error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "LLDP: " fmt, ##__VA_ARGS__) +#define log_lldp_errno(error, fmt, ...) log_internal(LOG_DEBUG, error, PROJECT_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_; diff --git a/src/systemd/src/libsystemd-network/network-internal.c b/src/systemd/src/libsystemd-network/network-internal.c index de219a6d..209ce884 100644 --- a/src/systemd/src/libsystemd-network/network-internal.c +++ b/src/systemd/src/libsystemd-network/network-internal.c @@ -14,6 +14,7 @@ #include "conf-parser.h" #include "device-util.h" #include "dhcp-lease-internal.h" +#include "env-util.h" #include "ether-addr-util.h" #include "hexdecoct.h" #include "log.h" @@ -27,7 +28,7 @@ #include "util.h" #if 0 /* NM_IGNORED */ -const char *net_get_name(sd_device *device) { +const char *net_get_name_persistent(sd_device *device) { const char *name, *field; assert(device); @@ -42,7 +43,7 @@ const char *net_get_name(sd_device *device) { #define HASH_KEY SD_ID128_MAKE(d3,1e,48,fa,90,fe,4b,4c,9d,af,d5,d7,a1,b1,2e,8a) -int net_get_unique_predictable_data(sd_device *device, uint64_t *result) { +int net_get_unique_predictable_data(sd_device *device, bool use_sysname, uint64_t *result) { size_t l, sz = 0; const char *name; int r; @@ -50,10 +51,10 @@ int net_get_unique_predictable_data(sd_device *device, uint64_t *result) { assert(device); - /* net_get_name() will return one of the device names based on stable information about the - * device. If this is not available, we fall back to using the device name. */ - name = net_get_name(device); - if (!name) + /* net_get_name_persistent() will return one of the device names based on stable information about + * the device. If this is not available, we fall back to using the actual device name. */ + name = net_get_name_persistent(device); + if (!name && use_sysname) (void) sd_device_get_sysname(device, &name); if (!name) return log_device_debug_errno(device, SYNTHETIC_ERRNO(ENODATA), @@ -76,26 +77,66 @@ int net_get_unique_predictable_data(sd_device *device, uint64_t *result) { return 0; } -static bool net_condition_test_strv(char * const *raw_patterns, - const char *string) { - if (strv_isempty(raw_patterns)) +static bool net_condition_test_strv(char * const *patterns, const char *string) { + char * const *p; + bool match = false, has_positive_rule = false; + + if (strv_isempty(patterns)) return true; - /* If the patterns begin with "!", edit it out and negate the test. */ - if (raw_patterns[0][0] == '!') { - char **patterns; - size_t i, length; + STRV_FOREACH(p, patterns) { + const char *q = *p; + bool invert; + + invert = *q == '!'; + q += invert; + + if (!invert) + has_positive_rule = true; + + if (string && fnmatch(q, string, 0) == 0) { + if (invert) + return false; + else + match = true; + } + } + + return has_positive_rule ? match : true; +} + +static int net_condition_test_property(char * const *match_property, sd_device *device) { + char * const *p; + + if (strv_isempty(match_property)) + return true; + + STRV_FOREACH(p, match_property) { + _cleanup_free_ char *key = NULL; + const char *val, *dev_val; + bool invert, v; + + invert = **p == '!'; + + val = strchr(*p + invert, '='); + if (!val) + return -EINVAL; - length = strv_length(raw_patterns) + 1; /* Include the NULL. */ - patterns = newa(char*, length); - patterns[0] = raw_patterns[0] + 1; /* Skip the "!". */ - for (i = 1; i < length; i++) - patterns[i] = raw_patterns[i]; + key = strndup(*p + invert, val - *p - invert); + if (!key) + return -ENOMEM; + + val++; - return !string || !strv_fnmatch(patterns, string, 0); + v = device && + sd_device_get_property_value(device, key, &dev_val) >= 0 && + fnmatch(val, dev_val, 0) == 0; + + if (invert ? v : !v) + return false; } - return string && strv_fnmatch(raw_patterns, string, 0); + return true; } bool net_match_config(Set *match_mac, @@ -103,12 +144,25 @@ bool net_match_config(Set *match_mac, char * const *match_drivers, char * const *match_types, char * const *match_names, + char * const *match_property, + sd_device *device, const struct ether_addr *dev_mac, - const char *dev_path, - const char *dev_driver, - const char *dev_type, const char *dev_name) { + const char *dev_path = NULL, *dev_driver = NULL, *dev_type = NULL, *mac_str; + + if (device) { + (void) sd_device_get_property_value(device, "ID_PATH", &dev_path); + (void) sd_device_get_property_value(device, "ID_NET_DRIVER", &dev_driver); + (void) sd_device_get_devtype(device, &dev_type); + + if (!dev_name) + (void) sd_device_get_sysname(device, &dev_name); + if (!dev_mac && + sd_device_get_sysattr_value(device, "address", &mac_str) >= 0) + dev_mac = ether_aton(mac_str); + } + if (match_mac && (!dev_mac || !set_contains(match_mac, dev_mac))) return false; @@ -124,6 +178,9 @@ bool net_match_config(Set *match_mac, if (!net_condition_test_strv(match_names, dev_name)) return false; + if (!net_condition_test_property(match_property, device)) + return false; + return true; } @@ -167,7 +224,7 @@ int config_parse_net_condition(const char *unit, return 0; } -int config_parse_ifnames( +int config_parse_match_strv( const char *unit, const char *filename, unsigned line, @@ -179,7 +236,9 @@ int config_parse_ifnames( void *data, void *userdata) { + const char *p = rvalue; char ***sv = data; + bool invert; int r; assert(filename); @@ -187,30 +246,154 @@ int config_parse_ifnames( assert(rvalue); assert(data); + if (isempty(rvalue)) { + *sv = strv_free(*sv); + return 0; + } + + invert = *p == '!'; + p += invert; + for (;;) { - _cleanup_free_ char *word = NULL; + _cleanup_free_ char *word = NULL, *k = NULL; - r = extract_first_word(&rvalue, &word, NULL, 0); + r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE); + if (r == 0) + return 0; + if (r == -ENOMEM) + return log_oom(); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse interface name list: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue); return 0; } + + if (invert) { + k = strjoin("!", word); + if (!k) + return log_oom(); + } else + k = TAKE_PTR(word); + + r = strv_consume(sv, TAKE_PTR(k)); + if (r < 0) + return log_oom(); + } +} + +int config_parse_match_ifnames( + 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) { + + const char *p = rvalue; + char ***sv = data; + bool invert; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + invert = *p == '!'; + p += invert; + + for (;;) { + _cleanup_free_ char *word = NULL, *k = NULL; + + r = extract_first_word(&p, &word, NULL, 0); if (r == 0) - break; + return 0; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "Failed to parse interface name list: %s", rvalue); + return 0; + } if (!ifname_valid(word)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not valid or too long, ignoring assignment: %s", rvalue); - return 0; + log_syntax(unit, LOG_ERR, filename, line, 0, + "Interface name is not valid or too long, ignoring assignment: %s", word); + continue; } - r = strv_push(sv, word); + if (invert) { + k = strjoin("!", word); + if (!k) + return log_oom(); + } else + k = TAKE_PTR(word); + + r = strv_consume(sv, TAKE_PTR(k)); if (r < 0) return log_oom(); - - word = NULL; } +} - return 0; +int config_parse_match_property( + 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) { + + const char *p = rvalue; + char ***sv = data; + bool invert; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + invert = *p == '!'; + p += invert; + + for (;;) { + _cleanup_free_ char *word = NULL, *k = NULL; + + r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE); + if (r == 0) + return 0; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "Invalid syntax, ignoring: %s", rvalue); + return 0; + } + + if (!env_assignment_is_valid(word)) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "Invalid property or value, ignoring assignment: %s", word); + continue; + } + + if (invert) { + k = strjoin("!", word); + if (!k) + return log_oom(); + } else + k = TAKE_PTR(word); + + r = strv_consume(sv, TAKE_PTR(k)); + if (r < 0) + return log_oom(); + } } int config_parse_ifalias(const char *unit, @@ -616,7 +799,7 @@ int serialize_dhcp_option(FILE *f, const char *key, const void *data, size_t siz assert(data); hex_buf = hexmem(data, size); - if (hex_buf == NULL) + if (!hex_buf) return -ENOMEM; fprintf(f, "%s=%s\n", key, hex_buf); diff --git a/src/systemd/src/libsystemd-network/network-internal.h b/src/systemd/src/libsystemd-network/network-internal.h index d895103b..487421fb 100644 --- a/src/systemd/src/libsystemd-network/network-internal.h +++ b/src/systemd/src/libsystemd-network/network-internal.h @@ -14,27 +14,28 @@ #define LINK_BRIDGE_PORT_PRIORITY_INVALID 128 #define LINK_BRIDGE_PORT_PRIORITY_MAX 63 +#if 0 /* NM_IGNORED */ bool net_match_config(Set *match_mac, char * const *match_path, char * const *match_driver, char * const *match_type, char * const *match_name, + char * const *match_property, + sd_device *device, const struct ether_addr *dev_mac, - const char *dev_path, - const char *dev_driver, - const char *dev_type, const char *dev_name); -#if 0 /* NM_IGNORED */ CONFIG_PARSER_PROTOTYPE(config_parse_net_condition); CONFIG_PARSER_PROTOTYPE(config_parse_hwaddr); CONFIG_PARSER_PROTOTYPE(config_parse_hwaddrs); -CONFIG_PARSER_PROTOTYPE(config_parse_ifnames); +CONFIG_PARSER_PROTOTYPE(config_parse_match_strv); +CONFIG_PARSER_PROTOTYPE(config_parse_match_ifnames); +CONFIG_PARSER_PROTOTYPE(config_parse_match_property); CONFIG_PARSER_PROTOTYPE(config_parse_ifalias); CONFIG_PARSER_PROTOTYPE(config_parse_bridge_port_priority); -int net_get_unique_predictable_data(sd_device *device, uint64_t *result); -const char *net_get_name(sd_device *device); +int net_get_unique_predictable_data(sd_device *device, bool use_sysname, uint64_t *result); +const char *net_get_name_persistent(sd_device *device); #endif /* NM_IGNORED */ size_t serialize_in_addrs(FILE *f, diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-client.c b/src/systemd/src/libsystemd-network/sd-dhcp-client.c index cadcc8a8..2f531bc7 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp-client.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp-client.c @@ -5,6 +5,7 @@ #include "nm-sd-adapt-core.h" +#include <errno.h> #include <net/ethernet.h> #include <net/if_arp.h> #include <stdio.h> @@ -89,7 +90,8 @@ struct sd_dhcp_client { uint32_t mtu; uint32_t xid; usec_t start_time; - unsigned attempt; + uint64_t attempt; + uint64_t max_attempts; usec_t request_sent; sd_event_source *timeout_t1; sd_event_source *timeout_t2; @@ -524,10 +526,18 @@ int sd_dhcp_client_set_mtu(sd_dhcp_client *client, uint32_t mtu) { return 0; } +int sd_dhcp_client_set_max_attempts(sd_dhcp_client *client, uint64_t max_attempts) { + assert_return(client, -EINVAL); + + client->max_attempts = max_attempts; + + return 0; +} + int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) { assert_return(client, -EINVAL); - if (!IN_SET(client->state, DHCP_STATE_BOUND, DHCP_STATE_RENEWING, DHCP_STATE_REBINDING)) + if (!IN_SET(client->state, DHCP_STATE_SELECTING, DHCP_STATE_BOUND, DHCP_STATE_RENEWING, DHCP_STATE_REBINDING)) return -EADDRNOTAVAIL; if (ret) @@ -536,11 +546,13 @@ int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) { return 0; } -static void client_notify(sd_dhcp_client *client, int event) { +static int client_notify(sd_dhcp_client *client, int event) { assert(client); if (client->callback) - client->callback(client, event, client->userdata); + return client->callback(client, event, client->userdata); + + return 0; } static int client_initialize(sd_dhcp_client *client) { @@ -555,7 +567,7 @@ static int client_initialize(sd_dhcp_client *client) { (void) event_source_disable(client->timeout_t2); (void) event_source_disable(client->timeout_expire); - client->attempt = 1; + client->attempt = 0; client->state = DHCP_STATE_INIT; client->xid = 0; @@ -569,7 +581,7 @@ static void client_stop(sd_dhcp_client *client, int error) { assert(client); if (error < 0) - log_dhcp_client(client, "STOPPED: %s", strerror(-error)); + log_dhcp_client_errno(client, error, "STOPPED: %m"); else if (error == SD_DHCP_CLIENT_EVENT_STOP) log_dhcp_client(client, "STOPPED"); else @@ -599,7 +611,7 @@ static int client_message_init( assert(ret); assert(_optlen); assert(_optoffset); - assert(IN_SET(type, DHCP_DISCOVER, DHCP_REQUEST)); + assert(IN_SET(type, DHCP_DISCOVER, DHCP_REQUEST, DHCP_RELEASE)); optlen = DHCP_MIN_OPTIONS_SIZE; size = sizeof(DHCPPacket) + optlen; @@ -690,7 +702,7 @@ static int client_message_init( MAY contain the Parameter Request List option. */ /* NOTE: in case that there would be an option to do not send * any PRL at all, the size should be checked before sending */ - if (client->req_opts_size > 0) { + if (client->req_opts_size > 0 && type != DHCP_RELEASE) { r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0, SD_DHCP_OPTION_PARAMETER_REQUEST_LIST, client->req_opts_size, client->req_opts); @@ -722,7 +734,7 @@ static int client_message_init( */ /* RFC7844 section 3: SHOULD NOT contain any other option. */ - if (!client->anonymize) { + if (!client->anonymize && type != DHCP_RELEASE) { max_size = htobe16(size); r = dhcp_option_append(&packet->dhcp, client->mtu, &optoffset, 0, SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE, @@ -854,6 +866,41 @@ static int client_send_discover(sd_dhcp_client *client) { return 0; } +static int client_send_release(sd_dhcp_client *client) { + _cleanup_free_ DHCPPacket *release = NULL; + size_t optoffset, optlen; + int r; + + assert(client); + assert(!IN_SET(client->state, DHCP_STATE_STOPPED)); + + r = client_message_init(client, &release, DHCP_RELEASE, + &optlen, &optoffset); + if (r < 0) + return r; + + /* Fill up release IP and MAC */ + release->dhcp.ciaddr = client->lease->address; + memcpy(&release->dhcp.chaddr, &client->mac_addr, client->mac_addr_len); + + r = dhcp_option_append(&release->dhcp, optlen, &optoffset, 0, + SD_DHCP_OPTION_END, 0, NULL); + if (r < 0) + return r; + + r = dhcp_network_send_udp_socket(client->fd, + client->lease->server_address, + DHCP_PORT_SERVER, + &release->dhcp, + sizeof(DHCPMessage) + optoffset); + if (r < 0) + return r; + + log_dhcp_client(client, "RELEASE"); + + return 0; +} + static int client_send_request(sd_dhcp_client *client) { _cleanup_free_ DHCPPacket *request = NULL; size_t optoffset, optlen; @@ -1054,10 +1101,12 @@ static int client_timeout_resend( case DHCP_STATE_REQUESTING: case DHCP_STATE_BOUND: - if (client->attempt < 64) - client->attempt *= 2; + if (client->attempt < client->max_attempts) + client->attempt++; + else + goto error; - next_timeout = time_now + (client->attempt - 1) * USEC_PER_SEC; + next_timeout = time_now + ((UINT64_C(1) << MIN(client->attempt, (uint64_t) 6)) - 1) * USEC_PER_SEC; break; @@ -1081,17 +1130,15 @@ static int client_timeout_resend( r = client_send_discover(client); if (r >= 0) { client->state = DHCP_STATE_SELECTING; - client->attempt = 1; - } else { - if (client->attempt >= 64) - goto error; - } + client->attempt = 0; + } else if (client->attempt >= client->max_attempts) + goto error; break; case DHCP_STATE_SELECTING: r = client_send_discover(client); - if (r < 0 && client->attempt >= 64) + if (r < 0 && client->attempt >= client->max_attempts) goto error; break; @@ -1101,7 +1148,7 @@ static int client_timeout_resend( case DHCP_STATE_RENEWING: case DHCP_STATE_REBINDING: r = client_send_request(client); - if (r < 0 && client->attempt >= 64) + if (r < 0 && client->attempt >= client->max_attempts) goto error; if (client->state == DHCP_STATE_INIT_REBOOT) @@ -1253,7 +1300,7 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) client->fd = asynchronous_close(client->fd); client->state = DHCP_STATE_REBINDING; - client->attempt = 1; + client->attempt = 0; r = dhcp_network_bind_raw_socket(client->ifindex, &client->link, client->xid, client->mac_addr, @@ -1273,7 +1320,7 @@ static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) DHCP_CLIENT_DONT_DESTROY(client); client->state = DHCP_STATE_RENEWING; - client->attempt = 1; + client->attempt = 0; return client_initialize_time_events(client); } @@ -1323,6 +1370,9 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, size_ sd_dhcp_lease_unref(client->lease); client->lease = TAKE_PTR(lease); + if (client_notify(client, SD_DHCP_CLIENT_EVENT_SELECTING) < 0) + return -ENOMSG; + log_dhcp_client(client, "OFFER"); return 0; @@ -1340,6 +1390,23 @@ static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force, return 0; } +static bool lease_equal(const sd_dhcp_lease *a, const sd_dhcp_lease *b) { + if (a->address != b->address) + return false; + + if (a->subnet_mask != b->subnet_mask) + return false; + + if (a->router_size != b->router_size) + return false; + + for (size_t i = 0; i < a->router_size; i++) + if (a->router[i].s_addr != b->router[i].s_addr) + return false; + + return true; +} + static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, size_t len) { _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL; _cleanup_free_ char *error_message = NULL; @@ -1392,12 +1459,10 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, size_t le r = SD_DHCP_CLIENT_EVENT_IP_ACQUIRE; if (client->lease) { - if (client->lease->address != lease->address || - client->lease->subnet_mask != lease->subnet_mask || - client->lease->router != lease->router) { - r = SD_DHCP_CLIENT_EVENT_IP_CHANGE; - } else + if (lease_equal(client->lease, lease)) r = SD_DHCP_CLIENT_EVENT_RENEW; + else + r = SD_DHCP_CLIENT_EVENT_IP_CHANGE; client->lease = sd_dhcp_lease_unref(client->lease); } @@ -1557,7 +1622,7 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i if (r >= 0) { client->state = DHCP_STATE_REQUESTING; - client->attempt = 1; + client->attempt = 0; r = event_reset_time(client->event, &client->timeout_resend, clock_boottime_or_monotonic(), @@ -1592,7 +1657,7 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i notify_event = r; client->state = DHCP_STATE_BOUND; - client->attempt = 1; + client->attempt = 0; client->last_addr = client->lease->address; @@ -1848,6 +1913,14 @@ int sd_dhcp_client_start(sd_dhcp_client *client) { return r; } +int sd_dhcp_client_send_release(sd_dhcp_client *client) { + assert_return(client, -EINVAL); + + client_send_release(client); + + return 0; +} + int sd_dhcp_client_stop(sd_dhcp_client *client) { DHCP_CLIENT_DONT_DESTROY(client); @@ -1931,10 +2004,10 @@ int sd_dhcp_client_new(sd_dhcp_client **ret, int anonymize) { .state = DHCP_STATE_INIT, .ifindex = -1, .fd = -1, - .attempt = 1, .mtu = DHCP_DEFAULT_MIN_SIZE, .port = DHCP_PORT_CLIENT, .anonymize = !!anonymize, + .max_attempts = (uint64_t) -1, }; /* NOTE: this could be moved to a function. */ if (anonymize) { diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c index 6c7aecc8..7559d066 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c @@ -6,8 +6,8 @@ #include "nm-sd-adapt-core.h" #include <arpa/inet.h> +#include <errno.h> #include <stdio.h> -#include <stdio_ext.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> @@ -335,7 +335,7 @@ static int lease_parse_string(const uint8_t *option, size_t len, char **ret) { if (memchr(option, 0, len - 1)) return -EINVAL; - string = strndup((const char *) option, len); + string = memdup_suffix0((const char *) option, len); if (!string) return -ENOMEM; @@ -833,7 +833,6 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { if (r < 0) goto fail; - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); (void) fchmod(fileno(f), 0644); fprintf(f, diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c index b72cd82d..f67a45bd 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c @@ -5,8 +5,10 @@ #include "nm-sd-adapt-core.h" +#include <errno.h> #include <string.h> #include <sys/ioctl.h> +#include <linux/if_arp.h> #include <linux/if_infiniband.h> #include "sd-dhcp6-client.h" diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c index 7263c96f..35caec7a 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c @@ -5,6 +5,7 @@ #include "nm-sd-adapt-core.h" +#include <errno.h> #include "alloc-util.h" #include "dhcp6-lease-internal.h" @@ -206,12 +207,8 @@ int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { r = dhcp6_option_parse_ip6addrs(optval, optlen, &lease->dns, lease->dns_count, &lease->dns_allocated); - if (r < 0) { - log_dhcp6_client(client, "Invalid DNS server option: %s", - strerror(-r)); - - return r; - } + if (r < 0) + return log_dhcp6_client_errno(client, r, "Invalid DNS server option: %m"); lease->dns_count = r; @@ -337,12 +334,8 @@ int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) r = dhcp6_option_parse_ip6addrs(optval, optlen, &lease->ntp, lease->ntp_count, &lease->ntp_allocated); - if (r < 0) { - log_dhcp6_client(client, "Invalid SNTP server option: %s", - strerror(-r)); - - return r; - } + if (r < 0) + return log_dhcp6_client_errno(client, r, "Invalid SNTP server option: %m"); lease->ntp_count = r; diff --git a/src/systemd/src/libsystemd-network/sd-ipv4acd.c b/src/systemd/src/libsystemd-network/sd-ipv4acd.c index 53ce8929..e7561480 100644 --- a/src/systemd/src/libsystemd-network/sd-ipv4acd.c +++ b/src/systemd/src/libsystemd-network/sd-ipv4acd.c @@ -6,6 +6,8 @@ #include "nm-sd-adapt-core.h" #include <arpa/inet.h> +#include <errno.h> +#include <netinet/if_ether.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -73,7 +75,7 @@ struct sd_ipv4acd { void* userdata; }; -#define log_ipv4acd_errno(acd, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "IPV4ACD: " fmt, ##__VA_ARGS__) +#define log_ipv4acd_errno(acd, error, fmt, ...) log_internal(LOG_DEBUG, error, PROJECT_FILE, __LINE__, __func__, "IPV4ACD: " fmt, ##__VA_ARGS__) #define log_ipv4acd(acd, fmt, ...) log_ipv4acd_errno(acd, 0, fmt, ##__VA_ARGS__) static void ipv4acd_set_state(sd_ipv4acd *acd, IPv4ACDState st, bool reset_counter) { diff --git a/src/systemd/src/libsystemd-network/sd-ipv4ll.c b/src/systemd/src/libsystemd-network/sd-ipv4ll.c index 4f186762..daac8839 100644 --- a/src/systemd/src/libsystemd-network/sd-ipv4ll.c +++ b/src/systemd/src/libsystemd-network/sd-ipv4ll.c @@ -6,6 +6,7 @@ #include "nm-sd-adapt-core.h" #include <arpa/inet.h> +#include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -51,7 +52,7 @@ struct sd_ipv4ll { void* userdata; }; -#define log_ipv4ll_errno(ll, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "IPV4LL: " fmt, ##__VA_ARGS__) +#define log_ipv4ll_errno(ll, error, fmt, ...) log_internal(LOG_DEBUG, error, PROJECT_FILE, __LINE__, __func__, "IPV4LL: " fmt, ##__VA_ARGS__) #define log_ipv4ll(ll, fmt, ...) log_ipv4ll_errno(ll, 0, fmt, ##__VA_ARGS__) static void ipv4ll_on_acd(sd_ipv4acd *ll, int event, void *userdata); diff --git a/src/systemd/src/libsystemd/sd-event/event-util.c b/src/systemd/src/libsystemd/sd-event/event-util.c index 2afacfe6..e8384cfd 100644 --- a/src/systemd/src/libsystemd/sd-event/event-util.c +++ b/src/systemd/src/libsystemd/sd-event/event-util.c @@ -2,6 +2,7 @@ #include "nm-sd-adapt-core.h" +#include <errno.h> #include "event-source.h" #include "event-util.h" diff --git a/src/systemd/src/libsystemd/sd-event/sd-event.c b/src/systemd/src/libsystemd/sd-event/sd-event.c index dad0801b..c3781706 100644 --- a/src/systemd/src/libsystemd/sd-event/sd-event.c +++ b/src/systemd/src/libsystemd/sd-event/sd-event.c @@ -25,6 +25,7 @@ #include "signal-util.h" #include "string-table.h" #include "string-util.h" +#include "strxcpyx.h" #include "time-util.h" #define DEFAULT_ACCURACY_USEC (250 * USEC_PER_MSEC) @@ -341,6 +342,12 @@ fail: DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_event, sd_event, event_free); +_public_ sd_event_source* sd_event_source_disable_unref(sd_event_source *s) { + if (s) + (void) sd_event_source_set_enabled(s, SD_EVENT_OFF); + return sd_event_source_unref(s); +} + static bool event_pid_changed(sd_event *e) { assert(e); @@ -3026,7 +3033,7 @@ static void event_close_inode_data_fds(sd_event *e) { /* Close the fds pointing to the inodes to watch now. We need to close them as they might otherwise pin * filesystems. But we can't close them right-away as we need them as long as the user still wants to make - * adjustments to the even source, such as changing the priority (which requires us to remove and readd a watch + * adjustments to the even source, such as changing the priority (which requires us to remove and re-add a watch * for the inode). Hence, let's close them when entering the first iteration after they were added, as a * compromise. */ @@ -3246,15 +3253,16 @@ _public_ int sd_event_dispatch(sd_event *e) { } static void event_log_delays(sd_event *e) { - char b[ELEMENTSOF(e->delays) * DECIMAL_STR_MAX(unsigned) + 1]; - unsigned i; - int o; + char b[ELEMENTSOF(e->delays) * DECIMAL_STR_MAX(unsigned) + 1], *p; + size_t l, i; - for (i = o = 0; i < ELEMENTSOF(e->delays); i++) { - o += snprintf(&b[o], sizeof(b) - o, "%u ", e->delays[i]); + p = b; + l = sizeof(b); + for (i = 0; i < ELEMENTSOF(e->delays); i++) { + l = strpcpyf(&p, l, "%u ", e->delays[i]); e->delays[i] = 0; } - log_debug("Event loop iterations: %.*s", o, b); + log_debug("Event loop iterations: %s", b); } _public_ int sd_event_run(sd_event *e, uint64_t timeout) { diff --git a/src/systemd/src/libsystemd/sd-id128/id128-util.c b/src/systemd/src/libsystemd/sd-id128/id128-util.c index 6245e9df..f8f0883c 100644 --- a/src/systemd/src/libsystemd/sd-id128/id128-util.c +++ b/src/systemd/src/libsystemd/sd-id128/id128-util.c @@ -2,6 +2,7 @@ #include "nm-sd-adapt-core.h" +#include <errno.h> #include <fcntl.h> #include <unistd.h> diff --git a/src/systemd/src/libsystemd/sd-id128/sd-id128.c b/src/systemd/src/libsystemd/sd-id128/sd-id128.c index d0cae981..eeb29e97 100644 --- a/src/systemd/src/libsystemd/sd-id128/sd-id128.c +++ b/src/systemd/src/libsystemd/sd-id128/sd-id128.c @@ -2,6 +2,7 @@ #include "nm-sd-adapt-core.h" +#include <errno.h> #include <fcntl.h> #include <unistd.h> diff --git a/src/systemd/src/shared/dns-domain.c b/src/systemd/src/shared/dns-domain.c deleted file mode 100644 index ebea861f..00000000 --- a/src/systemd/src/shared/dns-domain.c +++ /dev/null @@ -1,1387 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-sd-adapt-core.h" - -#if 0 /* NM_IGNORED */ -#if HAVE_LIBIDN2 -# include <idn2.h> -#elif HAVE_LIBIDN -# include <idna.h> -# include <stringprep.h> -#endif -#endif - -#include <endian.h> -#include <netinet/in.h> -#include <stdio.h> -#include <string.h> -#include <sys/socket.h> - -#include "alloc-util.h" -#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" -#include "string-util.h" -#include "strv.h" -#include "utf8.h" - -int dns_label_unescape(const char **name, char *dest, size_t sz, DNSLabelFlags flags) { - const char *n; - char *d, last_char = 0; - int r = 0; - - assert(name); - assert(*name); - - n = *name; - d = dest; - - for (;;) { - if (*n == 0 || *n == '.') { - if (FLAGS_SET(flags, DNS_LABEL_LDH) && last_char == '-') - /* Trailing dash */ - return -EINVAL; - - if (*n == '.') - n++; - break; - } - - if (r >= DNS_LABEL_MAX) - return -EINVAL; - - if (sz <= 0) - return -ENOBUFS; - - if (*n == '\\') { - /* Escaped character */ - if (FLAGS_SET(flags, DNS_LABEL_NO_ESCAPES)) - return -EINVAL; - - n++; - - if (*n == 0) - /* Ending NUL */ - return -EINVAL; - - 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--; - r++; - n++; - - } else if (n[0] >= '0' && n[0] <= '9') { - unsigned k; - - /* Escaped literal ASCII character */ - - if (!(n[1] >= '0' && n[1] <= '9') || - !(n[2] >= '0' && n[2] <= '9')) - return -EINVAL; - - k = ((unsigned) (n[0] - '0') * 100) + - ((unsigned) (n[1] - '0') * 10) + - ((unsigned) (n[2] - '0')); - - /* Don't allow anything that doesn't - * fit in 8bit. Note that we do allow - * control characters, as some servers - * (e.g. cloudflare) are happy to - * generate labels with them - * inside. */ - 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--; - r++; - - n += 3; - } else - return -EINVAL; - - } else if ((uint8_t) *n >= (uint8_t) ' ' && *n != 127) { - - /* 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--; - r++; - n++; - } else - return -EINVAL; - } - - /* Empty label that is not at the end? */ - if (r == 0 && *n) - return -EINVAL; - - /* More than one trailing dot? */ - if (*n == '.') - return -EINVAL; - - if (sz >= 1 && d) - *d = 0; - - *name = n; - return r; -} - -#if 0 /* NM_IGNORED */ -/* @label_terminal: terminal character of a label, updated to point to the terminal character of - * the previous label (always skipping one dot) or to NULL if there are no more - * labels. */ -int dns_label_unescape_suffix(const char *name, const char **label_terminal, char *dest, size_t sz) { - const char *terminal; - int r; - - assert(name); - assert(label_terminal); - assert(dest); - - /* no more labels */ - if (!*label_terminal) { - if (sz >= 1) - *dest = 0; - - return 0; - } - - terminal = *label_terminal; - assert(IN_SET(*terminal, 0, '.')); - - /* Skip current terminal character (and accept domain names ending it ".") */ - if (*terminal == 0) - terminal--; - if (terminal >= name && *terminal == '.') - terminal--; - - /* Point name to the last label, and terminal to the preceding terminal symbol (or make it a NULL pointer) */ - for (;;) { - if (terminal < name) { - /* Reached the first label, so indicate that there are no more */ - terminal = NULL; - break; - } - - /* Find the start of the last label */ - if (*terminal == '.') { - const char *y; - unsigned slashes = 0; - - for (y = terminal - 1; y >= name && *y == '\\'; y--) - slashes++; - - if (slashes % 2 == 0) { - /* The '.' was not escaped */ - name = terminal + 1; - break; - } else { - terminal = y; - continue; - } - } - - terminal--; - } - - r = dns_label_unescape(&name, dest, sz, 0); - if (r < 0) - return r; - - *label_terminal = terminal; - - return r; -} -#endif /* NM_IGNORED */ - -int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) { - char *q; - - /* DNS labels must be between 1 and 63 characters long. A - * zero-length label does not exist. See RFC 2182, Section - * 11. */ - - if (l <= 0 || l > DNS_LABEL_MAX) - return -EINVAL; - if (sz < 1) - return -ENOBUFS; - - assert(p); - assert(dest); - - q = dest; - while (l > 0) { - - if (IN_SET(*p, '.', '\\')) { - - /* Dot or backslash */ - - if (sz < 3) - return -ENOBUFS; - - *(q++) = '\\'; - *(q++) = *p; - - sz -= 2; - - } else if (IN_SET(*p, '_', '-') || - (*p >= '0' && *p <= '9') || - (*p >= 'a' && *p <= 'z') || - (*p >= 'A' && *p <= 'Z')) { - - /* Proper character */ - - if (sz < 2) - return -ENOBUFS; - - *(q++) = *p; - sz -= 1; - - } else { - - /* Everything else */ - - if (sz < 5) - return -ENOBUFS; - - *(q++) = '\\'; - *(q++) = '0' + (char) ((uint8_t) *p / 100); - *(q++) = '0' + (char) (((uint8_t) *p / 10) % 10); - *(q++) = '0' + (char) ((uint8_t) *p % 10); - - sz -= 4; - } - - p++; - l--; - } - - *q = 0; - return (int) (q - dest); -} - -#if 0 /* NM_IGNORED */ -int dns_label_escape_new(const char *p, size_t l, char **ret) { - _cleanup_free_ char *s = NULL; - int r; - - assert(p); - assert(ret); - - if (l <= 0 || l > DNS_LABEL_MAX) - return -EINVAL; - - s = new(char, DNS_LABEL_ESCAPED_MAX); - if (!s) - return -ENOMEM; - - r = dns_label_escape(p, l, s, DNS_LABEL_ESCAPED_MAX); - if (r < 0) - return r; - - *ret = TAKE_PTR(s); - - return r; -} - -#if HAVE_LIBIDN -int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) { - _cleanup_free_ uint32_t *input = NULL; - size_t input_size, l; - const char *p; - bool contains_8bit = false; - char buffer[DNS_LABEL_MAX+1]; - - assert(encoded); - assert(decoded); - - /* Converts an U-label into an A-label */ - - if (encoded_size <= 0) - return -EINVAL; - - for (p = encoded; p < encoded + encoded_size; p++) - if ((uint8_t) *p > 127) - contains_8bit = true; - - if (!contains_8bit) { - if (encoded_size > DNS_LABEL_MAX) - return -EINVAL; - - return 0; - } - - input = stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size); - if (!input) - return -ENOMEM; - - if (idna_to_ascii_4i(input, input_size, buffer, 0) != 0) - return -EINVAL; - - l = strlen(buffer); - - /* Verify that the result is not longer than one DNS label. */ - if (l <= 0 || l > DNS_LABEL_MAX) - return -EINVAL; - if (l > decoded_max) - return -ENOBUFS; - - memcpy(decoded, buffer, l); - - /* If there's room, append a trailing NUL byte, but only then */ - if (decoded_max > l) - decoded[l] = 0; - - return (int) l; -} - -int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) { - size_t input_size, output_size; - _cleanup_free_ uint32_t *input = NULL; - _cleanup_free_ char *result = NULL; - uint32_t *output = NULL; - size_t w; - - /* To be invoked after unescaping. Converts an A-label into an U-label. */ - - assert(encoded); - assert(decoded); - - if (encoded_size <= 0 || encoded_size > DNS_LABEL_MAX) - return -EINVAL; - - if (!memory_startswith(encoded, encoded_size, IDNA_ACE_PREFIX)) - return 0; - - input = stringprep_utf8_to_ucs4(encoded, encoded_size, &input_size); - if (!input) - return -ENOMEM; - - output_size = input_size; - output = newa(uint32_t, output_size); - - idna_to_unicode_44i(input, input_size, output, &output_size, 0); - - result = stringprep_ucs4_to_utf8(output, output_size, NULL, &w); - if (!result) - return -ENOMEM; - if (w <= 0) - return -EINVAL; - if (w > decoded_max) - return -ENOBUFS; - - memcpy(decoded, result, w); - - /* Append trailing NUL byte if there's space, but only then. */ - if (decoded_max > w) - decoded[w] = 0; - - return w; -} -#endif -#endif /* NM_IGNORED */ - -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; - bool first = true; - int r; - - if (a) - p = a; - else if (b) - p = TAKE_PTR(b); - else - goto finish; - - for (;;) { - char label[DNS_LABEL_MAX]; - - r = dns_label_unescape(&p, label, sizeof label, flags); - if (r < 0) - return r; - if (r == 0) { - if (*p != 0) - return -EINVAL; - - if (b) { - /* Now continue with the second string, if there is one */ - p = TAKE_PTR(b); - continue; - } - - break; - } - - if (_ret) { - if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) - return -ENOMEM; - - r = dns_label_escape(label, r, ret + n + !first, DNS_LABEL_ESCAPED_MAX); - if (r < 0) - return r; - - if (!first) - ret[n] = '.'; - } else { - char escaped[DNS_LABEL_ESCAPED_MAX]; - - r = dns_label_escape(label, r, escaped, sizeof(escaped)); - if (r < 0) - return r; - } - - if (!first) - n++; - else - first = false; - - n += r; - } - -finish: - if (n > DNS_HOSTNAME_MAX) - return -EINVAL; - - if (_ret) { - if (n == 0) { - /* Nothing appended? If so, generate at least a single dot, to indicate the DNS root domain */ - if (!GREEDY_REALLOC(ret, allocated, 2)) - return -ENOMEM; - - ret[n++] = '.'; - } else { - if (!GREEDY_REALLOC(ret, allocated, n + 1)) - return -ENOMEM; - } - - ret[n] = 0; - *_ret = TAKE_PTR(ret); - } - - return 0; -} - -#if 0 /* NM_IGNORED */ -void dns_name_hash_func(const char *p, struct siphash *state) { - int r; - - assert(p); - - for (;;) { - char label[DNS_LABEL_MAX+1]; - - r = dns_label_unescape(&p, label, sizeof label, 0); - if (r < 0) - break; - if (r == 0) - break; - - ascii_strlower_n(label, r); - siphash24_compress(label, r, state); - siphash24_compress_byte(0, state); /* make sure foobar and foo.bar result in different hashes */ - } - - /* enforce that all names are terminated by the empty label */ - string_hash_func("", state); -} - -int dns_name_compare_func(const char *a, const char *b) { - const char *x, *y; - int r, q; - - assert(a); - assert(b); - - x = a + strlen(a); - y = b + strlen(b); - - for (;;) { - char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX]; - - if (x == NULL && y == NULL) - return 0; - - r = dns_label_unescape_suffix(a, &x, la, sizeof(la)); - q = dns_label_unescape_suffix(b, &y, lb, sizeof(lb)); - if (r < 0 || q < 0) - return CMP(r, q); - - r = ascii_strcasecmp_nn(la, r, lb, q); - if (r != 0) - return r; - } -} - -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; - - assert(x); - assert(y); - - for (;;) { - char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX]; - - r = dns_label_unescape(&x, la, sizeof la, 0); - if (r < 0) - return r; - - q = dns_label_unescape(&y, lb, sizeof lb, 0); - if (q < 0) - return q; - - if (r != q) - return false; - if (r == 0) - return true; - - if (ascii_strcasecmp_n(la, lb, r) != 0) - return false; - } -} - -int dns_name_endswith(const char *name, const char *suffix) { - const char *n, *s, *saved_n = NULL; - int r, q; - - assert(name); - assert(suffix); - - n = name; - s = suffix; - - for (;;) { - char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX]; - - 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, 0); - if (q < 0) - return q; - - if (r == 0 && q == 0) - return true; - if (r == 0 && saved_n == n) - return false; - - if (r != q || ascii_strcasecmp_n(ln, ls, r) != 0) { - - /* Not the same, let's jump back, and try with the next label again */ - s = suffix; - n = TAKE_PTR(saved_n); - } - } -} - -int dns_name_startswith(const char *name, const char *prefix) { - const char *n, *p; - int r, q; - - assert(name); - assert(prefix); - - n = name; - p = prefix; - - for (;;) { - char ln[DNS_LABEL_MAX], lp[DNS_LABEL_MAX]; - - 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, 0); - if (q < 0) - return q; - - if (r != q) - return false; - if (ascii_strcasecmp_n(ln, lp, r) != 0) - return false; - } -} - -int dns_name_change_suffix(const char *name, const char *old_suffix, const char *new_suffix, char **ret) { - const char *n, *s, *saved_before = NULL, *saved_after = NULL, *prefix; - int r, q; - - assert(name); - assert(old_suffix); - assert(new_suffix); - assert(ret); - - n = name; - s = old_suffix; - - for (;;) { - char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX]; - - if (!saved_before) - saved_before = n; - - 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, 0); - if (q < 0) - return q; - - if (r == 0 && q == 0) - break; - if (r == 0 && saved_after == n) { - *ret = NULL; /* doesn't match */ - return 0; - } - - if (r != q || ascii_strcasecmp_n(ln, ls, r) != 0) { - - /* Not the same, let's jump back, and try with the next label again */ - s = old_suffix; - n = TAKE_PTR(saved_after); - saved_before = NULL; - } - } - - /* Found it! Now generate the new name */ - prefix = strndupa(name, saved_before - name); - - r = dns_name_concat(prefix, new_suffix, 0, ret); - if (r < 0) - return r; - - return 1; -} - -int dns_name_between(const char *a, const char *b, const char *c) { - /* Determine if b is strictly greater than a and strictly smaller than c. - We consider the order of names to be circular, so that if a is - strictly greater than c, we consider b to be between them if it is - either greater than a or smaller than c. This is how the canonical - DNS name order used in NSEC records work. */ - - if (dns_name_compare_func(a, c) < 0) - /* - a and c are properly ordered: - a<---b--->c - */ - return dns_name_compare_func(a, b) < 0 && - dns_name_compare_func(b, c) < 0; - else - /* - a and c are equal or 'reversed': - <--b--c a-----> - or: - <-----c a--b--> - */ - return dns_name_compare_func(b, c) < 0 || - dns_name_compare_func(a, b) < 0; -} - -int dns_name_reverse(int family, const union in_addr_union *a, char **ret) { - const uint8_t *p; - int r; - - assert(a); - assert(ret); - - p = (const uint8_t*) a; - - if (family == AF_INET) - r = asprintf(ret, "%u.%u.%u.%u.in-addr.arpa", p[3], p[2], p[1], p[0]); - else if (family == AF_INET6) - r = asprintf(ret, "%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.%c.ip6.arpa", - hexchar(p[15] & 0xF), hexchar(p[15] >> 4), hexchar(p[14] & 0xF), hexchar(p[14] >> 4), - hexchar(p[13] & 0xF), hexchar(p[13] >> 4), hexchar(p[12] & 0xF), hexchar(p[12] >> 4), - hexchar(p[11] & 0xF), hexchar(p[11] >> 4), hexchar(p[10] & 0xF), hexchar(p[10] >> 4), - hexchar(p[ 9] & 0xF), hexchar(p[ 9] >> 4), hexchar(p[ 8] & 0xF), hexchar(p[ 8] >> 4), - hexchar(p[ 7] & 0xF), hexchar(p[ 7] >> 4), hexchar(p[ 6] & 0xF), hexchar(p[ 6] >> 4), - hexchar(p[ 5] & 0xF), hexchar(p[ 5] >> 4), hexchar(p[ 4] & 0xF), hexchar(p[ 4] >> 4), - hexchar(p[ 3] & 0xF), hexchar(p[ 3] >> 4), hexchar(p[ 2] & 0xF), hexchar(p[ 2] >> 4), - hexchar(p[ 1] & 0xF), hexchar(p[ 1] >> 4), hexchar(p[ 0] & 0xF), hexchar(p[ 0] >> 4)); - else - return -EAFNOSUPPORT; - if (r < 0) - return -ENOMEM; - - return 0; -} - -int dns_name_address(const char *p, int *family, union in_addr_union *address) { - int r; - - assert(p); - assert(family); - assert(address); - - r = dns_name_endswith(p, "in-addr.arpa"); - if (r < 0) - return r; - if (r > 0) { - uint8_t a[4]; - unsigned i; - - for (i = 0; i < ELEMENTSOF(a); i++) { - char label[DNS_LABEL_MAX+1]; - - r = dns_label_unescape(&p, label, sizeof label, 0); - if (r < 0) - return r; - if (r == 0) - return -EINVAL; - if (r > 3) - return -EINVAL; - - r = safe_atou8(label, &a[i]); - if (r < 0) - return r; - } - - r = dns_name_equal(p, "in-addr.arpa"); - if (r <= 0) - return r; - - *family = AF_INET; - address->in.s_addr = htobe32(((uint32_t) a[3] << 24) | - ((uint32_t) a[2] << 16) | - ((uint32_t) a[1] << 8) | - (uint32_t) a[0]); - - return 1; - } - - r = dns_name_endswith(p, "ip6.arpa"); - if (r < 0) - return r; - if (r > 0) { - struct in6_addr a; - unsigned i; - - for (i = 0; i < ELEMENTSOF(a.s6_addr); i++) { - char label[DNS_LABEL_MAX+1]; - int x, y; - - r = dns_label_unescape(&p, label, sizeof label, 0); - if (r <= 0) - return r; - if (r != 1) - return -EINVAL; - x = unhexchar(label[0]); - if (x < 0) - return -EINVAL; - - r = dns_label_unescape(&p, label, sizeof label, 0); - if (r <= 0) - return r; - if (r != 1) - return -EINVAL; - y = unhexchar(label[0]); - if (y < 0) - return -EINVAL; - - a.s6_addr[ELEMENTSOF(a.s6_addr) - i - 1] = (uint8_t) y << 4 | (uint8_t) x; - } - - r = dns_name_equal(p, "ip6.arpa"); - if (r <= 0) - return r; - - *family = AF_INET6; - address->in6 = a; - return 1; - } - - return 0; -} -#endif /* NM_IGNORED */ - -bool dns_name_is_root(const char *name) { - - assert(name); - - /* There are exactly two ways to encode the root domain name: - * as empty string, or with a single dot. */ - - return STR_IN_SET(name, "", "."); -} - -bool dns_name_is_single_label(const char *name) { - int r; - - assert(name); - - r = dns_name_parent(&name); - if (r <= 0) - return false; - - return dns_name_is_root(name); -} - -/* Encode a domain name according to RFC 1035 Section 3.1, without compression */ -int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len, bool canonical) { - uint8_t *label_length, *out; - int r; - - assert(domain); - assert(buffer); - - out = buffer; - - do { - /* Reserve a byte for label length */ - if (len <= 0) - return -ENOBUFS; - len--; - label_length = out; - out++; - - /* Convert and copy a single label. Note that - * 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, 0); - if (r < 0) - return r; - - /* Optionally, output the name in DNSSEC canonical - * format, as described in RFC 4034, section 6.2. Or - * in other words: in lower-case. */ - if (canonical) - ascii_strlower_n((char*) out, (size_t) r); - - /* Fill label length, move forward */ - *label_length = r; - out += r; - len -= r; - - } while (r != 0); - - /* Verify the maximum size of the encoded name. The trailing - * dot + NUL byte account are included this time, hence - * compare against DNS_HOSTNAME_MAX + 2 (which is 255) this - * time. */ - if (out - buffer > DNS_HOSTNAME_MAX + 2) - return -EINVAL; - - return out - buffer; -} - -#if 0 /* NM_IGNORED */ -static bool srv_type_label_is_valid(const char *label, size_t n) { - size_t k; - - assert(label); - - if (n < 2) /* Label needs to be at least 2 chars long */ - return false; - - if (label[0] != '_') /* First label char needs to be underscore */ - return false; - - /* Second char must be a letter */ - if (!(label[1] >= 'A' && label[1] <= 'Z') && - !(label[1] >= 'a' && label[1] <= 'z')) - return false; - - /* Third and further chars must be alphanumeric or a hyphen */ - for (k = 2; k < n; k++) { - if (!(label[k] >= 'A' && label[k] <= 'Z') && - !(label[k] >= 'a' && label[k] <= 'z') && - !(label[k] >= '0' && label[k] <= '9') && - label[k] != '-') - return false; - } - - return true; -} - -bool dns_srv_type_is_valid(const char *name) { - unsigned c = 0; - int r; - - if (!name) - return false; - - for (;;) { - char label[DNS_LABEL_MAX]; - - /* This more or less implements RFC 6335, Section 5.1 */ - - r = dns_label_unescape(&name, label, sizeof label, 0); - if (r < 0) - return false; - if (r == 0) - break; - - if (c >= 2) - return false; - - if (!srv_type_label_is_valid(label, r)) - return false; - - c++; - } - - return c == 2; /* exactly two labels */ -} - -bool dnssd_srv_type_is_valid(const char *name) { - return dns_srv_type_is_valid(name) && - ((dns_name_endswith(name, "_tcp") > 0) || - (dns_name_endswith(name, "_udp") > 0)); /* Specific to DNS-SD. RFC 6763, Section 7 */ -} - -bool dns_service_name_is_valid(const char *name) { - size_t l; - - /* This more or less implements RFC 6763, Section 4.1.1 */ - - if (!name) - return false; - - if (!utf8_is_valid(name)) - return false; - - if (string_has_cc(name, NULL)) - return false; - - l = strlen(name); - if (l <= 0) - return false; - if (l > 63) - return false; - - return true; -} - -int dns_service_join(const char *name, const char *type, const char *domain, char **ret) { - char escaped[DNS_LABEL_ESCAPED_MAX]; - _cleanup_free_ char *n = NULL; - int r; - - assert(type); - assert(domain); - assert(ret); - - if (!dns_srv_type_is_valid(type)) - return -EINVAL; - - if (!name) - return dns_name_concat(type, domain, 0, ret); - - if (!dns_service_name_is_valid(name)) - return -EINVAL; - - r = dns_label_escape(name, strlen(name), escaped, sizeof(escaped)); - if (r < 0) - return r; - - r = dns_name_concat(type, domain, 0, &n); - if (r < 0) - return r; - - return dns_name_concat(escaped, n, 0, ret); -} - -static bool dns_service_name_label_is_valid(const char *label, size_t n) { - char *s; - - assert(label); - - if (memchr(label, 0, n)) - return false; - - s = strndupa(label, n); - return dns_service_name_is_valid(s); -} - -int dns_service_split(const char *joined, char **_name, char **_type, char **_domain) { - _cleanup_free_ char *name = NULL, *type = NULL, *domain = NULL; - const char *p = joined, *q = NULL, *d = NULL; - char a[DNS_LABEL_MAX], b[DNS_LABEL_MAX], c[DNS_LABEL_MAX]; - int an, bn, cn, r; - unsigned x = 0; - - assert(joined); - - /* Get first label from the full name */ - an = dns_label_unescape(&p, a, sizeof(a), 0); - if (an < 0) - return an; - - if (an > 0) { - x++; - - /* If there was a first label, try to get the second one */ - bn = dns_label_unescape(&p, b, sizeof(b), 0); - if (bn < 0) - return bn; - - if (bn > 0) { - x++; - - /* If there was a second label, try to get the third one */ - q = p; - cn = dns_label_unescape(&p, c, sizeof(c), 0); - if (cn < 0) - return cn; - - if (cn > 0) - x++; - } else - cn = 0; - } else - an = 0; - - if (x >= 2 && srv_type_label_is_valid(b, bn)) { - - if (x >= 3 && srv_type_label_is_valid(c, cn)) { - - if (dns_service_name_label_is_valid(a, an)) { - /* OK, got <name> . <type> . <type2> . <domain> */ - - name = strndup(a, an); - if (!name) - return -ENOMEM; - - type = strjoin(b, ".", c); - if (!type) - return -ENOMEM; - - d = p; - goto finish; - } - - } else if (srv_type_label_is_valid(a, an)) { - - /* OK, got <type> . <type2> . <domain> */ - - name = NULL; - - type = strjoin(a, ".", b); - if (!type) - return -ENOMEM; - - d = q; - goto finish; - } - } - - name = NULL; - type = NULL; - d = joined; - -finish: - r = dns_name_normalize(d, 0, &domain); - if (r < 0) - return r; - - if (_domain) - *_domain = TAKE_PTR(domain); - - if (_type) - *_type = TAKE_PTR(type); - - if (_name) - *_name = TAKE_PTR(name); - - return 0; -} - -static int dns_name_build_suffix_table(const char *name, const char *table[]) { - const char *p; - unsigned n = 0; - int r; - - assert(name); - assert(table); - - p = name; - for (;;) { - if (n > DNS_N_LABELS_MAX) - return -EINVAL; - - table[n] = p; - r = dns_name_parent(&p); - if (r < 0) - return r; - if (r == 0) - break; - - n++; - } - - return (int) n; -} - -int dns_name_suffix(const char *name, unsigned n_labels, const char **ret) { - const char* labels[DNS_N_LABELS_MAX+1]; - int n; - - assert(name); - assert(ret); - - n = dns_name_build_suffix_table(name, labels); - if (n < 0) - return n; - - if ((unsigned) n < n_labels) - return -EINVAL; - - *ret = labels[n - n_labels]; - return (int) (n - n_labels); -} - -int dns_name_skip(const char *a, unsigned n_labels, const char **ret) { - int r; - - assert(a); - assert(ret); - - for (; n_labels > 0; n_labels--) { - r = dns_name_parent(&a); - if (r < 0) - return r; - if (r == 0) { - *ret = ""; - return 0; - } - } - - *ret = a; - return 1; -} - -int dns_name_count_labels(const char *name) { - unsigned n = 0; - const char *p; - int r; - - assert(name); - - p = name; - for (;;) { - r = dns_name_parent(&p); - if (r < 0) - return r; - if (r == 0) - break; - - if (n >= DNS_N_LABELS_MAX) - return -EINVAL; - - n++; - } - - return (int) n; -} - -int dns_name_equal_skip(const char *a, unsigned n_labels, const char *b) { - int r; - - assert(a); - assert(b); - - r = dns_name_skip(a, n_labels, &a); - if (r <= 0) - return r; - - return dns_name_equal(a, b); -} - -int dns_name_common_suffix(const char *a, const char *b, const char **ret) { - const char *a_labels[DNS_N_LABELS_MAX+1], *b_labels[DNS_N_LABELS_MAX+1]; - int n = 0, m = 0, k = 0, r, q; - - assert(a); - assert(b); - assert(ret); - - /* Determines the common suffix of domain names a and b */ - - n = dns_name_build_suffix_table(a, a_labels); - if (n < 0) - return n; - - m = dns_name_build_suffix_table(b, b_labels); - if (m < 0) - return m; - - for (;;) { - char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX]; - const char *x, *y; - - if (k >= n || k >= m) { - *ret = a_labels[n - k]; - return 0; - } - - x = a_labels[n - 1 - k]; - 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, 0); - if (q < 0) - return q; - - if (r != q || ascii_strcasecmp_n(la, lb, r) != 0) { - *ret = a_labels[n - k]; - return 0; - } - - k++; - } -} - -int dns_name_apply_idna(const char *name, char **ret) { - /* Return negative on error, 0 if not implemented, positive on success. */ - -#if HAVE_LIBIDN2 - int r; - _cleanup_free_ char *t = NULL; - - assert(name); - assert(ret); - - r = idn2_lookup_u8((uint8_t*) name, (uint8_t**) &t, - IDN2_NFC_INPUT | IDN2_NONTRANSITIONAL); - log_debug("idn2_lookup_u8: %s → %s", name, t); - if (r == IDN2_OK) { - if (!startswith(name, "xn--")) { - _cleanup_free_ char *s = NULL; - - r = idn2_to_unicode_8z8z(t, &s, 0); - if (r != IDN2_OK) { - log_debug("idn2_to_unicode_8z8z(\"%s\") failed: %d/%s", - t, r, idn2_strerror(r)); - return 0; - } - - if (!streq_ptr(name, s)) { - log_debug("idn2 roundtrip failed: \"%s\" → \"%s\" → \"%s\", ignoring.", - name, t, s); - return 0; - } - } - - *ret = TAKE_PTR(t); - - return 1; /* *ret has been written */ - } - - log_debug("idn2_lookup_u8(\"%s\") failed: %d/%s", name, r, idn2_strerror(r)); - if (r == IDN2_2HYPHEN) - /* 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; - return -EINVAL; -#elif HAVE_LIBIDN - _cleanup_free_ char *buf = NULL; - size_t n = 0, allocated = 0; - bool first = true; - int r, q; - - assert(name); - assert(ret); - - for (;;) { - char label[DNS_LABEL_MAX]; - - 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); - if (q < 0) - return q; - if (q > 0) - r = q; - - if (!GREEDY_REALLOC(buf, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) - return -ENOMEM; - - r = dns_label_escape(label, r, buf + n + !first, DNS_LABEL_ESCAPED_MAX); - if (r < 0) - return r; - - if (first) - first = false; - else - buf[n++] = '.'; - - n += r; - } - - if (n > DNS_HOSTNAME_MAX) - return -EINVAL; - - if (!GREEDY_REALLOC(buf, allocated, n + 1)) - return -ENOMEM; - - buf[n] = 0; - *ret = TAKE_PTR(buf); - - return 1; -#else - return 0; -#endif -} - -int dns_name_is_valid_or_address(const char *name) { - /* Returns > 0 if the specified name is either a valid IP address formatted as string or a valid DNS name */ - - if (isempty(name)) - return 0; - - if (in_addr_from_string_auto(name, NULL, NULL) >= 0) - return 1; - - return dns_name_is_valid(name); -} -#endif /* NM_IGNORED */ diff --git a/src/systemd/src/shared/dns-domain.h b/src/systemd/src/shared/dns-domain.h deleted file mode 100644 index 17db7c52..00000000 --- a/src/systemd/src/shared/dns-domain.h +++ /dev/null @@ -1,113 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> - -#include "hashmap.h" -#include "in-addr-util.h" - -/* Length of a single label, with all escaping removed, excluding any trailing dot or NUL byte */ -#define DNS_LABEL_MAX 63 - -/* Worst case length of a single label, with all escaping applied and room for a trailing NUL byte. */ -#define DNS_LABEL_ESCAPED_MAX (DNS_LABEL_MAX*4+1) - -/* Maximum length of a full hostname, consisting of a series of unescaped labels, and no trailing dot or NUL byte */ -#define DNS_HOSTNAME_MAX 253 - -/* Maximum length of a full hostname, on the wire, including the final NUL byte */ -#define DNS_WIRE_FORMAT_HOSTNAME_MAX 255 - -/* Maximum number of labels per valid hostname */ -#define DNS_N_LABELS_MAX 127 - -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, 0); -} - -#if 0 /* NM_IGNORED */ -#if HAVE_LIBIDN -int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max); -int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max); -#endif -#endif /* NM_IGNORED */ - -int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, 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, 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, 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) - return r; - return 1; -} - -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); -int dns_name_equal(const char *x, const char *y); -int dns_name_endswith(const char *name, const char *suffix); -int dns_name_startswith(const char *name, const char *prefix); - -int dns_name_change_suffix(const char *name, const char *old_suffix, const char *new_suffix, char **ret); - -int dns_name_reverse(int family, const union in_addr_union *a, char **ret); -int dns_name_address(const char *p, int *family, union in_addr_union *a); - -bool dns_name_is_root(const char *name); -bool dns_name_is_single_label(const char *name); - -int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len, bool canonical); - -bool dns_srv_type_is_valid(const char *name); -bool dnssd_srv_type_is_valid(const char *name); -bool dns_service_name_is_valid(const char *name); - -int dns_service_join(const char *name, const char *type, const char *domain, char **ret); -int dns_service_split(const char *joined, char **name, char **type, char **domain); - -int dns_name_suffix(const char *name, unsigned n_labels, const char **ret); -int dns_name_count_labels(const char *name); - -int dns_name_skip(const char *a, unsigned n_labels, const char **ret); -int dns_name_equal_skip(const char *a, unsigned n_labels, const char *b); - -int dns_name_common_suffix(const char *a, const char *b, const char **ret); - -int dns_name_apply_idna(const char *name, char **ret); - -int dns_name_is_valid_or_address(const char *name); diff --git a/src/systemd/src/systemd/sd-dhcp-client.h b/src/systemd/src/systemd/sd-dhcp-client.h index bd0d429d..ab62368e 100644 --- a/src/systemd/src/systemd/sd-dhcp-client.h +++ b/src/systemd/src/systemd/sd-dhcp-client.h @@ -38,6 +38,7 @@ enum { SD_DHCP_CLIENT_EVENT_IP_CHANGE = 2, SD_DHCP_CLIENT_EVENT_EXPIRED = 3, SD_DHCP_CLIENT_EVENT_RENEW = 4, + SD_DHCP_CLIENT_EVENT_SELECTING = 5, }; enum { @@ -98,7 +99,7 @@ enum { typedef struct sd_dhcp_client sd_dhcp_client; -typedef void (*sd_dhcp_client_callback_t)(sd_dhcp_client *client, int event, void *userdata); +typedef int (*sd_dhcp_client_callback_t)(sd_dhcp_client *client, int event, void *userdata); int sd_dhcp_client_set_callback( sd_dhcp_client *client, sd_dhcp_client_callback_t cb, @@ -154,6 +155,9 @@ int sd_dhcp_client_get_client_id( int sd_dhcp_client_set_mtu( sd_dhcp_client *client, uint32_t mtu); +int sd_dhcp_client_set_max_attempts( + sd_dhcp_client *client, + uint64_t attempt); int sd_dhcp_client_set_client_port( sd_dhcp_client *client, uint16_t port); @@ -172,6 +176,7 @@ int sd_dhcp_client_get_lease( int sd_dhcp_client_stop(sd_dhcp_client *client); int sd_dhcp_client_start(sd_dhcp_client *client); +int sd_dhcp_client_send_release(sd_dhcp_client *client); sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client); sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client); diff --git a/src/systemd/src/systemd/sd-event.h b/src/systemd/src/systemd/sd-event.h index 7bb86093..b14c9269 100644 --- a/src/systemd/src/systemd/sd-event.h +++ b/src/systemd/src/systemd/sd-event.h @@ -113,6 +113,7 @@ int sd_event_get_iteration(sd_event *e, uint64_t *ret); sd_event_source* sd_event_source_ref(sd_event_source *s); sd_event_source* sd_event_source_unref(sd_event_source *s); +sd_event_source* sd_event_source_disable_unref(sd_event_source *s); sd_event *sd_event_source_get_event(sd_event_source *s); void* sd_event_source_get_userdata(sd_event_source *s); @@ -149,6 +150,7 @@ 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); _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_event_source, sd_event_source_unref); +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_event_source, sd_event_source_disable_unref); _SD_END_DECLARATIONS; diff --git a/src/systemd/src/systemd/sd-id128.h b/src/systemd/src/systemd/sd-id128.h index c5fbe0a4..9b00b76e 100644 --- a/src/systemd/src/systemd/sd-id128.h +++ b/src/systemd/src/systemd/sd-id128.h @@ -54,7 +54,6 @@ int sd_id128_get_boot_app_specific(sd_id128_t app_id, 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) 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 diff --git a/src/tests/config/nm-test-device.c b/src/tests/config/nm-test-device.c index 3a8ef266..dd624b44 100644 --- a/src/tests/config/nm-test-device.c +++ b/src/tests/config/nm-test-device.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/tests/config/nm-test-device.h b/src/tests/config/nm-test-device.h index c193b860..586bbe6d 100644 --- a/src/tests/config/nm-test-device.h +++ b/src/tests/config/nm-test-device.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/tests/config/test-config.c b/src/tests/config/test-config.c index 10084b7f..a5ef1431 100644 --- a/src/tests/config/test-config.c +++ b/src/tests/config/test-config.c @@ -1,4 +1,3 @@ -/* -*- 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 diff --git a/src/tests/meson.build b/src/tests/meson.build index 17857676..b2bc13ab 100644 --- a/src/tests/meson.build +++ b/src/tests/meson.build @@ -1,8 +1,8 @@ subdir('config') test_units = [ - 'test-general', - 'test-general-with-expect', + 'test-core', + 'test-core-with-expect', 'test-ip4-config', 'test-ip6-config', 'test-dcb', @@ -18,7 +18,7 @@ foreach test_unit: test_units ) test( - 'src/' + test_unit, + test_unit, test_script, args: test_args + [exe.full_path()], timeout: default_test_timeout, @@ -34,7 +34,7 @@ cflags = [ exe = executable( test_unit, - [test_unit + '.c'], + test_unit + '.c', include_directories: src_inc, dependencies: libnm_core_dep, c_args: cflags, @@ -45,7 +45,7 @@ exe = executable( ) test( - 'src/' + test_unit, + test_unit, test_script, args: test_args + [exe.full_path()], ) diff --git a/src/tests/test-general-with-expect.c b/src/tests/test-core-with-expect.c index 8339fa5d..d38025ec 100644 --- a/src/tests/test-general-with-expect.c +++ b/src/tests/test-core-with-expect.c @@ -1,4 +1,3 @@ -/* -*- 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 @@ -385,7 +384,12 @@ test_nm_utils_kill_child (void) err = waitpid (child_pid, &exit_status, 0); } while (err == -1 && errno == EINTR); g_assert (err == child_pid); - g_assert (WIFEXITED (exit_status) && WEXITSTATUS(exit_status) == 0); + if (WIFEXITED (exit_status)) + g_assert_cmpint (WEXITSTATUS (exit_status), ==, 0); + else { + g_assert_cmpint (exit_status, ==, 0); + g_assert_not_reached (); + } } /*****************************************************************************/ @@ -541,7 +545,7 @@ test_nm_utils_new_vlan_name (void) guint vlan_id; /* Create a random VLAN id between 0 and 4094 */ - vlan_id = nmtst_get_rand_int () % 4095; + vlan_id = nmtst_get_rand_uint32 () % 4095; vlan_id_s = g_strdup_printf (".%d", vlan_id); diff --git a/src/tests/test-general.c b/src/tests/test-core.c index 0dee566e..3eaad104 100644 --- a/src/tests/test-general.c +++ b/src/tests/test-core.c @@ -1,4 +1,3 @@ -/* -*- 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 @@ -109,6 +108,17 @@ test_nm_utils_ip6_address_clear_host_address (void) /*****************************************************************************/ static void +test_logging_domains (void) +{ + const char *s; + + s = nm_logging_all_domains_to_string (); + g_assert (s && s[0]); +} + +/*****************************************************************************/ + +static void _test_same_prefix (const char *a1, const char *a2, guint8 plen) { struct in6_addr a = *nmtst_inet6_from_string (a1); @@ -136,7 +146,7 @@ test_nm_utils_ip6_address_same_prefix (void) nmtst_rand_buf (NULL, a.ptr, sizeof (a)); nmtst_rand_buf (NULL, b.ptr, sizeof (b)); again_plen: - plen = nmtst_get_rand_int () % 129; + plen = nmtst_get_rand_uint32 () % 129; if (!is_same && NM_IN_SET (plen, 0, 128)) goto again_plen; @@ -170,7 +180,7 @@ again_plen: for (n = 0; n < N; n++) { nmtst_rand_buf (NULL, a.ptr, sizeof (a)); nmtst_rand_buf (NULL, b.ptr, sizeof (b)); - plen = nmtst_get_rand_int () % 129; + plen = nmtst_get_rand_uint32 () % 129; memset (addrmask.ptr, 0xFF, sizeof (addrmask)); nm_utils_ip6_address_clear_host_address (&addrmask.val, &addrmask.val, plen); @@ -187,7 +197,7 @@ again_plen: nmtst_rand_buf (NULL, a.ptr, sizeof (a)); nmtst_rand_buf (NULL, b.ptr, sizeof (b)); - plen = nmtst_get_rand_int () % 129; + plen = nmtst_get_rand_uint32 () % 129; if (!plen) continue; @@ -1445,7 +1455,7 @@ test_nm_utils_strbuf_append (void) const char *_str_iter; \ gs_free char *_str = NULL; \ \ - switch (nmtst_get_rand_int () % 4) { \ + switch (nmtst_get_rand_uint32 () % 4) { \ case 0: \ nm_utils_strbuf_append (_buf, _len, (format), __VA_ARGS__); \ break; \ @@ -1473,7 +1483,7 @@ test_nm_utils_strbuf_append (void) gsize *_len = (len); \ const char *_str = (str); \ \ - switch (nmtst_get_rand_int () % 4) { \ + switch (nmtst_get_rand_uint32 () % 4) { \ case 0: \ nm_utils_strbuf_append (_buf, _len, "%s", _str ?: ""); \ break; \ @@ -1498,7 +1508,7 @@ test_nm_utils_strbuf_append (void) gsize *_len = (len); \ char _ch = (ch); \ \ - switch (nmtst_get_rand_int () % 4) { \ + switch (nmtst_get_rand_uint32 () % 4) { \ case 0: \ nm_utils_strbuf_append (_buf, _len, "%c", _ch); \ break; \ @@ -1516,7 +1526,7 @@ test_nm_utils_strbuf_append (void) 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); + const int s_len = nmtst_get_rand_uint32 () % (sizeof (str) - 5); char *t_buf; gsize t_len; int test_mode; @@ -1532,7 +1542,7 @@ test_nm_utils_strbuf_append (void) t_buf = buf; t_len = buf_len; - test_mode = nmtst_get_rand_int () % 5; + test_mode = nmtst_get_rand_uint32 () % 5; switch (test_mode) { case 0: @@ -1557,7 +1567,7 @@ test_nm_utils_strbuf_append (void) g_snprintf (t_buf, t_len, "%s", str); if ( t_len > 0 && strlen (str) >= buf_len - && (nmtst_get_rand_int () % 2)) { + && (nmtst_get_rand_uint32 () % 2)) { /* the string was truncated by g_snprintf(). That means, at the last position in the * buffer is now NUL. * Replace the NUL by the actual character, and check that nm_utils_strbuf_seek_end() @@ -1677,7 +1687,7 @@ test_duplicate_decl_specifier (void) (_a > _b ? _a : _b); \ }) - v_result[0] = TEST_MAX (v_const[0], nmtst_get_rand_int () % 5) + v2; + v_result[0] = TEST_MAX (v_const[0], nmtst_get_rand_uint32 () % 5) + v2; NM_PRAGMA_WARNING_REENABLE } @@ -2180,6 +2190,8 @@ main (int argc, char **argv) { nmtst_init_with_logging (&argc, &argv, NULL, "ALL"); + g_test_add_func ("/general/test_logging_domains", test_logging_domains); + g_test_add_func ("/general/nm_utils_strbuf_append", test_nm_utils_strbuf_append); g_test_add_func ("/general/nm_utils_ip6_address_clear_host_address", test_nm_utils_ip6_address_clear_host_address); diff --git a/src/tests/test-dcb.c b/src/tests/test-dcb.c index 6291f254..55206f1d 100644 --- a/src/tests/test-dcb.c +++ b/src/tests/test-dcb.c @@ -1,4 +1,3 @@ -/* -*- 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 diff --git a/src/tests/test-ip4-config.c b/src/tests/test-ip4-config.c index f98c84c3..d4d0c1bb 100644 --- a/src/tests/test-ip4-config.c +++ b/src/tests/test-ip4-config.c @@ -1,4 +1,3 @@ -/* -*- 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 diff --git a/src/tests/test-ip6-config.c b/src/tests/test-ip6-config.c index 8f5f41d7..bc212ba7 100644 --- a/src/tests/test-ip6-config.c +++ b/src/tests/test-ip6-config.c @@ -1,4 +1,3 @@ -/* -*- 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 diff --git a/src/tests/test-secret-agent.py b/src/tests/test-secret-agent.py index d3f82802..d8cabc65 100755 --- a/src/tests/test-secret-agent.py +++ b/src/tests/test-secret-agent.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- from gi.repository import GLib import sys diff --git a/src/tests/test-systemd.c b/src/tests/test-systemd.c index 20cbd50e..090dd691 100644 --- a/src/tests/test-systemd.c +++ b/src/tests/test-systemd.c @@ -1,4 +1,3 @@ -/* -*- 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 @@ -157,7 +156,7 @@ test_sd_event (void) g_assert_cmpint (sd_event_default (NULL), ==, 0); - for (i = 0, n = (nmtst_get_rand_int () % (G_N_ELEMENTS (other_events) + 1)); i < n; i++) { + for (i = 0, n = (nmtst_get_rand_uint32 () % (G_N_ELEMENTS (other_events) + 1)); i < n; i++) { r = sd_event_default (&other_events[i]); g_assert (r >= 0 && other_events[i]); } @@ -177,7 +176,7 @@ test_sd_event (void) g_assert (!user_data.event_source); event = sd_event_unref (event); - for (i = 0, n = (nmtst_get_rand_int () % (G_N_ELEMENTS (other_events) + 1)); i < n; i++) + for (i = 0, n = (nmtst_get_rand_uint32 () % (G_N_ELEMENTS (other_events) + 1)); i < n; i++) other_events[i] = sd_event_unref (other_events[i]); nm_clear_g_source (&sd_id); for (i = 0, n = G_N_ELEMENTS (other_events); i < n; i++) @@ -311,15 +310,15 @@ test_nm_sd_utils_unbase64mem (void) _test_unbase64mem ("YQ==", "a"); _test_unbase64mem_inval ("YQ==a"); - rnd_len = nmtst_get_rand_int () % sizeof (rnd_buf); + rnd_len = nmtst_get_rand_uint32 () % sizeof (rnd_buf); for (i = 0; i < rnd_len; i++) - rnd_buf[i] = nmtst_get_rand_int () % 256; + rnd_buf[i] = nmtst_get_rand_uint32 () % 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; + char ch = nmtst_get_rand_uint32 () % 256; if (ch != '=') _test_unbase64char (ch, TRUE); diff --git a/src/tests/test-utils.c b/src/tests/test-utils.c index c326f790..f3eb66ef 100644 --- a/src/tests/test-utils.c +++ b/src/tests/test-utils.c @@ -1,4 +1,3 @@ -/* -*- 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 diff --git a/src/tests/test-wired-defname.c b/src/tests/test-wired-defname.c index 97a6c319..27e0590b 100644 --- a/src/tests/test-wired-defname.c +++ b/src/tests/test-wired-defname.c @@ -1,4 +1,3 @@ -/* -*- 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 diff --git a/src/vpn/nm-vpn-connection.c b/src/vpn/nm-vpn-connection.c index 5acf491a..598af04e 100644 --- a/src/vpn/nm-vpn-connection.c +++ b/src/vpn/nm-vpn-connection.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -103,7 +102,7 @@ typedef struct { char *username; VpnState vpn_state; - guint dispatcher_id; + NMDispatcherCallId *dispatcher_id; NMActiveConnectionStateReason failure_reason; NMVpnServiceState service_state; @@ -124,8 +123,7 @@ typedef struct { GVariant *connect_hash; guint connect_timeout; NMProxyConfig *proxy_config; - NMPacrunnerManager *pacrunner_manager; - NMPacrunnerCallId *pacrunner_call_id; + NMPacrunnerConfId *pacrunner_conf_id; gboolean has_ip4; NMIP4Config *ip4_config; guint32 ip4_internal_gw; @@ -419,22 +417,28 @@ vpn_cleanup (NMVpnConnection *self, NMDevice *parent_dev) } static void -dispatcher_pre_down_done (guint call_id, gpointer user_data) +dispatcher_pre_down_done (NMDispatcherCallId *call_id, gpointer user_data) { NMVpnConnection *self = NM_VPN_CONNECTION (user_data); NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); - priv->dispatcher_id = 0; + nm_assert (call_id); + nm_assert (priv->dispatcher_id == call_id); + + priv->dispatcher_id = NULL; _set_vpn_state (self, STATE_DISCONNECTED, NM_ACTIVE_CONNECTION_STATE_REASON_USER_DISCONNECTED, FALSE); } static void -dispatcher_pre_up_done (guint call_id, gpointer user_data) +dispatcher_pre_up_done (NMDispatcherCallId *call_id, gpointer user_data) { NMVpnConnection *self = NM_VPN_CONNECTION (user_data); NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); - priv->dispatcher_id = 0; + nm_assert (call_id); + nm_assert (priv->dispatcher_id == call_id); + + priv->dispatcher_id = NULL; _set_vpn_state (self, STATE_ACTIVATED, NM_ACTIVE_CONNECTION_STATE_REASON_NONE, FALSE); } @@ -443,10 +447,8 @@ dispatcher_cleanup (NMVpnConnection *self) { NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); - if (priv->dispatcher_id) { - nm_dispatcher_call_cancel (priv->dispatcher_id); - priv->dispatcher_id = 0; - } + if (priv->dispatcher_id) + nm_dispatcher_call_cancel (g_steal_pointer (&priv->dispatcher_id)); } static void @@ -552,18 +554,12 @@ _set_vpn_state (NMVpnConnection *self, NULL); if (priv->proxy_config) { - nm_pacrunner_manager_remove_clear (priv->pacrunner_manager, - &priv->pacrunner_call_id); - if (!priv->pacrunner_manager) { - /* the pending call doesn't keep NMPacrunnerManager alive. - * Take a reference to it. */ - priv->pacrunner_manager = g_object_ref (nm_pacrunner_manager_get ()); - } - priv->pacrunner_call_id = nm_pacrunner_manager_send (priv->pacrunner_manager, - priv->ip_iface, - priv->proxy_config, - priv->ip4_config, - priv->ip6_config); + nm_pacrunner_manager_remove_clear (&priv->pacrunner_conf_id); + priv->pacrunner_conf_id = nm_pacrunner_manager_add (nm_pacrunner_manager_get (), + priv->proxy_config, + priv->ip_iface, + priv->ip4_config, + priv->ip6_config); } break; case STATE_DEACTIVATING: @@ -594,8 +590,7 @@ _set_vpn_state (NMVpnConnection *self, } } - nm_pacrunner_manager_remove_clear (priv->pacrunner_manager, - &priv->pacrunner_call_id); + nm_pacrunner_manager_remove_clear (&priv->pacrunner_conf_id); break; case STATE_FAILED: case STATE_DISCONNECTED: @@ -2801,9 +2796,7 @@ dispose (GObject *object) fw_call_cleanup (self); - nm_pacrunner_manager_remove_clear (priv->pacrunner_manager, - &priv->pacrunner_call_id); - g_clear_object (&priv->pacrunner_manager); + nm_pacrunner_manager_remove_clear (&priv->pacrunner_conf_id); G_OBJECT_CLASS (nm_vpn_connection_parent_class)->dispose (object); } diff --git a/src/vpn/nm-vpn-connection.h b/src/vpn/nm-vpn-connection.h index e70590b2..1527b689 100644 --- a/src/vpn/nm-vpn-connection.h +++ b/src/vpn/nm-vpn-connection.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/vpn/nm-vpn-manager.c b/src/vpn/nm-vpn-manager.c index 0b27b9de..36043c93 100644 --- a/src/vpn/nm-vpn-manager.c +++ b/src/vpn/nm-vpn-manager.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/vpn/nm-vpn-manager.h b/src/vpn/nm-vpn-manager.h index 8b1bec1e..bcbd19f1 100644 --- a/src/vpn/nm-vpn-manager.h +++ b/src/vpn/nm-vpn-manager.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify |