diff options
| author | Michael Biebl <biebl@debian.org> | 2015-07-14 19:38:58 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2015-07-14 19:38:58 +0200 |
| commit | 50a58f0fabd8a34c1b6108a107e08abe3c1ccd24 (patch) | |
| tree | 6790165f39daee79e2b6c6617483320613493367 /src | |
| parent | f408e27bccfacf347605a8d98649975a68f38a17 (diff) | |
Imported Upstream version 1.0.4 upstream/1.0.4
Diffstat (limited to 'src')
198 files changed, 25309 insertions, 13261 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index a24ac6c4..7339058c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -5,7 +5,6 @@ include $(GLIB_MAKEFILE) SUBDIRS = \ . \ devices/adsl \ - devices/wifi \ dhcp-manager \ ppp-manager \ settings/plugins @@ -16,6 +15,10 @@ SUBDIRS += \ devices/bluetooth endif +if WITH_WIFI +SUBDIRS += devices/wifi +endif + if WITH_WIMAX SUBDIRS += devices/wimax endif @@ -217,6 +220,10 @@ nm_sources = \ platform/nm-linux-platform.h \ platform/nm-platform.c \ platform/nm-platform.h \ + platform/nm-platform-utils.c \ + platform/nm-platform-utils.h \ + platform/nmp-object.c \ + platform/nmp-object.h \ platform/wifi/wifi-utils-nl80211.c \ platform/wifi/wifi-utils-nl80211.h \ platform/wifi/wifi-utils-private.h \ @@ -269,8 +276,6 @@ nm_sources = \ supplicant-manager/nm-supplicant-settings-verify.c \ supplicant-manager/nm-supplicant-settings-verify.h \ supplicant-manager/nm-supplicant-types.h \ - supplicant-manager/nm-call-store.c \ - supplicant-manager/nm-call-store.h \ \ vpn-manager/nm-vpn-connection.c \ vpn-manager/nm-vpn-connection.h \ @@ -285,6 +290,8 @@ nm_sources = \ nm-active-connection.h \ nm-config.c \ nm-config.h \ + nm-config-data.c \ + nm-config-data.h \ nm-connection-provider.c \ nm-connection-provider.h \ nm-connectivity.c \ @@ -293,6 +300,8 @@ nm_sources = \ nm-dbus-manager.h \ nm-dcb.c \ nm-dcb.h \ + nm-route-manager.c \ + nm-route-manager.h \ nm-default-route-manager.c \ nm-default-route-manager.h \ nm-dhcp4-config.c \ @@ -319,10 +328,10 @@ nm_sources = \ nm-auth-utils.h \ nm-manager.c \ nm-manager.h \ + nm-multi-index.c \ + nm-multi-index.h \ nm-policy.c \ nm-policy.h \ - nm-posix-signals.c \ - nm-posix-signals.h \ nm-properties-changed-signal.c \ nm-properties-changed-signal.h \ nm-rfkill-manager.c \ @@ -474,6 +483,10 @@ libnm_iface_helper_la_SOURCES = \ platform/nm-linux-platform.h \ platform/nm-platform.c \ platform/nm-platform.h \ + platform/nm-platform-utils.c \ + platform/nm-platform-utils.h \ + platform/nmp-object.c \ + platform/nmp-object.h \ platform/wifi/wifi-utils-nl80211.c \ platform/wifi/wifi-utils-nl80211.h \ platform/wifi/wifi-utils-private.h \ @@ -487,6 +500,9 @@ libnm_iface_helper_la_SOURCES = \ rdisc/nm-rdisc.c \ rdisc/nm-rdisc.h \ \ + nm-route-manager.c \ + nm-route-manager.h \ + \ nm-ip4-config.c \ nm-ip4-config.h \ nm-ip6-config.c \ @@ -496,8 +512,8 @@ libnm_iface_helper_la_SOURCES = \ nm-enum-types.h \ nm-logging.c \ nm-logging.h \ - nm-posix-signals.c \ - nm-posix-signals.h \ + nm-multi-index.c \ + nm-multi-index.h \ NetworkManagerUtils.c \ NetworkManagerUtils.h diff --git a/src/Makefile.in b/src/Makefile.in index da305209..2b999969 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -94,9 +94,10 @@ host_triplet = @host@ @WITH_MODEM_MANAGER_1_TRUE@ devices/wwan \ @WITH_MODEM_MANAGER_1_TRUE@ devices/bluetooth -@WITH_WIMAX_TRUE@am__append_2 = devices/wimax -@WITH_TEAMDCTL_TRUE@am__append_3 = devices/team -@ENABLE_TESTS_TRUE@am__append_4 = \ +@WITH_WIFI_TRUE@am__append_2 = devices/wifi +@WITH_WIMAX_TRUE@am__append_3 = devices/wimax +@WITH_TEAMDCTL_TRUE@am__append_4 = devices/team +@ENABLE_TESTS_TRUE@am__append_5 = \ @ENABLE_TESTS_TRUE@ dhcp-manager/tests \ @ENABLE_TESTS_TRUE@ dnsmasq-manager/tests \ @ENABLE_TESTS_TRUE@ platform \ @@ -105,21 +106,21 @@ host_triplet = @host@ @ENABLE_TESTS_TRUE@ tests sbin_PROGRAMS = NetworkManager$(EXEEXT) -@SESSION_TRACKING_SYSTEMD_TRUE@am__append_5 = nm-session-monitor-systemd.c -@SESSION_TRACKING_CK_TRUE@@SESSION_TRACKING_SYSTEMD_FALSE@am__append_6 = nm-session-monitor-ck.c -@SESSION_TRACKING_CK_FALSE@@SESSION_TRACKING_SYSTEMD_FALSE@am__append_7 = nm-session-monitor-null.c -@SUSPEND_RESUME_SYSTEMD_TRUE@am__append_8 = nm-sleep-monitor-systemd.c +@SESSION_TRACKING_SYSTEMD_TRUE@am__append_6 = nm-session-monitor-systemd.c +@SESSION_TRACKING_CK_TRUE@@SESSION_TRACKING_SYSTEMD_FALSE@am__append_7 = nm-session-monitor-ck.c +@SESSION_TRACKING_CK_FALSE@@SESSION_TRACKING_SYSTEMD_FALSE@am__append_8 = nm-session-monitor-null.c +@SUSPEND_RESUME_SYSTEMD_TRUE@am__append_9 = nm-sleep-monitor-systemd.c # UPower suspend/resume used whenever systemd is not enabled -@SUSPEND_RESUME_SYSTEMD_FALSE@am__append_9 = nm-sleep-monitor-upower.c -@WITH_WEXT_TRUE@am__append_10 = \ +@SUSPEND_RESUME_SYSTEMD_FALSE@am__append_10 = nm-sleep-monitor-upower.c +@WITH_WEXT_TRUE@am__append_11 = \ @WITH_WEXT_TRUE@ platform/wifi/wifi-utils-wext.c \ @WITH_WEXT_TRUE@ platform/wifi/wifi-utils-wext.h -@WITH_WIMAX_TRUE@am__append_11 = devices/wimax/nm-device-wimax.h -@WITH_WIMAX_TRUE@am__append_12 = -I$(top_srcdir)/src/devices/wimax -@SESSION_TRACKING_CK_TRUE@am__append_13 = -DCKDB_PATH=\"${CKDB_PATH}\" -@WITH_LIBSOUP_TRUE@am__append_14 = $(LIBSOUP_LIBS) -@WITH_WEXT_TRUE@am__append_15 = \ +@WITH_WIMAX_TRUE@am__append_12 = devices/wimax/nm-device-wimax.h +@WITH_WIMAX_TRUE@am__append_13 = -I$(top_srcdir)/src/devices/wimax +@SESSION_TRACKING_CK_TRUE@am__append_14 = -DCKDB_PATH=\"${CKDB_PATH}\" +@WITH_LIBSOUP_TRUE@am__append_15 = $(LIBSOUP_LIBS) +@WITH_WEXT_TRUE@am__append_16 = \ @WITH_WEXT_TRUE@ platform/wifi/wifi-utils-wext.c \ @WITH_WEXT_TRUE@ platform/wifi/wifi-utils-wext.h @@ -185,7 +186,9 @@ am__libNetworkManager_la_SOURCES_DIST = devices/nm-device-bond.h \ dnsmasq-manager/nm-dnsmasq-utils.h platform/nm-fake-platform.c \ platform/nm-fake-platform.h platform/nm-linux-platform.c \ platform/nm-linux-platform.h platform/nm-platform.c \ - platform/nm-platform.h platform/wifi/wifi-utils-nl80211.c \ + platform/nm-platform.h platform/nm-platform-utils.c \ + platform/nm-platform-utils.h platform/nmp-object.c \ + platform/nmp-object.h platform/wifi/wifi-utils-nl80211.c \ platform/wifi/wifi-utils-nl80211.h \ platform/wifi/wifi-utils-private.h platform/wifi/wifi-utils.c \ platform/wifi/wifi-utils.h rdisc/nm-fake-rdisc.c \ @@ -220,30 +223,30 @@ am__libNetworkManager_la_SOURCES_DIST = devices/nm-device-bond.h \ supplicant-manager/nm-supplicant-settings-verify.c \ supplicant-manager/nm-supplicant-settings-verify.h \ supplicant-manager/nm-supplicant-types.h \ - supplicant-manager/nm-call-store.c \ - supplicant-manager/nm-call-store.h \ vpn-manager/nm-vpn-connection.c \ vpn-manager/nm-vpn-connection.h vpn-manager/nm-vpn-manager.c \ vpn-manager/nm-vpn-manager.h vpn-manager/nm-vpn-service.c \ vpn-manager/nm-vpn-service.h nm-activation-request.c \ nm-activation-request.h nm-active-connection.c \ nm-active-connection.h nm-config.c nm-config.h \ - nm-connection-provider.c nm-connection-provider.h \ - nm-connectivity.c nm-connectivity.h nm-dbus-manager.c \ - nm-dbus-manager.h nm-dcb.c nm-dcb.h nm-default-route-manager.c \ - nm-default-route-manager.h nm-dhcp4-config.c nm-dhcp4-config.h \ - nm-dhcp6-config.c nm-dhcp6-config.h nm-dispatcher.c \ - nm-dispatcher.h nm-enum-types.c nm-enum-types.h \ - nm-firewall-manager.c nm-firewall-manager.h nm-ip4-config.c \ - nm-ip4-config.h nm-ip6-config.c nm-ip6-config.h nm-logging.c \ - nm-logging.h nm-auth-manager.c nm-auth-manager.h \ - nm-auth-subject.c nm-auth-subject.h nm-auth-utils.c \ - nm-auth-utils.h nm-manager.c nm-manager.h nm-policy.c \ - nm-policy.h nm-posix-signals.c nm-posix-signals.h \ - nm-properties-changed-signal.c nm-properties-changed-signal.h \ - nm-rfkill-manager.c nm-rfkill-manager.h nm-session-monitor.h \ - nm-session-utils.c nm-session-utils.h nm-sleep-monitor.h \ - nm-types.h NetworkManagerUtils.c NetworkManagerUtils.h \ + nm-config-data.c nm-config-data.h nm-connection-provider.c \ + nm-connection-provider.h nm-connectivity.c nm-connectivity.h \ + nm-dbus-manager.c nm-dbus-manager.h nm-dcb.c nm-dcb.h \ + nm-route-manager.c nm-route-manager.h \ + nm-default-route-manager.c nm-default-route-manager.h \ + nm-dhcp4-config.c nm-dhcp4-config.h nm-dhcp6-config.c \ + nm-dhcp6-config.h nm-dispatcher.c nm-dispatcher.h \ + nm-enum-types.c nm-enum-types.h nm-firewall-manager.c \ + nm-firewall-manager.h nm-ip4-config.c nm-ip4-config.h \ + nm-ip6-config.c nm-ip6-config.h nm-logging.c nm-logging.h \ + nm-auth-manager.c nm-auth-manager.h nm-auth-subject.c \ + nm-auth-subject.h nm-auth-utils.c nm-auth-utils.h nm-manager.c \ + nm-manager.h nm-multi-index.c nm-multi-index.h nm-policy.c \ + nm-policy.h nm-properties-changed-signal.c \ + nm-properties-changed-signal.h nm-rfkill-manager.c \ + nm-rfkill-manager.h nm-session-monitor.h nm-session-utils.c \ + nm-session-utils.h nm-sleep-monitor.h nm-types.h \ + NetworkManagerUtils.c NetworkManagerUtils.h \ nm-session-monitor-systemd.c nm-session-monitor-ck.c \ nm-session-monitor-null.c nm-sleep-monitor-systemd.c \ nm-sleep-monitor-upower.c platform/wifi/wifi-utils-wext.c \ @@ -276,27 +279,27 @@ am__objects_8 = $(am__objects_1) $(am__objects_1) nm-device.lo \ nm-dns-unbound.lo nm-dns-manager.lo nm-dns-plugin.lo \ nm-dns-utils.lo nm-dnsmasq-manager.lo nm-dnsmasq-utils.lo \ nm-fake-platform.lo nm-linux-platform.lo nm-platform.lo \ - wifi-utils-nl80211.lo wifi-utils.lo nm-fake-rdisc.lo \ - nm-lndp-rdisc.lo nm-rdisc.lo nm-ppp-manager.lo \ - nm-agent-manager.lo nm-inotify-helper.lo nm-secret-agent.lo \ - nm-settings-connection.lo nm-settings.lo \ + nm-platform-utils.lo nmp-object.lo wifi-utils-nl80211.lo \ + wifi-utils.lo nm-fake-rdisc.lo nm-lndp-rdisc.lo nm-rdisc.lo \ + nm-ppp-manager.lo nm-agent-manager.lo nm-inotify-helper.lo \ + nm-secret-agent.lo nm-settings-connection.lo nm-settings.lo \ nm-system-config-interface.lo nm-keyfile-connection.lo \ plugin.lo reader.lo utils.lo writer.lo nm-supplicant-config.lo \ nm-supplicant-interface.lo nm-supplicant-manager.lo \ - nm-supplicant-settings-verify.lo nm-call-store.lo \ - nm-vpn-connection.lo nm-vpn-manager.lo nm-vpn-service.lo \ - nm-activation-request.lo nm-active-connection.lo nm-config.lo \ + nm-supplicant-settings-verify.lo nm-vpn-connection.lo \ + nm-vpn-manager.lo nm-vpn-service.lo nm-activation-request.lo \ + nm-active-connection.lo nm-config.lo nm-config-data.lo \ nm-connection-provider.lo nm-connectivity.lo \ - nm-dbus-manager.lo nm-dcb.lo nm-default-route-manager.lo \ - nm-dhcp4-config.lo nm-dhcp6-config.lo nm-dispatcher.lo \ - nm-enum-types.lo nm-firewall-manager.lo nm-ip4-config.lo \ - nm-ip6-config.lo nm-logging.lo nm-auth-manager.lo \ - nm-auth-subject.lo nm-auth-utils.lo nm-manager.lo nm-policy.lo \ - nm-posix-signals.lo nm-properties-changed-signal.lo \ - nm-rfkill-manager.lo nm-session-utils.lo \ - NetworkManagerUtils.lo $(am__objects_2) $(am__objects_3) \ - $(am__objects_4) $(am__objects_5) $(am__objects_6) \ - $(am__objects_7) + nm-dbus-manager.lo nm-dcb.lo nm-route-manager.lo \ + nm-default-route-manager.lo nm-dhcp4-config.lo \ + nm-dhcp6-config.lo nm-dispatcher.lo nm-enum-types.lo \ + nm-firewall-manager.lo nm-ip4-config.lo nm-ip6-config.lo \ + nm-logging.lo nm-auth-manager.lo nm-auth-subject.lo \ + nm-auth-utils.lo nm-manager.lo nm-multi-index.lo nm-policy.lo \ + nm-properties-changed-signal.lo nm-rfkill-manager.lo \ + nm-session-utils.lo NetworkManagerUtils.lo $(am__objects_2) \ + $(am__objects_3) $(am__objects_4) $(am__objects_5) \ + $(am__objects_6) $(am__objects_7) am_libNetworkManager_la_OBJECTS = $(am__objects_8) $(am__objects_1) libNetworkManager_la_OBJECTS = $(am_libNetworkManager_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) @@ -315,23 +318,26 @@ am__libnm_iface_helper_la_SOURCES_DIST = \ dhcp-manager/nm-dhcp-manager.c dhcp-manager/nm-dhcp-manager.h \ platform/nm-linux-platform.c platform/nm-linux-platform.h \ platform/nm-platform.c platform/nm-platform.h \ + platform/nm-platform-utils.c platform/nm-platform-utils.h \ + platform/nmp-object.c platform/nmp-object.h \ platform/wifi/wifi-utils-nl80211.c \ platform/wifi/wifi-utils-nl80211.h \ platform/wifi/wifi-utils-private.h platform/wifi/wifi-utils.c \ platform/wifi/wifi-utils.h rdisc/nm-fake-rdisc.c \ rdisc/nm-fake-rdisc.h rdisc/nm-lndp-rdisc.c \ rdisc/nm-lndp-rdisc.h rdisc/nm-rdisc.c rdisc/nm-rdisc.h \ - nm-ip4-config.c nm-ip4-config.h nm-ip6-config.c \ - nm-ip6-config.h nm-enum-types.c nm-enum-types.h nm-logging.c \ - nm-logging.h nm-posix-signals.c nm-posix-signals.h \ - NetworkManagerUtils.c NetworkManagerUtils.h \ - platform/wifi/wifi-utils-wext.c \ + nm-route-manager.c nm-route-manager.h nm-ip4-config.c \ + nm-ip4-config.h nm-ip6-config.c nm-ip6-config.h \ + nm-enum-types.c nm-enum-types.h nm-logging.c nm-logging.h \ + nm-multi-index.c nm-multi-index.h NetworkManagerUtils.c \ + NetworkManagerUtils.h platform/wifi/wifi-utils-wext.c \ platform/wifi/wifi-utils-wext.h am_libnm_iface_helper_la_OBJECTS = nm-dhcp-client.lo nm-dhcp-utils.lo \ nm-dhcp-manager.lo nm-linux-platform.lo nm-platform.lo \ - wifi-utils-nl80211.lo wifi-utils.lo nm-fake-rdisc.lo \ - nm-lndp-rdisc.lo nm-rdisc.lo nm-ip4-config.lo nm-ip6-config.lo \ - nm-enum-types.lo nm-logging.lo nm-posix-signals.lo \ + nm-platform-utils.lo nmp-object.lo wifi-utils-nl80211.lo \ + wifi-utils.lo nm-fake-rdisc.lo nm-lndp-rdisc.lo nm-rdisc.lo \ + nm-route-manager.lo nm-ip4-config.lo nm-ip6-config.lo \ + nm-enum-types.lo nm-logging.lo nm-multi-index.lo \ NetworkManagerUtils.lo $(am__objects_7) libnm_iface_helper_la_OBJECTS = $(am_libnm_iface_helper_la_OBJECTS) libsystemd_dhcp_la_DEPENDENCIES = $(am__DEPENDENCIES_1) @@ -493,10 +499,11 @@ am__define_uniq_tagged_files = \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags -DIST_SUBDIRS = . devices/adsl devices/wifi dhcp-manager ppp-manager \ - settings/plugins devices/wwan devices/bluetooth devices/wimax \ - devices/team dhcp-manager/tests dnsmasq-manager/tests platform \ - rdisc supplicant-manager/tests tests +DIST_SUBDIRS = . devices/adsl dhcp-manager ppp-manager \ + settings/plugins devices/wwan devices/bluetooth devices/wifi \ + devices/wimax devices/team dhcp-manager/tests \ + dnsmasq-manager/tests platform rdisc supplicant-manager/tests \ + tests am__DIST_COMMON = $(srcdir)/Makefile.in \ $(top_srcdir)/build-aux/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) @@ -529,6 +536,7 @@ ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -642,6 +650,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ +LOG_DRIVER = @LOG_DRIVER@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ @@ -785,9 +794,9 @@ with_dhcpcd = @with_dhcpcd@ with_netconfig = @with_netconfig@ with_resolvconf = @with_resolvconf@ with_valgrind = @with_valgrind@ -SUBDIRS = . devices/adsl devices/wifi dhcp-manager ppp-manager \ - settings/plugins $(am__append_1) $(am__append_2) \ - $(am__append_3) $(am__append_4) +SUBDIRS = . devices/adsl dhcp-manager ppp-manager settings/plugins \ + $(am__append_1) $(am__append_2) $(am__append_3) \ + $(am__append_4) $(am__append_5) # add each subdirectory that contains a libNM source file. $(sort) is being used # primarily for its side effect of removing duplicates. @@ -798,7 +807,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/libnm-core \ -DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE $(foreach \ d,$(sort $(dir \ $(libNetworkManager_la_SOURCES))),-I$(top_srcdir)/src/$d) \ - $(am__append_12) $(DBUS_CFLAGS) $(GLIB_CFLAGS) $(GUDEV_CFLAGS) \ + $(am__append_13) $(DBUS_CFLAGS) $(GLIB_CFLAGS) $(GUDEV_CFLAGS) \ $(LIBNL_CFLAGS) $(LIBNDP_CFLAGS) $(LIBSOUP_CFLAGS) \ $(SYSTEMD_LOGIN_CFLAGS) $(SYSTEMD_DHCP_CFLAGS) \ -DBINDIR=\"$(bindir)\" -DDATADIR=\"$(datadir)\" \ @@ -812,7 +821,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/libnm-core \ -DDHCLIENT_PATH=\"$(DHCLIENT_PATH)\" \ -DDHCPCD_PATH=\"$(DHCPCD_PATH)\" \ -DPPPD_PLUGIN_DIR=\"$(PPPD_PLUGIN_DIR)\" $(NULL) \ - $(am__append_13) + $(am__append_14) noinst_LTLIBRARIES = \ libNetworkManager.la \ libnm-iface-helper.la \ @@ -952,7 +961,9 @@ nm_sources = $(nm_device_headers) $(nm_dhcp_client_headers) \ dnsmasq-manager/nm-dnsmasq-utils.h platform/nm-fake-platform.c \ platform/nm-fake-platform.h platform/nm-linux-platform.c \ platform/nm-linux-platform.h platform/nm-platform.c \ - platform/nm-platform.h platform/wifi/wifi-utils-nl80211.c \ + platform/nm-platform.h platform/nm-platform-utils.c \ + platform/nm-platform-utils.h platform/nmp-object.c \ + platform/nmp-object.h platform/wifi/wifi-utils-nl80211.c \ platform/wifi/wifi-utils-nl80211.h \ platform/wifi/wifi-utils-private.h platform/wifi/wifi-utils.c \ platform/wifi/wifi-utils.h rdisc/nm-fake-rdisc.c \ @@ -987,36 +998,36 @@ nm_sources = $(nm_device_headers) $(nm_dhcp_client_headers) \ supplicant-manager/nm-supplicant-settings-verify.c \ supplicant-manager/nm-supplicant-settings-verify.h \ supplicant-manager/nm-supplicant-types.h \ - supplicant-manager/nm-call-store.c \ - supplicant-manager/nm-call-store.h \ vpn-manager/nm-vpn-connection.c \ vpn-manager/nm-vpn-connection.h vpn-manager/nm-vpn-manager.c \ vpn-manager/nm-vpn-manager.h vpn-manager/nm-vpn-service.c \ vpn-manager/nm-vpn-service.h nm-activation-request.c \ nm-activation-request.h nm-active-connection.c \ nm-active-connection.h nm-config.c nm-config.h \ - nm-connection-provider.c nm-connection-provider.h \ - nm-connectivity.c nm-connectivity.h nm-dbus-manager.c \ - nm-dbus-manager.h nm-dcb.c nm-dcb.h nm-default-route-manager.c \ - nm-default-route-manager.h nm-dhcp4-config.c nm-dhcp4-config.h \ - nm-dhcp6-config.c nm-dhcp6-config.h nm-dispatcher.c \ - nm-dispatcher.h nm-enum-types.c nm-enum-types.h \ - nm-firewall-manager.c nm-firewall-manager.h nm-ip4-config.c \ - nm-ip4-config.h nm-ip6-config.c nm-ip6-config.h nm-logging.c \ - nm-logging.h nm-auth-manager.c nm-auth-manager.h \ - nm-auth-subject.c nm-auth-subject.h nm-auth-utils.c \ - nm-auth-utils.h nm-manager.c nm-manager.h nm-policy.c \ - nm-policy.h nm-posix-signals.c nm-posix-signals.h \ - nm-properties-changed-signal.c nm-properties-changed-signal.h \ - nm-rfkill-manager.c nm-rfkill-manager.h nm-session-monitor.h \ - nm-session-utils.c nm-session-utils.h nm-sleep-monitor.h \ - nm-types.h NetworkManagerUtils.c NetworkManagerUtils.h \ - $(am__append_5) $(am__append_6) $(am__append_7) \ - $(am__append_8) $(am__append_9) $(am__append_10) + nm-config-data.c nm-config-data.h nm-connection-provider.c \ + nm-connection-provider.h nm-connectivity.c nm-connectivity.h \ + nm-dbus-manager.c nm-dbus-manager.h nm-dcb.c nm-dcb.h \ + nm-route-manager.c nm-route-manager.h \ + nm-default-route-manager.c nm-default-route-manager.h \ + nm-dhcp4-config.c nm-dhcp4-config.h nm-dhcp6-config.c \ + nm-dhcp6-config.h nm-dispatcher.c nm-dispatcher.h \ + nm-enum-types.c nm-enum-types.h nm-firewall-manager.c \ + nm-firewall-manager.h nm-ip4-config.c nm-ip4-config.h \ + nm-ip6-config.c nm-ip6-config.h nm-logging.c nm-logging.h \ + nm-auth-manager.c nm-auth-manager.h nm-auth-subject.c \ + nm-auth-subject.h nm-auth-utils.c nm-auth-utils.h nm-manager.c \ + nm-manager.h nm-multi-index.c nm-multi-index.h nm-policy.c \ + nm-policy.h nm-properties-changed-signal.c \ + nm-properties-changed-signal.h nm-rfkill-manager.c \ + nm-rfkill-manager.h nm-session-monitor.h nm-session-utils.c \ + nm-session-utils.h nm-sleep-monitor.h nm-types.h \ + NetworkManagerUtils.c NetworkManagerUtils.h $(am__append_6) \ + $(am__append_7) $(am__append_8) $(am__append_9) \ + $(am__append_10) $(am__append_11) GLIB_GENERATED = nm-enum-types.h nm-enum-types.c GLIB_MKENUMS_H_FLAGS = --identifier-prefix NM --fhead '\#include <nm-core-enum-types.h>\n' GLIB_MKENUMS_C_FLAGS = --identifier-prefix NM -nm_enum_types_sources = $(nm_sources) $(am__append_11) +nm_enum_types_sources = $(nm_sources) $(am__append_12) BUILT_SOURCES = $(GLIB_GENERATED) $(glue_sources) glue_sources = \ nm-access-point-glue.h \ @@ -1052,7 +1063,7 @@ libNetworkManager_la_LIBADD = \ $(top_builddir)/libnm-core/libnm-core.la libsystemd-dhcp.la \ $(DBUS_LIBS) $(GLIB_LIBS) $(GUDEV_LIBS) $(LIBNL_LIBS) \ $(SYSTEMD_LOGIN_LIBS) $(LIBNDP_LIBS) $(LIBDL) $(LIBM) \ - $(am__append_14) + $(am__append_15) NetworkManager_LDFLAGS = -rdynamic ###################### @@ -1061,16 +1072,19 @@ libnm_iface_helper_la_SOURCES = dhcp-manager/nm-dhcp-client.c \ dhcp-manager/nm-dhcp-utils.h dhcp-manager/nm-dhcp-manager.c \ dhcp-manager/nm-dhcp-manager.h platform/nm-linux-platform.c \ platform/nm-linux-platform.h platform/nm-platform.c \ - platform/nm-platform.h platform/wifi/wifi-utils-nl80211.c \ + platform/nm-platform.h platform/nm-platform-utils.c \ + platform/nm-platform-utils.h platform/nmp-object.c \ + platform/nmp-object.h platform/wifi/wifi-utils-nl80211.c \ platform/wifi/wifi-utils-nl80211.h \ platform/wifi/wifi-utils-private.h platform/wifi/wifi-utils.c \ platform/wifi/wifi-utils.h rdisc/nm-fake-rdisc.c \ rdisc/nm-fake-rdisc.h rdisc/nm-lndp-rdisc.c \ rdisc/nm-lndp-rdisc.h rdisc/nm-rdisc.c rdisc/nm-rdisc.h \ - nm-ip4-config.c nm-ip4-config.h nm-ip6-config.c \ - nm-ip6-config.h nm-enum-types.c nm-enum-types.h nm-logging.c \ - nm-logging.h nm-posix-signals.c nm-posix-signals.h \ - NetworkManagerUtils.c NetworkManagerUtils.h $(am__append_15) + nm-route-manager.c nm-route-manager.h nm-ip4-config.c \ + nm-ip4-config.h nm-ip6-config.c nm-ip6-config.h \ + nm-enum-types.c nm-enum-types.h nm-logging.c nm-logging.h \ + nm-multi-index.c nm-multi-index.h NetworkManagerUtils.c \ + NetworkManagerUtils.h $(am__append_16) libnm_iface_helper_la_LIBADD = \ $(top_builddir)/libnm-core/libnm-core.la \ libsystemd-dhcp.la \ @@ -1311,7 +1325,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-auth-manager.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-auth-subject.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-auth-utils.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-call-store.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-config-data.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-config.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-connection-provider.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-connectivity.Plo@am__quote@ @@ -1363,13 +1377,15 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-lndp-rdisc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-logging.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-manager.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-multi-index.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-platform-utils.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-platform.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-policy.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-posix-signals.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-ppp-manager.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-properties-changed-signal.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-rdisc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-rfkill-manager.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-route-manager.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-secret-agent.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-session-monitor-ck.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-session-monitor-null.Plo@am__quote@ @@ -1387,6 +1403,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-vpn-connection.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-vpn-manager.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-vpn-service.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmp-object.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plugin.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/reader.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utils.Plo@am__quote@ @@ -1542,6 +1559,20 @@ nm-platform.lo: platform/nm-platform.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-platform.lo `test -f 'platform/nm-platform.c' || echo '$(srcdir)/'`platform/nm-platform.c +nm-platform-utils.lo: platform/nm-platform-utils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-platform-utils.lo -MD -MP -MF $(DEPDIR)/nm-platform-utils.Tpo -c -o nm-platform-utils.lo `test -f 'platform/nm-platform-utils.c' || echo '$(srcdir)/'`platform/nm-platform-utils.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nm-platform-utils.Tpo $(DEPDIR)/nm-platform-utils.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='platform/nm-platform-utils.c' object='nm-platform-utils.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-platform-utils.lo `test -f 'platform/nm-platform-utils.c' || echo '$(srcdir)/'`platform/nm-platform-utils.c + +nmp-object.lo: platform/nmp-object.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nmp-object.lo -MD -MP -MF $(DEPDIR)/nmp-object.Tpo -c -o nmp-object.lo `test -f 'platform/nmp-object.c' || echo '$(srcdir)/'`platform/nmp-object.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nmp-object.Tpo $(DEPDIR)/nmp-object.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='platform/nmp-object.c' object='nmp-object.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nmp-object.lo `test -f 'platform/nmp-object.c' || echo '$(srcdir)/'`platform/nmp-object.c + wifi-utils-nl80211.lo: platform/wifi/wifi-utils-nl80211.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT wifi-utils-nl80211.lo -MD -MP -MF $(DEPDIR)/wifi-utils-nl80211.Tpo -c -o wifi-utils-nl80211.lo `test -f 'platform/wifi/wifi-utils-nl80211.c' || echo '$(srcdir)/'`platform/wifi/wifi-utils-nl80211.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/wifi-utils-nl80211.Tpo $(DEPDIR)/wifi-utils-nl80211.Plo @@ -1689,13 +1720,6 @@ nm-supplicant-settings-verify.lo: supplicant-manager/nm-supplicant-settings-veri @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-supplicant-settings-verify.lo `test -f 'supplicant-manager/nm-supplicant-settings-verify.c' || echo '$(srcdir)/'`supplicant-manager/nm-supplicant-settings-verify.c -nm-call-store.lo: supplicant-manager/nm-call-store.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-call-store.lo -MD -MP -MF $(DEPDIR)/nm-call-store.Tpo -c -o nm-call-store.lo `test -f 'supplicant-manager/nm-call-store.c' || echo '$(srcdir)/'`supplicant-manager/nm-call-store.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nm-call-store.Tpo $(DEPDIR)/nm-call-store.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='supplicant-manager/nm-call-store.c' object='nm-call-store.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-call-store.lo `test -f 'supplicant-manager/nm-call-store.c' || echo '$(srcdir)/'`supplicant-manager/nm-call-store.c - nm-vpn-connection.lo: vpn-manager/nm-vpn-connection.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-vpn-connection.lo -MD -MP -MF $(DEPDIR)/nm-vpn-connection.Tpo -c -o nm-vpn-connection.lo `test -f 'vpn-manager/nm-vpn-connection.c' || echo '$(srcdir)/'`vpn-manager/nm-vpn-connection.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nm-vpn-connection.Tpo $(DEPDIR)/nm-vpn-connection.Plo diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c index 24cb949c..4f842494 100644 --- a/src/NetworkManagerUtils.c +++ b/src/NetworkManagerUtils.c @@ -47,8 +47,8 @@ #include "nm-setting-wireless.h" #include "nm-setting-wireless-security.h" #include "nm-auth-utils.h" -#include "nm-posix-signals.h" #include "nm-dbus-glib-types.h" +#include "gsystem-local-alloc.h" /* * Some toolchains (E.G. uClibc 0.9.33 and earlier) don't export @@ -59,6 +59,60 @@ #define CLOCK_BOOTTIME 7 #endif +G_STATIC_ASSERT (sizeof (NMUtilsTestFlags) <= sizeof (int)); +int _nm_utils_testing = 0; + +gboolean +nm_utils_get_testing_initialized () +{ + NMUtilsTestFlags flags; + + flags = (NMUtilsTestFlags) _nm_utils_testing; + if (flags == NM_UTILS_TEST_NONE) + flags = (NMUtilsTestFlags) g_atomic_int_get (&_nm_utils_testing); + return flags != NM_UTILS_TEST_NONE; +} + +NMUtilsTestFlags +nm_utils_get_testing () +{ + NMUtilsTestFlags flags; + + flags = (NMUtilsTestFlags) _nm_utils_testing; + if (flags != NM_UTILS_TEST_NONE) { + /* Flags already initialized. Return them. */ + return flags & NM_UTILS_TEST_ALL; + } + + /* Accessing nm_utils_get_testing() causes us to set the flags to initialized. + * Detecting running tests also based on g_test_initialized(). */ + flags = _NM_UTILS_TEST_INITIALIZED; + if (g_test_initialized ()) + flags |= _NM_UTILS_TEST_GENERAL; + + if (g_atomic_int_compare_and_exchange (&_nm_utils_testing, 0, (int) flags)) { + /* Done. We set it. */ + return flags & NM_UTILS_TEST_ALL; + } + /* It changed in the meantime (??). Re-read the value. */ + return ((NMUtilsTestFlags) _nm_utils_testing) & NM_UTILS_TEST_ALL; +} + +void +_nm_utils_set_testing (NMUtilsTestFlags flags) +{ + g_assert (!NM_FLAGS_ANY (flags, ~NM_UTILS_TEST_ALL)); + + /* mask out everything except ALL, and always set GENERAL. */ + flags = (flags & NM_UTILS_TEST_ALL) | (_NM_UTILS_TEST_GENERAL | _NM_UTILS_TEST_INITIALIZED); + + if (!g_atomic_int_compare_and_exchange (&_nm_utils_testing, 0, (int) flags)) { + /* We only allow setting _nm_utils_set_testing() once, before fetching the + * value with nm_utils_get_testing(). */ + g_return_if_reached (); + } +} + /* * nm_ethernet_address_is_valid: * @addr: pointer to a binary or ASCII Ethernet address @@ -150,6 +204,80 @@ nm_utils_ip6_address_clear_host_address (struct in6_addr *dst, const struct in6_ return dst; } +void +nm_utils_array_remove_at_indexes (GArray *array, const guint *indexes_to_delete, gsize len) +{ + gsize elt_size; + guint index_to_delete; + guint i_src; + guint mm_src, mm_dst, mm_len; + gsize i_itd; + guint res_length; + + g_return_if_fail (array); + if (!len) + return; + g_return_if_fail (indexes_to_delete); + + elt_size = g_array_get_element_size (array); + + i_itd = 0; + index_to_delete = indexes_to_delete[0]; + if (index_to_delete >= array->len) + g_return_if_reached (); + + res_length = array->len - 1; + + mm_dst = index_to_delete; + mm_src = index_to_delete; + mm_len = 0; + + for (i_src = index_to_delete; i_src < array->len; i_src++) { + if (i_src < index_to_delete) + mm_len++; + else { + /* we require indexes_to_delete to contain non-repeated, ascending + * indexes. Otherwise we would need to presort the indexes. */ + while (TRUE) { + guint dd; + + if (i_itd + 1 >= len) { + index_to_delete = G_MAXUINT; + break; + } + + dd = indexes_to_delete[++i_itd]; + if (dd > index_to_delete) { + if (dd >= array->len) + g_warn_if_reached (); + else { + g_assert (res_length > 0); + res_length--; + } + index_to_delete = dd; + break; + } + g_warn_if_reached (); + } + + if (mm_len) { + memmove (&array->data[mm_dst * elt_size], + &array->data[mm_src * elt_size], + mm_len * elt_size); + mm_dst += mm_len; + mm_src += mm_len + 1; + mm_len = 0; + } else + mm_src++; + } + } + if (mm_len) { + memmove (&array->data[mm_dst * elt_size], + &array->data[mm_src * elt_size], + mm_len * elt_size); + } + g_array_set_size (array, res_length); +} int nm_spawn_process (const char *args, GError **error) @@ -163,7 +291,7 @@ nm_spawn_process (const char *args, GError **error) g_return_val_if_fail (!error || !*error, -1); if (g_shell_parse_argv (args, &num_args, &argv, &local)) { - g_spawn_sync ("/", argv, NULL, 0, nm_unblock_posix_signals, NULL, NULL, NULL, &status, &local); + g_spawn_sync ("/", argv, NULL, 0, NULL, NULL, NULL, NULL, &status, &local); g_strfreev (argv); } @@ -175,9 +303,62 @@ nm_spawn_process (const char *args, GError **error) return status; } +static const char * +_trunk_first_line (char *str) +{ + char *s; + + s = strchr (str, '\n'); + if (s) + s[0] = '\0'; + return str; +} + +int +nm_utils_modprobe (GError **error, gboolean suppress_error_logging, const char *arg1, ...) +{ + gs_unref_ptrarray GPtrArray *argv = NULL; + int exit_status; + gs_free char *_log_str = NULL; +#define ARGV_TO_STR(argv) (_log_str ? _log_str : (_log_str = g_strjoinv (" ", (char **) argv->pdata))) + GError *local = NULL; + va_list ap; + NMLogLevel llevel = suppress_error_logging ? LOGL_DEBUG : LOGL_ERR; + gs_free char *std_out = NULL, *std_err = NULL; + + g_return_val_if_fail (!error || !*error, -1); + g_return_val_if_fail (arg1, -1); + + /* construct the argument list */ + argv = g_ptr_array_sized_new (4); + g_ptr_array_add (argv, "/sbin/modprobe"); + g_ptr_array_add (argv, (char *) arg1); + + va_start (ap, arg1); + while ((arg1 = va_arg (ap, const char *))) + g_ptr_array_add (argv, (char *) arg1); + va_end (ap); + + g_ptr_array_add (argv, NULL); + + nm_log_dbg (LOGD_CORE, "modprobe: '%s'", ARGV_TO_STR (argv)); + if (!g_spawn_sync (NULL, (char **) argv->pdata, NULL, 0, NULL, NULL, &std_out, &std_err, &exit_status, &local)) { + nm_log (llevel, LOGD_CORE, "modprobe: '%s' failed: %s", ARGV_TO_STR (argv), local->message); + g_propagate_error (error, local); + return -1; + } else if (exit_status != 0) + nm_log (llevel, LOGD_CORE, "modprobe: '%s' exited with error %d%s%s%s%s%s%s", ARGV_TO_STR (argv), exit_status, + std_out&&*std_out ? " (" : "", std_out&&*std_out ? _trunk_first_line (std_out) : "", std_out&&*std_out ? ")" : "", + std_err&&*std_err ? " (" : "", std_err&&*std_err ? _trunk_first_line (std_err) : "", std_err&&*std_err ? ")" : ""); + + return exit_status; +} + /** * nm_utils_get_start_time_for_pid: * @pid: the process identifier + * @out_state: return the state character, like R, S, Z. See `man 5 proc`. + * @out_ppid: parent process id * * Originally copied from polkit source (src/polkit/polkitunixprocess.c) * and adjusted. @@ -188,16 +369,18 @@ nm_spawn_process (const char *args, GError **error) * The returned start time counts since boot, in the unit HZ (with HZ usually being (1/100) seconds) **/ guint64 -nm_utils_get_start_time_for_pid (pid_t pid) +nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_ppid) { guint64 start_time; - gchar *filename; - gchar *contents; + gs_free gchar *filename = NULL; + gs_free gchar *contents = NULL; size_t length; - gchar **tokens; + gs_strfreev gchar **tokens = NULL; guint num_tokens; gchar *p; gchar *endp; + char state = '\0'; + gint64 ppid = 0; start_time = 0; contents = NULL; @@ -220,6 +403,8 @@ nm_utils_get_start_time_for_pid (pid_t pid) if (p - contents >= (int) length) goto out; + state = p[0]; + tokens = g_strsplit (p, " ", 0); num_tokens = g_strv_length (tokens); @@ -227,15 +412,19 @@ nm_utils_get_start_time_for_pid (pid_t pid) if (num_tokens < 20) goto out; - start_time = strtoull (tokens[19], &endp, 10); - if (endp == tokens[19]) - goto out; + if (out_ppid) + ppid = _nm_utils_ascii_str_to_int64 (tokens[1], 10, 1, G_MAXINT, 0); - g_strfreev (tokens); + errno = 0; + start_time = strtoull (tokens[19], &endp, 10); + if (*endp != '\0' || errno != 0) + start_time = 0; - out: - g_free (filename); - g_free (contents); +out: + if (out_state) + *out_state = state; + if (out_ppid) + *out_ppid = ppid; return start_time; } @@ -514,13 +703,15 @@ _sleep_duration_convert_ms_to_us (guint32 sleep_duration_msec) * sent unless the child already exited. If the child does not exit within @wait_before_kill_msec milliseconds, * the function will send %SIGKILL and waits for the child indefinitly. If @wait_before_kill_msec is zero, no * %SIGKILL signal will be sent. + * + * In case of error, errno is preserved to contain the last reason of failure. **/ gboolean nm_utils_kill_child_sync (pid_t pid, int sig, guint64 log_domain, const char *log_name, int *child_status, guint32 wait_before_kill_msec, guint32 sleep_duration_msec) { - int status = 0, errsv; + int status = 0, errsv = 0; pid_t ret; gboolean success = FALSE; gboolean was_waiting = FALSE, send_kill = FALSE; @@ -666,6 +857,7 @@ nm_utils_kill_child_sync (pid_t pid, int sig, guint64 log_domain, const char *lo out: if (child_status) *child_status = success ? status : -1; + errno = success ? 0 : errsv; return success; } @@ -681,30 +873,36 @@ out: * @log_name: name of the process to kill for logging. * @wait_before_kill_msec: Waittime in milliseconds before sending %SIGKILL signal. Set this value * to zero, not to send %SIGKILL. If @sig is already %SIGKILL, this parameter has no effect. + * If @max_wait_msec is set but less then @wait_before_kill_msec, the final %SIGKILL will also + * not be send. * @sleep_duration_msec: the synchronous function sleeps repeatedly waiting for the child to terminate. * Set to zero, to use the default (meaning 20 wakeups per seconds). + * @max_wait_msec: if 0, waits indefinitely until the process is gone (or a zombie). Otherwise, this + * is the maxium wait time until returning. If @max_wait_msec is non-zero but smaller then @wait_before_kill_msec, + * we will not send a final %SIGKILL. * * Kill a non-child process synchronously and wait. This function will not return before the - * process with PID @pid is gone. + * process with PID @pid is gone, the process is a zombie, or @max_wait_msec expires. **/ void nm_utils_kill_process_sync (pid_t pid, guint64 start_time, int sig, guint64 log_domain, const char *log_name, guint32 wait_before_kill_msec, - guint32 sleep_duration_msec) + guint32 sleep_duration_msec, guint32 max_wait_msec) { int errsv; guint64 start_time0; - gint64 wait_until, now, wait_start_us; + gint64 wait_until_sigkill, now, wait_start_us, max_wait_until; gulong sleep_time, sleep_duration_usec; int loop_count = 0; gboolean was_waiting = FALSE; char buf_wait[KC_WAITED_TO_STRING]; + char p_state; g_return_if_fail (pid > 0); g_return_if_fail (log_name != NULL); g_return_if_fail (wait_before_kill_msec > 0); - start_time0 = nm_utils_get_start_time_for_pid (pid); + start_time0 = nm_utils_get_start_time_for_pid (pid, &p_state, NULL); if (start_time0 == 0) { nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": cannot kill process %ld because it seems already gone", LOG_NAME_ARGS, (long int) pid); @@ -716,6 +914,17 @@ nm_utils_kill_process_sync (pid_t pid, guint64 start_time, int sig, guint64 log_ return; } + switch (p_state) { + case 'Z': + case 'x': + case 'X': + nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": cannot kill process %ld because it is already a zombie (%c)", + LOG_NAME_ARGS, (long int) pid, p_state); + return; + default: + break; + } + if (kill (pid, sig) != 0) { errsv = errno; /* ESRCH means, process does not exist or is already a zombie. */ @@ -734,10 +943,19 @@ nm_utils_kill_process_sync (pid_t pid, guint64 start_time, int sig, guint64 log_ wait_start_us = nm_utils_get_monotonic_timestamp_us (); sleep_duration_usec = _sleep_duration_convert_ms_to_us (sleep_duration_msec); - wait_until = wait_start_us + (((gint64) wait_before_kill_msec) * 1000L); + if (sig != SIGKILL) + wait_until_sigkill = wait_start_us + (((gint64) wait_before_kill_msec) * 1000L); + else + wait_until_sigkill = 0; + if (max_wait_msec > 0) { + max_wait_until = wait_start_us + (((gint64) max_wait_msec) * 1000L); + if (wait_until_sigkill > 0 && wait_until_sigkill > max_wait_msec) + wait_until_sigkill = 0; + } else + max_wait_until = 0; while (TRUE) { - start_time = nm_utils_get_start_time_for_pid (pid); + start_time = nm_utils_get_start_time_for_pid (pid, &p_state, NULL); if (start_time != start_time0) { nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": process is gone after sending signal %s%s", @@ -745,6 +963,17 @@ nm_utils_kill_process_sync (pid_t pid, guint64 start_time, int sig, guint64 log_ was_waiting ? _kc_waited_to_string (buf_wait, wait_start_us) : ""); return; } + switch (p_state) { + case 'Z': + case 'x': + case 'X': + nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": process is a zombie (%c) after sending signal %s%s", + LOG_NAME_ARGS, p_state, _kc_signal_to_string (sig), + was_waiting ? _kc_waited_to_string (buf_wait, wait_start_us) : ""); + return; + default: + break; + } if (kill (pid, 0) != 0) { errsv = errno; @@ -762,9 +991,25 @@ nm_utils_kill_process_sync (pid_t pid, guint64 start_time, int sig, guint64 log_ } sleep_time = sleep_duration_usec; - if (wait_until != 0) { - now = nm_utils_get_monotonic_timestamp_us (); - if (sig != SIGKILL && now >= wait_until) { + now = nm_utils_get_monotonic_timestamp_us (); + + if ( max_wait_until != 0 + && now >= max_wait_until) { + if (wait_until_sigkill != 0) { + /* wait_before_kill_msec is not larger then max_wait_until but we did not yet send + * SIGKILL. Although we already reached our timeout, we don't want to skip sending + * the signal. Even if we don't wait for the process to disappear. */ + nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": sending SIGKILL", LOG_NAME_ARGS); + kill (pid, SIGKILL); + } + nm_log_warn (log_domain, LOG_NAME_PROCESS_FMT ": timeout %u msec waiting for process to disappear (after sending %s)%s", + LOG_NAME_ARGS, (unsigned) max_wait_until, _kc_signal_to_string (sig), + was_waiting ? _kc_waited_to_string (buf_wait, wait_start_us) : ""); + return; + } + + if (wait_until_sigkill != 0) { + if (now >= wait_until_sigkill) { /* Still not dead. SIGKILL now... */ nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": sending SIGKILL", LOG_NAME_ARGS); if (kill (pid, SIGKILL) != 0) { @@ -781,17 +1026,24 @@ nm_utils_kill_process_sync (pid_t pid, guint64 start_time, int sig, guint64 log_ return; } sig = SIGKILL; - was_waiting = TRUE; - wait_until = 0; + wait_until_sigkill = 0; loop_count = 0; /* reset the loop_count. Now we really expect the process to die quickly. */ + } else + sleep_time = MIN (wait_until_sigkill - now, sleep_duration_usec); + } + + if (!was_waiting) { + if (wait_until_sigkill != 0) { + nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": waiting up to %ld milliseconds for process to disappear before sending KILL signal after sending %s...", + LOG_NAME_ARGS, (long) wait_before_kill_msec, _kc_signal_to_string (sig)); + } else if (max_wait_until != 0) { + nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": waiting up to %ld milliseconds for process to disappear after sending %s...", + LOG_NAME_ARGS, (long) max_wait_msec, _kc_signal_to_string (sig)); } else { - if (!was_waiting) { - nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": waiting up to %ld milliseconds for process to disappear after sending %s...", - LOG_NAME_ARGS, (long) wait_before_kill_msec, _kc_signal_to_string (sig)); - was_waiting = TRUE; - } - sleep_time = MIN (wait_until - now, sleep_duration_usec); + nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": waiting for process to disappear after sending %s...", + LOG_NAME_ARGS, _kc_signal_to_string (sig)); } + was_waiting = TRUE; } if (loop_count < 20) { @@ -827,55 +1079,133 @@ nm_utils_find_helper(const char *progname, const char *try_first, GError **error /******************************************************************************************/ -gboolean -nm_match_spec_string (const GSList *specs, const char *match) +#define MAC_TAG "mac:" +#define INTERFACE_NAME_TAG "interface-name:" +#define DEVICE_TYPE_TAG "type:" +#define SUBCHAN_TAG "s390-subchannels:" +#define EXCEPT_TAG "except:" + +static const char * +_match_except (const char *spec_str, gboolean *out_except) +{ + if (!g_ascii_strncasecmp (spec_str, EXCEPT_TAG, STRLEN (EXCEPT_TAG))) { + spec_str += STRLEN (EXCEPT_TAG); + *out_except = TRUE; + } else + *out_except = FALSE; + return spec_str; +} + +NMMatchSpecMatchType +nm_match_spec_device_type (const GSList *specs, const char *device_type) { const GSList *iter; + NMMatchSpecMatchType match = NM_MATCH_SPEC_NO_MATCH; + + if (!device_type || !*device_type) + return NM_MATCH_SPEC_NO_MATCH; for (iter = specs; iter; iter = g_slist_next (iter)) { - if (!g_ascii_strcasecmp ((const char *) iter->data, match)) - return TRUE; - } + const char *spec_str = iter->data; + gboolean except; - return FALSE; + if (!spec_str || !*spec_str) + continue; + + spec_str = _match_except (spec_str, &except); + + if (g_ascii_strncasecmp (spec_str, DEVICE_TYPE_TAG, STRLEN (DEVICE_TYPE_TAG)) != 0) + continue; + + spec_str += STRLEN (DEVICE_TYPE_TAG); + if (strcmp (spec_str, device_type) == 0) { + if (except) + return NM_MATCH_SPEC_NEG_MATCH; + match = NM_MATCH_SPEC_MATCH; + } + } + return match; } -gboolean +NMMatchSpecMatchType nm_match_spec_hwaddr (const GSList *specs, const char *hwaddr) { const GSList *iter; + NMMatchSpecMatchType match = NM_MATCH_SPEC_NO_MATCH; - g_return_val_if_fail (hwaddr != NULL, FALSE); + g_return_val_if_fail (hwaddr != NULL, NM_MATCH_SPEC_NO_MATCH); for (iter = specs; iter; iter = g_slist_next (iter)) { const char *spec_str = iter->data; + gboolean except; - if ( !g_ascii_strncasecmp (spec_str, "mac:", 4) - && nm_utils_hwaddr_matches (spec_str + 4, -1, hwaddr, -1)) - return TRUE; + if (!spec_str || !*spec_str) + continue; - if (nm_utils_hwaddr_matches (spec_str, -1, hwaddr, -1)) - return TRUE; - } + spec_str = _match_except (spec_str, &except); - return FALSE; + if ( !g_ascii_strncasecmp (spec_str, INTERFACE_NAME_TAG, STRLEN (INTERFACE_NAME_TAG)) + || !g_ascii_strncasecmp (spec_str, SUBCHAN_TAG, STRLEN (SUBCHAN_TAG)) + || !g_ascii_strncasecmp (spec_str, DEVICE_TYPE_TAG, STRLEN (DEVICE_TYPE_TAG))) + continue; + + if (!g_ascii_strncasecmp (spec_str, MAC_TAG, STRLEN (MAC_TAG))) + spec_str += STRLEN (MAC_TAG); + else if (except) + continue; + + if (nm_utils_hwaddr_matches (spec_str, -1, hwaddr, -1)) { + if (except) + return NM_MATCH_SPEC_NEG_MATCH; + match = NM_MATCH_SPEC_MATCH; + } + } + return match; } -gboolean +NMMatchSpecMatchType nm_match_spec_interface_name (const GSList *specs, const char *interface_name) { - char *iface_match; - gboolean matched; + const GSList *iter; + NMMatchSpecMatchType match = NM_MATCH_SPEC_NO_MATCH; - g_return_val_if_fail (interface_name != NULL, FALSE); + g_return_val_if_fail (interface_name != NULL, NM_MATCH_SPEC_NO_MATCH); - if (nm_match_spec_string (specs, interface_name)) - return TRUE; + for (iter = specs; iter; iter = g_slist_next (iter)) { + const char *spec_str = iter->data; + gboolean use_pattern = FALSE; + gboolean except; + + if (!spec_str || !*spec_str) + continue; + + spec_str = _match_except (spec_str, &except); + + if ( !g_ascii_strncasecmp (spec_str, MAC_TAG, STRLEN (MAC_TAG)) + || !g_ascii_strncasecmp (spec_str, SUBCHAN_TAG, STRLEN (SUBCHAN_TAG)) + || !g_ascii_strncasecmp (spec_str, DEVICE_TYPE_TAG, STRLEN (DEVICE_TYPE_TAG))) + continue; - iface_match = g_strdup_printf ("interface-name:%s", interface_name); - matched = nm_match_spec_string (specs, iface_match); - g_free (iface_match); - return matched; + if (!g_ascii_strncasecmp (spec_str, INTERFACE_NAME_TAG, STRLEN (INTERFACE_NAME_TAG))) { + spec_str += STRLEN (INTERFACE_NAME_TAG); + if (spec_str[0] == '=') + spec_str += 1; + else { + if (spec_str[0] == '~') + spec_str += 1; + use_pattern=TRUE; + } + } else if (except) + continue; + + if ( !strcmp (spec_str, interface_name) + || (use_pattern && g_pattern_match_simple (spec_str, interface_name))) { + if (except) + return NM_MATCH_SPEC_NEG_MATCH; + match = NM_MATCH_SPEC_MATCH; + } + } + return match; } #define BUFSIZE 10 @@ -944,33 +1274,225 @@ parse_subchannels (const char *subchannels, guint32 *a, guint32 *b, guint32 *c) return TRUE; } -#define SUBCHAN_TAG "s390-subchannels:" - -gboolean +NMMatchSpecMatchType nm_match_spec_s390_subchannels (const GSList *specs, const char *subchannels) { const GSList *iter; guint32 a = 0, b = 0, c = 0; guint32 spec_a = 0, spec_b = 0, spec_c = 0; + NMMatchSpecMatchType match = NM_MATCH_SPEC_NO_MATCH; - g_return_val_if_fail (subchannels != NULL, FALSE); + g_return_val_if_fail (subchannels != NULL, NM_MATCH_SPEC_NO_MATCH); if (!parse_subchannels (subchannels, &a, &b, &c)) - return FALSE; + return NM_MATCH_SPEC_NO_MATCH; for (iter = specs; iter; iter = g_slist_next (iter)) { - const char *spec = iter->data; + const char *spec_str = iter->data; + gboolean except; + + if (!spec_str || !*spec_str) + continue; + + spec_str = _match_except (spec_str, &except); - if (!strncmp (spec, SUBCHAN_TAG, strlen (SUBCHAN_TAG))) { - spec += strlen (SUBCHAN_TAG); - if (parse_subchannels (spec, &spec_a, &spec_b, &spec_c)) { - if (a == spec_a && b == spec_b && c == spec_c) - return TRUE; + if (!g_ascii_strncasecmp (spec_str, SUBCHAN_TAG, STRLEN (SUBCHAN_TAG))) { + spec_str += STRLEN (SUBCHAN_TAG); + if (parse_subchannels (spec_str, &spec_a, &spec_b, &spec_c)) { + if (a == spec_a && b == spec_b && c == spec_c) { + if (except) + return NM_MATCH_SPEC_NEG_MATCH; + match = NM_MATCH_SPEC_MATCH; + } } } } + return match; +} - return FALSE; +/** + * nm_match_spec_split: + * @value: the string of device specs + * + * Splits the specs from the string and returns them as individual + * entires in a #GSList. + * + * It does not validate any specs, it basically just does a special + * strsplit with ',' or ';' as separators and supporting '\\' as + * escape character. + * + * Leading and trailing spaces of each entry are removed. But the user + * can preserve them by specifying "\\s has 2 leading" or "has 2 trailing \\s". + * + * Specs can have a qualifier like "interface-name:". We still don't strip + * any whitespace after the colon, so "interface-name: X" matches an interface + * named " X". + * + * Returns: (transfer-full): the list of device specs. + */ +GSList * +nm_match_spec_split (const char *value) +{ + char *string_value, *p, *q0, *q; + GSList *pieces = NULL; + int trailing_ws; + + if (!value || !*value) + return NULL; + + /* Copied from glibs g_key_file_parse_value_as_string() function + * and adjusted. */ + + string_value = g_new (gchar, strlen (value) + 1); + + p = (gchar *) value; + + /* skip over leading whitespace */ + while (g_ascii_isspace (*p)) + p++; + + q0 = q = string_value; + trailing_ws = 0; + while (*p) { + if (*p == '\\') { + p++; + + switch (*p) { + case 's': + *q = ' '; + break; + case 'n': + *q = '\n'; + break; + case 't': + *q = '\t'; + break; + case 'r': + *q = '\r'; + break; + case '\\': + *q = '\\'; + break; + case '\0': + break; + default: + if (NM_IN_SET (*p, ',', ';')) + *q = *p; + else { + *q++ = '\\'; + *q = *p; + } + break; + } + if (*p == '\0') + break; + p++; + trailing_ws = 0; + } else { + *q = *p; + if (*p == '\0') + break; + if (g_ascii_isspace (*p)) { + trailing_ws++; + p++; + } else if (NM_IN_SET (*p, ',', ';')) { + if (q0 < q - trailing_ws) + pieces = g_slist_prepend (pieces, g_strndup (q0, (q - q0) - trailing_ws)); + q0 = q + 1; + p++; + trailing_ws = 0; + while (g_ascii_isspace (*p)) + p++; + } else + p++; + } + q++; + } + + *q = '\0'; + if (q0 < q - trailing_ws) + pieces = g_slist_prepend (pieces, g_strndup (q0, (q - q0) - trailing_ws)); + g_free (string_value); + return g_slist_reverse (pieces); +} + +/** + * nm_match_spec_join: + * @specs: the device specs to join + * + * This is based on g_key_file_parse_string_as_value(), analog to + * nm_match_spec_split() which is based on g_key_file_parse_value_as_string(). + * + * Returns: (transfer-full): a joined list of device specs that can be + * split again with nm_match_spec_split(). Note that + * nm_match_spec_split (nm_match_spec_join (specs)) yields the original + * result (which is not true the other way around because there are multiple + * ways to encode the same joined specs string). + */ +char * +nm_match_spec_join (GSList *specs) +{ + const char *p; + GString *str; + + str = g_string_new (""); + + for (; specs; specs = specs->next) { + p = specs->data; + + if (!p || !*p) + continue; + + if (str->len > 0) + g_string_append_c (str, ','); + + /* escape leading whitespace */ + switch (*p) { + case ' ': + g_string_append (str, "\\s"); + p++; + break; + case '\t': + g_string_append (str, "\\t"); + p++; + break; + } + + for (; *p; p++) { + switch (*p) { + case '\n': + g_string_append (str, "\\n"); + break; + case '\r': + g_string_append (str, "\\r"); + break; + case '\\': + g_string_append (str, "\\\\"); + break; + case ',': + g_string_append (str, "\\,"); + break; + case ';': + g_string_append (str, "\\;"); + break; + default: + g_string_append_c (str, *p); + break; + } + } + + /* escape trailing whitespaces */ + switch (str->str[str->len - 1]) { + case ' ': + g_string_overwrite (str, str->len - 1, "\\s"); + break; + case '\t': + g_string_overwrite (str, str->len - 1, "\\t"); + break; + } + } + + return g_string_free (str, FALSE); } const char * @@ -1165,7 +1687,7 @@ get_new_connection_ifname (const GSList *existing, for (i = 0; i < 500; i++) { name = g_strdup_printf ("%s%d", prefix, i); - if (nm_platform_link_exists (name)) + if (nm_platform_link_get_by_ifname (NM_PLATFORM_GET, name)) goto next; for (iter = existing, found = FALSE; iter; iter = g_slist_next (iter)) { @@ -1204,7 +1726,8 @@ nm_utils_get_ip_config_method (NMConnection *connection, return NM_SETTING_IP4_CONFIG_METHOD_DISABLED; else { s_ip4 = nm_connection_get_setting_ip4_config (connection); - g_return_val_if_fail (s_ip4 != NULL, NM_SETTING_IP4_CONFIG_METHOD_AUTO); + if (!s_ip4) + return NM_SETTING_IP4_CONFIG_METHOD_DISABLED; method = nm_setting_ip_config_get_method (s_ip4); g_return_val_if_fail (method != NULL, NM_SETTING_IP4_CONFIG_METHOD_AUTO); @@ -1218,7 +1741,8 @@ nm_utils_get_ip_config_method (NMConnection *connection, return NM_SETTING_IP6_CONFIG_METHOD_IGNORE; else { s_ip6 = nm_connection_get_setting_ip6_config (connection); - g_return_val_if_fail (s_ip6 != NULL, NM_SETTING_IP6_CONFIG_METHOD_AUTO); + if (!s_ip6) + return NM_SETTING_IP6_CONFIG_METHOD_IGNORE; method = nm_setting_ip_config_get_method (s_ip6); g_return_val_if_fail (method != NULL, NM_SETTING_IP6_CONFIG_METHOD_AUTO); @@ -1642,123 +2166,6 @@ nm_utils_cmp_connection_by_autoconnect_priority (NMConnection **a, NMConnection return 0; } -/* nm_utils_ascii_str_to_int64: - * - * A wrapper for g_ascii_strtoll, that checks whether the whole string - * can be successfully converted to a number and is within a given - * range. On any error, @fallback will be returned and %errno will be set - * to a non-zero value. On success, %errno will be set to zero, check %errno - * for errors. Any trailing or leading (ascii) white space is ignored and the - * functions is locale independent. - * - * The function is guaranteed to return a value between @min and @max - * (inclusive) or @fallback. Also, the parsing is rather strict, it does - * not allow for any unrecognized characters, except leading and trailing - * white space. - **/ -gint64 -nm_utils_ascii_str_to_int64 (const char *str, guint base, gint64 min, gint64 max, gint64 fallback) -{ - gint64 v; - size_t len; - char buf[64], *s, *str_free = NULL; - - if (str) { - while (g_ascii_isspace (str[0])) - str++; - } - if (!str || !str[0]) { - errno = EINVAL; - return fallback; - } - - len = strlen (str); - if (g_ascii_isspace (str[--len])) { - /* backward search the first non-ws character. - * We already know that str[0] is non-ws. */ - while (g_ascii_isspace (str[--len])) - ; - - /* str[len] is now the last non-ws character... */ - len++; - - if (len >= sizeof (buf)) - s = str_free = g_malloc (len + 1); - else - s = buf; - - memcpy (s, str, len); - s[len] = 0; - - /* - g_assert (len > 0 && len < strlen (str) && len == strlen (s)); - g_assert (!g_ascii_isspace (str[len-1]) && g_ascii_isspace (str[len])); - g_assert (strncmp (str, s, len) == 0); - */ - - str = s; - } - - errno = 0; - v = g_ascii_strtoll (str, &s, base); - - if (errno != 0) - v = fallback; - else if (s[0] != 0) { - errno = EINVAL; - v = fallback; - } else if (v > max || v < min) { - errno = ERANGE; - v = fallback; - } - - if (G_UNLIKELY (str_free)) - g_free (str_free); - return v; -} - -/** - * nm_utils_uuid_generate_from_strings: - * @string1: a variadic list of strings. Must be NULL terminated. - * - * Returns a variant3 UUID based on the concatenated C strings. - * It does not simply concatenate them, but also includes the - * terminating '\0' character. For example "a", "b", gives - * "a\0b\0". - * - * This has the advantage, that the following invocations - * all give different UUIDs: (NULL), (""), ("",""), ("","a"), ("a",""), - * ("aa"), ("aa", ""), ("", "aa"), ... - */ -char * -nm_utils_uuid_generate_from_strings (const char *string1, ...) -{ - GString *str; - va_list args; - const char *s; - char *uuid; - - if (!string1) - return nm_utils_uuid_generate_from_string (NULL, 0, NM_UTILS_UUID_TYPE_VARIANT3, NM_UTILS_UUID_NS); - - str = g_string_sized_new (120); /* effectively allocates power of 2 (128)*/ - - g_string_append_len (str, string1, strlen (string1) + 1); - - va_start (args, string1); - s = va_arg (args, const char *); - while (s) { - g_string_append_len (str, s, strlen (s) + 1); - s = va_arg (args, const char *); - } - va_end (args); - - uuid = nm_utils_uuid_generate_from_string (str->str, str->len, NM_UTILS_UUID_TYPE_VARIANT3, NM_UTILS_UUID_NS); - - g_string_free (str, TRUE); - return uuid; -} - /**************************************************************************/ static gint64 monotonic_timestamp_offset_sec; @@ -2253,11 +2660,10 @@ ASSERT_VALID_PATH_COMPONENT (const char *name) return name; fail: - if (name) - nm_log_err (LOGD_CORE, "Failed asserting path component: NULL"); - else - nm_log_err (LOGD_CORE, "Failed asserting path component: \"%s\"", name); - g_error ("FATAL: Failed asserting path component: %s", name ? name : "(null)"); + nm_log_err (LOGD_CORE, "Failed asserting path component: %s%s%s", + NM_PRINT_FMT_QUOTED (name, "\"", name, "\"", "(null)")); + g_error ("FATAL: Failed asserting path component: %s%s%s", + NM_PRINT_FMT_QUOTED (name, "\"", name, "\"", "(null)")); g_assert_not_reached (); } @@ -2555,3 +2961,21 @@ nm_utils_ip6_routes_from_gvalue (const GValue *value) return g_slist_reverse (list); } + +/** + * nm_utils_setpgid: + * @unused: unused + * + * This can be passed as a child setup function to the g_spawn*() family + * of functions, to ensure that the child is in its own process group + * (and thus, in some situations, will not be killed when NetworkManager + * is killed). + */ +void +nm_utils_setpgid (gpointer unused G_GNUC_UNUSED) +{ + pid_t pid; + + pid = getpid (); + setpgid (pid, pid); +} diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h index ae1edd82..1864547f 100644 --- a/src/NetworkManagerUtils.h +++ b/src/NetworkManagerUtils.h @@ -51,14 +51,7 @@ nm_utils_ip6_route_metric_normalize (guint32 metric) int nm_spawn_process (const char *args, GError **error); -/* check if @flags has exactly one flag (@check) set. You should call this - * only with @check being a compile time constant and a power of two. */ -#define NM_FLAGS_HAS(flags, check) \ - ( (G_STATIC_ASSERT_EXPR ( ((check) != 0) && ((check) & ((check)-1)) == 0 )), (NM_FLAGS_ANY ((flags), (check))) ) - -#define NM_FLAGS_ANY(flags, check) ( ( ((flags) & (check)) != 0 ) ? TRUE : FALSE ) -#define NM_FLAGS_ALL(flags, check) ( ( ((flags) & (check)) == (check) ) ? TRUE : FALSE ) - +int nm_utils_modprobe (GError **error, gboolean suppress_error_loggin, const char *arg1, ...) G_GNUC_NULL_TERMINATED; /** * str_if_set: @@ -76,11 +69,11 @@ str_if_set (const char *str, const char *fallback) return str ? str : fallback; } -guint64 nm_utils_get_start_time_for_pid (pid_t pid); +guint64 nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_ppid); void nm_utils_kill_process_sync (pid_t pid, guint64 start_time, int sig, guint64 log_domain, const char *log_name, guint32 wait_before_kill_msec, - guint32 sleep_duration_msec); + guint32 sleep_duration_msec, guint32 max_wait_msec); typedef void (*NMUtilsKillChildAsyncCb) (pid_t pid, gboolean success, int child_status, void *user_data); void nm_utils_kill_child_async (pid_t pid, int sig, guint64 log_domain, const char *log_name, @@ -94,10 +87,18 @@ const char *nm_utils_find_helper (const char *progname, const char *try_first, GError **error); -gboolean nm_match_spec_string (const GSList *specs, const char *string); -gboolean nm_match_spec_hwaddr (const GSList *specs, const char *hwaddr); -gboolean nm_match_spec_s390_subchannels (const GSList *specs, const char *subchannels); -gboolean nm_match_spec_interface_name (const GSList *specs, const char *interface_name); +typedef enum { + NM_MATCH_SPEC_NO_MATCH = 0, + NM_MATCH_SPEC_MATCH = 1, + NM_MATCH_SPEC_NEG_MATCH = 2, +} NMMatchSpecMatchType; + +NMMatchSpecMatchType nm_match_spec_device_type (const GSList *specs, const char *device_type); +NMMatchSpecMatchType nm_match_spec_hwaddr (const GSList *specs, const char *hwaddr); +NMMatchSpecMatchType nm_match_spec_s390_subchannels (const GSList *specs, const char *subchannels); +NMMatchSpecMatchType nm_match_spec_interface_name (const GSList *specs, const char *interface_name); +GSList *nm_match_spec_split (const char *value); +char *nm_match_spec_join (GSList *specs); const char *nm_utils_get_shared_wifi_permission (NMConnection *connection); @@ -155,19 +156,13 @@ int nm_utils_cmp_connection_by_autoconnect_priority (NMConnection **a, NMConnect void nm_utils_log_connection_diff (NMConnection *connection, NMConnection *diff_base, guint32 level, guint64 domain, const char *name, const char *prefix); -gint64 nm_utils_ascii_str_to_int64 (const char *str, guint base, gint64 min, gint64 max, gint64 fallback); - -#define NM_UTILS_UUID_NS "b425e9fb-7598-44b4-9e3b-5a2e3aaa4905" - -char *nm_utils_uuid_generate_from_strings (const char *string1, ...) G_GNUC_NULL_TERMINATED; - #define NM_UTILS_NS_PER_SECOND ((gint64) 1000000000) gint64 nm_utils_get_monotonic_timestamp_ns (void); gint64 nm_utils_get_monotonic_timestamp_us (void); gint64 nm_utils_get_monotonic_timestamp_ms (void); gint32 nm_utils_get_monotonic_timestamp_s (void); -const char *ASSERT_VALID_PATH_COMPONENT (const char *name) G_GNUC_WARN_UNUSED_RESULT; +const char *ASSERT_VALID_PATH_COMPONENT (const char *name); const char *nm_utils_ip6_property_path (const char *ifname, const char *property); const char *nm_utils_ip4_property_path (const char *ifname, const char *property); @@ -184,12 +179,12 @@ gboolean nm_utils_is_specific_hostname (const char *name); * and should not normally be treated as a %guint64, but this is done for * convenience of validity checking and initialization. */ -typedef struct { +struct _NMUtilsIPv6IfaceId { union { guint64 id; guint8 id_u8[8]; }; -} NMUtilsIPv6IfaceId; +}; #define NM_UTILS_IPV6_IFACE_ID_INIT { .id = 0 } @@ -211,4 +206,28 @@ GHashTable *nm_utils_connection_dict_to_hash (GVariant *dict); GSList *nm_utils_ip4_routes_from_gvalue (const GValue *value); GSList *nm_utils_ip6_routes_from_gvalue (const GValue *value); +void nm_utils_array_remove_at_indexes (GArray *array, const guint *indexes_to_delete, gsize len); + +void nm_utils_setpgid (gpointer unused); + +typedef enum { + NM_UTILS_TEST_NONE = 0, + + /* Internal flag, marking that either nm_utils_get_testing() or _nm_utils_set_testing() was called. */ + _NM_UTILS_TEST_INITIALIZED = (1LL << 0), + + /* Indicate that test mode is enabled in general. Explicitly calling _nm_utils_set_testing() will always set this flag. */ + _NM_UTILS_TEST_GENERAL = (1LL << 1), + + /* Don't check the owner of keyfiles during testing. */ + NM_UTILS_TEST_NO_KEYFILE_OWNER_CHECK = (1LL << 2), + + _NM_UTILS_TEST_LAST, + NM_UTILS_TEST_ALL = (((_NM_UTILS_TEST_LAST - 1) << 1) - 1) & ~(_NM_UTILS_TEST_INITIALIZED), +} NMUtilsTestFlags; + +gboolean nm_utils_get_testing_initialized (void); +NMUtilsTestFlags nm_utils_get_testing (void); +void _nm_utils_set_testing (NMUtilsTestFlags flags); + #endif /* __NETWORKMANAGER_UTILS_H__ */ diff --git a/src/devices/adsl/Makefile.in b/src/devices/adsl/Makefile.in index 02da85e7..6f7fe174 100644 --- a/src/devices/adsl/Makefile.in +++ b/src/devices/adsl/Makefile.in @@ -223,6 +223,7 @@ ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -336,6 +337,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ +LOG_DRIVER = @LOG_DRIVER@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ diff --git a/src/devices/adsl/nm-atm-manager.c b/src/devices/adsl/nm-atm-manager.c index 6f34c02b..0830f85d 100644 --- a/src/devices/adsl/nm-atm-manager.c +++ b/src/devices/adsl/nm-atm-manager.c @@ -25,6 +25,7 @@ #include <gmodule.h> #include "nm-atm-manager.h" +#include "nm-setting-adsl.h" #include "nm-device-adsl.h" #include "nm-device-factory.h" #include "nm-logging.h" @@ -203,11 +204,9 @@ handle_uevent (GUdevClient *client, adsl_remove (self, device); } -static NMDeviceType -get_device_type (NMDeviceFactory *factory) -{ - return NM_DEVICE_TYPE_ADSL; -} +NM_DEVICE_FACTORY_DECLARE_TYPES ( + NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_ADSL_SETTING_NAME) +) /*********************************************************************/ @@ -224,7 +223,7 @@ nm_atm_manager_init (NMAtmManager *self) static void device_factory_interface_init (NMDeviceFactory *factory_iface) { - factory_iface->get_device_type = get_device_type; + factory_iface->get_supported_types = get_supported_types; factory_iface->start = start; } diff --git a/src/devices/adsl/nm-device-adsl.c b/src/devices/adsl/nm-device-adsl.c index e664ff48..4d2cac5b 100644 --- a/src/devices/adsl/nm-device-adsl.c +++ b/src/devices/adsl/nm-device-adsl.c @@ -73,7 +73,7 @@ typedef struct { /**************************************************************/ -static guint32 +static NMDeviceCapabilities get_generic_capabilities (NMDevice *dev) { return ( NM_DEVICE_CAP_CARRIER_DETECT @@ -144,7 +144,7 @@ set_nas_iface (NMDeviceAdsl *self, int idx, const char *name) g_return_if_fail (name != NULL); g_warn_if_fail (priv->nas_ifindex <= 0); - priv->nas_ifindex = idx > 0 ? idx : nm_platform_link_get_ifindex (name); + priv->nas_ifindex = idx > 0 ? idx : nm_platform_link_get_ifindex (NM_PLATFORM_GET, name); g_warn_if_fail (priv->nas_ifindex > 0); g_warn_if_fail (priv->nas_ifname == NULL); @@ -295,7 +295,7 @@ error: } static void -link_changed_cb (NMPlatform *platform, int ifindex, NMPlatformLink *info, NMPlatformSignalChangeType change_type, NMPlatformReason reason, NMDeviceAdsl *self) +link_changed_cb (NMPlatform *platform, NMPObjectType obj_type, int ifindex, NMPlatformLink *info, NMPlatformSignalChangeType change_type, NMPlatformReason reason, NMDeviceAdsl *self) { if (change_type == NM_PLATFORM_SIGNAL_REMOVED) { NMDeviceAdslPrivate *priv = NM_DEVICE_ADSL_GET_PRIVATE (self); @@ -352,7 +352,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_reason) _LOGD (LOGD_ADSL, "ATM setup successful"); /* otherwise we're good for stage3 */ - nm_platform_link_set_up (priv->nas_ifindex); + nm_platform_link_set_up (NM_PLATFORM_GET, priv->nas_ifindex, NULL); ret = NM_ACT_STAGE_RETURN_SUCCESS; } else if (g_strcmp0 (protocol, NM_SETTING_ADSL_PROTOCOL_PPPOA) == 0) { @@ -492,7 +492,7 @@ carrier_update_cb (gpointer user_data) path = g_strdup_printf ("/sys/class/atm/%s/carrier", ASSERT_VALID_PATH_COMPONENT (nm_device_get_iface (NM_DEVICE (self)))); - carrier = (int) nm_platform_sysctl_get_int_checked (path, 10, 0, 1, -1); + carrier = (int) nm_platform_sysctl_get_int_checked (NM_PLATFORM_GET, path, 10, 0, 1, -1); g_free (path); if (carrier != -1) @@ -526,7 +526,7 @@ get_atm_index (const char *iface) path = g_strdup_printf ("/sys/class/atm/%s/atmindex", ASSERT_VALID_PATH_COMPONENT (iface)); - idx = (int) nm_platform_sysctl_get_int_checked (path, 10, 0, G_MAXINT, -1); + idx = (int) nm_platform_sysctl_get_int_checked (NM_PLATFORM_GET, path, 10, 0, G_MAXINT, -1); g_free (path); return idx; diff --git a/src/devices/bluetooth/Makefile.in b/src/devices/bluetooth/Makefile.in index b8e6dcda..6233a751 100644 --- a/src/devices/bluetooth/Makefile.in +++ b/src/devices/bluetooth/Makefile.in @@ -243,6 +243,7 @@ ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -356,6 +357,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ +LOG_DRIVER = @LOG_DRIVER@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ diff --git a/src/devices/bluetooth/nm-bluez-device.c b/src/devices/bluetooth/nm-bluez-device.c index fe1a97bb..d5426052 100644 --- a/src/devices/bluetooth/nm-bluez-device.c +++ b/src/devices/bluetooth/nm-bluez-device.c @@ -28,6 +28,7 @@ #include "nm-core-internal.h" +#include "nm-bt-error.h" #include "nm-bluez-common.h" #include "nm-bluez-device.h" #include "nm-logging.h" @@ -436,8 +437,6 @@ nm_bluez_device_disconnect (NMBluezDevice *self) } else if (priv->bluez_version == 5) { #if WITH_BLUEZ5_DUN nm_bluez5_dun_cleanup (priv->b5_dun_context); -#else - g_assert_not_reached (); #endif priv->connected = FALSE; goto out; @@ -559,7 +558,11 @@ nm_bluez_device_connect_async (NMBluezDevice *self, priv->b5_dun_context = nm_bluez5_dun_new (priv->adapter_address, priv->address); nm_bluez5_dun_connect (priv->b5_dun_context, bluez5_dun_connect_cb, simple); #else - g_assert_not_reached (); + g_simple_async_result_set_error (simple, + NM_BT_ERROR, + NM_BT_ERROR_DUN_CONNECT_FAILED, + "NetworkManager built without support for Bluez 5"); + g_simple_async_result_complete (simple); #endif return; } diff --git a/src/devices/bluetooth/nm-bluez-manager.c b/src/devices/bluetooth/nm-bluez-manager.c index e176a4b4..df4eb46d 100644 --- a/src/devices/bluetooth/nm-bluez-manager.c +++ b/src/devices/bluetooth/nm-bluez-manager.c @@ -29,6 +29,7 @@ #include "nm-logging.h" #include "nm-bluez-manager.h" #include "nm-device-factory.h" +#include "nm-setting-bluetooth.h" #include "nm-bluez4-manager.h" #include "nm-bluez5-manager.h" #include "nm-bluez-device.h" @@ -37,6 +38,7 @@ #include "nm-device-bt.h" #include "nm-dbus-manager.h" +#include "nm-platform.h" typedef struct { int bluez_version; @@ -368,11 +370,10 @@ start (NMDeviceFactory *factory) check_bluez_and_try_setup (NM_BLUEZ_MANAGER (factory)); } -static NMDeviceType -get_device_type (NMDeviceFactory *factory) -{ - return NM_DEVICE_TYPE_BT; -} +NM_DEVICE_FACTORY_DECLARE_TYPES ( + NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_BNEP) + NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_BLUETOOTH_SETTING_NAME) +) /*********************************************************************/ @@ -405,10 +406,19 @@ nm_bluez_manager_init (NMBluezManager *self) g_assert (priv->provider); } +static NMDevice * +new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error) +{ + g_warn_if_fail (plink->type == NM_LINK_TYPE_BNEP); + *out_ignore = TRUE; + return NULL; +} + static void device_factory_interface_init (NMDeviceFactory *factory_iface) { - factory_iface->get_device_type = get_device_type; + factory_iface->get_supported_types = get_supported_types; + factory_iface->new_link = new_link; factory_iface->start = start; } diff --git a/src/devices/bluetooth/nm-device-bt.c b/src/devices/bluetooth/nm-device-bt.c index 61371ddd..88033e2e 100644 --- a/src/devices/bluetooth/nm-device-bt.c +++ b/src/devices/bluetooth/nm-device-bt.c @@ -124,7 +124,7 @@ get_connection_bt_type (NMConnection *connection) return NM_BT_CAPABILITY_NONE; } -static guint32 +static NMDeviceCapabilities get_generic_capabilities (NMDevice *device) { return NM_DEVICE_CAP_IS_NON_KERNEL; @@ -479,6 +479,13 @@ device_state_changed (NMDevice *device, if (priv->modem) nm_modem_device_state_changed (priv->modem, new_state, old_state, reason); + + /* Need to recheck available connections whenever MM appears or disappears, + * since the device could be both DUN and NAP capable and thus may not + * change state (which rechecks available connections) when MM comes and goes. + */ + if (priv->mm_running && (priv->capabilities & NM_BT_CAPABILITY_DUN)) + nm_device_recheck_available_connections (device); } static void @@ -944,60 +951,19 @@ is_available (NMDevice *dev, NMDeviceCheckDevAvailableFlags flags) } static void -handle_availability_change (NMDeviceBt *self, - gboolean old_available, - NMDeviceStateReason unavailable_reason) -{ - NMDevice *device = NM_DEVICE (self); - NMDeviceState state; - gboolean available; - - state = nm_device_get_state (device); - if (state < NM_DEVICE_STATE_UNAVAILABLE) { - _LOGD (LOGD_BT, "availability blocked by UNMANAGED state"); - return; - } - - available = nm_device_is_available (device, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE); - if (available == old_available) - return; - - if (available) { - if (state != NM_DEVICE_STATE_UNAVAILABLE) - _LOGW (LOGD_CORE | LOGD_BT, "not in expected unavailable state!"); - - nm_device_state_changed (device, - NM_DEVICE_STATE_DISCONNECTED, - NM_DEVICE_STATE_REASON_NONE); - } else { - nm_device_state_changed (device, - NM_DEVICE_STATE_UNAVAILABLE, - unavailable_reason); - } -} - -static void set_mm_running (NMDeviceBt *self, gboolean running) { NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self); - gboolean old_available; - if (priv->mm_running == running) - return; - - _LOGD (LOGD_BT, "ModemManager now %s", - running ? "available" : "unavailable"); + if (priv->mm_running != running) { + _LOGD (LOGD_BT, "ModemManager now %s", + running ? "available" : "unavailable"); - old_available = nm_device_is_available (NM_DEVICE (self), NM_DEVICE_CHECK_DEV_AVAILABLE_NONE); - priv->mm_running = running; - handle_availability_change (self, old_available, NM_DEVICE_STATE_REASON_MODEM_MANAGER_UNAVAILABLE); - - /* Need to recheck available connections whenever MM appears or disappears, - * since the device could be both DUN and NAP capable and thus may not - * change state (which rechecks available connections) when MM comes and goes. - */ - if (priv->capabilities & NM_BT_CAPABILITY_DUN) - nm_device_recheck_available_connections (NM_DEVICE (self)); + priv->mm_running = running; + nm_device_queue_recheck_available (NM_DEVICE (self), + NM_DEVICE_STATE_REASON_NONE, + NM_DEVICE_STATE_REASON_MODEM_MANAGER_UNAVAILABLE); + } } static void diff --git a/src/devices/nm-device-bond.c b/src/devices/nm-device-bond.c index 0b2c97d1..7f139424 100644 --- a/src/devices/nm-device-bond.c +++ b/src/devices/nm-device-bond.c @@ -37,6 +37,7 @@ #include "nm-enum-types.h" #include "nm-device-factory.h" #include "nm-core-internal.h" +#include "nm-ip4-config.h" #include "nm-device-bond-glue.h" @@ -60,18 +61,16 @@ enum { /******************************************************************/ -static guint32 +static NMDeviceCapabilities get_generic_capabilities (NMDevice *dev) { - return NM_DEVICE_CAP_CARRIER_DETECT; + return NM_DEVICE_CAP_CARRIER_DETECT | NM_DEVICE_CAP_IS_SOFTWARE; } static gboolean is_available (NMDevice *dev, NMDeviceCheckDevAvailableFlags flags) { - if (NM_DEVICE_GET_CLASS (dev)->is_up) - return NM_DEVICE_GET_CLASS (dev)->is_up (dev); - return FALSE; + return TRUE; } static gboolean @@ -144,7 +143,7 @@ set_bond_attr (NMDevice *device, const char *attr, const char *value) gboolean ret; int ifindex = nm_device_get_ifindex (device); - ret = nm_platform_master_set_option (ifindex, attr, value); + ret = nm_platform_master_set_option (NM_PLATFORM_GET, ifindex, attr, value); if (!ret) _LOGW (LOGD_HW, "failed to set bonding attribute '%s' to '%s'", attr, value); return ret; @@ -178,7 +177,7 @@ update_connection (NMDevice *device, NMConnection *connection) /* Read bond options from sysfs and update the Bond setting to match */ options = nm_setting_bond_get_valid_options (s_bond); while (options && *options) { - gs_free char *value = nm_platform_master_get_option (ifindex, *options); + gs_free char *value = nm_platform_master_get_option (NM_PLATFORM_GET, ifindex, *options); const char *defvalue = nm_setting_bond_get_option_default (s_bond, *options); if (value && !ignore_if_zero (*options, value) && (g_strcmp0 (value, defvalue) != 0)) { @@ -334,7 +333,7 @@ apply_bonding_config (NMDevice *device) } /* Clear ARP targets */ - contents = nm_platform_master_get_option (ifindex, "arp_ip_target"); + contents = nm_platform_master_get_option (NM_PLATFORM_GET, ifindex, "arp_ip_target"); set_arp_targets (device, contents, " \n", "-"); g_free (contents); @@ -377,6 +376,25 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) return ret; } +static void +ip4_config_pre_commit (NMDevice *self, NMIP4Config *config) +{ + NMConnection *connection; + NMSettingWired *s_wired; + guint32 mtu; + + connection = nm_device_get_connection (self); + g_assert (connection); + s_wired = nm_connection_get_setting_wired (connection); + + if (s_wired) { + /* MTU override */ + mtu = nm_setting_wired_get_mtu (s_wired); + if (mtu) + nm_ip4_config_set_mtu (config, mtu, NM_IP_CONFIG_SOURCE_USER); + } +} + static gboolean enslave_slave (NMDevice *device, NMDevice *slave, @@ -391,7 +409,8 @@ enslave_slave (NMDevice *device, if (configure) { nm_device_take_down (slave, TRUE); - success = nm_platform_link_enslave (nm_device_get_ip_ifindex (device), + success = nm_platform_link_enslave (NM_PLATFORM_GET, + nm_device_get_ip_ifindex (device), nm_device_get_ip_ifindex (slave)); nm_device_bring_up (slave, TRUE, &no_firmware); @@ -415,7 +434,8 @@ release_slave (NMDevice *device, gboolean success = TRUE, no_firmware = FALSE; if (configure) { - success = nm_platform_link_release (nm_device_get_ip_ifindex (device), + success = nm_platform_link_release (NM_PLATFORM_GET, + nm_device_get_ip_ifindex (device), nm_device_get_ip_ifindex (slave)); if (success) { @@ -510,6 +530,7 @@ nm_device_bond_class_init (NMDeviceBondClass *klass) parent_class->master_update_slave_connection = master_update_slave_connection; parent_class->act_stage1_prepare = act_stage1_prepare; + parent_class->ip4_config_pre_commit = ip4_config_pre_commit; parent_class->enslave_slave = enslave_slave; parent_class->release_slave = release_slave; @@ -532,18 +553,15 @@ nm_device_bond_class_init (NMDeviceBondClass *klass) #define NM_BOND_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_BOND_FACTORY, NMBondFactory)) static NMDevice * -new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error) +new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error) { - if (plink->type == NM_LINK_TYPE_BOND) { - return (NMDevice *) g_object_new (NM_TYPE_DEVICE_BOND, - NM_DEVICE_PLATFORM_DEVICE, plink, - NM_DEVICE_DRIVER, "bonding", - NM_DEVICE_TYPE_DESC, "Bond", - NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_BOND, - NM_DEVICE_IS_MASTER, TRUE, - NULL); - } - return NULL; + return (NMDevice *) g_object_new (NM_TYPE_DEVICE_BOND, + NM_DEVICE_PLATFORM_DEVICE, plink, + NM_DEVICE_DRIVER, "bonding", + NM_DEVICE_TYPE_DESC, "Bond", + NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_BOND, + NM_DEVICE_IS_MASTER, TRUE, + NULL); } static NMDevice * @@ -552,19 +570,18 @@ create_virtual_device_for_connection (NMDeviceFactory *factory, NMDevice *parent, GError **error) { - const char *iface; - - if (!nm_connection_is_type (connection, NM_SETTING_BOND_SETTING_NAME)) - return NULL; - - iface = nm_connection_get_interface_name (connection); - g_return_val_if_fail (iface != NULL, NULL); - - if ( !nm_platform_bond_add (iface) - && nm_platform_get_error () != NM_PLATFORM_ERROR_EXISTS) { - nm_log_warn (LOGD_DEVICE | LOGD_BOND, "(%s): failed to create bonding master interface for '%s': %s", - iface, nm_connection_get_id (connection), - nm_platform_get_error_msg ()); + const char *iface = nm_connection_get_interface_name (connection); + NMPlatformError plerr; + + g_assert (iface); + + plerr = nm_platform_bond_add (NM_PLATFORM_GET, iface, NULL); + if (plerr != NM_PLATFORM_ERROR_SUCCESS && plerr != NM_PLATFORM_ERROR_EXISTS) { + g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, + "Failed to create bond interface '%s' for '%s': %s", + iface, + nm_connection_get_id (connection), + nm_platform_error_to_string (plerr)); return NULL; } @@ -577,7 +594,9 @@ create_virtual_device_for_connection (NMDeviceFactory *factory, NULL); } -DEFINE_DEVICE_FACTORY_INTERNAL(BOND, Bond, bond, +NM_DEVICE_FACTORY_DEFINE_INTERNAL (BOND, Bond, bond, + NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_BOND) + NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_BOND_SETTING_NAME), factory_iface->new_link = new_link; factory_iface->create_virtual_device_for_connection = create_virtual_device_for_connection; ) diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c index a2ed4b41..e1cafd60 100644 --- a/src/devices/nm-device-bridge.c +++ b/src/devices/nm-device-bridge.c @@ -59,18 +59,16 @@ enum { /******************************************************************/ -static guint32 +static NMDeviceCapabilities get_generic_capabilities (NMDevice *dev) { - return NM_DEVICE_CAP_CARRIER_DETECT; + return NM_DEVICE_CAP_CARRIER_DETECT | NM_DEVICE_CAP_IS_SOFTWARE; } static gboolean is_available (NMDevice *dev, NMDeviceCheckDevAvailableFlags flags) { - if (NM_DEVICE_GET_CLASS (dev)->is_up) - return NM_DEVICE_GET_CLASS (dev)->is_up (dev); - return FALSE; + return TRUE; } static gboolean @@ -213,9 +211,9 @@ commit_option (NMDevice *device, NMSetting *setting, const Option *option, gbool value = g_strdup_printf ("%u", uval); if (slave) - nm_platform_slave_set_option (ifindex, option->sysname, value); + nm_platform_slave_set_option (NM_PLATFORM_GET, ifindex, option->sysname, value); else - nm_platform_master_set_option (ifindex, option->sysname, value); + nm_platform_master_set_option (NM_PLATFORM_GET, ifindex, option->sysname, value); } static void @@ -259,7 +257,7 @@ update_connection (NMDevice *device, NMConnection *connection) } for (option = master_options; option->name; option++) { - gs_free char *str = nm_platform_master_get_option (ifindex, option->sysname); + gs_free char *str = nm_platform_master_get_option (NM_PLATFORM_GET, ifindex, option->sysname); int value; if (str) { @@ -298,7 +296,7 @@ master_update_slave_connection (NMDevice *device, } for (option = slave_options; option->name; option++) { - gs_free char *str = nm_platform_slave_get_option (ifindex_slave, option->sysname); + gs_free char *str = nm_platform_slave_get_option (NM_PLATFORM_GET, ifindex_slave, option->sysname); int value; if (str) { @@ -346,7 +344,7 @@ enslave_slave (NMDevice *device, NMDeviceBridge *self = NM_DEVICE_BRIDGE (device); if (configure) { - if (!nm_platform_link_enslave (nm_device_get_ip_ifindex (device), nm_device_get_ip_ifindex (slave))) + if (!nm_platform_link_enslave (NM_PLATFORM_GET, nm_device_get_ip_ifindex (device), nm_device_get_ip_ifindex (slave))) return FALSE; commit_slave_options (slave, nm_connection_get_setting_bridge_port (connection)); @@ -372,7 +370,8 @@ release_slave (NMDevice *device, gboolean success = TRUE; if (configure) { - success = nm_platform_link_release (nm_device_get_ip_ifindex (device), + success = nm_platform_link_release (NM_PLATFORM_GET, + nm_device_get_ip_ifindex (device), nm_device_get_ip_ifindex (slave)); if (success) { @@ -477,18 +476,15 @@ nm_device_bridge_class_init (NMDeviceBridgeClass *klass) #define NM_BRIDGE_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_BRIDGE_FACTORY, NMBridgeFactory)) static NMDevice * -new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error) +new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error) { - if (plink->type == NM_LINK_TYPE_BRIDGE) { - return (NMDevice *) g_object_new (NM_TYPE_DEVICE_BRIDGE, - NM_DEVICE_PLATFORM_DEVICE, plink, - NM_DEVICE_DRIVER, "bridge", - NM_DEVICE_TYPE_DESC, "Bridge", - NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_BRIDGE, - NM_DEVICE_IS_MASTER, TRUE, - NULL); - } - return NULL; + return (NMDevice *) g_object_new (NM_TYPE_DEVICE_BRIDGE, + NM_DEVICE_PLATFORM_DEVICE, plink, + NM_DEVICE_DRIVER, "bridge", + NM_DEVICE_TYPE_DESC, "Bridge", + NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_BRIDGE, + NM_DEVICE_IS_MASTER, TRUE, + NULL); } static NMDevice * @@ -497,21 +493,16 @@ create_virtual_device_for_connection (NMDeviceFactory *factory, NMDevice *parent, GError **error) { - const char *iface; + const char *iface = nm_connection_get_interface_name (connection); NMSettingBridge *s_bridge; const char *mac_address_str; guint8 mac_address[NM_UTILS_HWADDR_LEN_MAX]; + NMPlatformError plerr; - if (!nm_connection_is_type (connection, NM_SETTING_BRIDGE_SETTING_NAME)) - return NULL; - - g_return_val_if_fail (connection != NULL, NULL); - - iface = nm_connection_get_interface_name (connection); - g_return_val_if_fail (iface != NULL, NULL); + g_assert (iface); s_bridge = nm_connection_get_setting_bridge (connection); - g_return_val_if_fail (s_bridge, NULL); + g_assert (s_bridge); mac_address_str = nm_setting_bridge_get_mac_address (s_bridge); if (mac_address_str) { @@ -519,13 +510,17 @@ create_virtual_device_for_connection (NMDeviceFactory *factory, mac_address_str = NULL; } - if ( !nm_platform_bridge_add (iface, + plerr = nm_platform_bridge_add (NM_PLATFORM_GET, + iface, mac_address_str ? mac_address : NULL, - mac_address_str ? ETH_ALEN : 0) - && nm_platform_get_error () != NM_PLATFORM_ERROR_EXISTS) { - nm_log_warn (LOGD_DEVICE | LOGD_BRIDGE, "(%s): failed to create bridge master interface for '%s': %s", - iface, nm_connection_get_id (connection), - nm_platform_get_error_msg ()); + mac_address_str ? ETH_ALEN : 0, + NULL); + if (plerr != NM_PLATFORM_ERROR_SUCCESS && plerr != NM_PLATFORM_ERROR_EXISTS) { + g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, + "Failed to create bridge interface '%s' for '%s': %s", + iface, + nm_connection_get_id (connection), + nm_platform_error_to_string (plerr)); return NULL; } @@ -538,7 +533,9 @@ create_virtual_device_for_connection (NMDeviceFactory *factory, NULL); } -DEFINE_DEVICE_FACTORY_INTERNAL(BRIDGE, Bridge, bridge, +NM_DEVICE_FACTORY_DEFINE_INTERNAL (BRIDGE, Bridge, bridge, + NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_BRIDGE) + NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_BRIDGE_SETTING_NAME), factory_iface->new_link = new_link; factory_iface->create_virtual_device_for_connection = create_virtual_device_for_connection; ) diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c index ac408d3d..af2cb8f2 100644 --- a/src/devices/nm-device-ethernet.c +++ b/src/devices/nm-device-ethernet.c @@ -26,10 +26,6 @@ #include <netinet/in.h> #include <string.h> #include <stdlib.h> -#include <linux/sockios.h> -#include <linux/ethtool.h> -#include <linux/version.h> -#include <sys/ioctl.h> #include <unistd.h> #include <errno.h> @@ -48,6 +44,7 @@ #include "nm-enum-types.h" #include "nm-dbus-manager.h" #include "nm-platform.h" +#include "nm-platform-utils.h" #include "nm-dcb.h" #include "nm-settings-connection.h" #include "nm-config.h" @@ -55,6 +52,7 @@ #include "nm-connection-provider.h" #include "nm-device-factory.h" #include "nm-core-internal.h" +#include "NetworkManagerUtils.h" #include "nm-device-ethernet-glue.h" @@ -99,9 +97,6 @@ typedef enum { } DcbWait; typedef struct { - char * perm_hw_addr; /* Permanent MAC address */ - char * initial_hw_addr; /* Initial MAC address (as seen when NM starts) */ - guint32 speed; Supplicant supplicant; @@ -156,26 +151,21 @@ static void _update_s390_subchannels (NMDeviceEthernet *self) { NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); - GUdevClient *client; GUdevDevice *dev; GUdevDevice *parent = NULL; const char *parent_path, *item, *driver; - const char *subsystems[] = { "net", NULL }; + int ifindex; GDir *dir; GError *error = NULL; - client = g_udev_client_new (subsystems); - if (!client) { - _LOGW (LOGD_DEVICE | LOGD_HW, "failed to initialize GUdev client"); - return; - } - - dev = g_udev_client_query_by_subsystem_and_name (client, "net", - nm_device_get_iface (NM_DEVICE (self))); + ifindex = nm_device_get_ifindex (NM_DEVICE (self)); + dev = (GUdevDevice *) nm_platform_link_get_udev_device (NM_PLATFORM_GET, ifindex); if (!dev) { - _LOGW (LOGD_DEVICE | LOGD_HW, "failed to find device with udev"); + _LOGW (LOGD_DEVICE | LOGD_HW, "failed to find device %d '%s' with udev", + ifindex, str_if_set (nm_device_get_iface (NM_DEVICE (self)), "(null)")); goto out; } + g_object_ref (dev); /* Try for the "ccwgroup" parent */ parent = g_udev_device_get_parent_with_subsystem (dev, "ccwgroup", NULL); @@ -210,7 +200,7 @@ _update_s390_subchannels (NMDeviceEthernet *self) || !strcmp (item, "portno")) { char *path, *value; path = g_strdup_printf ("%s/%s", parent_path, item); - value = nm_platform_sysctl_get (path); + value = nm_platform_sysctl_get (NM_PLATFORM_GET, path); if (value && *value) g_hash_table_insert (priv->s390_options, g_strdup (item), g_strdup (value)); else @@ -247,7 +237,6 @@ out: g_object_unref (parent); if (dev) g_object_unref (dev); - g_object_unref (client); } static GObject* @@ -263,7 +252,7 @@ constructor (GType type, if (object) { #ifndef G_DISABLE_ASSERT int ifindex = nm_device_get_ifindex (NM_DEVICE (object)); - NMLinkType link_type = nm_platform_link_get_type (ifindex); + NMLinkType link_type = nm_platform_link_get_type (NM_PLATFORM_GET, ifindex); g_assert ( link_type == NM_LINK_TYPE_ETHERNET || link_type == NM_LINK_TYPE_VETH @@ -310,72 +299,12 @@ nm_device_ethernet_init (NMDeviceEthernet *self) priv->s390_options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); } -static void -update_permanent_hw_address (NMDevice *dev) -{ - NMDeviceEthernet *self = NM_DEVICE_ETHERNET (dev); - NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); - struct ifreq req; - struct ethtool_perm_addr *epaddr = NULL; - int fd, ret, errsv; - const char *mac; - - g_return_if_fail (priv->perm_hw_addr == NULL); - - fd = socket (PF_INET, SOCK_DGRAM, 0); - if (fd < 0) { - _LOGW (LOGD_HW, "couldn't open control socket."); - return; - } - - /* Get permanent MAC address */ - memset (&req, 0, sizeof (struct ifreq)); - strncpy (req.ifr_name, nm_device_get_iface (dev), IFNAMSIZ); - - epaddr = g_malloc0 (sizeof (struct ethtool_perm_addr) + ETH_ALEN); - epaddr->cmd = ETHTOOL_GPERMADDR; - epaddr->size = ETH_ALEN; - req.ifr_data = (void *) epaddr; - - errno = 0; - ret = ioctl (fd, SIOCETHTOOL, &req); - errsv = errno; - if ((ret < 0) || !nm_ethernet_address_is_valid (epaddr->data, ETH_ALEN)) { - _LOGD (LOGD_HW | LOGD_ETHER, "unable to read permanent MAC address (error %d)", errsv); - /* Fall back to current address */ - mac = nm_device_get_hw_address (dev); - if (mac) - nm_utils_hwaddr_aton (mac, epaddr->data, ETH_ALEN); - else - memset (epaddr->data, 0, ETH_ALEN); - } - - priv->perm_hw_addr = nm_utils_hwaddr_ntoa (epaddr->data, ETH_ALEN); - - g_free (epaddr); - close (fd); -} - -static void -update_initial_hw_address (NMDevice *dev) -{ - NMDeviceEthernet *self = NM_DEVICE_ETHERNET (dev); - NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); - - /* This sets initial MAC address from current MAC address. It should only - * be called from NMDevice constructor() to really get the initial address. - */ - priv->initial_hw_addr = g_strdup (nm_device_get_hw_address (dev)); - - _LOGD (LOGD_DEVICE | LOGD_ETHER, "read initial MAC address %s", priv->initial_hw_addr); -} - -static guint32 +static NMDeviceCapabilities get_generic_capabilities (NMDevice *device) { NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device); - if (nm_platform_link_supports_carrier_detect (nm_device_get_ifindex (device))) + if (nm_platform_link_supports_carrier_detect (NM_PLATFORM_GET, nm_device_get_ifindex (device))) return NM_DEVICE_CAP_CARRIER_DETECT; else { _LOGI (LOGD_HW, "driver '%s' does not support carrier detection.", @@ -421,7 +350,6 @@ static gboolean check_connection_compatible (NMDevice *device, NMConnection *connection) { NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device); - NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); NMSettingWired *s_wired; if (!NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->check_connection_compatible (device, connection)) @@ -438,7 +366,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection) return FALSE; if (s_wired) { - const char *mac; + const char *mac, *perm_hw_addr; gboolean try_mac = TRUE; const char * const *mac_blacklist; int i; @@ -446,21 +374,25 @@ check_connection_compatible (NMDevice *device, NMConnection *connection) if (!match_subchans (self, s_wired, &try_mac)) return FALSE; + perm_hw_addr = nm_device_get_permanent_hw_address (device); mac = nm_setting_wired_get_mac_address (s_wired); - if (try_mac && mac && !nm_utils_hwaddr_matches (mac, -1, priv->perm_hw_addr, -1)) - return FALSE; - - /* Check for MAC address blacklist */ - mac_blacklist = nm_setting_wired_get_mac_address_blacklist (s_wired); - for (i = 0; mac_blacklist[i]; i++) { - if (!nm_utils_hwaddr_valid (mac_blacklist[i], ETH_ALEN)) { - g_warn_if_reached (); + if (perm_hw_addr) { + if (try_mac && mac && !nm_utils_hwaddr_matches (mac, -1, perm_hw_addr, -1)) return FALSE; - } - if (nm_utils_hwaddr_matches (mac_blacklist[i], -1, priv->perm_hw_addr, -1)) - return FALSE; - } + /* Check for MAC address blacklist */ + mac_blacklist = nm_setting_wired_get_mac_address_blacklist (s_wired); + for (i = 0; mac_blacklist[i]; i++) { + if (!nm_utils_hwaddr_valid (mac_blacklist[i], ETH_ALEN)) { + g_warn_if_reached (); + return FALSE; + } + + if (nm_utils_hwaddr_matches (mac_blacklist[i], -1, perm_hw_addr, -1)) + return FALSE; + } + } else if (mac) + return FALSE; } return TRUE; @@ -1160,7 +1092,7 @@ dcb_state (NMDevice *device, gboolean timeout) g_return_if_fail (nm_device_get_state (device) == NM_DEVICE_STATE_CONFIG); - carrier = nm_platform_link_is_connected (nm_device_get_ifindex (device)); + carrier = nm_platform_link_is_connected (NM_PLATFORM_GET, nm_device_get_ifindex (device)); _LOGD (LOGD_DCB, "dcb_state() wait %d carrier %d timeout %d", priv->dcb_wait, carrier, timeout); switch (priv->dcb_wait) { @@ -1278,7 +1210,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *reason) s_dcb = (NMSettingDcb *) device_get_setting (device, NM_TYPE_SETTING_DCB); if (s_dcb) { /* lldpad really really wants the carrier to be up */ - if (nm_platform_link_is_connected (nm_device_get_ifindex (device))) { + if (nm_platform_link_is_connected (NM_PLATFORM_GET, nm_device_get_ifindex (device))) { if (!dcb_enable (device)) { *reason = NM_DEVICE_STATE_REASON_DCB_FCOE_FAILED; return NM_ACT_STAGE_RETURN_FAILURE; @@ -1391,8 +1323,8 @@ deactivate (NMDevice *device) NM_DEVICE_ETHERNET_GET_PRIVATE (device)->last_pppoe_time = nm_utils_get_monotonic_timestamp_s (); /* Reset MAC address back to initial address */ - if (priv->initial_hw_addr) - nm_device_set_hw_addr (device, priv->initial_hw_addr, "reset", LOGD_ETHER); + if (nm_device_get_initial_hw_address (device)) + nm_device_set_hw_addr (device, nm_device_get_initial_hw_address (device), "reset", LOGD_ETHER); } static gboolean @@ -1402,10 +1334,10 @@ complete_connection (NMDevice *device, const GSList *existing_connections, GError **error) { - NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device); NMSettingWired *s_wired; NMSettingPppoe *s_pppoe; const char *setting_mac; + const char *perm_hw_addr; s_pppoe = nm_connection_get_setting_pppoe (connection); @@ -1432,21 +1364,22 @@ complete_connection (NMDevice *device, nm_connection_add_setting (connection, NM_SETTING (s_wired)); } - 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, priv->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 { - if (!nm_utils_hwaddr_matches (priv->perm_hw_addr, -1, NULL, ETH_ALEN)) { + perm_hw_addr = nm_device_get_permanent_hw_address (device); + if (perm_hw_addr) { + 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, priv->perm_hw_addr, + NM_SETTING_WIRED_MAC_ADDRESS, perm_hw_addr, NULL); } } @@ -1463,7 +1396,7 @@ new_default_connection (NMDevice *self) const char *hw_address; char *defname, *uuid; - if (!nm_config_get_ethernet_can_auto_default (nm_config_get (), self)) + if (nm_config_get_no_auto_default_for_device (nm_config_get (), self)) return NULL; hw_address = nm_device_get_hw_address (self); @@ -1495,15 +1428,19 @@ new_default_connection (NMDevice *self) return connection; } -static gboolean +static NMMatchSpecMatchType spec_match_list (NMDevice *device, const GSList *specs) { + NMMatchSpecMatchType matched = NM_MATCH_SPEC_NO_MATCH, m; NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device); - if (priv->subchannels && nm_match_spec_s390_subchannels (specs, priv->subchannels)) - return TRUE; - - return NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->spec_match_list (device, specs); + if (priv->subchannels) + matched = nm_match_spec_s390_subchannels (specs, priv->subchannels); + if (matched != NM_MATCH_SPEC_NEG_MATCH) { + m = NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->spec_match_list (device, specs); + matched = MAX (matched, m); + } + return matched; } static void @@ -1511,6 +1448,7 @@ update_connection (NMDevice *device, NMConnection *connection) { NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device); NMSettingWired *s_wired = nm_connection_get_setting_wired (connection); + const char *perm_hw_addr = nm_device_get_permanent_hw_address (device); const char *mac = nm_device_get_hw_address (device); const char *mac_prop = NM_SETTING_WIRED_MAC_ADDRESS; GHashTableIter iter; @@ -1524,11 +1462,11 @@ update_connection (NMDevice *device, NMConnection *connection) /* If the device reports a permanent address, use that for the MAC address * and the current MAC, if different, is the cloned MAC. */ - if (!nm_utils_hwaddr_matches (priv->perm_hw_addr, -1, NULL, ETH_ALEN)) { - g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, priv->perm_hw_addr, NULL); + if (perm_hw_addr) { + g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, perm_hw_addr, NULL); mac_prop = NULL; - if (mac && !nm_utils_hwaddr_matches (priv->perm_hw_addr, -1, mac, -1)) + if (mac && !nm_utils_hwaddr_matches (perm_hw_addr, -1, mac, -1)) mac_prop = NM_SETTING_WIRED_CLONED_MAC_ADDRESS; } @@ -1562,37 +1500,10 @@ get_link_speed (NMDevice *device) { NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device); NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); - struct ifreq ifr; - struct ethtool_cmd edata = { - .cmd = ETHTOOL_GSET, - }; guint32 speed; - int fd; - fd = socket (PF_INET, SOCK_DGRAM, 0); - if (fd < 0) { - _LOGW (LOGD_HW | LOGD_ETHER, "couldn't open ethtool control socket."); + if (!nmp_utils_ethtool_get_link_speed (nm_device_get_iface (device), &speed)) return; - } - - memset (&ifr, 0, sizeof (struct ifreq)); - strncpy (ifr.ifr_name, nm_device_get_iface (device), IFNAMSIZ); - ifr.ifr_data = (char *) &edata; - - if (ioctl (fd, SIOCETHTOOL, &ifr) < 0) { - close (fd); - return; - } - close (fd); - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) - speed = edata.speed; -#else - speed = ethtool_cmd_speed (&edata); -#endif - if (speed == G_MAXUINT16 || speed == G_MAXUINT32) - speed = 0; - if (priv->speed == speed) return; @@ -1612,6 +1523,17 @@ carrier_changed (NMDevice *device, gboolean carrier) } static void +link_changed (NMDevice *device, NMPlatformLink *info) +{ + NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device); + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); + + NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->link_changed (device, info); + if (!priv->subchan1 && info->initialized) + _update_s390_subchannels (self); +} + +static void dispose (GObject *object) { NMDeviceEthernet *self = NM_DEVICE_ETHERNET (object); @@ -1634,8 +1556,6 @@ finalize (GObject *object) NMDeviceEthernet *self = NM_DEVICE_ETHERNET (object); NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); - g_free (priv->perm_hw_addr); - g_free (priv->initial_hw_addr); g_clear_object (&priv->supplicant.mgr); g_free (priv->subchan1); g_free (priv->subchan2); @@ -1656,7 +1576,7 @@ get_property (GObject *object, guint prop_id, switch (prop_id) { case PROP_PERM_HW_ADDRESS: - g_value_set_string (value, priv->perm_hw_addr); + g_value_set_string (value, nm_device_get_permanent_hw_address (NM_DEVICE (object))); break; case PROP_SPEED: g_value_set_uint (value, priv->speed); @@ -1696,8 +1616,6 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass) object_class->set_property = set_property; parent_class->get_generic_capabilities = get_generic_capabilities; - parent_class->update_permanent_hw_address = update_permanent_hw_address; - parent_class->update_initial_hw_address = update_initial_hw_address; parent_class->check_connection_compatible = check_connection_compatible; parent_class->complete_connection = complete_connection; parent_class->new_default_connection = new_default_connection; @@ -1710,6 +1628,7 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass) parent_class->spec_match_list = spec_match_list; parent_class->update_connection = update_connection; parent_class->carrier_changed = carrier_changed; + parent_class->link_changed = link_changed; parent_class->state_changed = device_state_changed; @@ -1739,19 +1658,18 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass) #define NM_ETHERNET_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_ETHERNET_FACTORY, NMEthernetFactory)) static NMDevice * -new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error) +new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error) { - if (plink->type == NM_LINK_TYPE_ETHERNET) { - return (NMDevice *) g_object_new (NM_TYPE_DEVICE_ETHERNET, - NM_DEVICE_PLATFORM_DEVICE, plink, - NM_DEVICE_TYPE_DESC, "Ethernet", - NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_ETHERNET, - NULL); - } - return NULL; + return (NMDevice *) g_object_new (NM_TYPE_DEVICE_ETHERNET, + NM_DEVICE_PLATFORM_DEVICE, plink, + NM_DEVICE_TYPE_DESC, "Ethernet", + NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_ETHERNET, + NULL); } -DEFINE_DEVICE_FACTORY_INTERNAL(ETHERNET, Ethernet, ethernet, \ - factory_iface->new_link = new_link; \ +NM_DEVICE_FACTORY_DEFINE_INTERNAL (ETHERNET, Ethernet, ethernet, + NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_ETHERNET) + NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_PPPOE_SETTING_NAME), + factory_iface->new_link = new_link; ) diff --git a/src/devices/nm-device-factory.c b/src/devices/nm-device-factory.c index abfb8d4a..13034808 100644 --- a/src/devices/nm-device-factory.c +++ b/src/devices/nm-device-factory.c @@ -19,8 +19,19 @@ */ #include "config.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <string.h> + +#include <gmodule.h> #include "nm-device-factory.h" +#include "nm-logging.h" +#include "nm-platform.h" + +const NMLinkType _nm_device_factory_no_default_links[] = { NM_LINK_TYPE_NONE }; +const char *_nm_device_factory_no_default_settings[] = { NULL }; enum { DEVICE_ADDED, @@ -29,130 +40,509 @@ enum { }; static guint signals[LAST_SIGNAL] = { 0 }; -static GSList *internal_types = NULL; +gboolean +nm_device_factory_emit_component_added (NMDeviceFactory *factory, GObject *component) +{ + gboolean consumed = FALSE; + + g_signal_emit (factory, signals[COMPONENT_ADDED], 0, component, &consumed); + return consumed; +} void -_nm_device_factory_internal_register_type (GType factory_type) +nm_device_factory_get_supported_types (NMDeviceFactory *factory, + const NMLinkType **out_link_types, + const char ***out_setting_types) { - g_return_if_fail (g_slist_find (internal_types, GUINT_TO_POINTER (factory_type)) == NULL); - internal_types = g_slist_prepend (internal_types, GUINT_TO_POINTER (factory_type)); + const NMLinkType *link_types_fallback; + const char **setting_types_fallback; + + g_return_if_fail (factory != NULL); + + if (!out_link_types) + out_link_types = &link_types_fallback; + if (!out_setting_types) + out_setting_types = &setting_types_fallback; + + NM_DEVICE_FACTORY_GET_INTERFACE (factory)->get_supported_types (factory, + out_link_types, + out_setting_types); } -const GSList * -nm_device_factory_get_internal_factory_types (void) +void +nm_device_factory_start (NMDeviceFactory *factory) { - return internal_types; + g_return_if_fail (factory != NULL); + + if (NM_DEVICE_FACTORY_GET_INTERFACE (factory)->start) + NM_DEVICE_FACTORY_GET_INTERFACE (factory)->start (factory); } -gboolean -nm_device_factory_emit_component_added (NMDeviceFactory *factory, GObject *component) +NMDevice * +nm_device_factory_new_link (NMDeviceFactory *factory, + NMPlatformLink *plink, + gboolean *out_ignore, + GError **error) { - gboolean consumed = FALSE; + NMDeviceFactory *interface; + const NMLinkType *link_types = NULL; + const char **setting_types = NULL; + int i; - g_signal_emit (factory, signals[COMPONENT_ADDED], 0, component, &consumed); - return consumed; + g_return_val_if_fail (factory != NULL, NULL); + g_return_val_if_fail (plink != NULL, NULL); + + /* Ensure the factory can create interfaces for this connection */ + nm_device_factory_get_supported_types (factory, &link_types, &setting_types); + for (i = 0; link_types[i] > NM_LINK_TYPE_UNKNOWN; i++) { + if (plink->type == link_types[i]) + break; + } + + if (link_types[i] == NM_LINK_TYPE_UNKNOWN) { + g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, + "Device factory %s does not support link type %s (%d)", + G_OBJECT_TYPE_NAME (factory), + plink->kind, plink->type); + return NULL; + } + + interface = NM_DEVICE_FACTORY_GET_INTERFACE (factory); + if (!interface->new_link) { + g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED, + "Device factory %s cannot manage new devices", + G_OBJECT_TYPE_NAME (factory)); + return NULL; + } + + return interface->new_link (factory, plink, out_ignore, error); } -static void -interface_init (gpointer g_iface) +NMDevice * +nm_device_factory_create_virtual_device_for_connection (NMDeviceFactory *factory, + NMConnection *connection, + NMDevice *parent, + GError **error) { - GType iface_type = G_TYPE_FROM_INTERFACE (g_iface); - static gboolean initialized = FALSE; + NMDeviceFactory *interface; + const char **setting_types = NULL; + gboolean found = FALSE; + int i; - if (G_LIKELY (initialized)) - return; + g_return_val_if_fail (factory, NULL); + g_return_val_if_fail (connection, NULL); + g_return_val_if_fail (!error || !*error, NULL); + + /* Ensure the factory can create interfaces for this connection */ + nm_device_factory_get_supported_types (factory, NULL, &setting_types); + for (i = 0; setting_types && setting_types[i]; i++) { + if (nm_connection_is_type (connection, setting_types[i])) { + found = TRUE; + break; + } + } + + if (!found) { + g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + "Device factory %s does not support connection type %s", + G_OBJECT_TYPE_NAME (factory), + nm_connection_get_connection_type (connection)); + return NULL; + } + + interface = NM_DEVICE_FACTORY_GET_INTERFACE (factory); + if (!interface->create_virtual_device_for_connection) { + g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED, + "Device factory %s cannot create virtual devices", + G_OBJECT_TYPE_NAME (factory)); + return NULL; + } + + return interface->create_virtual_device_for_connection (factory, connection, parent, error); +} + +const char * +nm_device_factory_get_connection_parent (NMDeviceFactory *factory, + NMConnection *connection) +{ + g_return_val_if_fail (factory != NULL, NULL); + g_return_val_if_fail (connection != NULL, NULL); + + if (!nm_connection_is_virtual (connection)) + return NULL; + + if (NM_DEVICE_FACTORY_GET_INTERFACE (factory)->get_connection_parent) + return NM_DEVICE_FACTORY_GET_INTERFACE (factory)->get_connection_parent (factory, connection); + return NULL; +} + +static char * +get_virtual_iface_name (NMDeviceFactory *factory, + NMConnection *connection, + const char *parent_iface) +{ + const char *iface; + + /* For any other virtual connection, NMSettingConnection:interface-name is + * the virtual device name. + */ + iface = nm_connection_get_interface_name (connection); + g_return_val_if_fail (iface != NULL, NULL); + return g_strdup (iface); +} + +char * +nm_device_factory_get_virtual_iface_name (NMDeviceFactory *factory, + NMConnection *connection, + const char *parent_iface) +{ + g_return_val_if_fail (factory != NULL, NULL); + g_return_val_if_fail (connection != NULL, NULL); + + if (!nm_connection_is_virtual (connection)) + return NULL; + + if (NM_DEVICE_FACTORY_GET_INTERFACE (factory)->get_virtual_iface_name) + return NM_DEVICE_FACTORY_GET_INTERFACE (factory)->get_virtual_iface_name (factory, connection, parent_iface); + return NULL; +} + +/*******************************************************************/ + +static void +default_init (NMDeviceFactory *factory_iface) +{ + factory_iface->get_virtual_iface_name = get_virtual_iface_name; /* Signals */ signals[DEVICE_ADDED] = g_signal_new (NM_DEVICE_FACTORY_DEVICE_ADDED, - iface_type, + NM_TYPE_DEVICE_FACTORY, G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (NMDeviceFactory, device_added), NULL, NULL, NULL, G_TYPE_NONE, 1, NM_TYPE_DEVICE); signals[COMPONENT_ADDED] = g_signal_new (NM_DEVICE_FACTORY_COMPONENT_ADDED, - iface_type, + NM_TYPE_DEVICE_FACTORY, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (NMDeviceFactory, component_added), g_signal_accumulator_true_handled, NULL, NULL, G_TYPE_BOOLEAN, 1, G_TYPE_OBJECT); - - initialized = TRUE; } GType nm_device_factory_get_type (void) { - static GType device_factory_type = 0; + static volatile gsize g_define_type_id__volatile = 0; + if (g_once_init_enter (&g_define_type_id__volatile)) { + GType g_define_type_id = + g_type_register_static_simple (G_TYPE_INTERFACE, + g_intern_static_string ("NMDeviceFactory"), + sizeof (NMDeviceFactory), + (GClassInitFunc) default_init, + 0, + (GInstanceInitFunc) NULL, + (GTypeFlags) 0); + g_type_interface_add_prerequisite (g_define_type_id, G_TYPE_OBJECT); + g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); + } + return g_define_type_id__volatile; +} + +/*******************************************************************/ + +static GSList *internal_types = NULL; +static GHashTable *factories_by_link = NULL; +static GHashTable *factories_by_setting = NULL; + +void +_nm_device_factory_internal_register_type (GType factory_type) +{ + g_return_if_fail (g_slist_find (internal_types, GUINT_TO_POINTER (factory_type)) == NULL); + internal_types = g_slist_prepend (internal_types, GUINT_TO_POINTER (factory_type)); +} + +static void __attribute__((destructor)) +_cleanup (void) +{ + g_clear_pointer (&internal_types, g_slist_free); + g_clear_pointer (&factories_by_link, g_hash_table_unref); + g_clear_pointer (&factories_by_setting, g_hash_table_unref); +} - if (!device_factory_type) { - const GTypeInfo device_factory_info = { - sizeof (NMDeviceFactory), /* class_size */ - interface_init, /* base_init */ - NULL, /* base_finalize */ - NULL, - NULL, /* class_finalize */ - NULL, /* class_data */ - 0, - 0, /* n_preallocs */ - NULL - }; +static NMDeviceFactory * +find_factory (const NMLinkType *needle_link_types, + const char **needle_setting_types) +{ + NMDeviceFactory *found; + guint i; - device_factory_type = g_type_register_static (G_TYPE_INTERFACE, - "NMDeviceFactory", - &device_factory_info, - 0); - g_type_interface_add_prerequisite (device_factory_type, G_TYPE_OBJECT); + g_return_val_if_fail (factories_by_link, NULL); + g_return_val_if_fail (factories_by_setting, NULL); + + /* NMLinkType search */ + for (i = 0; needle_link_types && needle_link_types[i] > NM_LINK_TYPE_UNKNOWN; i++) { + found = g_hash_table_lookup (factories_by_link, GUINT_TO_POINTER (needle_link_types[i])); + if (found) + return found; + } + + /* NMSetting name search */ + for (i = 0; needle_setting_types && needle_setting_types[i]; i++) { + found = g_hash_table_lookup (factories_by_setting, needle_setting_types[i]); + if (found) + return found; } - return device_factory_type; + return NULL; } -NMDeviceType -nm_device_factory_get_device_type (NMDeviceFactory *factory) +NMDeviceFactory * +nm_device_factory_manager_find_factory_for_link_type (NMLinkType link_type) { - g_return_val_if_fail (factory != NULL, NM_DEVICE_TYPE_UNKNOWN); + const NMLinkType ltypes[2] = { link_type, NM_LINK_TYPE_NONE }; - return NM_DEVICE_FACTORY_GET_INTERFACE (factory)->get_device_type (factory); + if (link_type == NM_LINK_TYPE_UNKNOWN) + return NULL; + g_return_val_if_fail (link_type > NM_LINK_TYPE_UNKNOWN, NULL); + return find_factory (ltypes, NULL); +} + +NMDeviceFactory * +nm_device_factory_manager_find_factory_for_connection (NMConnection *connection) +{ + const char *stypes[2] = { nm_connection_get_connection_type (connection), NULL }; + + g_assert (stypes[0]); + return find_factory (NULL, stypes); } void -nm_device_factory_start (NMDeviceFactory *factory) +nm_device_factory_manager_for_each_factory (NMDeviceFactoryManagerFactoryFunc callback, + gpointer user_data) { - g_return_if_fail (factory != NULL); + GHashTableIter iter; + NMDeviceFactory *factory; + GSList *list_iter, *list = NULL; - if (NM_DEVICE_FACTORY_GET_INTERFACE (factory)->start) - NM_DEVICE_FACTORY_GET_INTERFACE (factory)->start (factory); + g_return_if_fail (factories_by_link); + g_return_if_fail (factories_by_setting); + + g_hash_table_iter_init (&iter, factories_by_link); + while (g_hash_table_iter_next (&iter, NULL, (gpointer) &factory)) { + if (!g_slist_find (list, factory)) + list = g_slist_prepend (list, factory); + } + + g_hash_table_iter_init (&iter, factories_by_setting); + while (g_hash_table_iter_next (&iter, NULL, (gpointer) &factory)) { + if (!g_slist_find (list, factory)) + list = g_slist_prepend (list, factory); + } + + for (list_iter = list; list_iter; list_iter = list_iter->next) + callback (list_iter->data, user_data); + + g_slist_free (list); } -NMDevice * -nm_device_factory_new_link (NMDeviceFactory *factory, - NMPlatformLink *plink, - GError **error) +#define PLUGIN_PREFIX "libnm-device-plugin-" +#define PLUGIN_PATH_TAG "NMManager-plugin-path" + +struct read_device_factory_paths_data { + char *path; + struct stat st; +}; + +static gint +read_device_factory_paths_sort_fcn (gconstpointer a, gconstpointer b) { - g_return_val_if_fail (factory != NULL, NULL); - g_return_val_if_fail (plink != NULL, NULL); + const struct read_device_factory_paths_data *da = a; + const struct read_device_factory_paths_data *db = b; + time_t ta, tb; - if (NM_DEVICE_FACTORY_GET_INTERFACE (factory)->new_link) - return NM_DEVICE_FACTORY_GET_INTERFACE (factory)->new_link (factory, plink, error); - return NULL; + ta = MAX (da->st.st_mtime, da->st.st_ctime); + tb = MAX (db->st.st_mtime, db->st.st_ctime); + + if (ta < tb) + return 1; + if (ta > tb) + return -1; + return 0; } -NMDevice * -nm_device_factory_create_virtual_device_for_connection (NMDeviceFactory *factory, - NMConnection *connection, - NMDevice *parent, - GError **error) +static char** +read_device_factory_paths (void) { - NMDeviceFactory *interface; + GDir *dir; + GError *error = NULL; + const char *item; + GArray *paths; + char **result; + guint i; - g_return_val_if_fail (factory, NULL); - g_return_val_if_fail (connection, NULL); - g_return_val_if_fail (!error || !*error, NULL); + dir = g_dir_open (NMPLUGINDIR, 0, &error); + if (!dir) { + nm_log_warn (LOGD_HW, "device plugin: failed to open directory %s: %s", + NMPLUGINDIR, + (error && error->message) ? error->message : "(unknown)"); + g_clear_error (&error); + return NULL; + } - interface = NM_DEVICE_FACTORY_GET_INTERFACE (factory); - if (interface->create_virtual_device_for_connection) - return interface->create_virtual_device_for_connection (factory, connection, parent, error); - return NULL; + paths = g_array_new (FALSE, FALSE, sizeof (struct read_device_factory_paths_data)); + + while ((item = g_dir_read_name (dir))) { + int errsv; + struct read_device_factory_paths_data data; + + if (!g_str_has_prefix (item, PLUGIN_PREFIX)) + continue; + if (g_str_has_suffix (item, ".la")) + continue; + + data.path = g_build_filename (NMPLUGINDIR, item, NULL); + + if (stat (data.path, &data.st) != 0) { + errsv = errno; + nm_log_warn (LOGD_HW, "device plugin: skip invalid file %s (error during stat: %s)", data.path, strerror (errsv)); + goto NEXT; + } + if (!S_ISREG (data.st.st_mode)) + goto NEXT; + if (data.st.st_uid != 0) { + nm_log_warn (LOGD_HW, "device plugin: skip invalid file %s (file must be owned by root)", data.path); + goto NEXT; + } + if (data.st.st_mode & (S_IWGRP | S_IWOTH | S_ISUID)) { + nm_log_warn (LOGD_HW, "device plugin: skip invalid file %s (invalid file permissions)", data.path); + goto NEXT; + } + + g_array_append_val (paths, data); + continue; +NEXT: + g_free (data.path); + } + g_dir_close (dir); + + /* sort filenames by modification time. */ + g_array_sort (paths, read_device_factory_paths_sort_fcn); + + result = g_new (char *, paths->len + 1); + for (i = 0; i < paths->len; i++) + result[i] = g_array_index (paths, struct read_device_factory_paths_data, i).path; + result[i] = NULL; + + g_array_free (paths, TRUE); + return result; +} + +static gboolean +_add_factory (NMDeviceFactory *factory, + gboolean check_duplicates, + const char *path, + NMDeviceFactoryManagerFactoryFunc callback, + gpointer user_data) +{ + NMDeviceFactory *found = NULL; + const NMLinkType *link_types = NULL; + const char **setting_types = NULL; + int i; + + g_return_val_if_fail (factories_by_link, FALSE); + g_return_val_if_fail (factories_by_setting, FALSE); + + nm_device_factory_get_supported_types (factory, &link_types, &setting_types); + if (check_duplicates) { + found = find_factory (link_types, setting_types); + if (found) { + nm_log_warn (LOGD_HW, "Loading device plugin failed: multiple plugins " + "for same type (using '%s' instead of '%s')", + (char *) g_object_get_data (G_OBJECT (found), PLUGIN_PATH_TAG), + path); + return FALSE; + } + } + + g_object_set_data_full (G_OBJECT (factory), PLUGIN_PATH_TAG, g_strdup (path), g_free); + for (i = 0; link_types && link_types[i] > NM_LINK_TYPE_UNKNOWN; i++) + g_hash_table_insert (factories_by_link, GUINT_TO_POINTER (link_types[i]), g_object_ref (factory)); + for (i = 0; setting_types && setting_types[i]; i++) + g_hash_table_insert (factories_by_setting, (char *) setting_types[i], g_object_ref (factory)); + + callback (factory, user_data); + + nm_log_info (LOGD_HW, "Loaded device plugin: %s (%s)", G_OBJECT_TYPE_NAME (factory), path); + return TRUE; +} + +void +nm_device_factory_manager_load_factories (NMDeviceFactoryManagerFactoryFunc callback, + gpointer user_data) +{ + NMDeviceFactory *factory; + const GSList *iter; + GError *error = NULL; + char **path, **paths; + + g_return_if_fail (factories_by_link == NULL); + g_return_if_fail (factories_by_setting == NULL); + + factories_by_link = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref); + factories_by_setting = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref); + + /* Register internal factories first */ + for (iter = internal_types; iter; iter = iter->next) { + GType ftype = (GType) GPOINTER_TO_SIZE (iter->data); + + factory = (NMDeviceFactory *) g_object_new (ftype, NULL); + g_assert (factory); + _add_factory (factory, FALSE, "internal", callback, user_data); + } + + paths = read_device_factory_paths (); + if (!paths) + return; + + for (path = paths; *path; path++) { + GModule *plugin; + NMDeviceFactoryCreateFunc create_func; + const char *item; + + item = strrchr (*path, '/'); + g_assert (item); + + plugin = g_module_open (*path, G_MODULE_BIND_LOCAL); + + if (!plugin) { + nm_log_warn (LOGD_HW, "(%s): failed to load plugin: %s", item, g_module_error ()); + continue; + } + + if (!g_module_symbol (plugin, "nm_device_factory_create", (gpointer) &create_func)) { + nm_log_warn (LOGD_HW, "(%s): failed to find device factory creator: %s", item, g_module_error ()); + g_module_close (plugin); + continue; + } + + factory = create_func (&error); + if (!factory) { + nm_log_warn (LOGD_HW, "(%s): failed to initialize device factory: %s", + item, error ? error->message : "unknown"); + g_clear_error (&error); + g_module_close (plugin); + continue; + } + g_clear_error (&error); + + if (_add_factory (factory, TRUE, g_module_name (plugin), callback, user_data)) + g_module_make_resident (plugin); + else + g_module_close (plugin); + + g_object_unref (factory); + } + g_strfreev (paths); } diff --git a/src/devices/nm-device-factory.h b/src/devices/nm-device-factory.h index ce672b8b..34c056d2 100644 --- a/src/devices/nm-device-factory.h +++ b/src/devices/nm-device-factory.h @@ -66,14 +66,19 @@ struct _NMDeviceFactory { GTypeInterface g_iface; /** - * get_device_type: + * get_supported_types: * @factory: the #NMDeviceFactory + * @out_link_types: on return, a %NM_LINK_TYPE_NONE terminated + * list of #NMLinkType that the plugin supports + * @out_setting_types: on return, a %NULL terminated list of + * base-type #NMSetting names that the plugin can create devices for * - * This function MUST be implemented. - * - * Returns: the #NMDeviceType that this plugin creates + * Returns the #NMLinkType and #NMSetting names that this plugin + * supports. This function MUST be implemented. */ - NMDeviceType (*get_device_type) (NMDeviceFactory *factory); + void (*get_supported_types) (NMDeviceFactory *factory, + const NMLinkType **out_link_types, + const char ***out_setting_types); /** * start: @@ -87,20 +92,27 @@ struct _NMDeviceFactory { /** * new_link: * @factory: the #NMDeviceFactory - * @link: the new link + * @plink: the new link + * @out_ignore: on return, %TRUE if the link should be ignored * @error: error if the link could be claimed but an error occurred * * The NetworkManager core was notified of a new link which the plugin * may want to claim and create a #NMDevice subclass for. If the link - * represents a device the factory is capable of claiming, but the device - * could not be created, %NULL should be returned and @error should be set. - * %NULL should always be returned and @error should never be set if the - * factory cannot create devices for the type which @link represents. + * represents a device which the factory does not support, or the link + * is supported but the device could not be created, %NULL should be + * returned and @error should be set. + * + * If the plugin cannot create a #NMDevice for the link and wants the + * core to ignore it, set @out_ignore to %TRUE and return no error. + * + * @plink is guaranteed to be one of the types the factory returns in + * get_supported_types(). * * Returns: the #NMDevice if the link was claimed and created, %NULL if not */ NMDevice * (*new_link) (NMDeviceFactory *factory, NMPlatformLink *plink, + gboolean *out_ignore, GError **error); /** @@ -121,6 +133,34 @@ struct _NMDeviceFactory { NMDevice *parent, GError **error); + /** + * get_connection_parent: + * @factory: the #NMDeviceFactory + * @connection: the #NMConnection to return the parent name for, if supported + * + * Given a connection, returns the a parent interface name, parent connection + * UUID, or parent device hardware address for @connection. + * + * Returns: the parent interface name, parent connection UUID, parent + * device hardware address, or %NULL + */ + const char * (*get_connection_parent) (NMDeviceFactory *factory, + NMConnection *connection); + + /** + * get_virtual_iface_name: + * @factory: the #NMDeviceFactory + * @connection: the #NMConnection to return the virtual interface name for + * @parent_iface: parent interface name + * + * Given a connection, returns the interface name that a device activating + * that connection would have. + * + * Returns: the interface name, or %NULL + */ + char * (*get_virtual_iface_name) (NMDeviceFactory *factory, + NMConnection *connection, + const char *parent_iface); /* Signals */ @@ -151,12 +191,22 @@ struct _NMDeviceFactory { GType nm_device_factory_get_type (void); -NMDeviceType nm_device_factory_get_device_type (NMDeviceFactory *factory); +void nm_device_factory_get_supported_types (NMDeviceFactory *factory, + const NMLinkType **out_link_types, + const char ***out_setting_types); + +const char *nm_device_factory_get_connection_parent (NMDeviceFactory *factory, + NMConnection *connection); + +char * nm_device_factory_get_virtual_iface_name (NMDeviceFactory *factory, + NMConnection *connection, + const char *parent_iface); void nm_device_factory_start (NMDeviceFactory *factory); NMDevice * nm_device_factory_new_link (NMDeviceFactory *factory, NMPlatformLink *plink, + gboolean *out_ignore, GError **error); NMDevice * nm_device_factory_create_virtual_device_for_connection (NMDeviceFactory *factory, @@ -168,15 +218,33 @@ NMDevice * nm_device_factory_create_virtual_device_for_connection (NMDeviceFacto gboolean nm_device_factory_emit_component_added (NMDeviceFactory *factory, GObject *component); +#define NM_DEVICE_FACTORY_DECLARE_LINK_TYPES(...) \ + { static const NMLinkType _df_links[] = { __VA_ARGS__, NM_LINK_TYPE_NONE }; *out_link_types = _df_links; } +#define NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES(...) \ + { static const char *_df_settings[] = { __VA_ARGS__, NULL }; *out_setting_types = _df_settings; } + +extern const NMLinkType _nm_device_factory_no_default_links[]; +extern const char *_nm_device_factory_no_default_settings[]; + +#define NM_DEVICE_FACTORY_DECLARE_TYPES(...) \ + static void \ + get_supported_types (NMDeviceFactory *factory, \ + const NMLinkType **out_link_types, \ + const char ***out_setting_types) \ + { \ + *out_link_types = _nm_device_factory_no_default_links; \ + *out_setting_types = _nm_device_factory_no_default_settings; \ + \ + { __VA_ARGS__; } \ + } \ + \ + /************************************************************************** * INTERNAL DEVICE FACTORY FUNCTIONS - devices provided by plugins should * not use these functions. **************************************************************************/ -#define DEFINE_DEVICE_FACTORY_INTERNAL(upper, mixed, lower, dfi_code) \ - DEFINE_DEVICE_FACTORY_INTERNAL_WITH_DEVTYPE(upper, mixed, lower, upper, dfi_code) - -#define DEFINE_DEVICE_FACTORY_INTERNAL_WITH_DEVTYPE(upper, mixed, lower, devtype, dfi_code) \ +#define NM_DEVICE_FACTORY_DEFINE_INTERNAL(upper, mixed, lower, st_code, dfi_code) \ typedef GObject NM##mixed##Factory; \ typedef GObjectClass NM##mixed##FactoryClass; \ \ @@ -198,16 +266,12 @@ gboolean nm_device_factory_emit_component_added (NMDeviceFactory *factory, g_type_ensure (NM_TYPE_##upper##_FACTORY); \ } \ \ - static NMDeviceType \ - get_device_type (NMDeviceFactory *factory) \ - { \ - return NM_DEVICE_TYPE_##devtype; \ - } \ + NM_DEVICE_FACTORY_DECLARE_TYPES(st_code) \ \ static void \ device_factory_interface_init (NMDeviceFactory *factory_iface) \ { \ - factory_iface->get_device_type = get_device_type; \ + factory_iface->get_supported_types = get_supported_types; \ dfi_code \ } \ \ @@ -222,6 +286,22 @@ gboolean nm_device_factory_emit_component_added (NMDeviceFactory *factory, } void _nm_device_factory_internal_register_type (GType factory_type); -const GSList *nm_device_factory_get_internal_factory_types (void); + +/************************************************************************** + * PRIVATE FACTORY FUNCTIONS - for factory consumers (eg, NMManager). + **************************************************************************/ + +typedef void (*NMDeviceFactoryManagerFactoryFunc) (NMDeviceFactory *factory, + gpointer user_data); + +void nm_device_factory_manager_load_factories (NMDeviceFactoryManagerFactoryFunc callback, + gpointer user_data); + +NMDeviceFactory * nm_device_factory_manager_find_factory_for_link_type (NMLinkType link_type); + +NMDeviceFactory * nm_device_factory_manager_find_factory_for_connection (NMConnection *connection); + +void nm_device_factory_manager_for_each_factory (NMDeviceFactoryManagerFactoryFunc callback, + gpointer user_data); #endif /* __NETWORKMANAGER_DEVICE_FACTORY_H__ */ diff --git a/src/devices/nm-device-generic.c b/src/devices/nm-device-generic.c index f103ef83..5841d834 100644 --- a/src/devices/nm-device-generic.c +++ b/src/devices/nm-device-generic.c @@ -45,15 +45,23 @@ enum { LAST_PROP }; -static guint32 +static NMDeviceCapabilities get_generic_capabilities (NMDevice *dev) { - if (nm_platform_link_supports_carrier_detect (nm_device_get_ifindex (dev))) + if (nm_platform_link_supports_carrier_detect (NM_PLATFORM_GET, nm_device_get_ifindex (dev))) return NM_DEVICE_CAP_CARRIER_DETECT; else return NM_DEVICE_CAP_NONE; } +static const char * +get_type_description (NMDevice *device) +{ + if (NM_DEVICE_GENERIC_GET_PRIVATE (device)->type_description) + return NM_DEVICE_GENERIC_GET_PRIVATE (device)->type_description; + return NM_DEVICE_CLASS (nm_device_generic_parent_class)->get_type_description (device); +} + static gboolean check_connection_compatible (NMDevice *device, NMConnection *connection) { @@ -117,7 +125,7 @@ constructed (GObject *object) int ifindex = nm_device_get_ip_ifindex (NM_DEVICE (self)); if (ifindex != 0) - priv->type_description = g_strdup (nm_platform_link_get_type_name (ifindex)); + priv->type_description = g_strdup (nm_platform_link_get_type_name (NM_PLATFORM_GET, ifindex)); } G_OBJECT_CLASS (nm_device_generic_parent_class)->constructed (object); @@ -184,6 +192,7 @@ nm_device_generic_class_init (NMDeviceGenericClass *klass) object_class->set_property = set_property; parent_class->get_generic_capabilities = get_generic_capabilities; + parent_class->get_type_description = get_type_description; parent_class->check_connection_compatible = check_connection_compatible; parent_class->update_connection = update_connection; diff --git a/src/devices/nm-device-gre.c b/src/devices/nm-device-gre.c index 9c25b398..b3510d2e 100644 --- a/src/devices/nm-device-gre.c +++ b/src/devices/nm-device-gre.c @@ -71,7 +71,7 @@ update_properties (NMDevice *device) GObject *object = G_OBJECT (device); NMPlatformGreProperties props; - if (!nm_platform_gre_get_properties (nm_device_get_ifindex (device), &props)) { + if (!nm_platform_gre_get_properties (NM_PLATFORM_GET, nm_device_get_ifindex (device), &props)) { _LOGW (LOGD_HW, "could not read gre properties"); return; } @@ -267,19 +267,17 @@ nm_device_gre_class_init (NMDeviceGreClass *klass) #define NM_GRE_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_GRE_FACTORY, NMGreFactory)) static NMDevice * -new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error) +new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error) { - if (plink->type == NM_LINK_TYPE_GRE || plink->type == NM_LINK_TYPE_GRETAP) { - return (NMDevice *) g_object_new (NM_TYPE_DEVICE_GRE, - NM_DEVICE_PLATFORM_DEVICE, plink, - NM_DEVICE_TYPE_DESC, "Gre", - NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_GENERIC, - NULL); - } - return NULL; + return (NMDevice *) g_object_new (NM_TYPE_DEVICE_GRE, + NM_DEVICE_PLATFORM_DEVICE, plink, + NM_DEVICE_TYPE_DESC, "Gre", + NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_GENERIC, + NULL); } -DEFINE_DEVICE_FACTORY_INTERNAL_WITH_DEVTYPE(GRE, Gre, gre, ETHERNET, \ - factory_iface->new_link = new_link; \ +NM_DEVICE_FACTORY_DEFINE_INTERNAL (GRE, Gre, gre, + NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_GRE, NM_LINK_TYPE_GRETAP), + factory_iface->new_link = new_link; ) diff --git a/src/devices/nm-device-infiniband.c b/src/devices/nm-device-infiniband.c index e1af8995..2e2483c0 100644 --- a/src/devices/nm-device-infiniband.c +++ b/src/devices/nm-device-infiniband.c @@ -44,12 +44,15 @@ G_DEFINE_TYPE (NMDeviceInfiniband, nm_device_infiniband, NM_TYPE_DEVICE) #define NM_DEVICE_INFINIBAND_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_INFINIBAND, NMDeviceInfinibandPrivate)) +#define NM_DEVICE_INFINIBAND_IS_PARTITION "is-partition" + typedef struct { - int dummy; + gboolean is_partition; } NMDeviceInfinibandPrivate; enum { PROP_0, + PROP_IS_PARTITION, LAST_PROP }; @@ -59,10 +62,15 @@ nm_device_infiniband_init (NMDeviceInfiniband * self) { } -static guint32 +static NMDeviceCapabilities get_generic_capabilities (NMDevice *dev) { - return NM_DEVICE_CAP_CARRIER_DETECT; + guint32 caps = NM_DEVICE_CAP_CARRIER_DETECT; + + if (NM_DEVICE_INFINIBAND_GET_PRIVATE (dev)->is_partition) + caps |= NM_DEVICE_CAP_IS_SOFTWARE; + + return caps; } static NMActStageReturn @@ -105,7 +113,7 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) } } - ok = nm_platform_sysctl_set (mode_path, transport_mode); + ok = nm_platform_sysctl_set (NM_PLATFORM_GET, mode_path, transport_mode); g_free (mode_path); if (!ok) { @@ -213,8 +221,8 @@ update_connection (NMDevice *device, NMConnection *connection) { NMSettingInfiniband *s_infiniband = nm_connection_get_setting_infiniband (connection); const char *mac = nm_device_get_hw_address (device); - char *mode_path, *contents = NULL; const char *transport_mode = "datagram"; + int ifindex; if (!s_infiniband) { s_infiniband = (NMSettingInfiniband *) nm_setting_infiniband_new (); @@ -224,16 +232,10 @@ update_connection (NMDevice *device, NMConnection *connection) if (mac && !nm_utils_hwaddr_matches (mac, -1, NULL, INFINIBAND_ALEN)) g_object_set (s_infiniband, NM_SETTING_INFINIBAND_MAC_ADDRESS, mac, NULL); - mode_path = g_strdup_printf ("/sys/class/net/%s/mode", - ASSERT_VALID_PATH_COMPONENT (nm_device_get_iface (device))); - contents = nm_platform_sysctl_get (mode_path); - g_free (mode_path); - if (contents) { - if (strstr (contents, "datagram")) + ifindex = nm_device_get_ifindex (device); + if (ifindex > 0) { + if (!nm_platform_infiniband_get_info (NM_PLATFORM_GET, ifindex, NULL, NULL, &transport_mode)) transport_mode = "datagram"; - else if (strstr (contents, "connected")) - transport_mode = "connected"; - g_free (contents); } g_object_set (G_OBJECT (s_infiniband), NM_SETTING_INFINIBAND_TRANSPORT_MODE, transport_mode, NULL); } @@ -243,6 +245,9 @@ get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { switch (prop_id) { + case PROP_IS_PARTITION: + g_value_set_boolean (value, NM_DEVICE_INFINIBAND_GET_PRIVATE (object)->is_partition); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -254,6 +259,9 @@ set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { switch (prop_id) { + case PROP_IS_PARTITION: + NM_DEVICE_INFINIBAND_GET_PRIVATE (object)->is_partition = g_value_get_boolean (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -281,6 +289,12 @@ nm_device_infiniband_class_init (NMDeviceInfinibandClass *klass) parent_class->ip4_config_pre_commit = ip4_config_pre_commit; /* properties */ + g_object_class_install_property + (object_class, PROP_IS_PARTITION, + g_param_spec_boolean (NM_DEVICE_INFINIBAND_IS_PARTITION, "", "", + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); nm_dbus_manager_register_exported_type (nm_dbus_manager_get (), G_TYPE_FROM_CLASS (klass), @@ -293,16 +307,14 @@ nm_device_infiniband_class_init (NMDeviceInfinibandClass *klass) #define NM_INFINIBAND_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_INFINIBAND_FACTORY, NMInfinibandFactory)) static NMDevice * -new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error) +new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error) { - if (plink->type == NM_LINK_TYPE_INFINIBAND) { - return (NMDevice *) g_object_new (NM_TYPE_DEVICE_INFINIBAND, - NM_DEVICE_PLATFORM_DEVICE, plink, - NM_DEVICE_TYPE_DESC, "InfiniBand", - NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_INFINIBAND, - NULL); - } - return NULL; + return (NMDevice *) g_object_new (NM_TYPE_DEVICE_INFINIBAND, + NM_DEVICE_PLATFORM_DEVICE, plink, + NM_DEVICE_TYPE_DESC, "InfiniBand", + NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_INFINIBAND, + NM_DEVICE_INFINIBAND_IS_PARTITION, (plink->parent > 0), + NULL); } static NMDevice * @@ -314,25 +326,30 @@ create_virtual_device_for_connection (NMDeviceFactory *factory, NMSettingInfiniband *s_infiniband; int p_key, parent_ifindex; const char *iface; + NMPlatformError plerr; - if (!nm_connection_is_type (connection, NM_SETTING_INFINIBAND_SETTING_NAME)) + if (!NM_IS_DEVICE_INFINIBAND (parent)) { + g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, + "Parent interface %s must be an InfiniBand interface", + nm_device_get_iface (parent)); return NULL; - - g_return_val_if_fail (NM_IS_DEVICE_INFINIBAND (parent), NULL); + } s_infiniband = nm_connection_get_setting_infiniband (connection); iface = nm_setting_infiniband_get_virtual_interface_name (s_infiniband); - g_return_val_if_fail (iface != NULL, NULL); + g_assert (iface); parent_ifindex = nm_device_get_ifindex (parent); p_key = nm_setting_infiniband_get_p_key (s_infiniband); - if ( !nm_platform_infiniband_partition_add (parent_ifindex, p_key) - && nm_platform_get_error () != NM_PLATFORM_ERROR_EXISTS) { - nm_log_warn (LOGD_DEVICE | LOGD_INFINIBAND, "(%s): failed to add InfiniBand P_Key interface for '%s': %s", - iface, nm_connection_get_id (connection), - nm_platform_get_error_msg ()); + plerr = nm_platform_infiniband_partition_add (NM_PLATFORM_GET, parent_ifindex, p_key, NULL); + if (plerr != NM_PLATFORM_ERROR_SUCCESS && plerr != NM_PLATFORM_ERROR_EXISTS) { + g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, + "Failed to create InfiniBand P_Key interface '%s' for '%s': %s", + iface, + nm_connection_get_id (connection), + nm_platform_error_to_string (plerr)); return NULL; } @@ -341,11 +358,49 @@ create_virtual_device_for_connection (NMDeviceFactory *factory, NM_DEVICE_DRIVER, nm_device_get_driver (parent), NM_DEVICE_TYPE_DESC, "InfiniBand", NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_INFINIBAND, + NM_DEVICE_INFINIBAND_IS_PARTITION, TRUE, NULL); } -DEFINE_DEVICE_FACTORY_INTERNAL(INFINIBAND, Infiniband, infiniband, \ - factory_iface->new_link = new_link; \ +static const char * +get_connection_parent (NMDeviceFactory *factory, NMConnection *connection) +{ + NMSettingInfiniband *s_infiniband; + + g_return_val_if_fail (nm_connection_is_type (connection, NM_SETTING_INFINIBAND_SETTING_NAME), NULL); + + s_infiniband = nm_connection_get_setting_infiniband (connection); + g_assert (s_infiniband); + + return nm_setting_infiniband_get_parent (s_infiniband); +} + +static char * +get_virtual_iface_name (NMDeviceFactory *factory, + NMConnection *connection, + const char *parent_iface) +{ + NMSettingInfiniband *s_infiniband; + + g_return_val_if_fail (nm_connection_is_type (connection, NM_SETTING_INFINIBAND_SETTING_NAME), NULL); + + s_infiniband = nm_connection_get_setting_infiniband (connection); + g_assert (s_infiniband); + + if (!parent_iface) + return NULL; + + g_return_val_if_fail (g_strcmp0 (parent_iface, nm_setting_infiniband_get_parent (s_infiniband)) == 0, NULL); + + return g_strdup (nm_setting_infiniband_get_virtual_interface_name (s_infiniband)); +} + +NM_DEVICE_FACTORY_DEFINE_INTERNAL (INFINIBAND, Infiniband, infiniband, + NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_INFINIBAND) + NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_INFINIBAND_SETTING_NAME), + factory_iface->new_link = new_link; factory_iface->create_virtual_device_for_connection = create_virtual_device_for_connection; + factory_iface->get_connection_parent = get_connection_parent; + factory_iface->get_virtual_iface_name = get_virtual_iface_name; ) diff --git a/src/devices/nm-device-macvlan.c b/src/devices/nm-device-macvlan.c index bcd38617..0bfe3fe9 100644 --- a/src/devices/nm-device-macvlan.c +++ b/src/devices/nm-device-macvlan.c @@ -64,7 +64,7 @@ update_properties (NMDevice *device) GObject *object = G_OBJECT (device); NMPlatformMacvlanProperties props; - if (!nm_platform_macvlan_get_properties (nm_device_get_ifindex (device), &props)) { + if (!nm_platform_macvlan_get_properties (NM_PLATFORM_GET, nm_device_get_ifindex (device), &props)) { _LOGW (LOGD_HW, "could not read macvlan properties"); return; } @@ -175,19 +175,17 @@ nm_device_macvlan_class_init (NMDeviceMacvlanClass *klass) #define NM_MACVLAN_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_MACVLAN_FACTORY, NMMacvlanFactory)) static NMDevice * -new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error) +new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error) { - if (plink->type == NM_LINK_TYPE_MACVLAN || plink->type == NM_LINK_TYPE_MACVTAP) { - return (NMDevice *) g_object_new (NM_TYPE_DEVICE_MACVLAN, - NM_DEVICE_PLATFORM_DEVICE, plink, - NM_DEVICE_TYPE_DESC, "Macvlan", - NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_GENERIC, - NULL); - } - return NULL; + return (NMDevice *) g_object_new (NM_TYPE_DEVICE_MACVLAN, + NM_DEVICE_PLATFORM_DEVICE, plink, + NM_DEVICE_TYPE_DESC, "Macvlan", + NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_GENERIC, + NULL); } -DEFINE_DEVICE_FACTORY_INTERNAL_WITH_DEVTYPE(MACVLAN, Macvlan, macvlan, ETHERNET, \ - factory_iface->new_link = new_link; \ +NM_DEVICE_FACTORY_DEFINE_INTERNAL (MACVLAN, Macvlan, macvlan, + NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_MACVLAN, NM_LINK_TYPE_MACVTAP), + factory_iface->new_link = new_link; ) diff --git a/src/devices/nm-device-private.h b/src/devices/nm-device-private.h index 1f781bbc..f34969ac 100644 --- a/src/devices/nm-device-private.h +++ b/src/devices/nm-device-private.h @@ -97,6 +97,9 @@ void nm_device_set_carrier (NMDevice *self, gboolean carrier); void nm_device_emit_recheck_auto_activate (NMDevice *device); void nm_device_queue_recheck_assume (NMDevice *device); +void nm_device_queue_recheck_available (NMDevice *device, + NMDeviceStateReason available_reason, + NMDeviceStateReason unavailable_reason); void nm_device_set_wwan_ip4_config (NMDevice *device, NMIP4Config *config); void nm_device_set_wwan_ip6_config (NMDevice *device, NMIP6Config *config); diff --git a/src/devices/nm-device-tun.c b/src/devices/nm-device-tun.c index 9a3fab5e..5aaff8b1 100644 --- a/src/devices/nm-device-tun.c +++ b/src/devices/nm-device-tun.c @@ -65,7 +65,7 @@ reload_tun_properties (NMDeviceTun *self) GObject *object = G_OBJECT (self); NMPlatformTunProperties props; - if (!nm_platform_tun_get_properties (nm_device_get_ifindex (NM_DEVICE (self)), &props)) { + if (!nm_platform_tun_get_properties (NM_PLATFORM_GET, nm_device_get_ifindex (NM_DEVICE (self)), &props)) { _LOGD (LOGD_HW, "could not read tun properties"); return; } @@ -123,7 +123,7 @@ constructed (GObject *object) gboolean properties_read; NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE (self); - properties_read = nm_platform_tun_get_properties (nm_device_get_ifindex (NM_DEVICE (self)), &priv->props); + properties_read = nm_platform_tun_get_properties (NM_PLATFORM_GET, nm_device_get_ifindex (NM_DEVICE (self)), &priv->props); G_OBJECT_CLASS (nm_device_tun_parent_class)->constructed (object); @@ -270,7 +270,7 @@ nm_device_tun_class_init (NMDeviceTunClass *klass) #define NM_TUN_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_TUN_FACTORY, NMTunFactory)) static NMDevice * -new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error) +new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error) { const char *mode = NULL; @@ -278,8 +278,10 @@ new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error) mode = "tun"; else if (plink->type == NM_LINK_TYPE_TAP) mode = "tap"; - else - return NULL; + else { + g_warn_if_reached (); + mode = "unknown"; + } return (NMDevice *) g_object_new (NM_TYPE_DEVICE_TUN, NM_DEVICE_PLATFORM_DEVICE, plink, @@ -289,7 +291,8 @@ new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error) NULL); } -DEFINE_DEVICE_FACTORY_INTERNAL_WITH_DEVTYPE(TUN, Tun, tun, GENERIC, \ - factory_iface->new_link = new_link; \ +NM_DEVICE_FACTORY_DEFINE_INTERNAL (TUN, Tun, tun, + NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_TUN, NM_LINK_TYPE_TAP), + factory_iface->new_link = new_link; ) diff --git a/src/devices/nm-device-veth.c b/src/devices/nm-device-veth.c index db29c00d..04f03fbb 100644 --- a/src/devices/nm-device-veth.c +++ b/src/devices/nm-device-veth.c @@ -82,7 +82,7 @@ get_peer (NMDeviceVeth *self) if (priv->ever_had_peer) return priv->peer; - if (!nm_platform_veth_get_properties (nm_device_get_ifindex (device), &props)) { + if (!nm_platform_veth_get_properties (NM_PLATFORM_GET, nm_device_get_ifindex (device), &props)) { _LOGW (LOGD_HW, "could not read veth properties"); return NULL; } @@ -96,13 +96,21 @@ get_peer (NMDeviceVeth *self) return priv->peer; } +static gboolean +can_unmanaged_external_down (NMDevice *self) +{ + /* Unless running in a container, an udev rule causes these to be + * unmanaged. If there's no udev then we're probably in a container + * and should IFF_UP and configure the veth ourselves even if we + * didn't create it. */ + return FALSE; +} /**************************************************************/ static void nm_device_veth_init (NMDeviceVeth *self) { - nm_device_set_initial_unmanaged_flag (NM_DEVICE (self), NM_UNMANAGED_DEFAULT, TRUE); } static void @@ -141,12 +149,15 @@ static void nm_device_veth_class_init (NMDeviceVethClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); g_type_class_add_private (klass, sizeof (NMDeviceVethPrivate)); object_class->get_property = get_property; object_class->dispose = dispose; + device_class->can_unmanaged_external_down = can_unmanaged_external_down; + /* properties */ g_object_class_install_property (object_class, PROP_PEER, @@ -166,19 +177,17 @@ nm_device_veth_class_init (NMDeviceVethClass *klass) #define NM_VETH_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VETH_FACTORY, NMVethFactory)) static NMDevice * -new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error) +new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error) { - if (plink->type == NM_LINK_TYPE_VETH) { - return (NMDevice *) g_object_new (NM_TYPE_DEVICE_VETH, - NM_DEVICE_PLATFORM_DEVICE, plink, - NM_DEVICE_TYPE_DESC, "Veth", - NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_ETHERNET, - NULL); - } - return NULL; + return (NMDevice *) g_object_new (NM_TYPE_DEVICE_VETH, + NM_DEVICE_PLATFORM_DEVICE, plink, + NM_DEVICE_TYPE_DESC, "Veth", + NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_ETHERNET, + NULL); } -DEFINE_DEVICE_FACTORY_INTERNAL_WITH_DEVTYPE(VETH, Veth, veth, ETHERNET, \ - factory_iface->new_link = new_link; \ +NM_DEVICE_FACTORY_DEFINE_INTERNAL (VETH, Veth, veth, + NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_VETH), + factory_iface->new_link = new_link; ) diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c index c69bcded..158e7de5 100644 --- a/src/devices/nm-device-vlan.c +++ b/src/devices/nm-device-vlan.c @@ -40,6 +40,7 @@ #include "nm-device-factory.h" #include "nm-manager.h" #include "nm-core-internal.h" +#include "gsystem-local-alloc.h" #include "nm-device-vlan-glue.h" @@ -51,14 +52,10 @@ G_DEFINE_TYPE (NMDeviceVlan, nm_device_vlan, NM_TYPE_DEVICE) #define NM_DEVICE_VLAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_VLAN, NMDeviceVlanPrivate)) typedef struct { - char *initial_hw_addr; - - gboolean disposed; gboolean invalid; NMDevice *parent; guint parent_state_id; - int vlan_id; } NMDeviceVlanPrivate; @@ -75,20 +72,68 @@ enum { /******************************************************************/ static void -update_initial_hw_address (NMDevice *dev) +parent_state_changed (NMDevice *parent, + NMDeviceState new_state, + NMDeviceState old_state, + NMDeviceStateReason reason, + gpointer user_data) +{ + NMDeviceVlan *self = NM_DEVICE_VLAN (user_data); + + /* We'll react to our own carrier state notifications. Ignore the parent's. */ + if (reason == NM_DEVICE_STATE_REASON_CARRIER) + return; + + nm_device_set_unmanaged (NM_DEVICE (self), NM_UNMANAGED_PARENT, !nm_device_get_managed (parent), reason); +} + +static void +nm_device_vlan_set_parent (NMDeviceVlan *self, NMDevice *parent, gboolean construct) { - NMDeviceVlan *self = NM_DEVICE_VLAN (dev); NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (self); + NMDevice *device = NM_DEVICE (self); + + if (parent == priv->parent) + return; + + if (priv->parent_state_id) { + g_signal_handler_disconnect (priv->parent, priv->parent_state_id); + priv->parent_state_id = 0; + } + g_clear_object (&priv->parent); - priv->initial_hw_addr = g_strdup (nm_device_get_hw_address (dev)); - _LOGD (LOGD_DEVICE | LOGD_VLAN, "read initial MAC address %s", priv->initial_hw_addr); + if (parent) { + priv->parent = g_object_ref (parent); + priv->parent_state_id = g_signal_connect (priv->parent, + "state-changed", + G_CALLBACK (parent_state_changed), + device); + + /* Set parent-dependent unmanaged flag */ + if (construct) { + nm_device_set_initial_unmanaged_flag (device, + NM_UNMANAGED_PARENT, + !nm_device_get_managed (parent)); + } else { + nm_device_set_unmanaged (device, + NM_UNMANAGED_PARENT, + !nm_device_get_managed (parent), + NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED); + } + } + + /* Recheck availability now that the parent has changed */ + nm_device_queue_recheck_available (self, + NM_DEVICE_STATE_REASON_PARENT_CHANGED, + NM_DEVICE_STATE_REASON_PARENT_CHANGED); + g_object_notify (G_OBJECT (device), NM_DEVICE_VLAN_PARENT); } -static guint32 +static NMDeviceCapabilities get_generic_capabilities (NMDevice *dev) { /* We assume VLAN interfaces always support carrier detect */ - return NM_DEVICE_CAP_CARRIER_DETECT; + return NM_DEVICE_CAP_CARRIER_DETECT | NM_DEVICE_CAP_IS_SOFTWARE; } static gboolean @@ -108,12 +153,55 @@ bring_up (NMDevice *dev, gboolean *no_firmware) /******************************************************************/ static gboolean +is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags) +{ + if (!NM_DEVICE_VLAN_GET_PRIVATE (device)->parent) + return FALSE; + + return NM_DEVICE_CLASS (nm_device_vlan_parent_class)->is_available (device, flags); +} + +static gboolean +component_added (NMDevice *device, GObject *component) +{ + NMDeviceVlan *self = NM_DEVICE_VLAN (device); + NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (self); + NMDevice *added_device; + int parent_ifindex = -1; + + if (priv->parent) + return FALSE; + + if (!NM_IS_DEVICE (component)) + return FALSE; + added_device = NM_DEVICE (component); + + if (!nm_platform_vlan_get_info (NM_PLATFORM_GET, nm_device_get_ifindex (device), &parent_ifindex, NULL)) { + _LOGW (LOGD_VLAN, "failed to get VLAN interface info while checking added component."); + return FALSE; + } + + if (nm_device_get_ifindex (added_device) != parent_ifindex) + return FALSE; + + nm_device_vlan_set_parent (self, added_device, FALSE); + + /* Don't claim parent exclusively */ + return FALSE; +} + +/******************************************************************/ + +static gboolean match_parent (NMDeviceVlan *self, const char *parent) { NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (self); g_return_val_if_fail (parent != NULL, FALSE); + if (!priv->parent) + return FALSE; + if (nm_utils_is_uuid (parent)) { NMActRequest *parent_req; NMConnection *parent_connection; @@ -239,32 +327,6 @@ complete_connection (NMDevice *device, return TRUE; } -static void parent_state_changed (NMDevice *parent, NMDeviceState new_state, - NMDeviceState old_state, - NMDeviceStateReason reason, - gpointer user_data); - -static void -nm_device_vlan_set_parent (NMDeviceVlan *device, NMDevice *parent) -{ - NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (device); - - if (priv->parent_state_id) { - g_signal_handler_disconnect (priv->parent, priv->parent_state_id); - priv->parent_state_id = 0; - } - g_clear_object (&priv->parent); - - if (parent) { - priv->parent = g_object_ref (parent); - priv->parent_state_id = g_signal_connect (priv->parent, - "state-changed", - G_CALLBACK (parent_state_changed), - device); - } - g_object_notify (G_OBJECT (device), NM_DEVICE_VLAN_PARENT); -} - static void update_connection (NMDevice *device, NMConnection *connection) { @@ -281,7 +343,7 @@ update_connection (NMDevice *device, NMConnection *connection) nm_connection_add_setting (connection, (NMSetting *) s_vlan); } - if (!nm_platform_vlan_get_info (ifindex, &parent_ifindex, &vlan_id)) { + if (!nm_platform_vlan_get_info (NM_PLATFORM_GET, ifindex, &parent_ifindex, &vlan_id)) { _LOGW (LOGD_VLAN, "failed to get VLAN interface info while updating connection."); return; } @@ -296,8 +358,7 @@ update_connection (NMDevice *device, NMConnection *connection) parent = nm_manager_get_device_by_ifindex (nm_manager_get (), parent_ifindex); g_assert (parent); - if (priv->parent != parent) - nm_device_vlan_set_parent (NM_DEVICE_VLAN (device), parent); + nm_device_vlan_set_parent (NM_DEVICE_VLAN (device), parent, FALSE); /* Update parent in the connection; default to parent's interface name */ new_parent = nm_device_get_iface (parent); @@ -353,12 +414,12 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) num = nm_setting_vlan_get_num_priorities (s_vlan, NM_VLAN_INGRESS_MAP); for (i = 0; i < num; i++) { if (nm_setting_vlan_get_priority (s_vlan, NM_VLAN_INGRESS_MAP, i, &from, &to)) - nm_platform_vlan_set_ingress_map (ifindex, from, to); + nm_platform_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, from, to); } num = nm_setting_vlan_get_num_priorities (s_vlan, NM_VLAN_EGRESS_MAP); for (i = 0; i < num; i++) { if (nm_setting_vlan_get_priority (s_vlan, NM_VLAN_EGRESS_MAP, i, &from, &to)) - nm_platform_vlan_set_egress_map (ifindex, from, to); + nm_platform_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, from, to); } } @@ -386,30 +447,9 @@ ip4_config_pre_commit (NMDevice *device, NMIP4Config *config) static void deactivate (NMDevice *device) { - NMDeviceVlan *self = NM_DEVICE_VLAN (device); - NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (self); - /* Reset MAC address back to initial address */ - if (priv->initial_hw_addr) - nm_device_set_hw_addr (device, priv->initial_hw_addr, "reset", LOGD_VLAN); -} - -/******************************************************************/ - -static void -parent_state_changed (NMDevice *parent, - NMDeviceState new_state, - NMDeviceState old_state, - NMDeviceStateReason reason, - gpointer user_data) -{ - NMDeviceVlan *self = NM_DEVICE_VLAN (user_data); - - /* We'll react to our own carrier state notifications. Ignore the parent's. */ - if (reason == NM_DEVICE_STATE_REASON_CARRIER) - return; - - nm_device_set_unmanaged (NM_DEVICE (self), NM_UNMANAGED_PARENT, !nm_device_get_managed (parent), reason); + if (nm_device_get_initial_hw_address (device)) + nm_device_set_hw_addr (device, nm_device_get_initial_hw_address (device), "reset", LOGD_VLAN); } /******************************************************************/ @@ -431,37 +471,40 @@ constructed (GObject *object) if (G_OBJECT_CLASS (nm_device_vlan_parent_class)->constructed) G_OBJECT_CLASS (nm_device_vlan_parent_class)->constructed (object); - if (!priv->parent) { - _LOGE (LOGD_VLAN, "no parent specified."); - priv->invalid = TRUE; - return; - } - - itype = nm_platform_link_get_type (ifindex); + itype = nm_platform_link_get_type (NM_PLATFORM_GET, ifindex); if (itype != NM_LINK_TYPE_VLAN) { _LOGE (LOGD_VLAN, "failed to get VLAN interface type."); priv->invalid = TRUE; return; } - if (!nm_platform_vlan_get_info (ifindex, &parent_ifindex, &vlan_id)) { + if (!nm_platform_vlan_get_info (NM_PLATFORM_GET, ifindex, &parent_ifindex, &vlan_id)) { _LOGW (LOGD_VLAN, "failed to get VLAN interface info."); priv->invalid = TRUE; return; } - if ( parent_ifindex < 0 - || parent_ifindex != nm_device_get_ip_ifindex (priv->parent) - || vlan_id < 0) { + if (parent_ifindex < 0 || vlan_id < 0) { _LOGW (LOGD_VLAN, "VLAN parent ifindex (%d) or VLAN ID (%d) invalid.", parent_ifindex, priv->vlan_id); priv->invalid = TRUE; return; } + if (priv->parent && parent_ifindex != nm_device_get_ip_ifindex (priv->parent)) { + _LOGW (LOGD_VLAN, "VLAN parent %s (%d) and parent ifindex %d don't match.", + nm_device_get_iface (priv->parent), + nm_device_get_ifindex (priv->parent), + parent_ifindex); + priv->invalid = TRUE; + return; + } + priv->vlan_id = vlan_id; - _LOGI (LOGD_HW | LOGD_VLAN, "VLAN ID %d with parent %s", - priv->vlan_id, nm_device_get_iface (priv->parent)); + _LOGI (LOGD_HW | LOGD_VLAN, "VLAN ID %d with parent %s (%d)", + priv->vlan_id, + priv->parent ? nm_device_get_iface (priv->parent) : "unknown", + parent_ifindex); } static void @@ -494,7 +537,7 @@ set_property (GObject *object, guint prop_id, switch (prop_id) { case PROP_INT_PARENT_DEVICE: - nm_device_vlan_set_parent (NM_DEVICE_VLAN (object), g_value_get_object (value)); + nm_device_vlan_set_parent (NM_DEVICE_VLAN (object), g_value_get_object (value), TRUE); break; case PROP_VLAN_ID: priv->vlan_id = g_value_get_uint (value); @@ -508,32 +551,12 @@ set_property (GObject *object, guint prop_id, static void dispose (GObject *object) { - NMDeviceVlan *self = NM_DEVICE_VLAN (object); - NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (self); - - if (priv->disposed) { - G_OBJECT_CLASS (nm_device_vlan_parent_class)->dispose (object); - return; - } - priv->disposed = TRUE; - - nm_device_vlan_set_parent (self, NULL); + nm_device_vlan_set_parent (NM_DEVICE_VLAN (object), NULL, FALSE); G_OBJECT_CLASS (nm_device_vlan_parent_class)->dispose (object); } static void -finalize (GObject *object) -{ - NMDeviceVlan *self = NM_DEVICE_VLAN (object); - NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (self); - - g_free (priv->initial_hw_addr); - - G_OBJECT_CLASS (nm_device_vlan_parent_class)->finalize (object); -} - -static void nm_device_vlan_class_init (NMDeviceVlanClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); @@ -548,14 +571,14 @@ nm_device_vlan_class_init (NMDeviceVlanClass *klass) object_class->get_property = get_property; object_class->set_property = set_property; object_class->dispose = dispose; - object_class->finalize = finalize; - parent_class->update_initial_hw_address = update_initial_hw_address; parent_class->get_generic_capabilities = get_generic_capabilities; parent_class->bring_up = bring_up; parent_class->act_stage1_prepare = act_stage1_prepare; parent_class->ip4_config_pre_commit = ip4_config_pre_commit; parent_class->deactivate = deactivate; + parent_class->is_available = is_available; + parent_class->component_added = component_added; parent_class->check_connection_compatible = check_connection_compatible; parent_class->complete_connection = complete_connection; @@ -594,30 +617,18 @@ nm_device_vlan_class_init (NMDeviceVlanClass *klass) #define NM_VLAN_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VLAN_FACTORY, NMVlanFactory)) static NMDevice * -new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error) +new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error) { int parent_ifindex = -1; NMDevice *parent, *device; - if (plink->type != NM_LINK_TYPE_VLAN) - return NULL; - - /* Have to find the parent device */ - if (!nm_platform_vlan_get_info (plink->ifindex, &parent_ifindex, NULL)) { - nm_log_err (LOGD_HW, "(%s): failed to get VLAN parent ifindex", plink->name); + /* Find the parent device */ + if (!nm_platform_vlan_get_info (NM_PLATFORM_GET, plink->ifindex, &parent_ifindex, NULL)) { + g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, + "VLAN parent ifindex unknown"); return NULL; } - parent = nm_manager_get_device_by_ifindex (nm_manager_get (), parent_ifindex); - if (!parent) { - /* If udev signaled the VLAN interface before it signaled - * the VLAN's parent at startup we may not know about the - * parent device yet. But we'll find it on the second pass - * from nm_manager_start(). - */ - nm_log_dbg (LOGD_HW, "(%s): VLAN parent interface unknown", plink->name); - return NULL; - } device = (NMDevice *) g_object_new (NM_TYPE_DEVICE_VLAN, NM_DEVICE_PLATFORM_DEVICE, plink, @@ -627,14 +638,12 @@ new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error) NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_VLAN, NULL); if (NM_DEVICE_VLAN_GET_PRIVATE (device)->invalid) { + g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, + "VLAN initialization failed"); g_object_unref (device); device = NULL; } - /* Set initial parent-dependent unmanaged flag */ - if (device) - nm_device_set_initial_unmanaged_flag (device, NM_UNMANAGED_PARENT, !nm_device_get_managed (parent)); - return device; } @@ -646,15 +655,17 @@ create_virtual_device_for_connection (NMDeviceFactory *factory, { NMDevice *device; NMSettingVlan *s_vlan; - char *iface; + gs_free char *iface = NULL; + NMPlatformError plerr; - if (!nm_connection_is_type (connection, NM_SETTING_VLAN_SETTING_NAME)) + if (!NM_IS_DEVICE (parent)) { + g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, + "VLAN interfaces must have parents"); return NULL; - - g_return_val_if_fail (NM_IS_DEVICE (parent), NULL); + } s_vlan = nm_connection_get_setting_vlan (connection); - g_return_val_if_fail (s_vlan != NULL, NULL); + g_assert (s_vlan); iface = g_strdup (nm_connection_get_interface_name (connection)); if (!iface) { @@ -662,14 +673,18 @@ create_virtual_device_for_connection (NMDeviceFactory *factory, nm_setting_vlan_get_id (s_vlan)); } - if ( !nm_platform_vlan_add (iface, + plerr = nm_platform_vlan_add (NM_PLATFORM_GET, + iface, nm_device_get_ifindex (parent), nm_setting_vlan_get_id (s_vlan), - nm_setting_vlan_get_flags (s_vlan)) - && nm_platform_get_error () != NM_PLATFORM_ERROR_EXISTS) { - nm_log_warn (LOGD_DEVICE | LOGD_VLAN, "(%s) failed to add VLAN interface for '%s'", - iface, nm_connection_get_id (connection)); - g_free (iface); + nm_setting_vlan_get_flags (s_vlan), + NULL); + if (plerr != NM_PLATFORM_ERROR_SUCCESS && plerr != NM_PLATFORM_ERROR_EXISTS) { + g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, + "Failed to create VLAN interface '%s' for '%s': %s", + iface, + nm_connection_get_id (connection), + nm_platform_error_to_string (plerr)); return NULL; } @@ -680,21 +695,74 @@ create_virtual_device_for_connection (NMDeviceFactory *factory, NM_DEVICE_TYPE_DESC, "VLAN", NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_VLAN, NULL); - g_free (iface); if (NM_DEVICE_VLAN_GET_PRIVATE (device)->invalid) { + g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, + "Failed to create VLAN interface '%s' for '%s': initialization failed", + iface, nm_connection_get_id (connection)); g_object_unref (device); device = NULL; } - /* Set initial parent-dependent unmanaged flag */ - if (device) - nm_device_set_initial_unmanaged_flag (device, NM_UNMANAGED_PARENT, !nm_device_get_managed (parent)); - return device; } -DEFINE_DEVICE_FACTORY_INTERNAL(VLAN, Vlan, vlan, \ - factory_iface->new_link = new_link; \ +static const char * +get_connection_parent (NMDeviceFactory *factory, NMConnection *connection) +{ + NMSettingVlan *s_vlan; + NMSettingWired *s_wired; + const char *parent = NULL; + + g_return_val_if_fail (nm_connection_is_type (connection, NM_SETTING_VLAN_SETTING_NAME), NULL); + + s_vlan = nm_connection_get_setting_vlan (connection); + g_assert (s_vlan); + + parent = nm_setting_vlan_get_parent (s_vlan); + if (parent) + return parent; + + /* Try the hardware address from the VLAN connection's hardware setting */ + s_wired = nm_connection_get_setting_wired (connection); + if (s_wired) + return nm_setting_wired_get_mac_address (s_wired); + + return NULL; +} + +static char * +get_virtual_iface_name (NMDeviceFactory *factory, + NMConnection *connection, + const char *parent_iface) +{ + const char *ifname; + NMSettingVlan *s_vlan; + + g_return_val_if_fail (nm_connection_is_type (connection, NM_SETTING_VLAN_SETTING_NAME), NULL); + + s_vlan = nm_connection_get_setting_vlan (connection); + g_assert (s_vlan); + + if (!parent_iface) + return NULL; + + ifname = nm_connection_get_interface_name (connection); + if (ifname) + return g_strdup (ifname); + + /* If the connection doesn't specify the interface name for the VLAN + * device, we create one for it using the VLAN ID and the parent + * interface's name. + */ + return nm_utils_new_vlan_name (parent_iface, nm_setting_vlan_get_id (s_vlan)); +} + +NM_DEVICE_FACTORY_DEFINE_INTERNAL (VLAN, Vlan, vlan, + NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_VLAN) + NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_VLAN_SETTING_NAME), + factory_iface->new_link = new_link; factory_iface->create_virtual_device_for_connection = create_virtual_device_for_connection; + factory_iface->get_connection_parent = get_connection_parent; + factory_iface->get_virtual_iface_name = get_virtual_iface_name; ) diff --git a/src/devices/nm-device-vxlan.c b/src/devices/nm-device-vxlan.c index ed6b8fa0..186e00de 100644 --- a/src/devices/nm-device-vxlan.c +++ b/src/devices/nm-device-vxlan.c @@ -76,7 +76,7 @@ update_properties (NMDevice *device) GObject *object = G_OBJECT (device); NMPlatformVxlanProperties props; - if (!nm_platform_vxlan_get_properties (nm_device_get_ifindex (device), &props)) { + if (!nm_platform_vxlan_get_properties (NM_PLATFORM_GET, nm_device_get_ifindex (device), &props)) { _LOGW (LOGD_HW, "could not read vxlan properties"); return; } @@ -199,7 +199,7 @@ get_property (GObject *object, guint prop_id, g_value_set_uint (value, priv->props.src_port_max); break; case PROP_PROXY: - g_value_set_uint (value, priv->props.proxy); + g_value_set_boolean (value, priv->props.proxy); break; case PROP_RSC: g_value_set_boolean (value, priv->props.rsc); @@ -353,19 +353,17 @@ nm_device_vxlan_class_init (NMDeviceVxlanClass *klass) #define NM_VXLAN_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VXLAN_FACTORY, NMVxlanFactory)) static NMDevice * -new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error) +new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error) { - if (plink->type == NM_LINK_TYPE_VXLAN) { - return (NMDevice *) g_object_new (NM_TYPE_DEVICE_VXLAN, - NM_DEVICE_PLATFORM_DEVICE, plink, - NM_DEVICE_TYPE_DESC, "Vxlan", - NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_GENERIC, - NULL); - } - return NULL; + return (NMDevice *) g_object_new (NM_TYPE_DEVICE_VXLAN, + NM_DEVICE_PLATFORM_DEVICE, plink, + NM_DEVICE_TYPE_DESC, "Vxlan", + NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_GENERIC, + NULL); } -DEFINE_DEVICE_FACTORY_INTERNAL_WITH_DEVTYPE(VXLAN, Vxlan, vxlan, GENERIC, \ - factory_iface->new_link = new_link; \ +NM_DEVICE_FACTORY_DEFINE_INTERNAL (VXLAN, Vxlan, vxlan, + NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_VXLAN), + factory_iface->new_link = new_link; ) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 47526281..c5d4c203 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -28,8 +28,6 @@ #include <string.h> #include <unistd.h> #include <errno.h> -#include <linux/sockios.h> -#include <linux/ethtool.h> #include <sys/ioctl.h> #include <signal.h> #include <sys/types.h> @@ -62,7 +60,6 @@ #include "nm-enum-types.h" #include "nm-settings-connection.h" #include "nm-connection-provider.h" -#include "nm-posix-signals.h" #include "nm-auth-utils.h" #include "nm-dbus-glib-types.h" #include "nm-dispatcher.h" @@ -70,12 +67,14 @@ #include "nm-dns-manager.h" #include "nm-core-internal.h" #include "nm-default-route-manager.h" +#include "nm-route-manager.h" #include "nm-device-logging.h" _LOG_DECLARE_SELF (NMDevice); static void impl_device_disconnect (NMDevice *self, DBusGMethodInvocation *context); static void impl_device_delete (NMDevice *self, DBusGMethodInvocation *context); +static void ip_check_ping_watch_cb (GPid pid, gint status, gpointer user_data); #include "nm-device-glue.h" @@ -139,6 +138,12 @@ enum { #define PENDING_ACTION_AUTOCONF6 "autoconf6" typedef enum { + CLEANUP_TYPE_DECONFIGURE, + CLEANUP_TYPE_KEEP, + CLEANUP_TYPE_REMOVED, +} CleanupType; + +typedef enum { IP_NONE = 0, IP_WAIT, IP_CONF, @@ -164,6 +169,9 @@ typedef struct { guint timeout; guint watch; GPid pid; + const char *binary; + const char *address; + guint deadline; } PingInfo; typedef struct { @@ -175,23 +183,28 @@ typedef struct { typedef struct { gboolean in_state_changed; gboolean initialized; + gboolean platform_link_initialized; + + guint device_link_changed_id; + guint device_ip_link_changed_id; NMDeviceState state; NMDeviceStateReason state_reason; QueuedState queued_state; - guint queued_ip_config_id; + guint queued_ip4_config_id; + guint queued_ip6_config_id; GSList *pending_actions; char * udi; char * path; char * iface; /* may change, could be renamed by user */ int ifindex; - gboolean is_software; char * ip_iface; int ip_ifindex; NMDeviceType type; char * type_desc; - guint32 capabilities; + char * type_description; + NMDeviceCapabilities capabilities; char * driver; char * driver_version; char * firmware_version; @@ -200,6 +213,8 @@ typedef struct { GHashTable * available_connections; char * hw_addr; guint hw_addr_len; + char * perm_hw_addr; + char * initial_hw_addr; char * physical_port_id; guint dev_id; @@ -220,6 +235,11 @@ typedef struct { gpointer act_source6_func; guint recheck_assume_id; struct { + guint call_id; + NMDeviceStateReason available_reason; + NMDeviceStateReason unavailable_reason; + } recheck_available; + struct { guint call_id; NMDeviceState post_state; NMDeviceStateReason post_state_reason; @@ -242,16 +262,18 @@ typedef struct { /* IP4 configuration info */ NMIP4Config * ip4_config; /* Combined config from VPN, settings, and device */ IpState ip4_state; + NMIP4Config * con_ip4_config; /* config from the setting */ NMIP4Config * dev_ip4_config; /* Config from DHCP, PPP, LLv4, etc */ NMIP4Config * ext_ip4_config; /* Stuff added outside NM */ - gboolean ext_ip4_config_had_any_addresses; NMIP4Config * wwan_ip4_config; /* WWAN configuration */ struct { gboolean v4_has; gboolean v4_is_assumed; + gboolean v4_configure_first_time; NMPlatformIP4Route v4; gboolean v6_has; gboolean v6_is_assumed; + gboolean v6_configure_first_time; NMPlatformIP6Route v6; } default_route; @@ -279,11 +301,12 @@ typedef struct { /* IP6 configuration info */ NMIP6Config * ip6_config; IpState ip6_state; + NMIP6Config * con_ip6_config; /* config from the setting */ NMIP6Config * vpn6_config; /* routes added by a VPN which uses this device */ NMIP6Config * wwan_ip6_config; NMIP6Config * ext_ip6_config; /* Stuff added outside NM */ - gboolean ext_ip6_config_had_any_addresses; gboolean nm_ipv6ll; /* TRUE if NM handles the device's IPv6LL address */ + guint32 ip6_mtu; NMRDisc * rdisc; gulong rdisc_changed_id; @@ -322,6 +345,7 @@ static gboolean nm_device_set_ip4_config (NMDevice *self, NMIP4Config *config, guint32 default_route_metric, gboolean commit, + gboolean routes_full_sync, NMDeviceStateReason *reason); static gboolean ip4_config_merge_and_apply (NMDevice *self, NMIP4Config *config, @@ -331,6 +355,7 @@ static gboolean ip4_config_merge_and_apply (NMDevice *self, static gboolean nm_device_set_ip6_config (NMDevice *self, NMIP6Config *config, gboolean commit, + gboolean routes_full_sync, NMDeviceStateReason *reason); static gboolean nm_device_master_add_slave (NMDevice *self, NMDevice *slave, gboolean configure); @@ -448,6 +473,8 @@ static const char *reason_table[] = { [NM_DEVICE_STATE_REASON_MODEM_AVAILABLE] = "modem-available", [NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT] = "sim-pin-incorrect", [NM_DEVICE_STATE_REASON_NEW_ACTIVATION] = "new-activation", + [NM_DEVICE_STATE_REASON_PARENT_CHANGED] = "parent-changed", + [NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED] = "parent-managed-changed", }; static const char * @@ -463,13 +490,19 @@ reason_to_string (NMDeviceStateReason reason) gboolean nm_device_ipv6_sysctl_set (NMDevice *self, const char *property, const char *value) { - return nm_platform_sysctl_set (nm_utils_ip6_property_path (nm_device_get_ip_iface (self), property), value); + return nm_platform_sysctl_set (NM_PLATFORM_GET, nm_utils_ip6_property_path (nm_device_get_ip_iface (self), property), value); } -static gboolean -device_has_capability (NMDevice *self, NMDeviceCapabilities caps) +static guint32 +nm_device_ipv6_sysctl_get_int32 (NMDevice *self, const char *property, gint32 fallback) { - return !!(NM_DEVICE_GET_PRIVATE (self)->capabilities & caps); + return nm_platform_sysctl_get_int32 (NM_PLATFORM_GET, nm_utils_ip6_property_path (nm_device_get_ip_iface (self), property), fallback); +} + +gboolean +nm_device_has_capability (NMDevice *self, NMDeviceCapabilities caps) +{ + return NM_FLAGS_ANY (NM_DEVICE_GET_PRIVATE (self)->capabilities, caps); } /***********************************************************/ @@ -486,7 +519,7 @@ nm_device_dbus_export (NMDevice *self) g_return_if_fail (priv->path == NULL); priv->path = g_strdup_printf ("/org/freedesktop/NetworkManager/Devices/%d", devcount++); - _LOGI (LOGD_DEVICE, "exported as %s", priv->path); + _LOGD (LOGD_DEVICE, "exported as %s", priv->path); nm_dbus_manager_register_object (nm_dbus_manager_get (), priv->path, self); } @@ -509,7 +542,7 @@ nm_device_get_udi (NMDevice *self) const char * nm_device_get_iface (NMDevice *self) { - g_return_val_if_fail (NM_IS_DEVICE (self), 0); + g_return_val_if_fail (NM_IS_DEVICE (self), NULL); return NM_DEVICE_GET_PRIVATE (self)->iface; } @@ -525,9 +558,7 @@ nm_device_get_ifindex (NMDevice *self) gboolean nm_device_is_software (NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - - return priv->is_software; + return NM_FLAGS_HAS (NM_DEVICE_GET_PRIVATE (self)->capabilities, NM_DEVICE_CAP_IS_SOFTWARE); } const char * @@ -550,7 +581,7 @@ nm_device_get_ip_ifindex (NMDevice *self) g_return_val_if_fail (self != NULL, 0); priv = NM_DEVICE_GET_PRIVATE (self); - /* If it's not set, default to iface */ + /* If it's not set, default to ifindex */ return priv->ip_iface ? priv->ip_ifindex : priv->ifindex; } @@ -571,13 +602,13 @@ nm_device_set_ip_iface (NMDevice *self, const char *iface) priv->ip_iface = g_strdup (iface); if (priv->ip_iface) { - priv->ip_ifindex = nm_platform_link_get_ifindex (priv->ip_iface); + priv->ip_ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, priv->ip_iface); if (priv->ip_ifindex > 0) { - if (nm_platform_check_support_user_ipv6ll ()) - nm_platform_link_set_user_ipv6ll_enabled (priv->ip_ifindex, TRUE); + if (nm_platform_check_support_user_ipv6ll (NM_PLATFORM_GET)) + nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, priv->ip_ifindex, TRUE); - if (!nm_platform_link_is_up (priv->ip_ifindex)) - nm_platform_link_set_up (priv->ip_ifindex); + if (!nm_platform_link_is_up (NM_PLATFORM_GET, priv->ip_ifindex)) + nm_platform_link_set_up (NM_PLATFORM_GET, priv->ip_ifindex, NULL); } else { /* Device IP interface must always be a kernel network interface */ _LOGW (LOGD_HW, "failed to look up interface index"); @@ -607,10 +638,10 @@ get_ip_iface_identifier (NMDevice *self, NMUtilsIPv6IfaceId *out_iid) ifindex = nm_device_get_ip_ifindex (self); g_assert (ifindex); - link_type = nm_platform_link_get_type (ifindex); + link_type = nm_platform_link_get_type (NM_PLATFORM_GET, ifindex); g_return_val_if_fail (link_type > NM_LINK_TYPE_UNKNOWN, 0); - hwaddr = nm_platform_link_get_address (ifindex, &hwaddr_len); + hwaddr = nm_platform_link_get_address (NM_PLATFORM_GET, ifindex, &hwaddr_len); if (!hwaddr_len) return FALSE; @@ -724,50 +755,63 @@ nm_device_get_priority (NMDevice *self) return 11000; } -guint32 -nm_device_get_ip4_route_metric (NMDevice *self) +static guint32 +_get_ipx_route_metric (NMDevice *self, + gboolean is_v4) { + char *value; + gint64 route_metric; + NMSettingIPConfig *s_ip; NMConnection *connection; - NMSettingIPConfig *s_ip = NULL; - gint64 route_metric = -1; g_return_val_if_fail (NM_IS_DEVICE (self), G_MAXUINT32); connection = nm_device_get_connection (self); - if (connection) - s_ip = nm_connection_get_setting_ip4_config (connection); + if (connection) { + s_ip = is_v4 + ? nm_connection_get_setting_ip4_config (connection) + : nm_connection_get_setting_ip6_config (connection); - /* Slave interfaces don't have IP settings, but we may get here when - * external changes are made or when noticing IP changes when starting - * the slave connection. - */ - if (s_ip) - route_metric = nm_setting_ip_config_get_route_metric (s_ip); + /* Slave interfaces don't have IP settings, but we may get here when + * external changes are made or when noticing IP changes when starting + * the slave connection. + */ + if (s_ip) { + route_metric = nm_setting_ip_config_get_route_metric (s_ip); + if (route_metric >= 0) + goto out; + } + } - return route_metric >= 0 ? route_metric : nm_device_get_priority (self); + /* use the current NMConfigData, which makes this configuration reloadable. + * Note that that means that the route-metric might change between SIGHUP. + * You must cache the returned value if that is a problem. */ + value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, + is_v4 ? "ipv4.route-metric" : "ipv6.route-metric", self); + if (value) { + route_metric = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXUINT32, -1); + g_free (value); + + if (route_metric >= 0) + goto out; + } + route_metric = nm_device_get_priority (self); +out: + if (!is_v4) + route_metric = nm_utils_ip6_route_metric_normalize (route_metric); + return route_metric; } guint32 -nm_device_get_ip6_route_metric (NMDevice *self) +nm_device_get_ip4_route_metric (NMDevice *self) { - NMConnection *connection; - NMSettingIPConfig *s_ip = NULL; - gint64 route_metric = -1; - - g_return_val_if_fail (NM_IS_DEVICE (self), G_MAXUINT32); - - connection = nm_device_get_connection (self); - if (connection) - s_ip = nm_connection_get_setting_ip6_config (connection); - - /* Slave interfaces don't have IP settings, but we may get here when - * external changes are made or when noticing IP changes when starting - * the slave connection. - */ - if (s_ip) - route_metric = nm_setting_ip_config_get_route_metric (s_ip); + return _get_ipx_route_metric (self, TRUE); +} - return route_metric >= 0 ? route_metric : nm_device_get_priority (self); +guint32 +nm_device_get_ip6_route_metric (NMDevice *self) +{ + return _get_ipx_route_metric (self, FALSE); } const NMPlatformIP4Route * @@ -808,6 +852,34 @@ nm_device_get_type_desc (NMDevice *self) return NM_DEVICE_GET_PRIVATE (self)->type_desc; } +const char * +nm_device_get_type_description (NMDevice *self) +{ + g_return_val_if_fail (self != NULL, NULL); + + /* Beware: this function should return the same + * value as nm_device_get_type_description() in libnm. */ + + return NM_DEVICE_GET_CLASS (self)->get_type_description (self); +} + +static const char * +get_type_description (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + if (!priv->type_description) { + const char *typename; + + typename = G_OBJECT_TYPE_NAME (self); + if (g_str_has_prefix (typename, "NMDevice")) + typename += 8; + priv->type_description = g_ascii_strdown (typename, -1); + } + + return priv->type_description; +} + gboolean nm_device_has_carrier (NMDevice *self) { @@ -1012,8 +1084,15 @@ nm_device_release_one_slave (NMDevice *self, NMDevice *slave, gboolean configure return success; } +/** + * can_unmanaged_external_down: + * @self: the device + * + * Check whether the device should stay NM_UNMANAGED_EXTERNAL_DOWN unless + * IFF_UP-ed externally. + */ static gboolean -is_software_external (NMDevice *self) +can_unmanaged_external_down (NMDevice *self) { return nm_device_is_software (self) && !nm_device_get_is_nm_owned (self); @@ -1030,22 +1109,75 @@ void nm_device_finish_init (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + gboolean platform_unmanaged = FALSE; g_assert (priv->initialized == FALSE); /* Do not manage externally created software devices until they are IFF_UP */ - if ( is_software_external (self) - && !nm_platform_link_is_up (priv->ifindex) + if ( NM_DEVICE_GET_CLASS (self)->can_unmanaged_external_down (self) + && !nm_platform_link_is_up (NM_PLATFORM_GET, priv->ifindex) && priv->ifindex > 0) nm_device_set_initial_unmanaged_flag (self, NM_UNMANAGED_EXTERNAL_DOWN, TRUE); if (priv->master) nm_device_enslave_slave (priv->master, self, NULL); + if (priv->ifindex > 0) { + if (priv->ifindex == 1) { + /* keep 'lo' as default-unmanaged. */ + + /* FIXME: either find a better way to unmange 'lo' that cannot be changed + * by user configuration (NM_UNMANGED_LOOPBACK?) or fix managing 'lo'. + * Currently it can happen that NM deletes 127.0.0.1 address. */ + nm_device_set_initial_unmanaged_flag (self, NM_UNMANAGED_DEFAULT, TRUE); + } else if (priv->platform_link_initialized || (priv->is_nm_owned && nm_device_is_software (self))) { + nm_platform_link_get_unmanaged (NM_PLATFORM_GET, priv->ifindex, &platform_unmanaged); + nm_device_set_initial_unmanaged_flag (self, NM_UNMANAGED_DEFAULT, platform_unmanaged); + } else { + /* Hardware and externally-created software links stay unmanaged + * until they are fully initialized by the platform. NM created + * links must be available for activation immediately and thus + * do not get the PLATFORM_INIT unmanaged flag set. + */ + nm_device_set_initial_unmanaged_flag (self, NM_UNMANAGED_PLATFORM_INIT, TRUE); + } + } + priv->initialized = TRUE; } static void +update_dynamic_ip_setup (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + g_hash_table_remove_all (priv->ip6_saved_properties); + + if (priv->dhcp4_client) { + if (!nm_device_dhcp4_renew (self, FALSE)) { + nm_device_state_changed (self, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_DHCP_FAILED); + return; + } + } + if (priv->dhcp6_client) { + if (!nm_device_dhcp6_renew (self, FALSE)) { + nm_device_state_changed (self, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_DHCP_FAILED); + return; + } + } + if (priv->rdisc) { + /* FIXME: todo */ + } + if (priv->dnsmasq_manager) { + /* FIXME: todo */ + } +} + +static void carrier_changed (NMDevice *self, gboolean carrier) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); @@ -1094,6 +1226,12 @@ carrier_changed (NMDevice *self, gboolean carrier) * the device. */ nm_device_emit_recheck_auto_activate (self); + } else if (priv->state == NM_DEVICE_STATE_ACTIVATED) { + /* If the device is active without a carrier (probably because it is + * tagged for carrier ignore) ensure that when the carrier appears we + * renew DHCP leases and such. + */ + update_dynamic_ip_setup (self); } } else { g_return_if_fail (priv->state >= NM_DEVICE_STATE_UNAVAILABLE); @@ -1176,37 +1314,6 @@ nm_device_set_carrier (NMDevice *self, gboolean carrier) } static void -update_for_ip_ifname_change (NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - - g_hash_table_remove_all (priv->ip6_saved_properties); - - if (priv->dhcp4_client) { - if (!nm_device_dhcp4_renew (self, FALSE)) { - nm_device_state_changed (self, - NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_DHCP_FAILED); - return; - } - } - if (priv->dhcp6_client) { - if (!nm_device_dhcp6_renew (self, FALSE)) { - nm_device_state_changed (self, - NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_DHCP_FAILED); - return; - } - } - if (priv->rdisc) { - /* FIXME: todo */ - } - if (priv->dnsmasq_manager) { - /* FIXME: todo */ - } -} - -static void device_set_master (NMDevice *self, int ifindex) { NMDevice *master; @@ -1223,35 +1330,58 @@ device_set_master (NMDevice *self, int ifindex) } else { _LOGW (LOGD_DEVICE, "enslaved to unknown device %d %s", ifindex, - nm_platform_link_get_name (ifindex)); + nm_platform_link_get_name (NM_PLATFORM_GET, ifindex)); } } -static void -device_link_changed (NMDevice *self, NMPlatformLink *info) +static gboolean +device_link_changed (NMDevice *self) { NMDeviceClass *klass = NM_DEVICE_GET_CLASS (self); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMUtilsIPv6IfaceId token_iid; gboolean ip_ifname_changed = FALSE; + gboolean platform_unmanaged = FALSE; + const char *udi; + NMPlatformLink info; + const NMPlatformLink *pllink; + int ifindex; + + priv->device_link_changed_id = 0; + + ifindex = nm_device_get_ifindex (self); + pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex); + if (!pllink) + return G_SOURCE_REMOVE; - if (info->udi && g_strcmp0 (info->udi, priv->udi)) { + info = *pllink; + + udi = nm_platform_link_get_udi (NM_PLATFORM_GET, info.ifindex); + if (udi && g_strcmp0 (udi, priv->udi)) { /* Update UDI to what udev gives us */ g_free (priv->udi); - priv->udi = g_strdup (info->udi); + priv->udi = g_strdup (udi); g_object_notify (G_OBJECT (self), NM_DEVICE_UDI); } + if (g_strcmp0 (info.driver, priv->driver)) { + /* Update driver to what udev gives us */ + g_free (priv->driver); + priv->driver = g_strdup (info.driver); + g_object_notify (G_OBJECT (self), NM_DEVICE_DRIVER); + } + /* Update MTU if it has changed. */ - if (priv->mtu != info->mtu) { - priv->mtu = info->mtu; + if (priv->mtu != info.mtu) { + priv->mtu = info.mtu; g_object_notify (G_OBJECT (self), NM_DEVICE_MTU); } - if (info->name[0] && strcmp (priv->iface, info->name) != 0) { + if (info.name[0] && strcmp (priv->iface, info.name) != 0) { _LOGI (LOGD_DEVICE, "interface index %d renamed iface from '%s' to '%s'", - priv->ifindex, priv->iface, info->name); + priv->ifindex, priv->iface, info.name); g_free (priv->iface); - priv->iface = g_strdup (info->name); + priv->iface = g_strdup (info.name); /* If the device has no explicit ip_iface, then changing iface changes ip_iface too. */ ip_ifname_changed = !priv->ip_iface; @@ -1270,30 +1400,36 @@ device_link_changed (NMDevice *self, NMPlatformLink *info) } /* Update slave status for external changes */ - if (priv->enslaved && info->master != nm_device_get_ifindex (priv->master)) + if (priv->enslaved && info.master != nm_device_get_ifindex (priv->master)) nm_device_release_one_slave (priv->master, self, FALSE, NM_DEVICE_STATE_REASON_NONE); - if (info->master && !priv->enslaved) { - device_set_master (self, info->master); + if (info.master && !priv->enslaved) { + device_set_master (self, info.master); if (priv->master) nm_device_enslave_slave (priv->master, self, NULL); } + if (priv->rdisc && nm_platform_link_get_ipv6_token (NM_PLATFORM_GET, priv->ifindex, &token_iid)) { + _LOGD (LOGD_DEVICE, "IPv6 tokenized identifier present on device %s", priv->iface); + if (nm_rdisc_set_iid (priv->rdisc, token_iid)) + nm_rdisc_start (priv->rdisc); + } + if (klass->link_changed) - klass->link_changed (self, info); + klass->link_changed (self, &info); /* Update DHCP, etc, if needed */ if (ip_ifname_changed) - update_for_ip_ifname_change (self); + update_dynamic_ip_setup (self); - if (priv->up != info->up) { - priv->up = info->up; + if (priv->up != NM_FLAGS_HAS (info.flags, IFF_UP)) { + priv->up = NM_FLAGS_HAS (info.flags, IFF_UP); /* Manage externally-created software interfaces only when they are IFF_UP */ g_assert (priv->ifindex > 0); - if (is_software_external (self)) { + if (NM_DEVICE_GET_CLASS (self)->can_unmanaged_external_down (self)) { gboolean external_down = nm_device_get_unmanaged_flag (self, NM_UNMANAGED_EXTERNAL_DOWN); - if (external_down && info->up) { + if (external_down && NM_FLAGS_HAS (info.flags, IFF_UP)) { if (nm_device_get_state (self) < NM_DEVICE_STATE_DISCONNECTED) { /* Ensure the assume check is queued before any queued state changes * from the transition to UNAVAILABLE. @@ -1316,7 +1452,7 @@ device_link_changed (NMDevice *self, NMPlatformLink *info) */ priv->unmanaged_flags &= ~NM_UNMANAGED_EXTERNAL_DOWN; } - } else if (!external_down && !info->up && nm_device_get_state (self) <= NM_DEVICE_STATE_DISCONNECTED) { + } else if (!external_down && !NM_FLAGS_HAS (info.flags, IFF_UP) && nm_device_get_state (self) <= NM_DEVICE_STATE_DISCONNECTED) { /* If the device is already disconnected and is set !IFF_UP, * unmanage it. */ @@ -1327,57 +1463,120 @@ device_link_changed (NMDevice *self, NMPlatformLink *info) } } } + + if (priv->ifindex > 0 && !priv->platform_link_initialized && info.initialized) { + priv->platform_link_initialized = TRUE; + + if (nm_platform_link_get_unmanaged (NM_PLATFORM_GET, priv->ifindex, &platform_unmanaged)) { + nm_device_set_unmanaged (self, + NM_UNMANAGED_DEFAULT, + platform_unmanaged, + NM_DEVICE_STATE_REASON_USER_REQUESTED); + } + + nm_device_set_unmanaged (self, + NM_UNMANAGED_PLATFORM_INIT, + FALSE, + NM_DEVICE_STATE_REASON_NOW_MANAGED); + } + + return G_SOURCE_REMOVE; } -static void -device_ip_link_changed (NMDevice *self, NMPlatformLink *info) +static gboolean +device_ip_link_changed (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + const NMPlatformLink *pllink; + int ip_ifindex; + + priv->device_ip_link_changed_id = 0; - if (info->name[0] && g_strcmp0 (priv->ip_iface, info->name)) { + ip_ifindex = nm_device_get_ip_ifindex (self); + pllink = nm_platform_link_get (NM_PLATFORM_GET, ip_ifindex); + if (!pllink) + return G_SOURCE_REMOVE; + + if (pllink->name[0] && g_strcmp0 (priv->ip_iface, pllink->name)) { _LOGI (LOGD_DEVICE, "interface index %d renamed ip_iface (%d) from '%s' to '%s'", priv->ifindex, nm_device_get_ip_ifindex (self), - priv->ip_iface, info->name); + priv->ip_iface, pllink->name); g_free (priv->ip_iface); - priv->ip_iface = g_strdup (info->name); + priv->ip_iface = g_strdup (pllink->name); g_object_notify (G_OBJECT (self), NM_DEVICE_IP_IFACE); - update_for_ip_ifname_change (self); + update_dynamic_ip_setup (self); } + return G_SOURCE_REMOVE; } static void link_changed_cb (NMPlatform *platform, + NMPObjectType obj_type, int ifindex, NMPlatformLink *info, NMPlatformSignalChangeType change_type, NMPlatformReason reason, NMDevice *self) { + NMDevicePrivate *priv; + if (change_type != NM_PLATFORM_SIGNAL_CHANGED) return; + priv = NM_DEVICE_GET_PRIVATE (self); + /* We don't filter by 'reason' because we are interested in *all* link * changes. For example a call to nm_platform_link_set_up() may result * in an internal carrier change (i.e. we ask the kernel to set IFF_UP * and it results in also setting IFF_LOWER_UP. */ - if (ifindex == nm_device_get_ifindex (self)) - device_link_changed (self, info); - else if (ifindex == nm_device_get_ip_ifindex (self)) - device_ip_link_changed (self, info); + if (ifindex == nm_device_get_ifindex (self)) { + if (!priv->device_link_changed_id) { + priv->device_link_changed_id = g_idle_add ((GSourceFunc) device_link_changed, self); + _LOGD (LOGD_DEVICE, "queued link change for ifindex %d", ifindex); + } + } else if (ifindex == nm_device_get_ip_ifindex (self)) { + if (!priv->device_ip_link_changed_id) { + priv->device_ip_link_changed_id = g_idle_add ((GSourceFunc) device_ip_link_changed, self); + _LOGD (LOGD_DEVICE, "queued link change for ip-ifindex %d", ifindex); + } + } } static void link_changed (NMDevice *self, NMPlatformLink *info) { /* Update carrier from link event if applicable. */ - if ( device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT) - && !device_has_capability (self, NM_DEVICE_CAP_NONSTANDARD_CARRIER)) + if ( nm_device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT) + && !nm_device_has_capability (self, NM_DEVICE_CAP_NONSTANDARD_CARRIER)) nm_device_set_carrier (self, info->connected); } +static void +config_changed_update_ignore_carrier (NMConfig *config, + NMConfigData *config_data, + NMConfigChangeFlags changes, + NMConfigData *old_data, + NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + if ( priv->state <= NM_DEVICE_STATE_DISCONNECTED + || priv->state > NM_DEVICE_STATE_ACTIVATED) + priv->ignore_carrier = nm_config_data_get_ignore_carrier (config_data, self); +} + +static void +check_carrier (NMDevice *self) +{ + int ifindex = nm_device_get_ip_ifindex (self); + + if (!nm_device_has_capability (self, NM_DEVICE_CAP_NONSTANDARD_CARRIER)) + nm_device_set_carrier (self, nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex)); +} + /** * nm_device_notify_component_added(): * @self: the #NMDevice @@ -1965,7 +2164,7 @@ device_has_config (NMDevice *self) return TRUE; /* Slaves are also configured by definition */ - if (nm_platform_link_get_master (priv->ifindex) > 0) + if (nm_platform_link_get_master (NM_PLATFORM_GET, priv->ifindex) > 0) return TRUE; return FALSE; @@ -2103,6 +2302,20 @@ nm_device_generate_connection (NMDevice *self, NMDevice *master) connection = NULL; } + /* Ignore any IPv6LL-only, not master connections without slaves, + * unless they are in the assume-ipv6ll-only list. + */ + if ( connection + && g_strcmp0 (ip4_method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0 + && g_strcmp0 (ip6_method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0 + && !nm_setting_connection_get_master (NM_SETTING_CONNECTION (s_con)) + && !priv->slaves + && !nm_config_data_get_assume_ipv6ll_only (NM_CONFIG_GET_DATA, self)) { + _LOGD (LOGD_DEVICE, "ignoring generated connection (IPv6LL-only and not in master-slave relationship)"); + g_object_unref (connection); + connection = NULL; + } + return connection; } @@ -2274,6 +2487,47 @@ nm_device_queue_recheck_assume (NMDevice *self) priv->recheck_assume_id = g_idle_add (nm_device_emit_recheck_assume, self); } +static gboolean +recheck_available (gpointer user_data) +{ + NMDevice *self = NM_DEVICE (user_data); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + gboolean now_available = nm_device_is_available (self, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE); + NMDeviceState state = nm_device_get_state (self); + NMDeviceState new_state = NM_DEVICE_STATE_UNKNOWN; + + priv->recheck_available.call_id = 0; + + if (state == NM_DEVICE_STATE_UNAVAILABLE && now_available) { + new_state = NM_DEVICE_STATE_DISCONNECTED; + nm_device_queue_state (self, new_state, priv->recheck_available.available_reason); + } else if (state >= NM_DEVICE_STATE_DISCONNECTED && !now_available) { + new_state = NM_DEVICE_STATE_UNAVAILABLE; + nm_device_queue_state (self, new_state, priv->recheck_available.unavailable_reason); + } + _LOGD (LOGD_DEVICE, "device is %savailable, %s %s", + now_available ? "" : "not ", + new_state == NM_DEVICE_STATE_UNAVAILABLE ? "no change required for" : "will transition to", + state_to_string (new_state == NM_DEVICE_STATE_UNAVAILABLE ? state : new_state)); + + priv->recheck_available.available_reason = NM_DEVICE_STATE_REASON_NONE; + priv->recheck_available.unavailable_reason = NM_DEVICE_STATE_REASON_NONE; + return G_SOURCE_REMOVE; +} + +void +nm_device_queue_recheck_available (NMDevice *self, + NMDeviceStateReason available_reason, + NMDeviceStateReason unavailable_reason) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + priv->recheck_available.available_reason = available_reason; + priv->recheck_available.unavailable_reason = unavailable_reason; + if (!priv->recheck_available.call_id) + priv->recheck_available.call_id = g_idle_add (recheck_available, self); +} + void nm_device_emit_recheck_auto_activate (NMDevice *self) { @@ -2432,7 +2686,7 @@ nm_device_activate_stage1_device_prepare (gpointer user_data) /* Notify the new ActiveConnection along with the state change */ g_object_notify (G_OBJECT (self), NM_DEVICE_ACTIVE_CONNECTION); - _LOGI (LOGD_DEVICE, "Activation: Stage 1 of 5 (Device Prepare) started..."); + _LOGD (LOGD_DEVICE, "Activation: Stage 1 of 5 (Device Prepare) started..."); nm_device_state_changed (self, NM_DEVICE_STATE_PREPARE, NM_DEVICE_STATE_REASON_NONE); /* Assumed connections were already set up outside NetworkManager */ @@ -2466,7 +2720,7 @@ nm_device_activate_stage1_device_prepare (gpointer user_data) nm_device_activate_schedule_stage2_device_config (self); out: - _LOGI (LOGD_DEVICE, "Activation: Stage 1 of 5 (Device Prepare) complete."); + _LOGD (LOGD_DEVICE, "Activation: Stage 1 of 5 (Device Prepare) complete."); return FALSE; } @@ -2489,7 +2743,7 @@ nm_device_activate_schedule_stage1_device_prepare (NMDevice *self) activation_source_schedule (self, nm_device_activate_stage1_device_prepare, 0); - _LOGI (LOGD_DEVICE, "Activation: Stage 1 of 5 (Device Prepare) scheduled..."); + _LOGD (LOGD_DEVICE, "Activation: Stage 1 of 5 (Device Prepare) scheduled..."); } static NMActStageReturn @@ -2520,7 +2774,7 @@ nm_device_activate_stage2_device_config (gpointer user_data) /* Clear the activation source ID now that this stage has run */ activation_source_clear (self, FALSE, 0); - _LOGI (LOGD_DEVICE, "Activation: Stage 2 of 5 (Device Configure) starting..."); + _LOGD (LOGD_DEVICE, "Activation: Stage 2 of 5 (Device Configure) starting..."); nm_device_state_changed (self, NM_DEVICE_STATE_CONFIG, NM_DEVICE_STATE_REASON_NONE); /* Assumed connections were already set up outside NetworkManager */ @@ -2555,12 +2809,12 @@ nm_device_activate_stage2_device_config (gpointer user_data) nm_device_queue_recheck_assume (info->slave); } - _LOGI (LOGD_DEVICE, "Activation: Stage 2 of 5 (Device Configure) successful."); + _LOGD (LOGD_DEVICE, "Activation: Stage 2 of 5 (Device Configure) successful."); nm_device_activate_schedule_stage3_ip_config_start (self); out: - _LOGI (LOGD_DEVICE, "Activation: Stage 2 of 5 (Device Configure) complete."); + _LOGD (LOGD_DEVICE, "Activation: Stage 2 of 5 (Device Configure) complete."); return FALSE; } @@ -2583,7 +2837,7 @@ nm_device_activate_schedule_stage2_device_config (NMDevice *self) activation_source_schedule (self, nm_device_activate_stage2_device_config, 0); - _LOGI (LOGD_DEVICE, "Activation: Stage 2 of 5 (Device Configure) scheduled..."); + _LOGD (LOGD_DEVICE, "Activation: Stage 2 of 5 (Device Configure) scheduled..."); } /*********************************************/ @@ -2759,23 +3013,6 @@ aipd_timeout_cb (gpointer user_data) return FALSE; } -static void -aipd_child_setup (gpointer user_data G_GNUC_UNUSED) -{ - /* We are in the child process at this point. - * Give child it's own program group for signal - * separation. - */ - pid_t pid = getpid (); - setpgid (pid, pid); - - /* - * We blocked signals in main(). We need to restore original signal - * mask for avahi-autoipd here so that it can receive signals. - */ - nm_unblock_posix_signals (NULL); -} - /* default to installed helper, but can be modified for testing */ const char *nm_device_autoipd_helper_path = LIBEXECDIR "/nm-avahi-autoipd.action"; @@ -2815,7 +3052,7 @@ aipd_start (NMDevice *self, NMDeviceStateReason *reason) g_free (cmdline); if (!g_spawn_async ("/", (char **) argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, - &aipd_child_setup, NULL, &(priv->aipd_pid), &error)) { + nm_utils_setpgid, NULL, &(priv->aipd_pid), &error)) { _LOGW (LOGD_DEVICE | LOGD_AUTOIP4, "Activation: Stage 3 of 5 (IP Configure Start) failed" " to start avahi-autoipd: %s", @@ -2825,7 +3062,7 @@ aipd_start (NMDevice *self, NMDeviceStateReason *reason) return NM_ACT_STAGE_RETURN_FAILURE; } - _LOGI (LOGD_DEVICE | LOGD_AUTOIP4, + _LOGD (LOGD_DEVICE | LOGD_AUTOIP4, "Activation: Stage 3 of 5 (IP Configure Start) started" " avahi-autoipd..."); @@ -2848,9 +3085,9 @@ _device_get_default_route_from_platform (NMDevice *self, int addr_family, NMPlat GArray *routes; if (addr_family == AF_INET) - routes = nm_platform_ip4_route_get_all (ifindex, NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT); + routes = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT); else - routes = nm_platform_ip6_route_get_all (ifindex, NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT); + routes = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT); if (routes) { guint route_metric = G_MAXUINT32, m; @@ -2885,10 +3122,62 @@ _device_get_default_route_from_platform (NMDevice *self, int addr_family, NMPlat } /*********************************************/ + +static void +ensure_con_ip4_config (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMConnection *connection; + + if (priv->con_ip4_config) + return; + + connection = nm_device_get_connection (self); + if (!connection) + return; + + priv->con_ip4_config = nm_ip4_config_new (); + nm_ip4_config_merge_setting (priv->con_ip4_config, + nm_connection_get_setting_ip4_config (connection), + nm_device_get_ip4_route_metric (self)); + + if (nm_device_uses_assumed_connection (self)) { + /* For assumed connections ignore all addresses and routes. */ + nm_ip4_config_reset_addresses (priv->con_ip4_config); + nm_ip4_config_reset_routes (priv->con_ip4_config); + } +} + +static void +ensure_con_ip6_config (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMConnection *connection; + + if (priv->con_ip6_config) + return; + + connection = nm_device_get_connection (self); + if (!connection) + return; + + priv->con_ip6_config = nm_ip6_config_new (); + nm_ip6_config_merge_setting (priv->con_ip6_config, + nm_connection_get_setting_ip6_config (connection), + nm_device_get_ip6_route_metric (self)); + + if (nm_device_uses_assumed_connection (self)) { + /* For assumed connections ignore all addresses and routes. */ + nm_ip6_config_reset_addresses (priv->con_ip6_config); + nm_ip6_config_reset_routes (priv->con_ip6_config); + } +} + +/*********************************************/ /* DHCPv4 stuff */ static void -dhcp4_cleanup (NMDevice *self, gboolean stop, gboolean release) +dhcp4_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); @@ -2901,7 +3190,8 @@ dhcp4_cleanup (NMDevice *self, gboolean stop, gboolean release) nm_device_remove_pending_action (self, PENDING_ACTION_DHCP4, FALSE); - if (stop) + if ( cleanup_type == CLEANUP_TYPE_DECONFIGURE + || cleanup_type == CLEANUP_TYPE_REMOVED) nm_dhcp_client_stop (priv->dhcp4_client, release); g_clear_object (&priv->dhcp4_client); @@ -2923,7 +3213,11 @@ ip4_config_merge_and_apply (NMDevice *self, NMConnection *connection; gboolean success; NMIP4Config *composite; + gboolean has_direct_route; const guint32 default_route_metric = nm_device_get_ip4_route_metric (self); + guint32 gateway; + gboolean connection_has_default_route, connection_is_never_default; + gboolean routes_full_sync; /* Merge all the configs into the composite config */ if (config) { @@ -2932,6 +3226,10 @@ ip4_config_merge_and_apply (NMDevice *self, } composite = nm_ip4_config_new (); + + if (commit) + ensure_con_ip4_config (self); + if (priv->dev_ip4_config) nm_ip4_config_merge (composite, priv->dev_ip4_config); if (priv->vpn4_config) @@ -2945,73 +3243,120 @@ ip4_config_merge_and_apply (NMDevice *self, if (priv->wwan_ip4_config) nm_ip4_config_merge (composite, priv->wwan_ip4_config); - /* Merge user overrides into the composite config. Generated+assumed - * connections come from the system not the user and merging them would - * be redundant, so don't bother. - */ + /* Apply ignore-auto-routes and ignore-auto-dns settings */ connection = nm_device_get_connection (self); + if (connection) { + NMSettingIPConfig *s_ip4 = nm_connection_get_setting_ip4_config (connection); + + if (s_ip4) { + if (nm_setting_ip_config_get_ignore_auto_routes (s_ip4)) + nm_ip4_config_reset_routes (composite); + if (nm_setting_ip_config_get_ignore_auto_dns (s_ip4)) { + nm_ip4_config_reset_nameservers (composite); + nm_ip4_config_reset_domains (composite); + nm_ip4_config_reset_searches (composite); + } + } + } + + /* Merge user overrides into the composite config. For assumed connections, + * con_ip4_config is empty. */ + if (priv->con_ip4_config) + nm_ip4_config_merge (composite, priv->con_ip4_config); + + + /* Add the default route. + * + * We keep track of the default route of a device in a private field. + * NMDevice needs to know the default route at this point, because the gateway + * might require a direct route (see below). + * + * But also, we don't want to add the default route to priv->ip4_config, + * because the default route from the setting might not be the same that + * NMDefaultRouteManager eventually configures (because the it might + * tweak the effective metric). + */ + + /* unless we come to a different conclusion below, we have no default route and + * the route is assumed. */ priv->default_route.v4_has = FALSE; priv->default_route.v4_is_assumed = TRUE; - if (connection) { - gboolean assumed = nm_device_uses_assumed_connection (self); - NMPlatformIP4Route *route = &priv->default_route.v4; - if (!nm_settings_connection_get_nm_generated_assumed (NM_SETTINGS_CONNECTION (connection))) { - nm_ip4_config_merge_setting (composite, - nm_connection_get_setting_ip4_config (connection), - default_route_metric); - } + routes_full_sync = commit + && priv->default_route.v4_configure_first_time + && !nm_device_uses_assumed_connection (self); - /* Add the default route. - * - * We keep track of the default route of a device in a private field. - * NMDevice needs to know the default route at this point, because the gateway - * might require a direct route (see below). - * - * But also, we don't want to add the default route to priv->ip4_config, - * because the default route from the setting might not be the same that - * NMDefaultRouteManager eventually configures (because the it might - * tweak the effective metric). + if (!commit) { + /* during a non-commit event, we always pickup whatever is configured. */ + goto END_ADD_DEFAULT_ROUTE; + } + + connection_has_default_route + = nm_default_route_manager_ip4_connection_has_default_route (nm_default_route_manager_get (), + connection, &connection_is_never_default); + + if ( !priv->default_route.v4_configure_first_time + && !nm_device_uses_assumed_connection (self) + && connection_is_never_default) { + /* If the connection is explicitly configured as never-default, we enforce the (absense of the) + * default-route only once. That allows the user to configure a connection as never-default, + * but he can add default routes externally (via a dispatcher script) and NM will not interfere. */ + goto END_ADD_DEFAULT_ROUTE; + } + + /* At this point, we treat assumed and non-assumed connections alike. + * For assumed connections we do that because we still manage RA and DHCP + * leases for them, so we must extend/update the default route on commits. + */ + + /* we are about to commit (for a non-assumed connection). Enforce whatever we have + * configured. */ + priv->default_route.v4_configure_first_time = FALSE; + priv->default_route.v4_is_assumed = FALSE; + + if (!connection_has_default_route) + goto END_ADD_DEFAULT_ROUTE; + + if (!nm_ip4_config_get_num_addresses (composite)) { + /* without addresses we can have no default route. */ + goto END_ADD_DEFAULT_ROUTE; + } + + gateway = nm_ip4_config_get_gateway (composite); + if ( !gateway + && nm_device_get_device_type (self) != NM_DEVICE_TYPE_MODEM) + goto END_ADD_DEFAULT_ROUTE; + + has_direct_route = ( gateway == 0 + || nm_ip4_config_get_subnet_for_host (composite, gateway) + || nm_ip4_config_get_direct_route_for_host (composite, gateway)); + + priv->default_route.v4_has = TRUE; + memset (&priv->default_route.v4, 0, sizeof (priv->default_route.v4)); + priv->default_route.v4.source = NM_IP_CONFIG_SOURCE_USER; + priv->default_route.v4.gateway = gateway; + priv->default_route.v4.metric = default_route_metric; + priv->default_route.v4.mss = nm_ip4_config_get_mss (composite); + + if (!has_direct_route) { + NMPlatformIP4Route r = priv->default_route.v4; + + /* add a direct route to the gateway */ + r.network = gateway; + r.plen = 32; + r.gateway = 0; + nm_ip4_config_add_route (composite, &r); + } + +END_ADD_DEFAULT_ROUTE: + + if (priv->default_route.v4_is_assumed) { + /* If above does not explicitly assign a default route, we always pick up the + * default route based on what is currently configured. + * That means that even managed connections with never-default, can + * get a default route (if configured externally). */ - if ( !assumed - && nm_default_route_manager_ip4_connection_has_default_route (nm_default_route_manager_get (), connection)) { - guint32 gateway = 0; - - priv->default_route.v4_is_assumed = FALSE; - if ( (!commit && priv->ext_ip4_config_had_any_addresses) - || ( commit && nm_ip4_config_get_num_addresses (composite))) { - /* For managed interfaces, we can only configure a gateway, if either the external config indicates - * that we already have addresses, or if we are about to commit any addresses. - * Otherwise adding a default route will fail, because NMDefaultRouteManager does not add any - * addresses for the route. */ - gateway = nm_ip4_config_get_gateway (composite); - if ( gateway - || nm_device_get_device_type (self) == NM_DEVICE_TYPE_MODEM) { - memset (route, 0, sizeof (*route)); - route->source = NM_IP_CONFIG_SOURCE_USER; - route->gateway = gateway; - route->metric = default_route_metric; - route->mss = nm_ip4_config_get_mss (composite); - priv->default_route.v4_has = TRUE; - - if ( gateway - && !nm_ip4_config_get_subnet_for_host (composite, gateway) - && !nm_ip4_config_get_direct_route_for_host (composite, gateway)) { - /* add a direct route to the gateway */ - NMPlatformIP4Route r = *route; - - r.network = gateway; - r.plen = 32; - r.gateway = 0; - nm_ip4_config_add_route (composite, &r); - } - } - } - } else { - /* For interfaces that are assumed and that have no default-route by configuration, we assume - * the default connection and pick up whatever is configured. */ - priv->default_route.v4_has = _device_get_default_route_from_platform (self, AF_INET, (NMPlatformIPRoute *) route); - } + priv->default_route.v4_has = _device_get_default_route_from_platform (self, AF_INET, (NMPlatformIPRoute *) &priv->default_route.v4); } /* Allow setting MTU etc */ @@ -3020,7 +3365,7 @@ ip4_config_merge_and_apply (NMDevice *self, NM_DEVICE_GET_CLASS (self)->ip4_config_pre_commit (self, composite); } - success = nm_device_set_ip4_config (self, composite, default_route_metric, commit, out_reason); + success = nm_device_set_ip4_config (self, composite, default_route_metric, commit, routes_full_sync, out_reason); g_object_unref (composite); return success; } @@ -3051,7 +3396,7 @@ dhcp4_fail (NMDevice *self, gboolean timeout) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - dhcp4_cleanup (self, TRUE, FALSE); + dhcp4_cleanup (self, CLEANUP_TYPE_DECONFIGURE, FALSE); if (timeout || (priv->ip4_state == IP_CONF)) nm_device_activate_schedule_ip4_config_timeout (self); else if (priv->ip4_state == IP_DONE) @@ -3143,7 +3488,7 @@ dhcp4_start (NMDevice *self, g_object_unref (priv->dhcp4_config); priv->dhcp4_config = nm_dhcp4_config_new (); - hw_addr = nm_platform_link_get_address (nm_device_get_ip_ifindex (self), &hw_addr_len); + hw_addr = nm_platform_link_get_address (NM_PLATFORM_GET, nm_device_get_ip_ifindex (self), &hw_addr_len); if (hw_addr_len) { tmp = g_byte_array_sized_new (hw_addr_len); g_byte_array_append (tmp, hw_addr, hw_addr_len); @@ -3196,7 +3541,7 @@ nm_device_dhcp4_renew (NMDevice *self, gboolean release) _LOGI (LOGD_DHCP4, "DHCPv4 lease renewal requested"); /* Terminate old DHCP instance and release the old lease */ - dhcp4_cleanup (self, TRUE, release); + dhcp4_cleanup (self, CLEANUP_TYPE_DECONFIGURE, release); connection = nm_device_get_connection (self); g_assert (connection); @@ -3451,7 +3796,7 @@ act_stage3_ip4_config_start (NMDevice *self, /* DHCPv6 stuff */ static void -dhcp6_cleanup (NMDevice *self, gboolean stop, gboolean release) +dhcp6_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); @@ -3464,7 +3809,8 @@ dhcp6_cleanup (NMDevice *self, gboolean stop, gboolean release) priv->dhcp6_state_sigid = 0; } - if (stop) + if ( cleanup_type == CLEANUP_TYPE_DECONFIGURE + || cleanup_type == CLEANUP_TYPE_REMOVED) nm_dhcp_client_stop (priv->dhcp6_client, release); g_clear_object (&priv->dhcp6_client); @@ -3487,10 +3833,16 @@ ip6_config_merge_and_apply (NMDevice *self, NMConnection *connection; gboolean success; NMIP6Config *composite; + gboolean has_direct_route; + const struct in6_addr *gateway; + gboolean connection_has_default_route, connection_is_never_default; + gboolean routes_full_sync; /* If no config was passed in, create a new one */ composite = nm_ip6_config_new (); - g_assert (composite); + + if (commit) + ensure_con_ip6_config (self); /* Merge all the IP configs into the composite config */ if (priv->ac_ip6_config) @@ -3508,72 +3860,119 @@ ip6_config_merge_and_apply (NMDevice *self, if (priv->wwan_ip6_config) nm_ip6_config_merge (composite, priv->wwan_ip6_config); - /* Merge user overrides into the composite config. Generated+assumed - * connections come from the system not the user and merging them would - * be redundant, so don't bother. - */ + /* Apply ignore-auto-routes and ignore-auto-dns settings */ connection = nm_device_get_connection (self); - priv->default_route.v6_has = FALSE; - priv->default_route.v6_is_assumed = TRUE; if (connection) { - gboolean assumed = nm_device_uses_assumed_connection (self); - NMPlatformIP6Route *route = &priv->default_route.v6; + NMSettingIPConfig *s_ip6 = nm_connection_get_setting_ip6_config (connection); - if (!nm_settings_connection_get_nm_generated_assumed (NM_SETTINGS_CONNECTION (connection))) { - nm_ip6_config_merge_setting (composite, - nm_connection_get_setting_ip6_config (connection), - nm_device_get_ip6_route_metric (self)); + if (s_ip6) { + if (nm_setting_ip_config_get_ignore_auto_routes (s_ip6)) + nm_ip6_config_reset_routes (composite); + if (nm_setting_ip_config_get_ignore_auto_dns (s_ip6)) { + nm_ip6_config_reset_nameservers (composite); + nm_ip6_config_reset_domains (composite); + nm_ip6_config_reset_searches (composite); + } } + } - /* Add the default route. - * - * We keep track of the default route of a device in a private field. - * NMDevice needs to know the default route at this point, because the gateway - * might require a direct route (see below). - * - * But also, we don't want to add the default route to priv->ip4_config, - * because the default route from the setting might not be the same that - * NMDefaultRouteManager eventually configures (because the it might - * tweak the effective metric). + /* Merge user overrides into the composite config. For assumed connections, + * con_ip6_config is empty. */ + if (priv->con_ip6_config) + nm_ip6_config_merge (composite, priv->con_ip6_config); + + /* Add the default route. + * + * We keep track of the default route of a device in a private field. + * NMDevice needs to know the default route at this point, because the gateway + * might require a direct route (see below). + * + * But also, we don't want to add the default route to priv->ip6_config, + * because the default route from the setting might not be the same that + * NMDefaultRouteManager eventually configures (because the it might + * tweak the effective metric). + */ + + /* unless we come to a different conclusion below, we have no default route and + * the route is assumed. */ + priv->default_route.v6_has = FALSE; + priv->default_route.v6_is_assumed = TRUE; + + routes_full_sync = commit + && priv->default_route.v6_configure_first_time + && !nm_device_uses_assumed_connection (self); + + if (!commit) { + /* during a non-commit event, we always pickup whatever is configured. */ + goto END_ADD_DEFAULT_ROUTE; + } + + connection_has_default_route + = nm_default_route_manager_ip6_connection_has_default_route (nm_default_route_manager_get (), + connection, &connection_is_never_default); + + if ( !priv->default_route.v6_configure_first_time + && !nm_device_uses_assumed_connection (self) + && connection_is_never_default) { + /* If the connection is explicitly configured as never-default, we enforce the (absence of the) + * default-route only once. That allows the user to configure a connection as never-default, + * but he can add default routes externally (via a dispatcher script) and NM will not interfere. */ + goto END_ADD_DEFAULT_ROUTE; + } + + /* At this point, we treat assumed and non-assumed connections alike. + * For assumed connections we do that because we still manage RA and DHCP + * leases for them, so we must extend/update the default route on commits. + */ + + /* we are about to commit (for a non-assumed connection). Enforce whatever we have + * configured. */ + priv->default_route.v6_configure_first_time = FALSE; + priv->default_route.v6_is_assumed = FALSE; + + if (!connection_has_default_route) + goto END_ADD_DEFAULT_ROUTE; + + if (!nm_ip6_config_get_num_addresses (composite)) { + /* without addresses we can have no default route. */ + goto END_ADD_DEFAULT_ROUTE; + } + + gateway = nm_ip6_config_get_gateway (composite); + if (!gateway) + goto END_ADD_DEFAULT_ROUTE; + + + has_direct_route = nm_ip6_config_get_direct_route_for_host (composite, gateway) != NULL; + + + + priv->default_route.v6_has = TRUE; + memset (&priv->default_route.v6, 0, sizeof (priv->default_route.v6)); + priv->default_route.v6.source = NM_IP_CONFIG_SOURCE_USER; + priv->default_route.v6.gateway = *gateway; + priv->default_route.v6.metric = nm_device_get_ip6_route_metric (self); + priv->default_route.v6.mss = nm_ip6_config_get_mss (composite); + + if (!has_direct_route) { + NMPlatformIP6Route r = priv->default_route.v6; + + /* add a direct route to the gateway */ + r.network = *gateway; + r.plen = 128; + r.gateway = in6addr_any; + nm_ip6_config_add_route (composite, &r); + } + +END_ADD_DEFAULT_ROUTE: + + if (priv->default_route.v6_is_assumed) { + /* If above does not explicitly assign a default route, we always pick up the + * default route based on what is currently configured. + * That means that even managed connections with never-default, can + * get a default route (if configured externally). */ - if ( !assumed - && nm_default_route_manager_ip6_connection_has_default_route (nm_default_route_manager_get (), connection)) { - const struct in6_addr *gateway = NULL; - - priv->default_route.v6_is_assumed = FALSE; - if ( (!commit && priv->ext_ip6_config_had_any_addresses) - || ( commit && nm_ip6_config_get_num_addresses (composite))) { - /* For managed interfaces, we can only configure a gateway, if either the external config indicates - * that we already have addresses, or if we are about to commit any addresses. - * Otherwise adding a default route will fail, because NMDefaultRouteManager does not add any - * addresses for the route. */ - gateway = nm_ip6_config_get_gateway (composite); - if (gateway) { - memset (route, 0, sizeof (*route)); - route->source = NM_IP_CONFIG_SOURCE_USER; - route->gateway = *gateway; - route->metric = nm_device_get_ip6_route_metric (self); - route->mss = nm_ip6_config_get_mss (composite); - priv->default_route.v6_has = TRUE; - - if ( gateway - && !nm_ip6_config_get_subnet_for_host (composite, gateway) - && !nm_ip6_config_get_direct_route_for_host (composite, gateway)) { - /* add a direct route to the gateway */ - NMPlatformIP6Route r = *route; - - r.network = *gateway; - r.plen = 128; - r.gateway = in6addr_any; - nm_ip6_config_add_route (composite, &r); - } - } - } - } else { - /* For interfaces that are assumed and that have no default-route by configuration, we assume - * the default connection and pick up whatever is configured. */ - priv->default_route.v6_has = _device_get_default_route_from_platform (self, AF_INET6, (NMPlatformIPRoute *) route); - } + priv->default_route.v6_has = _device_get_default_route_from_platform (self, AF_INET6, (NMPlatformIPRoute *) &priv->default_route.v6); } nm_ip6_config_addresses_sort (composite, @@ -3585,7 +3984,7 @@ ip6_config_merge_and_apply (NMDevice *self, NM_DEVICE_GET_CLASS (self)->ip6_config_pre_commit (self, composite); } - success = nm_device_set_ip6_config (self, composite, commit, out_reason); + success = nm_device_set_ip6_config (self, composite, commit, routes_full_sync, out_reason); g_object_unref (composite); return success; } @@ -3623,7 +4022,7 @@ dhcp6_fail (NMDevice *self, gboolean timeout) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - dhcp6_cleanup (self, TRUE, FALSE); + dhcp6_cleanup (self, CLEANUP_TYPE_DECONFIGURE, FALSE); if (priv->dhcp6_mode == NM_RDISC_DHCP_LEVEL_MANAGED) { if (timeout || (priv->ip6_state == IP_CONF)) @@ -3648,7 +4047,7 @@ dhcp6_timeout (NMDevice *self, NMDhcpClient *client) dhcp6_fail (self, TRUE); else { /* not a hard failure; just live with the RA info */ - dhcp6_cleanup (self, TRUE, FALSE); + dhcp6_cleanup (self, CLEANUP_TYPE_DECONFIGURE, FALSE); if (priv->ip6_state == IP_CONF) nm_device_activate_schedule_ip6_config_result (self); } @@ -3740,7 +4139,7 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection) s_ip6 = nm_connection_get_setting_ip6_config (connection); g_assert (s_ip6); - hw_addr = nm_platform_link_get_address (nm_device_get_ip_ifindex (self), &hw_addr_len); + hw_addr = nm_platform_link_get_address (NM_PLATFORM_GET, nm_device_get_ip_ifindex (self), &hw_addr_len); if (hw_addr_len) { tmp = g_byte_array_sized_new (hw_addr_len); g_byte_array_append (tmp, hw_addr, hw_addr_len); @@ -3823,7 +4222,7 @@ nm_device_dhcp6_renew (NMDevice *self, gboolean release) _LOGI (LOGD_DHCP6, "DHCPv6 lease renewal requested"); /* Terminate old DHCP instance and release the old lease */ - dhcp6_cleanup (self, TRUE, release); + dhcp6_cleanup (self, CLEANUP_TYPE_DECONFIGURE, release); /* Start DHCP again on the interface */ return dhcp6_start (self, FALSE, NULL); @@ -3945,7 +4344,8 @@ check_and_add_ipv6ll_addr (NMDevice *self) lladdr.s6_addr16[0] = htons (0xfe80); nm_utils_ipv6_addr_set_interface_identfier (&lladdr, iid); _LOGD (LOGD_IP6, "adding IPv6LL address %s", nm_utils_inet6_ntop (&lladdr, NULL)); - if (!nm_platform_ip6_address_add (ip_ifindex, + if (!nm_platform_ip6_address_add (NM_PLATFORM_GET, + ip_ifindex, lladdr, in6addr_any, 64, @@ -3995,7 +4395,7 @@ print_support_extended_ifa_flags (NMSettingIP6ConfigPrivacy use_tempaddr) if (s_libnl == -1) { s_libnl = !!nm_platform_check_support_libnl_extended_ifa_flags (); - s_kernel = !!nm_platform_check_support_kernel_extended_ifa_flags (); + s_kernel = !!nm_platform_check_support_kernel_extended_ifa_flags (NM_PLATFORM_GET); if (s_libnl && s_kernel) { nm_log_dbg (LOGD_IP6, "kernel and libnl support extended IFA_FLAGS (needed by NM for IPv6 private addresses)"); @@ -4031,6 +4431,58 @@ print_support_extended_ifa_flags (NMSettingIP6ConfigPrivacy use_tempaddr) warn = 2; } +static void nm_device_ipv6_set_mtu (NMDevice *self, guint32 mtu); + +static void +nm_device_set_mtu (NMDevice *self, guint32 mtu) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + int ifindex = nm_device_get_ifindex (self); + + if (mtu) + priv->mtu = mtu; + + /* Ensure the IPv6 MTU is still alright. */ + if (priv->ip6_mtu) + nm_device_ipv6_set_mtu (self, priv->ip6_mtu); + + if (priv->mtu != nm_platform_link_get_mtu (NM_PLATFORM_GET, ifindex)) + nm_platform_link_set_mtu (NM_PLATFORM_GET, ifindex, priv->mtu); +} + +static void +nm_device_ipv6_set_mtu (NMDevice *self, guint32 mtu) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + guint32 plat_mtu = nm_device_ipv6_sysctl_get_int32 (self, "mtu", priv->mtu); + char val[16]; + + priv->ip6_mtu = mtu ?: plat_mtu; + + if (priv->ip6_mtu && priv->mtu < priv->ip6_mtu) { + _LOGW (LOGD_DEVICE | LOGD_IP6, "Lowering IPv6 MTU (%d) to match device MTU (%d)", + priv->ip6_mtu, priv->mtu); + priv->ip6_mtu = priv->mtu; + } + + if (priv->ip6_mtu < 1280) { + _LOGW (LOGD_DEVICE | LOGD_IP6, "IPv6 MTU (%d) smaller than 1280, adjusting", + priv->ip6_mtu); + priv->ip6_mtu = 1280; + } + + if (priv->mtu < priv->ip6_mtu) { + _LOGW (LOGD_DEVICE | LOGD_IP6, "Raising device MTU (%d) to match IPv6 MTU (%d)", + priv->mtu, priv->ip6_mtu); + nm_device_set_mtu (self, priv->ip6_mtu); + } + + if (priv->ip6_mtu != plat_mtu) { + g_snprintf (val, sizeof (val), "%d", mtu); + nm_device_ipv6_sysctl_set (self, "mtu", val); + } +} + static void rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *self) { @@ -4048,7 +4500,7 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *self) * from adding a prefix route for this address. **/ system_support = nm_platform_check_support_libnl_extended_ifa_flags () && - nm_platform_check_support_kernel_extended_ifa_flags (); + nm_platform_check_support_kernel_extended_ifa_flags (NM_PLATFORM_GET); } if (system_support) @@ -4151,13 +4603,13 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *self) } if (changed & NM_RDISC_CONFIG_DHCP_LEVEL) { - dhcp6_cleanup (self, TRUE, TRUE); + dhcp6_cleanup (self, CLEANUP_TYPE_DECONFIGURE, TRUE); priv->dhcp6_mode = rdisc->dhcp_level; if (priv->dhcp6_mode != NM_RDISC_DHCP_LEVEL_NONE) { NMDeviceStateReason reason; - _LOGI (LOGD_DEVICE | LOGD_DHCP6, + _LOGD (LOGD_DEVICE | LOGD_DHCP6, "Activation: Stage 3 of 5 (IP Configure Start) starting DHCPv6" " as requested by IPv6 router..."); if (!dhcp6_start (self, FALSE, &reason)) { @@ -4169,14 +4621,10 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *self) } if (changed & NM_RDISC_CONFIG_HOP_LIMIT) - nm_platform_sysctl_set_ip6_hop_limit_safe (nm_device_get_ip_iface (self), rdisc->hop_limit); - - if (changed & NM_RDISC_CONFIG_MTU) { - char val[16]; + nm_platform_sysctl_set_ip6_hop_limit_safe (NM_PLATFORM_GET, nm_device_get_ip_iface (self), rdisc->hop_limit); - g_snprintf (val, sizeof (val), "%d", rdisc->mtu); - nm_device_ipv6_sysctl_set (self, "mtu", val); - } + if (changed & NM_RDISC_CONFIG_MTU) + priv->ip6_mtu = rdisc->mtu; nm_device_activate_schedule_ip6_config_result (self); } @@ -4213,11 +4661,12 @@ addrconf6_start_with_link_ready (NMDevice *self) g_assert (priv->rdisc); - if (!nm_device_get_ip_iface_identifier (self, &iid)) { + if (nm_platform_link_get_ipv6_token (NM_PLATFORM_GET, priv->ifindex, &iid)) { + _LOGD (LOGD_DEVICE, "IPv6 tokenized identifier present on device %s", priv->iface); + } else if (!nm_device_get_ip_iface_identifier (self, &iid)) { _LOGW (LOGD_IP6, "failed to get interface identifier; IPv6 cannot continue"); return FALSE; } - nm_rdisc_set_iid (priv->rdisc, iid); /* Apply any manual configuration before starting RA */ if (!ip6_config_merge_and_apply (self, TRUE, NULL)) @@ -4236,6 +4685,8 @@ addrconf6_start_with_link_ready (NMDevice *self) NM_RDISC_RA_TIMEOUT, G_CALLBACK (rdisc_ra_timeout), self); + + nm_rdisc_set_iid (priv->rdisc, iid); nm_rdisc_start (priv->rdisc); return TRUE; } @@ -4325,7 +4776,7 @@ save_ip6_properties (NMDevice *self) g_hash_table_remove_all (priv->ip6_saved_properties); for (i = 0; i < G_N_ELEMENTS (ip6_properties_to_save); i++) { - value = nm_platform_sysctl_get (nm_utils_ip6_property_path (ifname, ip6_properties_to_save[i])); + value = nm_platform_sysctl_get (NM_PLATFORM_GET, nm_utils_ip6_property_path (ifname, ip6_properties_to_save[i])); if (value) { g_hash_table_insert (priv->ip6_saved_properties, (char *) ip6_properties_to_save[i], @@ -4363,10 +4814,9 @@ set_nm_ipv6ll (NMDevice *self, gboolean enable) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); int ifindex = nm_device_get_ip_ifindex (self); - const char *iface = nm_device_get_ip_iface (self); char *value; - if (!nm_platform_check_support_user_ipv6ll ()) + if (!nm_platform_check_support_user_ipv6ll (NM_PLATFORM_GET)) return; priv->nm_ipv6ll = enable; @@ -4374,13 +4824,13 @@ set_nm_ipv6ll (NMDevice *self, gboolean enable) const char *detail = enable ? "enable" : "disable"; _LOGD (LOGD_IP6, "will %s userland IPv6LL", detail); - if ( !nm_platform_link_set_user_ipv6ll_enabled (ifindex, enable) - && nm_platform_get_error () != NM_PLATFORM_ERROR_NOT_FOUND) + if (!nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, ifindex, enable)) _LOGW (LOGD_IP6, "failed to %s userspace IPv6LL address handling", detail); if (enable) { /* Bounce IPv6 to ensure the kernel stops IPv6LL address generation */ - value = nm_platform_sysctl_get (nm_utils_ip6_property_path (iface, "disable_ipv6")); + value = nm_platform_sysctl_get (NM_PLATFORM_GET, + nm_utils_ip6_property_path (nm_device_get_ip_iface (self), "disable_ipv6")); if (g_strcmp0 (value, "0") == 0) nm_device_ipv6_sysctl_set (self, "disable_ipv6", "1"); g_free (value); @@ -4392,8 +4842,10 @@ set_nm_ipv6ll (NMDevice *self, gboolean enable) } } +/************************************************************************/ + static NMSettingIP6ConfigPrivacy -use_tempaddr_clamp (NMSettingIP6ConfigPrivacy use_tempaddr) +_ip6_privacy_clamp (NMSettingIP6ConfigPrivacy use_tempaddr) { switch (use_tempaddr) { case NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED: @@ -4405,45 +4857,51 @@ use_tempaddr_clamp (NMSettingIP6ConfigPrivacy use_tempaddr) } } -/* Get net.ipv6.conf.default.use_tempaddr value from /etc/sysctl.conf or - * /lib/sysctl.d/sysctl.conf - */ static NMSettingIP6ConfigPrivacy -ip6_use_tempaddr (void) +_ip6_privacy_get (NMDevice *self) { - char *contents = NULL; - const char *group_name = "[forged_group]\n"; - char *sysctl_data = NULL; - GKeyFile *keyfile; - GError *error = NULL; - gint tmp; - NMSettingIP6ConfigPrivacy ret = NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN; + NMSettingIP6ConfigPrivacy ip6_privacy; + gs_free char *value = NULL; + NMConnection *connection; - /* Read file contents to a string. */ - if (!g_file_get_contents ("/etc/sysctl.conf", &contents, NULL, NULL)) - if (!g_file_get_contents ("/lib/sysctl.d/sysctl.conf", &contents, NULL, NULL)) - return NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN; + g_return_val_if_fail (self, NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); - /* Prepend a group so that we can use GKeyFile parser. */ - sysctl_data = g_strdup_printf ("%s%s", group_name, contents); + /* 1.) First look at the per-connection setting. If it is not -1 (unknown), + * use it. */ + connection = nm_device_get_connection (self); + if (connection) { + NMSettingIPConfig *s_ip6 = nm_connection_get_setting_ip6_config (connection); - keyfile = g_key_file_new (); - if (!g_key_file_load_from_data (keyfile, sysctl_data, -1, G_KEY_FILE_NONE, NULL)) - goto done; + if (s_ip6) { + ip6_privacy = nm_setting_ip6_config_get_ip6_privacy (NM_SETTING_IP6_CONFIG (s_ip6)); + ip6_privacy = _ip6_privacy_clamp (ip6_privacy); + if (ip6_privacy != NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN) + return ip6_privacy; + } + } - tmp = g_key_file_get_integer (keyfile, "forged_group", "net.ipv6.conf.default.use_tempaddr", &error); - if (error == NULL) - ret = use_tempaddr_clamp (tmp); + value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, + "ipv6.ip6-privacy", self); -done: - g_free (contents); - g_free (sysctl_data); - g_clear_error (&error); - g_key_file_free (keyfile); + /* 2.) use the default value from the configuration. */ + ip6_privacy = _nm_utils_ascii_str_to_int64 (value, 10, + NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN, + NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR, + NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); + if (ip6_privacy != NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN) + return ip6_privacy; - return ret; + /* 3.) No valid default-value configured. Fallback to reading sysctl. + * + * Instead of reading static config files in /etc, just read the current sysctl value. + * This works as NM only writes to "/proc/sys/net/ipv6/conf/IFNAME/use_tempaddr", but leaves + * the "default" entry untouched. */ + ip6_privacy = nm_platform_sysctl_get_int32 (NM_PLATFORM_GET, "/proc/sys/net/ipv6/conf/default/use_tempaddr", NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); + return _ip6_privacy_clamp (ip6_privacy); } +/****************************************************************/ + static gboolean ip6_requires_slaves (NMConnection *connection) { @@ -4525,6 +4983,13 @@ act_stage3_ip6_config_start (NMDevice *self, return NM_ACT_STAGE_RETURN_STOP; } + /* Ensure the MTU makes sense. If it was below 1280 the kernel would not + * expose any ipv6 sysctls or allow presence of any addresses on the interface, + * including LL, which * would make it impossible to autoconfigure MTU to a + * correct value. */ + if (!nm_device_uses_assumed_connection (self)) + nm_device_ipv6_set_mtu (self, priv->ip6_mtu); + /* Any method past this point requires an IPv6LL address. Use NM-controlled * IPv6LL if this is not an assumed connection, since assumed connections * will already have IPv6 set up. @@ -4535,18 +5000,7 @@ act_stage3_ip6_config_start (NMDevice *self, /* Re-enable IPv6 on the interface */ set_disable_ipv6 (self, "0"); - /* Enable/disable IPv6 Privacy Extensions. - * If a global value is configured by sysadmin (e.g. /etc/sysctl.conf), - * use that value instead of per-connection value. - */ - ip6_privacy = ip6_use_tempaddr (); - if (ip6_privacy == NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN) { - NMSettingIPConfig *s_ip6 = nm_connection_get_setting_ip6_config (connection); - - if (s_ip6) - ip6_privacy = nm_setting_ip6_config_get_ip6_privacy (NM_SETTING_IP6_CONFIG (s_ip6)); - } - ip6_privacy = use_tempaddr_clamp (ip6_privacy); + ip6_privacy = _ip6_privacy_get (self); if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0) { if (!addrconf6_start (self, ip6_privacy)) { @@ -4734,11 +5188,11 @@ nm_device_activate_stage3_ip_config_start (gpointer user_data) priv->ip4_state = priv->ip6_state = IP_WAIT; - _LOGI (LOGD_DEVICE, "Activation: Stage 3 of 5 (IP Configure Start) started..."); + _LOGD (LOGD_DEVICE, "Activation: Stage 3 of 5 (IP Configure Start) started..."); nm_device_state_changed (self, NM_DEVICE_STATE_IP_CONFIG, NM_DEVICE_STATE_REASON_NONE); /* Device should be up before we can do anything with it */ - if (!nm_platform_link_is_up (nm_device_get_ip_ifindex (self))) + if (!nm_platform_link_is_up (NM_PLATFORM_GET, nm_device_get_ip_ifindex (self))) _LOGW (LOGD_DEVICE, "interface %s not up for IP configuration", nm_device_get_ip_iface (self)); /* If the device is a slave, then we don't do any IP configuration but we @@ -4772,7 +5226,7 @@ nm_device_activate_stage3_ip_config_start (gpointer user_data) nm_device_check_ip_failed (self, TRUE); out: - _LOGI (LOGD_DEVICE, "Activation: Stage 3 of 5 (IP Configure Start) complete."); + _LOGD (LOGD_DEVICE, "Activation: Stage 3 of 5 (IP Configure Start) complete."); return FALSE; } @@ -4796,7 +5250,7 @@ fw_change_zone_cb (GError *error, gpointer user_data) } activation_source_schedule (self, nm_device_activate_stage3_ip_config_start, 0); - _LOGI (LOGD_DEVICE, "Activation: Stage 3 of 5 (IP Configure Start) scheduled."); + _LOGD (LOGD_DEVICE, "Activation: Stage 3 of 5 (IP Configure Start) scheduled."); } /* @@ -4829,7 +5283,7 @@ nm_device_activate_schedule_stage3_ip_config_start (NMDevice *self) if (nm_device_uses_assumed_connection (self)) { _LOGD (LOGD_DEVICE, "Activation: skip setting firewall zone '%s' for assumed device", zone ? zone : "default"); activation_source_schedule (self, nm_device_activate_stage3_ip_config_start, 0); - _LOGI (LOGD_DEVICE, "Activation: Stage 3 of 5 (IP Configure Start) scheduled."); + _LOGD (LOGD_DEVICE, "Activation: Stage 3 of 5 (IP Configure Start) scheduled."); return; } @@ -4870,8 +5324,7 @@ nm_device_activate_ip4_config_timeout (gpointer user_data) /* Clear the activation source ID now that this stage has run */ activation_source_clear (self, FALSE, AF_INET); - _LOGI (LOGD_DEVICE | LOGD_IP4, - "Activation: Stage 4 of 5 (IPv4 Configure Timeout) started..."); + _LOGD (LOGD_DEVICE | LOGD_IP4, "Activation: Stage 4 of 5 (IPv4 Configure Timeout) started..."); ret = NM_DEVICE_GET_CLASS (self)->act_stage4_ip4_config_timeout (self, &reason); if (ret == NM_ACT_STAGE_RETURN_POSTPONE) @@ -4887,8 +5340,7 @@ nm_device_activate_ip4_config_timeout (gpointer user_data) nm_device_check_ip_failed (self, FALSE); out: - _LOGI (LOGD_DEVICE | LOGD_IP4, - "Activation: Stage 4 of 5 (IPv4 Configure Timeout) complete."); + _LOGD (LOGD_DEVICE | LOGD_IP4, "Activation: Stage 4 of 5 (IPv4 Configure Timeout) complete."); return FALSE; } @@ -4911,8 +5363,7 @@ nm_device_activate_schedule_ip4_config_timeout (NMDevice *self) activation_source_schedule (self, nm_device_activate_ip4_config_timeout, AF_INET); - _LOGI (LOGD_DEVICE | LOGD_IP4, - "Activation: Stage 4 of 5 (IPv4 Configure Timeout) scheduled..."); + _LOGD (LOGD_DEVICE | LOGD_IP4, "Activation: Stage 4 of 5 (IPv4 Configure Timeout) scheduled..."); } @@ -4945,8 +5396,7 @@ nm_device_activate_ip6_config_timeout (gpointer user_data) /* Clear the activation source ID now that this stage has run */ activation_source_clear (self, FALSE, AF_INET6); - _LOGI (LOGD_DEVICE | LOGD_IP6, - "Activation: Stage 4 of 5 (IPv6 Configure Timeout) started..."); + _LOGD (LOGD_DEVICE | LOGD_IP6, "Activation: Stage 4 of 5 (IPv6 Configure Timeout) started..."); ret = NM_DEVICE_GET_CLASS (self)->act_stage4_ip6_config_timeout (self, &reason); if (ret == NM_ACT_STAGE_RETURN_POSTPONE) @@ -4962,8 +5412,7 @@ nm_device_activate_ip6_config_timeout (gpointer user_data) nm_device_check_ip_failed (self, FALSE); out: - _LOGI (LOGD_DEVICE | LOGD_IP6, - "Activation: Stage 4 of 5 (IPv6 Configure Timeout) complete."); + _LOGD (LOGD_DEVICE | LOGD_IP6, "Activation: Stage 4 of 5 (IPv6 Configure Timeout) complete."); return FALSE; } @@ -4986,57 +5435,33 @@ nm_device_activate_schedule_ip6_config_timeout (NMDevice *self) activation_source_schedule (self, nm_device_activate_ip6_config_timeout, AF_INET6); - _LOGI (LOGD_DEVICE | LOGD_IP6, - "Activation: Stage 4 of 5 (IPv6 Configure Timeout) scheduled..."); -} - -static void -share_child_setup (gpointer user_data G_GNUC_UNUSED) -{ - /* We are in the child process at this point */ - pid_t pid = getpid (); - setpgid (pid, pid); - - nm_unblock_posix_signals (NULL); + _LOGD (LOGD_DEVICE | LOGD_IP6, "Activation: Stage 4 of 5 (IPv6 Configure Timeout) scheduled..."); } static gboolean share_init (void) { - int status; char *modules[] = { "ip_tables", "iptable_nat", "nf_nat_ftp", "nf_nat_irc", "nf_nat_sip", "nf_nat_tftp", "nf_nat_pptp", "nf_nat_h323", NULL }; char **iter; int errsv; - if (!nm_platform_sysctl_set ("/proc/sys/net/ipv4/ip_forward", "1")) { + if (!nm_platform_sysctl_set (NM_PLATFORM_GET, "/proc/sys/net/ipv4/ip_forward", "1")) { errsv = errno; nm_log_err (LOGD_SHARING, "share: error starting IP forwarding: (%d) %s", errsv, strerror (errsv)); return FALSE; } - if (!nm_platform_sysctl_set ("/proc/sys/net/ipv4/ip_dynaddr", "1")) { + if (!nm_platform_sysctl_set (NM_PLATFORM_GET, "/proc/sys/net/ipv4/ip_dynaddr", "1")) { errsv = errno; nm_log_err (LOGD_SHARING, "share: error starting IP forwarding: (%d) %s", errsv, strerror (errsv)); } - for (iter = modules; *iter; iter++) { - char *argv[3] = { "/sbin/modprobe", *iter, NULL }; - char *envp[1] = { NULL }; - GError *error = NULL; - - if (!g_spawn_sync ("/", argv, envp, G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL, - share_child_setup, NULL, NULL, NULL, &status, &error)) { - nm_log_err (LOGD_SHARING, "share: error loading NAT module %s: (%d) %s", - *iter, error ? error->code : 0, - (error && error->message) ? error->message : "unknown"); - if (error) - g_error_free (error); - } - } + for (iter = modules; *iter; iter++) + nm_utils_modprobe (NULL, FALSE, *iter, NULL); return TRUE; } @@ -5154,8 +5579,7 @@ send_arps (NMDevice *self, const char *mode_arg) "arping: run %s", (tmp_str = g_strjoinv (" ", (char **) argv))); success = g_spawn_async (NULL, (char **) argv, NULL, G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL, - nm_unblock_posix_signals, - NULL, NULL, &error); + NULL, NULL, NULL, &error); if (!success) { _LOGW (LOGD_DEVICE | LOGD_IP4, "arping: could not send ARP for local address %s: %s", @@ -5231,7 +5655,7 @@ nm_device_activate_ip4_config_commit (gpointer user_data) /* Clear the activation source ID now that this stage has run */ activation_source_clear (self, FALSE, AF_INET); - _LOGI (LOGD_DEVICE, "Activation: Stage 5 of 5 (IPv4 Commit) started..."); + _LOGD (LOGD_DEVICE, "Activation: Stage 5 of 5 (IPv4 Commit) started..."); req = nm_device_get_act_request (self); g_assert (req); @@ -5240,16 +5664,15 @@ nm_device_activate_ip4_config_commit (gpointer user_data) /* Interface must be IFF_UP before IP config can be applied */ ip_ifindex = nm_device_get_ip_ifindex (self); - if (!nm_platform_link_is_up (ip_ifindex) && !nm_device_uses_assumed_connection (self)) { - nm_platform_link_set_up (ip_ifindex); - if (!nm_platform_link_is_up (ip_ifindex)) + if (!nm_platform_link_is_up (NM_PLATFORM_GET, ip_ifindex) && !nm_device_uses_assumed_connection (self)) { + nm_platform_link_set_up (NM_PLATFORM_GET, ip_ifindex, NULL); + if (!nm_platform_link_is_up (NM_PLATFORM_GET, ip_ifindex)) _LOGW (LOGD_DEVICE, "interface %s not up for IP configuration", nm_device_get_ip_iface (self)); } /* NULL to use the existing priv->dev_ip4_config */ if (!ip4_config_merge_and_apply (self, NULL, TRUE, &reason)) { - _LOGI (LOGD_DEVICE | LOGD_IP4, - "Activation: Stage 5 of 5 (IPv4 Commit) failed"); + _LOGD (LOGD_DEVICE | LOGD_IP4, "Activation: Stage 5 of 5 (IPv4 Commit) failed"); nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); goto out; } @@ -5291,11 +5714,28 @@ nm_device_activate_ip4_config_commit (gpointer user_data) nm_device_state_changed (self, NM_DEVICE_STATE_IP_CHECK, NM_DEVICE_STATE_REASON_NONE); out: - _LOGI (LOGD_DEVICE, "Activation: Stage 5 of 5 (IPv4 Commit) complete."); + _LOGD (LOGD_DEVICE, "Activation: Stage 5 of 5 (IPv4 Commit) complete."); return FALSE; } +static void +nm_device_queued_ip_config_change_clear (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + if (priv->queued_ip4_config_id) { + _LOGD (LOGD_DEVICE, "clearing queued IP4 config change"); + g_source_remove (priv->queued_ip4_config_id); + priv->queued_ip4_config_id = 0; + } + if (priv->queued_ip6_config_id) { + _LOGD (LOGD_DEVICE, "clearing queued IP6 config change"); + g_source_remove (priv->queued_ip6_config_id); + priv->queued_ip6_config_id = 0; + } +} + void nm_device_activate_schedule_ip4_config_result (NMDevice *self, NMIP4Config *config) { @@ -5308,10 +5748,10 @@ nm_device_activate_schedule_ip4_config_result (NMDevice *self, NMIP4Config *conf if (config) priv->dev_ip4_config = g_object_ref (config); + nm_device_queued_ip_config_change_clear (self); activation_source_schedule (self, nm_device_activate_ip4_config_commit, AF_INET); - _LOGI (LOGD_DEVICE | LOGD_IP4, - "Activation: Stage 5 of 5 (IPv4 Configure Commit) scheduled..."); + _LOGD (LOGD_DEVICE | LOGD_IP4, "Activation: Stage 5 of 5 (IPv4 Configure Commit) scheduled..."); } gboolean @@ -5333,7 +5773,6 @@ nm_device_activate_ip6_config_commit (gpointer user_data) { NMDevice *self = NM_DEVICE (user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - guint level = (priv->ip6_state == IP_DONE) ? LOGL_DEBUG : LOGL_INFO; NMActRequest *req; NMConnection *connection; NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; @@ -5342,7 +5781,7 @@ nm_device_activate_ip6_config_commit (gpointer user_data) /* Clear the activation source ID now that this stage has run */ activation_source_clear (self, FALSE, AF_INET6); - _LOG (level, LOGD_DEVICE, "Activation: Stage 5 of 5 (IPv6 Commit) started..."); + _LOGD (LOGD_DEVICE, "Activation: Stage 5 of 5 (IPv6 Commit) started..."); req = nm_device_get_act_request (self); g_assert (req); @@ -5351,9 +5790,9 @@ nm_device_activate_ip6_config_commit (gpointer user_data) /* Interface must be IFF_UP before IP config can be applied */ ip_ifindex = nm_device_get_ip_ifindex (self); - if (!nm_platform_link_is_up (ip_ifindex) && !nm_device_uses_assumed_connection (self)) { - nm_platform_link_set_up (ip_ifindex); - if (!nm_platform_link_is_up (ip_ifindex)) + if (!nm_platform_link_is_up (NM_PLATFORM_GET, ip_ifindex) && !nm_device_uses_assumed_connection (self)) { + nm_platform_link_set_up (NM_PLATFORM_GET, ip_ifindex, NULL); + if (!nm_platform_link_is_up (NM_PLATFORM_GET, ip_ifindex)) _LOGW (LOGD_DEVICE, "interface %s not up for IP configuration", nm_device_get_ip_iface (self)); } @@ -5382,12 +5821,11 @@ nm_device_activate_ip6_config_commit (gpointer user_data) if (nm_device_get_state (self) == NM_DEVICE_STATE_IP_CONFIG) nm_device_state_changed (self, NM_DEVICE_STATE_IP_CHECK, NM_DEVICE_STATE_REASON_NONE); } else { - _LOGW (LOGD_DEVICE | LOGD_IP6, - "Activation: Stage 5 of 5 (IPv6 Commit) failed"); + _LOGW (LOGD_DEVICE | LOGD_IP6, "Activation: Stage 5 of 5 (IPv6 Commit) failed"); nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); } - _LOG (level, LOGD_DEVICE, "Activation: Stage 5 of 5 (IPv6 Commit) complete."); + _LOGD (LOGD_DEVICE, "Activation: Stage 5 of 5 (IPv6 Commit) complete."); return FALSE; } @@ -5396,7 +5834,6 @@ void nm_device_activate_schedule_ip6_config_result (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - guint level = (priv->ip6_state == IP_DONE) ? LOGL_DEBUG : LOGL_INFO; g_return_if_fail (NM_IS_DEVICE (self)); @@ -5408,8 +5845,7 @@ nm_device_activate_schedule_ip6_config_result (NMDevice *self) activation_source_schedule (self, nm_device_activate_ip6_config_commit, AF_INET6); - _LOG (level, LOGD_DEVICE | LOGD_IP6, - "Activation: Stage 5 of 5 (IPv6 Commit) scheduled..."); + _LOGD (LOGD_DEVICE | LOGD_IP6, "Activation: Stage 5 of 5 (IPv6 Commit) scheduled..."); } gboolean @@ -5525,7 +5961,7 @@ delete_on_deactivate_link_delete (gpointer user_data) _LOGD (LOGD_DEVICE, "delete_on_deactivate: cleanup and delete virtual link #%d (id=%u)", data->ifindex, data->idle_add_id); - nm_platform_link_delete (data->ifindex); + nm_platform_link_delete (NM_PLATFORM_GET, data->ifindex); g_free (data); return FALSE; } @@ -5659,7 +6095,7 @@ delete_cb (NMDevice *self, } /* Authorized */ - nm_platform_link_delete (nm_device_get_ifindex (self)); + nm_platform_link_delete (NM_PLATFORM_GET, nm_device_get_ifindex (self)); dbus_g_method_return (context); } @@ -5687,22 +6123,29 @@ impl_device_delete (NMDevice *self, DBusGMethodInvocation *context) NULL); } -static void +static gboolean _device_activate (NMDevice *self, NMActRequest *req) { NMDevicePrivate *priv; NMConnection *connection; - g_return_if_fail (NM_IS_DEVICE (self)); - g_return_if_fail (NM_IS_ACT_REQUEST (req)); + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + g_return_val_if_fail (NM_IS_ACT_REQUEST (req), FALSE); + + /* Ensure the activation request is still valid; the master may have + * already failed in which case activation of this device should not proceed. + */ + if (nm_active_connection_get_state (NM_ACTIVE_CONNECTION (req)) >= NM_ACTIVE_CONNECTION_STATE_DEACTIVATING) + return FALSE; priv = NM_DEVICE_GET_PRIVATE (self); connection = nm_act_request_get_connection (req); g_assert (connection); - _LOGI (LOGD_DEVICE, "Activation: starting connection '%s'", - nm_connection_get_id (connection)); + _LOGI (LOGD_DEVICE, "Activation: starting connection '%s' (%s)", + nm_connection_get_id (connection), + nm_connection_get_uuid (connection)); delete_on_deactivate_unschedule (self); @@ -5719,6 +6162,7 @@ _device_activate (NMDevice *self, NMActRequest *req) priv->act_request = g_object_ref (req); nm_device_activate_schedule_stage1_device_prepare (self); + return TRUE; } static void @@ -5785,6 +6229,26 @@ _carrier_wait_check_act_request_must_queue (NMDevice *self, NMActRequest *req) } void +nm_device_steal_connection (NMDevice *self, NMConnection *connection) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + _LOGW (LOGD_DEVICE, "disconnecting connection '%s' for new activation request.", + nm_connection_get_id (connection)); + + if ( priv->queued_act_request + && connection == nm_active_connection_get_connection (NM_ACTIVE_CONNECTION (priv->queued_act_request))) + _clear_queued_act_request (priv); + + if ( priv->act_request + && connection == nm_active_connection_get_connection (NM_ACTIVE_CONNECTION (priv->act_request)) + && priv->state < NM_DEVICE_STATE_DEACTIVATING) + nm_device_state_changed (self, + NM_DEVICE_STATE_DEACTIVATING, + NM_DEVICE_STATE_REASON_NEW_ACTIVATION); +} + +void nm_device_queue_activation (NMDevice *self, NMActRequest *req) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); @@ -5794,7 +6258,8 @@ nm_device_queue_activation (NMDevice *self, NMActRequest *req) if (!priv->act_request && !must_queue) { /* Just activate immediately */ - _device_activate (self, req); + if (!_device_activate (self, req)) + g_assert_not_reached (); return; } @@ -5805,8 +6270,8 @@ nm_device_queue_activation (NMDevice *self, NMActRequest *req) _LOGD (LOGD_DEVICE, "queue activation request waiting for %s", must_queue ? "carrier" : "currently active connection to disconnect"); + /* Deactivate existing activation request first */ if (priv->act_request) { - /* Deactivate existing activation request first */ _LOGI (LOGD_DEVICE, "disconnecting for new activation request."); nm_device_state_changed (self, NM_DEVICE_STATE_DEACTIVATING, @@ -5864,6 +6329,7 @@ nm_device_set_ip4_config (NMDevice *self, NMIP4Config *new_config, guint32 default_route_metric, gboolean commit, + gboolean routes_full_sync, NMDeviceStateReason *reason) { NMDevicePrivate *priv; @@ -5886,10 +6352,14 @@ nm_device_set_ip4_config (NMDevice *self, if (commit && new_config) { gboolean assumed = nm_device_uses_assumed_connection (self); - /* for assumed devices we set the device_route_metric to the default which will - * stop nm_platform_ip4_address_sync() to replace the device routes. */ + nm_device_set_mtu (self, nm_ip4_config_get_mtu (new_config)); + + /* For assumed devices we must not touch the kernel-routes, such as the device-route. + * FIXME: this is wrong in case where "assumed" means "take-over-seamlessly". In this + * case, we should manage the device route, for example on new DHCP lease. */ success = nm_ip4_config_commit (new_config, ip_ifindex, - assumed ? NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE : default_route_metric); + routes_full_sync, + assumed ? (gint64) -1 : (gint64) default_route_metric); if (!success) reason_local = NM_DEVICE_STATE_REASON_CONFIG_FAILED; } @@ -5994,6 +6464,7 @@ static gboolean nm_device_set_ip6_config (NMDevice *self, NMIP6Config *new_config, gboolean commit, + gboolean routes_full_sync, NMDeviceStateReason *reason) { NMDevicePrivate *priv; @@ -6014,7 +6485,10 @@ nm_device_set_ip6_config (NMDevice *self, /* Always commit to nm-platform to update lifetimes */ if (commit && new_config) { - success = nm_ip6_config_commit (new_config, ip_ifindex); + nm_device_ipv6_set_mtu (self, priv->ip6_mtu); + success = nm_ip6_config_commit (new_config, + ip_ifindex, + routes_full_sync); if (!success) reason_local = NM_DEVICE_STATE_REASON_CONFIG_FAILED; } @@ -6187,19 +6661,67 @@ ip_check_gw_ping_cleanup (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (priv->gw_ping.watch) { - g_source_remove (priv->gw_ping.watch); - priv->gw_ping.watch = 0; - } - if (priv->gw_ping.timeout) { - g_source_remove (priv->gw_ping.timeout); - priv->gw_ping.timeout = 0; - } + nm_clear_g_source (&priv->gw_ping.watch); + nm_clear_g_source (&priv->gw_ping.timeout); if (priv->gw_ping.pid) { nm_utils_kill_child_async (priv->gw_ping.pid, SIGTERM, priv->gw_ping.log_domain, "ping", 1000, NULL, NULL); priv->gw_ping.pid = 0; } + + g_clear_pointer (&priv->gw_ping.binary, g_free); + g_clear_pointer (&priv->gw_ping.address, g_free); +} + +static gboolean +spawn_ping (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + gs_free char *str_timeout = NULL; + gs_free char *tmp_str = NULL; + const char *args[] = { priv->gw_ping.binary, "-I", nm_device_get_ip_iface (self), + "-c", "1", "-w", NULL, priv->gw_ping.address, NULL }; + gs_free_error GError *error = NULL; + gboolean ret; + + args[6] = str_timeout = g_strdup_printf ("%u", priv->gw_ping.deadline); + tmp_str = g_strjoinv (" ", (gchar **) args); + _LOGD (priv->gw_ping.log_domain, "ping: running '%s'", tmp_str); + + ret = g_spawn_async ("/", + (gchar **) args, + NULL, + G_SPAWN_DO_NOT_REAP_CHILD, + NULL, + NULL, + &priv->gw_ping.pid, + &error); + + if (!ret) { + _LOGW (priv->gw_ping.log_domain, "ping: could not spawn %s: %s", + priv->gw_ping.binary, error->message); + } + + return ret; +} + +static gboolean +respawn_ping_cb (gpointer user_data) +{ + NMDevice *self = NM_DEVICE (user_data); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + priv->gw_ping.watch = 0; + + if (spawn_ping (self)) { + priv->gw_ping.watch = g_child_watch_add (priv->gw_ping.pid, + ip_check_ping_watch_cb, self); + } else { + ip_check_gw_ping_cleanup (self); + ip_check_pre_up (self); + } + + return FALSE; } static void @@ -6208,6 +6730,7 @@ ip_check_ping_watch_cb (GPid pid, gint status, gpointer user_data) NMDevice *self = NM_DEVICE (user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); guint log_domain = priv->gw_ping.log_domain; + gboolean success = FALSE; if (!priv->gw_ping.watch) return; @@ -6215,18 +6738,25 @@ ip_check_ping_watch_cb (GPid pid, gint status, gpointer user_data) priv->gw_ping.pid = 0; if (WIFEXITED (status)) { - if (WEXITSTATUS (status) == 0) + if (WEXITSTATUS (status) == 0) { _LOGD (log_domain, "ping: gateway ping succeeded"); - else { + success = TRUE; + } else { _LOGW (log_domain, "ping: gateway ping failed with error code %d", WEXITSTATUS (status)); } } else _LOGW (log_domain, "ping: stopped unexpectedly with status %d", status); - /* We've got connectivity, proceed to pre_up */ - ip_check_gw_ping_cleanup (self); - ip_check_pre_up (self); + if (success) { + /* We've got connectivity, proceed to pre_up */ + ip_check_gw_ping_cleanup (self); + ip_check_pre_up (self); + } else { + /* If ping exited with an error it may have returned early, + * wait 1 second and restart it */ + priv->gw_ping.watch = g_timeout_add_seconds (1, respawn_ping_cb, self); + } } static gboolean @@ -6245,46 +6775,30 @@ ip_check_ping_timeout_cb (gpointer user_data) } static gboolean -spawn_ping (NMDevice *self, +start_ping (NMDevice *self, guint log_domain, const char *binary, const char *address, guint timeout) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - const char *args[] = { binary, "-I", nm_device_get_ip_iface (self), "-c", "1", "-w", NULL, address, NULL }; - GError *error = NULL; - char *str_timeout; - gs_free char *tmp_str = NULL; - gboolean success; g_return_val_if_fail (priv->gw_ping.watch == 0, FALSE); g_return_val_if_fail (priv->gw_ping.timeout == 0, FALSE); - args[6] = str_timeout = g_strdup_printf ("%u", timeout); - - _LOGD (log_domain, "ping: running '%s'", - (tmp_str = g_strjoinv (" ", (gchar **) args))); + priv->gw_ping.log_domain = log_domain; + priv->gw_ping.address = g_strdup (address); + priv->gw_ping.binary = g_strdup (binary); + priv->gw_ping.deadline = timeout + 10; /* the proper termination is enforced by a timer */ - success = g_spawn_async ("/", - (gchar **) args, - NULL, - G_SPAWN_DO_NOT_REAP_CHILD, - nm_unblock_posix_signals, - NULL, - &priv->gw_ping.pid, - &error); - if (success) { - priv->gw_ping.log_domain = log_domain; + if (spawn_ping (self)) { priv->gw_ping.watch = g_child_watch_add (priv->gw_ping.pid, ip_check_ping_watch_cb, self); - priv->gw_ping.timeout = g_timeout_add_seconds (timeout + 1, ip_check_ping_timeout_cb, self); - } else { - _LOGW (log_domain, "ping: could not spawn %s: %s", binary, error->message); - g_clear_error (&error); + priv->gw_ping.timeout = g_timeout_add_seconds (timeout, ip_check_ping_timeout_cb, self); + return TRUE; } - g_free (str_timeout); - return success; + ip_check_gw_ping_cleanup (self); + return FALSE; } static void @@ -6337,7 +6851,7 @@ nm_device_start_ip_check (NMDevice *self) } if (buf[0]) - spawn_ping (self, log_domain, ping_binary, buf, timeout); + start_ping (self, log_domain, ping_binary, buf, timeout); /* If no ping was started, just advance to pre_up */ if (!priv->gw_ping.pid) @@ -6375,7 +6889,7 @@ is_up (NMDevice *self) { int ifindex = nm_device_get_ip_ifindex (self); - return ifindex > 0 ? nm_platform_link_is_up (ifindex) : TRUE; + return ifindex > 0 ? nm_platform_link_is_up (NM_PLATFORM_GET, ifindex) : TRUE; } gboolean @@ -6400,7 +6914,7 @@ nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware) do { g_usleep (200); - if (!nm_platform_link_refresh (ifindex)) + if (!nm_platform_link_refresh (NM_PLATFORM_GET, ifindex)) return FALSE; device_is_up = nm_device_is_up (self); } while (!device_is_up && nm_utils_get_monotonic_timestamp_us () < wait_until); @@ -6419,7 +6933,7 @@ nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware) * complete (via a pending action) until either the carrier turns on, or * a timeout is reached. */ - if (device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT)) { + if (nm_device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT)) { if (priv->carrier_wait_id) g_source_remove (priv->carrier_wait_id); else @@ -6434,15 +6948,6 @@ nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware) return TRUE; } -static void -check_carrier (NMDevice *self) -{ - int ifindex = nm_device_get_ip_ifindex (self); - - if (!device_has_capability (self, NM_DEVICE_CAP_NONSTANDARD_CARRIER)) - nm_device_set_carrier (self, nm_platform_link_is_connected (ifindex)); -} - static gboolean bring_up (NMDevice *self, gboolean *no_firmware) { @@ -6455,12 +6960,10 @@ bring_up (NMDevice *self, gboolean *no_firmware) return TRUE; } - result = nm_platform_link_set_up (ifindex); - if (no_firmware) - *no_firmware = nm_platform_get_error () == NM_PLATFORM_ERROR_NO_FIRMWARE; + result = nm_platform_link_set_up (NM_PLATFORM_GET, ifindex, no_firmware); /* Store carrier immediately. */ - if (result && device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT)) + if (result && nm_device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT)) check_carrier (self); return result; @@ -6487,7 +6990,7 @@ nm_device_take_down (NMDevice *self, gboolean block) do { g_usleep (200); - if (!nm_platform_link_refresh (ifindex)) + if (!nm_platform_link_refresh (NM_PLATFORM_GET, ifindex)) return; device_is_up = nm_device_is_up (self); } while (device_is_up && nm_utils_get_monotonic_timestamp_us () < wait_until); @@ -6507,7 +7010,7 @@ take_down (NMDevice *self) int ifindex = nm_device_get_ip_ifindex (self); if (ifindex > 0) - return nm_platform_link_set_down (ifindex); + return nm_platform_link_set_down (NM_PLATFORM_GET, ifindex); /* devices without ifindex are always up. */ _LOGD (LOGD_HW, "cannot take down device without ifindex"); @@ -6634,11 +7137,10 @@ capture_lease_config (NMDevice *self, } static void -update_ip_config (NMDevice *self, gboolean initial) +update_ip4_config (NMDevice *self, gboolean initial) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); int ifindex; - gboolean linklocal6_just_completed = FALSE; gboolean capture_resolv_conf; NMDnsManagerResolvConfMode resolv_conf_mode; @@ -6652,13 +7154,35 @@ update_ip_config (NMDevice *self, gboolean initial) /* IPv4 */ g_clear_object (&priv->ext_ip4_config); priv->ext_ip4_config = nm_ip4_config_capture (ifindex, capture_resolv_conf); - priv->ext_ip4_config_had_any_addresses = ( priv->ext_ip4_config - && nm_ip4_config_get_num_addresses (priv->ext_ip4_config) > 0); if (priv->ext_ip4_config) { if (initial) { g_clear_object (&priv->dev_ip4_config); capture_lease_config (self, priv->ext_ip4_config, &priv->dev_ip4_config, NULL, NULL); } + + /* FIXME: ext_ip4_config does not contain routes with source==RTPROT_KERNEL. + * Hence, we will wrongly remove device-routes with metric=0 if they were added by + * the user on purpose. This should be fixed by also tracking and exposing + * kernel routes. */ + + /* This function was called upon external changes. Remove the configuration + * (adresses,routes) that is no longer present externally from the interal + * config. This way, we don't readd addresses that were manually removed + * by the user. */ + if (priv->con_ip4_config) + nm_ip4_config_intersect (priv->con_ip4_config, priv->ext_ip4_config); + if (priv->dev_ip4_config) + nm_ip4_config_intersect (priv->dev_ip4_config, priv->ext_ip4_config); + if (priv->vpn4_config) + nm_ip4_config_intersect (priv->vpn4_config, priv->ext_ip4_config); + if (priv->wwan_ip4_config) + nm_ip4_config_intersect (priv->wwan_ip4_config, priv->ext_ip4_config); + + /* Remove parts from ext_ip4_config to only contain the information that + * was configured externally -- we already have the same configuration from + * internal origins. */ + if (priv->con_ip4_config) + nm_ip4_config_subtract (priv->ext_ip4_config, priv->con_ip4_config); if (priv->dev_ip4_config) nm_ip4_config_subtract (priv->ext_ip4_config, priv->dev_ip4_config); if (priv->vpn4_config) @@ -6668,18 +7192,53 @@ update_ip_config (NMDevice *self, gboolean initial) ip4_config_merge_and_apply (self, NULL, FALSE, NULL); } +} + +static void +update_ip6_config (NMDevice *self, gboolean initial) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + int ifindex; + gboolean linklocal6_just_completed = FALSE; + gboolean capture_resolv_conf; + NMDnsManagerResolvConfMode resolv_conf_mode; + + ifindex = nm_device_get_ip_ifindex (self); + if (!ifindex) + return; + + resolv_conf_mode = nm_dns_manager_get_resolv_conf_mode (nm_dns_manager_get ()); + capture_resolv_conf = initial && (resolv_conf_mode == NM_DNS_MANAGER_RESOLV_CONF_EXPLICIT); /* IPv6 */ g_clear_object (&priv->ext_ip6_config); priv->ext_ip6_config = nm_ip6_config_capture (ifindex, capture_resolv_conf, NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); - priv->ext_ip6_config_had_any_addresses = ( priv->ext_ip6_config - && nm_ip6_config_get_num_addresses (priv->ext_ip6_config) > 0); if (priv->ext_ip6_config) { /* Check this before modifying ext_ip6_config */ linklocal6_just_completed = priv->linklocal6_timeout_id && have_ip6_address (priv->ext_ip6_config, TRUE); + /* This function was called upon external changes. Remove the configuration + * (adresses,routes) that is no longer present externally from the interal + * config. This way, we don't readd addresses that were manually removed + * by the user. */ + if (priv->con_ip6_config) + nm_ip6_config_intersect (priv->con_ip6_config, priv->ext_ip6_config); + if (priv->ac_ip6_config) + nm_ip6_config_intersect (priv->ac_ip6_config, priv->ext_ip6_config); + if (priv->dhcp6_ip6_config) + nm_ip6_config_intersect (priv->dhcp6_ip6_config, priv->ext_ip6_config); + if (priv->wwan_ip6_config) + nm_ip6_config_intersect (priv->wwan_ip6_config, priv->ext_ip6_config); + if (priv->vpn6_config) + nm_ip6_config_intersect (priv->vpn6_config, priv->ext_ip6_config); + + /* Remove parts from ext_ip6_config to only contain the information that + * was configured externally -- we already have the same configuration from + * internal origins. */ + if (priv->con_ip6_config) + nm_ip6_config_subtract (priv->ext_ip6_config, priv->con_ip6_config); if (priv->ac_ip6_config) nm_ip6_config_subtract (priv->ext_ip6_config, priv->ac_ip6_config); if (priv->dhcp6_ip6_config) @@ -6703,11 +7262,12 @@ update_ip_config (NMDevice *self, gboolean initial) void nm_device_capture_initial_config (NMDevice *self) { - update_ip_config (self, TRUE); + update_ip4_config (self, TRUE); + update_ip6_config (self, TRUE); } static gboolean -queued_ip_config_change (gpointer user_data) +queued_ip4_config_change (gpointer user_data) { NMDevice *self = NM_DEVICE (user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); @@ -6716,8 +7276,27 @@ queued_ip_config_change (gpointer user_data) if (priv->queued_state.id) return TRUE; - priv->queued_ip_config_id = 0; - update_ip_config (self, FALSE); + priv->queued_ip4_config_id = 0; + g_object_ref (self); + update_ip4_config (self, FALSE); + g_object_unref (self); + + return FALSE; +} + +static gboolean +queued_ip6_config_change (gpointer user_data) +{ + NMDevice *self = NM_DEVICE (user_data); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + /* Wait for any queued state changes */ + if (priv->queued_state.id) + return TRUE; + + priv->queued_ip6_config_id = 0; + g_object_ref (self); + update_ip6_config (self, FALSE); /* If no IPv6 link-local address exists but other addresses do then we * must add the LL address to remain conformant with RFC 3513 chapter 2.1 @@ -6727,36 +7306,43 @@ queued_ip_config_change (gpointer user_data) if (priv->ip6_config && nm_ip6_config_get_num_addresses (priv->ip6_config)) check_and_add_ipv6ll_addr (self); + g_object_unref (self); + return FALSE; } static void -device_ip_changed (NMPlatform *platform, - int ifindex, - gpointer platform_object, - NMPlatformSignalChangeType change_type, - NMPlatformReason reason, - NMDevice *self) +device_ipx_changed (NMPlatform *platform, + NMPObjectType obj_type, + int ifindex, + gpointer platform_object, + NMPlatformSignalChangeType change_type, + NMPlatformReason reason, + NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - - if (nm_device_get_ip_ifindex (self) == ifindex) { - if (!priv->queued_ip_config_id) - priv->queued_ip_config_id = g_idle_add (queued_ip_config_change, self); - - _LOGD (LOGD_DEVICE, "queued IP config change"); - } -} + NMDevicePrivate *priv; -static void -nm_device_queued_ip_config_change_clear (NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + if (nm_device_get_ip_ifindex (self) != ifindex) + return; - if (priv->queued_ip_config_id) { - _LOGD (LOGD_DEVICE, "clearing queued IP config change"); - g_source_remove (priv->queued_ip_config_id); - priv->queued_ip_config_id = 0; + priv = NM_DEVICE_GET_PRIVATE (self); + switch (obj_type) { + case NMP_OBJECT_TYPE_IP4_ADDRESS: + case NMP_OBJECT_TYPE_IP4_ROUTE: + if (!priv->queued_ip4_config_id) { + priv->queued_ip4_config_id = g_idle_add (queued_ip4_config_change, self); + _LOGD (LOGD_DEVICE, "queued IP4 config change"); + } + break; + case NMP_OBJECT_TYPE_IP6_ADDRESS: + case NMP_OBJECT_TYPE_IP6_ROUTE: + if (!priv->queued_ip6_config_id) { + priv->queued_ip6_config_id = g_idle_add (queued_ip6_config_change, self); + _LOGD (LOGD_DEVICE, "queued IP6 config change"); + } + break; + default: + g_return_if_reached (); } } @@ -6840,7 +7426,7 @@ nm_device_set_unmanaged (NMDevice *self, if (unmanaged) nm_device_state_changed (self, NM_DEVICE_STATE_UNMANAGED, reason); - else + else if (nm_device_get_state (self) == NM_DEVICE_STATE_UNMANAGED) nm_device_state_changed (self, NM_DEVICE_STATE_UNAVAILABLE, reason); } } @@ -7099,7 +7685,7 @@ cp_connection_updated (NMConnectionProvider *cp, NMConnection *connection, gpoin gboolean nm_device_supports_vlans (NMDevice *self) { - return nm_platform_link_supports_vlans (nm_device_get_ifindex (self)); + return nm_platform_link_supports_vlans (NM_PLATFORM_GET, nm_device_get_ifindex (self)); } /** @@ -7206,16 +7792,16 @@ nm_device_has_pending_action (NMDevice *self) /***********************************************************/ static void -_cleanup_ip_pre (NMDevice *self, gboolean deconfigure) +_cleanup_ip_pre (NMDevice *self, CleanupType cleanup_type) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); priv->ip4_state = priv->ip6_state = IP_NONE; nm_device_queued_ip_config_change_clear (self); - dhcp4_cleanup (self, deconfigure, FALSE); + dhcp4_cleanup (self, cleanup_type, FALSE); arp_cleanup (self); - dhcp6_cleanup (self, deconfigure, FALSE); + dhcp6_cleanup (self, cleanup_type, FALSE); linklocal6_cleanup (self); addrconf6_cleanup (self); dnsmasq_cleanup (self); @@ -7223,10 +7809,9 @@ _cleanup_ip_pre (NMDevice *self, gboolean deconfigure) } static void -_cleanup_generic_pre (NMDevice *self, gboolean deconfigure) +_cancel_activation (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMConnection *connection; /* Clean up when device was deactivated during call to firewall */ if (priv->fw_call) { @@ -7234,8 +7819,22 @@ _cleanup_generic_pre (NMDevice *self, gboolean deconfigure) priv->fw_call = NULL; } + ip_check_gw_ping_cleanup (self); + + /* Break the activation chain */ + activation_source_clear (self, TRUE, AF_INET); + activation_source_clear (self, TRUE, AF_INET6); +} + +static void +_cleanup_generic_pre (NMDevice *self, CleanupType cleanup_type) +{ + NMConnection *connection; + + _cancel_activation (self); + connection = nm_device_get_connection (self); - if ( deconfigure + if ( cleanup_type == CLEANUP_TYPE_DECONFIGURE && connection && !nm_device_uses_assumed_connection (self)) { nm_firewall_manager_remove_from_zone (nm_firewall_manager_get (), @@ -7243,28 +7842,24 @@ _cleanup_generic_pre (NMDevice *self, gboolean deconfigure) NULL); } - ip_check_gw_ping_cleanup (self); - - /* Break the activation chain */ - activation_source_clear (self, TRUE, AF_INET); - activation_source_clear (self, TRUE, AF_INET6); - /* Clear any queued transitions */ nm_device_queued_state_clear (self); - _cleanup_ip_pre (self, deconfigure); + _cleanup_ip_pre (self, cleanup_type); } static void -_cleanup_generic_post (NMDevice *self, gboolean deconfigure) +_cleanup_generic_post (NMDevice *self, CleanupType cleanup_type) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMDeviceStateReason ignored = NM_DEVICE_STATE_REASON_NONE; priv->default_route.v4_has = FALSE; priv->default_route.v4_is_assumed = TRUE; + priv->default_route.v4_configure_first_time = TRUE; priv->default_route.v6_has = FALSE; priv->default_route.v6_is_assumed = TRUE; + priv->default_route.v6_configure_first_time = TRUE; nm_default_route_manager_ip4_update_default_route (nm_default_route_manager_get (), self); nm_default_route_manager_ip6_update_default_route (nm_default_route_manager_get (), self); @@ -7272,22 +7867,21 @@ _cleanup_generic_post (NMDevice *self, gboolean deconfigure) /* Clean up IP configs; this does not actually deconfigure the * interface; the caller must flush routes and addresses explicitly. */ - nm_device_set_ip4_config (self, NULL, 0, TRUE, &ignored); - nm_device_set_ip6_config (self, NULL, TRUE, &ignored); + nm_device_set_ip4_config (self, NULL, 0, TRUE, TRUE, &ignored); + nm_device_set_ip6_config (self, NULL, TRUE, TRUE, &ignored); + g_clear_object (&priv->con_ip4_config); g_clear_object (&priv->dev_ip4_config); g_clear_object (&priv->ext_ip4_config); g_clear_object (&priv->wwan_ip4_config); g_clear_object (&priv->vpn4_config); g_clear_object (&priv->ip4_config); + g_clear_object (&priv->con_ip6_config); g_clear_object (&priv->ac_ip6_config); g_clear_object (&priv->ext_ip6_config); g_clear_object (&priv->vpn6_config); g_clear_object (&priv->wwan_ip6_config); g_clear_object (&priv->ip6_config); - priv->ext_ip4_config_had_any_addresses = FALSE; - priv->ext_ip6_config_had_any_addresses = FALSE; - clear_act_request (self); /* Clear legacy IPv4 address property */ @@ -7296,7 +7890,7 @@ _cleanup_generic_post (NMDevice *self, gboolean deconfigure) g_object_notify (G_OBJECT (self), NM_DEVICE_IP4_ADDRESS); } - if (deconfigure) { + if (cleanup_type == CLEANUP_TYPE_DECONFIGURE) { /* Check if the device was deactivated, and if so, delete_link. * Don't call delete_link synchronously because we are currently * handling a state change -- which is not reentrant. */ @@ -7317,7 +7911,7 @@ _cleanup_generic_post (NMDevice *self, gboolean deconfigure) * */ static void -nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, gboolean deconfigure) +nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType cleanup_type) { NMDevicePrivate *priv; int ifindex; @@ -7325,17 +7919,17 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, gboolean deconfig g_return_if_fail (NM_IS_DEVICE (self)); if (reason == NM_DEVICE_STATE_REASON_NOW_MANAGED) - _LOGI (LOGD_DEVICE, "preparing device"); + _LOGD (LOGD_DEVICE, "preparing device"); else - _LOGI (LOGD_DEVICE, "deactivating device (reason '%s') [%d]", reason_to_string (reason), reason); + _LOGD (LOGD_DEVICE, "deactivating device (reason '%s') [%d]", reason_to_string (reason), reason); /* Save whether or not we tried IPv6 for later */ priv = NM_DEVICE_GET_PRIVATE (self); - _cleanup_generic_pre (self, deconfigure); + _cleanup_generic_pre (self, cleanup_type); /* Turn off kernel IPv6 */ - if (deconfigure) { + if (cleanup_type == CLEANUP_TYPE_DECONFIGURE) { set_disable_ipv6 (self, "1"); nm_device_ipv6_sysctl_set (self, "accept_ra", "0"); nm_device_ipv6_sysctl_set (self, "use_tempaddr", "0"); @@ -7356,11 +7950,11 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, gboolean deconfig /* Take out any entries in the routing table and any IP address the device had. */ ifindex = nm_device_get_ip_ifindex (self); if (ifindex > 0) { - nm_platform_route_flush (ifindex); - nm_platform_address_flush (ifindex); + nm_route_manager_route_flush (nm_route_manager_get (), ifindex); + nm_platform_address_flush (NM_PLATFORM_GET, ifindex); } - _cleanup_generic_post (self, deconfigure); + _cleanup_generic_post (self, cleanup_type); } static char * @@ -7689,11 +8283,11 @@ _set_state_full (NMDevice *self, nm_device_set_firmware_missing (self, FALSE); if (old_state > NM_DEVICE_STATE_UNMANAGED) { if (reason == NM_DEVICE_STATE_REASON_REMOVED) { - nm_device_cleanup (self, reason, FALSE); + nm_device_cleanup (self, reason, CLEANUP_TYPE_REMOVED); } else { /* Clean up if the device is now unmanaged but was activated */ if (nm_device_get_act_request (self)) - nm_device_cleanup (self, reason, TRUE); + nm_device_cleanup (self, reason, CLEANUP_TYPE_DECONFIGURE); nm_device_take_down (self, TRUE); set_nm_ipv6ll (self, FALSE); restore_ip6_properties (self); @@ -7722,7 +8316,7 @@ _set_state_full (NMDevice *self, * Note that we "deactivate" the device even when coming from * UNMANAGED, to ensure that it's in a clean state. */ - nm_device_cleanup (self, reason, TRUE); + nm_device_cleanup (self, reason, CLEANUP_TYPE_DECONFIGURE); } break; case NM_DEVICE_STATE_DISCONNECTED: @@ -7732,7 +8326,7 @@ _set_state_full (NMDevice *self, */ set_nm_ipv6ll (self, TRUE); - nm_device_cleanup (self, reason, TRUE); + nm_device_cleanup (self, reason, CLEANUP_TYPE_DECONFIGURE); } else if (old_state < NM_DEVICE_STATE_DISCONNECTED) { if (reason != NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED) { /* Ensure IPv6 is set up as it may not have been done when @@ -7747,7 +8341,7 @@ _set_state_full (NMDevice *self, /* Clean up any half-done IP operations if the device's layer2 * finds out it needs authentication during IP config. */ - _cleanup_ip_pre (self, TRUE); + _cleanup_ip_pre (self, CLEANUP_TYPE_DECONFIGURE); } break; default: @@ -7774,8 +8368,9 @@ _set_state_full (NMDevice *self, * reasons. */ if (nm_device_is_available (self, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE)) { - _LOGD (LOGD_DEVICE, "device is available, will transition to DISCONNECTED"); - nm_device_queue_state (self, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_NONE); + nm_device_queue_recheck_available (self, + NM_DEVICE_STATE_REASON_NONE, + NM_DEVICE_STATE_REASON_NONE); } else { if (old_state == NM_DEVICE_STATE_UNMANAGED) _LOGD (LOGD_DEVICE, "device not yet available for transition to DISCONNECTED"); @@ -7785,6 +8380,14 @@ _set_state_full (NMDevice *self, } break; case NM_DEVICE_STATE_DEACTIVATING: + _cancel_activation (self); + + if (nm_device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT)) { + /* We cache the ignore_carrier state to not react on config-reloads while the connection + * is active. But on deactivating, reset the ignore-carrier flag to the current state. */ + priv->ignore_carrier = nm_config_data_get_ignore_carrier (NM_CONFIG_GET_DATA, self); + } + if (quitting) { nm_dispatcher_call_sync (DISPATCHER_ACTION_PRE_DOWN, nm_act_request_get_connection (req), @@ -7807,13 +8410,18 @@ _set_state_full (NMDevice *self, if ( priv->queued_act_request && !priv->queued_act_request_is_waiting_for_carrier) { NMActRequest *queued_req; + gboolean success; queued_req = priv->queued_act_request; priv->queued_act_request = NULL; - _device_activate (self, queued_req); + success = _device_activate (self, queued_req); g_object_unref (queued_req); - } else if ( old_state > NM_DEVICE_STATE_DISCONNECTED - && nm_device_get_default_unmanaged (self)) + if (success) + break; + /* fall through */ + } + if ( old_state > NM_DEVICE_STATE_DISCONNECTED + && nm_device_get_default_unmanaged (self)) nm_device_queue_state (self, NM_DEVICE_STATE_UNMANAGED, NM_DEVICE_STATE_REASON_NONE); break; case NM_DEVICE_STATE_ACTIVATED: @@ -8026,7 +8634,7 @@ nm_device_update_hw_address (NMDevice *self) if (ifindex <= 0) return; - hwaddr = nm_platform_link_get_address (ifindex, &hwaddrlen); + hwaddr = nm_platform_link_get_address (NM_PLATFORM_GET, ifindex, &hwaddrlen); if (hwaddrlen) { if (!priv->hw_addr || !nm_utils_hwaddr_matches (priv->hw_addr, -1, hwaddr, hwaddrlen)) { @@ -8072,7 +8680,7 @@ nm_device_set_hw_addr (NMDevice *self, const char *addr, /* Can't change MAC address while device is up */ nm_device_take_down (self, FALSE); - success = nm_platform_link_set_address (nm_device_get_ip_ifindex (self), addr_bytes, priv->hw_addr_len); + success = nm_platform_link_set_address (NM_PLATFORM_GET, nm_device_get_ip_ifindex (self), addr_bytes, priv->hw_addr_len); if (success) { /* MAC address succesfully changed; update the current MAC to match */ nm_device_update_hw_address (self); @@ -8094,6 +8702,22 @@ nm_device_set_hw_addr (NMDevice *self, const char *addr, return success; } +const char * +nm_device_get_permanent_hw_address (NMDevice *self) +{ + g_return_val_if_fail (NM_IS_DEVICE (self), NULL); + + return NM_DEVICE_GET_PRIVATE (self)->perm_hw_addr; +} + +const char * +nm_device_get_initial_hw_address (NMDevice *self) +{ + g_return_val_if_fail (NM_IS_DEVICE (self), NULL); + + return NM_DEVICE_GET_PRIVATE (self)->initial_hw_addr; +} + /** * nm_device_spec_match_list: * @self: an #NMDevice @@ -8123,24 +8747,34 @@ nm_device_spec_match_list (NMDevice *self, const GSList *specs) if (!specs) return FALSE; - return NM_DEVICE_GET_CLASS (self)->spec_match_list (self, specs); + return NM_DEVICE_GET_CLASS (self)->spec_match_list (self, specs) == NM_MATCH_SPEC_MATCH; } -static gboolean +static NMMatchSpecMatchType spec_match_list (NMDevice *self, const GSList *specs) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - gboolean matched = FALSE; - - if (nm_match_spec_string (specs, "*")) - return TRUE; - - if (priv->hw_addr_len) - matched = nm_match_spec_hwaddr (specs, priv->hw_addr); - - if (!matched) - matched = nm_match_spec_interface_name (specs, nm_device_get_iface (self)); + NMMatchSpecMatchType matched = NM_MATCH_SPEC_NO_MATCH, m; + const GSList *iter; + for (iter = specs; iter; iter = g_slist_next (iter)) { + if (!strcmp ((const char *) iter->data, "*")) { + matched = NM_MATCH_SPEC_MATCH; + break; + } + } + if (priv->hw_addr_len) { + m = nm_match_spec_hwaddr (specs, priv->hw_addr); + matched = MAX (matched, m); + } + if (matched != NM_MATCH_SPEC_NEG_MATCH) { + m = nm_match_spec_interface_name (specs, nm_device_get_iface (self)); + matched = MAX (matched, m); + } + if (matched != NM_MATCH_SPEC_NEG_MATCH) { + m = nm_match_spec_device_type (specs, nm_device_get_type_description (self)); + matched = MAX (matched, m); + } return matched; } @@ -8165,47 +8799,9 @@ nm_device_init (NMDevice *self) priv->ip6_saved_properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free); priv->default_route.v4_is_assumed = TRUE; + priv->default_route.v4_configure_first_time = TRUE; priv->default_route.v6_is_assumed = TRUE; -} - -/* - * Get driver info from SIOCETHTOOL ioctl() for 'iface' - * Returns driver and firmware versions to 'driver_version and' 'firmware_version' - */ -static gboolean -device_get_driver_info (NMDevice *self, const char *iface, char **driver_version, char **firmware_version) -{ - struct ethtool_drvinfo drvinfo; - struct ifreq req; - int fd; - - fd = socket (PF_INET, SOCK_DGRAM, 0); - if (fd < 0) { - _LOGW (LOGD_HW, "couldn't open control socket."); - return FALSE; - } - - /* Get driver and firmware version info */ - memset (&drvinfo, 0, sizeof (drvinfo)); - memset (&req, 0, sizeof (struct ifreq)); - strncpy (req.ifr_name, iface, IFNAMSIZ); - drvinfo.cmd = ETHTOOL_GDRVINFO; - req.ifr_data = &drvinfo; - - errno = 0; - if (ioctl (fd, SIOCETHTOOL, &req) < 0) { - _LOGD (LOGD_HW, "SIOCETHTOOL ioctl() failed: cmd=ETHTOOL_GDRVINFO, iface=%s, errno=%d", - iface, errno); - close (fd); - return FALSE; - } - if (driver_version) - *driver_version = g_strdup (drvinfo.version); - if (firmware_version) - *firmware_version = g_strdup (drvinfo.fw_version); - - close (fd); - return TRUE; + priv->default_route.v6_configure_first_time = TRUE; } static GObject* @@ -8243,27 +8839,40 @@ constructor (GType type, if (NM_DEVICE_GET_CLASS (self)->get_generic_capabilities) priv->capabilities |= NM_DEVICE_GET_CLASS (self)->get_generic_capabilities (self); - if (priv->ifindex <= 0 && !device_has_capability (self, NM_DEVICE_CAP_IS_NON_KERNEL)) - _LOGW (LOGD_HW, "failed to look up interface index"); + if (priv->ifindex > 0) { + priv->physical_port_id = nm_platform_link_get_physical_port_id (NM_PLATFORM_GET, priv->ifindex); + priv->dev_id = nm_platform_link_get_dev_id (NM_PLATFORM_GET, priv->ifindex); + if (nm_platform_link_is_software (NM_PLATFORM_GET, priv->ifindex)) + priv->capabilities |= NM_DEVICE_CAP_IS_SOFTWARE; + priv->mtu = nm_platform_link_get_mtu (NM_PLATFORM_GET, priv->ifindex); + + nm_platform_link_get_driver_info (NM_PLATFORM_GET, + priv->ifindex, + NULL, + &priv->driver_version, + &priv->firmware_version); + } - device_get_driver_info (self, priv->iface, &priv->driver_version, &priv->firmware_version); + if (NM_DEVICE_GET_CLASS (self)->get_generic_capabilities) + priv->capabilities |= NM_DEVICE_GET_CLASS (self)->get_generic_capabilities (self); /* Watch for external IP config changes */ platform = nm_platform_get (); - g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, G_CALLBACK (device_ip_changed), self); - g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, G_CALLBACK (device_ip_changed), self); - g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (device_ip_changed), self); - g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (device_ip_changed), self); + g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, G_CALLBACK (device_ipx_changed), self); + g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, G_CALLBACK (device_ipx_changed), self); + g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (device_ipx_changed), self); + g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (device_ipx_changed), self); g_signal_connect (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, G_CALLBACK (link_changed_cb), self); /* trigger initial ip config change to initialize ip-config */ - priv->queued_ip_config_id = g_idle_add (queued_ip_config_change, self); + priv->queued_ip4_config_id = g_idle_add (queued_ip4_config_change, self); + priv->queued_ip6_config_id = g_idle_add (queued_ip6_config_change, self); - if (nm_platform_check_support_user_ipv6ll ()) { + if (nm_platform_check_support_user_ipv6ll (NM_PLATFORM_GET)) { int ip_ifindex = nm_device_get_ip_ifindex (self); if (ip_ifindex > 0) - priv->nm_ipv6ll = nm_platform_link_get_user_ipv6ll_enabled (ip_ifindex); + priv->nm_ipv6ll = nm_platform_link_get_user_ipv6ll_enabled (NM_PLATFORM_GET, ip_ifindex); } return object; @@ -8282,18 +8891,39 @@ constructed (GObject *object) nm_device_update_hw_address (self); - if (NM_DEVICE_GET_CLASS (self)->update_permanent_hw_address) - NM_DEVICE_GET_CLASS (self)->update_permanent_hw_address (self); + if (priv->hw_addr_len) { + priv->initial_hw_addr = g_strdup (priv->hw_addr); + _LOGD (LOGD_DEVICE | LOGD_HW, "read initial MAC address %s", priv->initial_hw_addr); - if (NM_DEVICE_GET_CLASS (self)->update_initial_hw_address) - NM_DEVICE_GET_CLASS (self)->update_initial_hw_address (self); + if (priv->ifindex > 0) { + guint8 buf[NM_UTILS_HWADDR_LEN_MAX]; + size_t len = 0; + + if (nm_platform_link_get_permanent_address (NM_PLATFORM_GET, priv->ifindex, buf, &len)) { + g_warn_if_fail (len == priv->hw_addr_len); + priv->perm_hw_addr = nm_utils_hwaddr_ntoa (buf, priv->hw_addr_len); + _LOGD (LOGD_DEVICE | LOGD_HW, "read permanent MAC address %s", + priv->perm_hw_addr); + } else { + /* Fall back to current address */ + _LOGD (LOGD_HW | LOGD_ETHER, "unable to read permanent MAC address"); + priv->perm_hw_addr = g_strdup (priv->hw_addr); + } + } + } - /* Have to call update_initial_hw_address() before calling get_ignore_carrier() */ - if (device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT)) { - priv->ignore_carrier = nm_config_get_ignore_carrier (nm_config_get (), self); + /* Note: initial hardware address must be read before calling get_ignore_carrier() */ + if (nm_device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT)) { + NMConfig *config = nm_config_get (); + + priv->ignore_carrier = nm_config_data_get_ignore_carrier (nm_config_get_data (config), self); + g_signal_connect (G_OBJECT (config), + NM_CONFIG_SIGNAL_CONFIG_CHANGED, + G_CALLBACK (config_changed_update_ignore_carrier), + self); check_carrier (self); - _LOGI (LOGD_HW, + _LOGD (LOGD_HW, "carrier is %s%s", priv->carrier ? "ON" : "OFF", priv->ignore_carrier ? " (but ignored)" : ""); @@ -8302,18 +8932,8 @@ constructed (GObject *object) priv->carrier = TRUE; } - if (priv->ifindex > 0) { - priv->is_software = nm_platform_link_is_software (priv->ifindex); - priv->physical_port_id = nm_platform_link_get_physical_port_id (priv->ifindex); - priv->dev_id = nm_platform_link_get_dev_id (priv->ifindex); - priv->mtu = nm_platform_link_get_mtu (priv->ifindex); - } - /* Indicate software device in capabilities. */ - if (priv->is_software) - priv->capabilities |= NM_DEVICE_CAP_IS_SOFTWARE; - /* Enslave ourselves */ - master = nm_platform_link_get_master (priv->ifindex); + master = nm_platform_link_get_master (NM_PLATFORM_GET, priv->ifindex); if (master) device_set_master (self, master); @@ -8355,9 +8975,11 @@ dispose (GObject *object) _LOGD (LOGD_DEVICE, "dispose(): %s", G_OBJECT_TYPE_NAME (self)); + g_signal_handlers_disconnect_by_func (nm_config_get (), config_changed_update_ignore_carrier, self); + dispatcher_cleanup (self); - _cleanup_generic_pre (self, FALSE); + _cleanup_generic_pre (self, CLEANUP_TYPE_KEEP); g_warn_if_fail (priv->slaves == NULL); g_assert (priv->master_ready_id == 0); @@ -8365,7 +8987,7 @@ dispose (GObject *object) /* Let the kernel manage IPv6LL again */ set_nm_ipv6ll (self, FALSE); - _cleanup_generic_post (self, FALSE); + _cleanup_generic_post (self, CLEANUP_TYPE_KEEP); g_hash_table_remove_all (priv->ip6_saved_properties); @@ -8374,6 +8996,11 @@ dispose (GObject *object) priv->recheck_assume_id = 0; } + if (priv->recheck_available.call_id) { + g_source_remove (priv->recheck_available.call_id); + priv->recheck_available.call_id = 0; + } + link_disconnect_action_cancel (self); if (priv->con_provider) { @@ -8393,9 +9020,12 @@ dispose (GObject *object) _clear_queued_act_request (priv); platform = nm_platform_get (); - g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (device_ip_changed), self); + g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (device_ipx_changed), self); g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (link_changed_cb), self); + nm_clear_g_source (&priv->device_link_changed_id); + nm_clear_g_source (&priv->device_ip_link_changed_id); + G_OBJECT_CLASS (nm_device_parent_class)->dispose (object); } @@ -8408,6 +9038,8 @@ finalize (GObject *object) _LOGD (LOGD_DEVICE, "finalize(): %s", G_OBJECT_TYPE_NAME (self)); g_free (priv->hw_addr); + g_free (priv->perm_hw_addr); + g_free (priv->initial_hw_addr); g_slist_free_full (priv->pending_actions, g_free); g_clear_pointer (&priv->physical_port_id, g_free); g_free (priv->udi); @@ -8418,6 +9050,7 @@ finalize (GObject *object) g_free (priv->driver_version); g_free (priv->firmware_version); g_free (priv->type_desc); + g_free (priv->type_description); g_free (priv->dhcp_anycast_address); g_hash_table_unref (priv->ip6_saved_properties); @@ -8435,19 +9068,20 @@ set_property (GObject *object, guint prop_id, NMPlatformLink *platform_device; const char *hw_addr, *p; guint count; - + switch (prop_id) { case PROP_PLATFORM_DEVICE: platform_device = g_value_get_pointer (value); if (platform_device) { g_free (priv->udi); - priv->udi = g_strdup (platform_device->udi); + priv->udi = g_strdup (nm_platform_link_get_udi (NM_PLATFORM_GET, platform_device->ifindex)); g_free (priv->iface); priv->iface = g_strdup (platform_device->name); priv->ifindex = platform_device->ifindex; - priv->up = platform_device->up; + priv->up = NM_FLAGS_HAS (platform_device->flags, IFF_UP); g_free (priv->driver); priv->driver = g_strdup (platform_device->driver); + priv->platform_link_initialized = platform_device->initialized; } break; case PROP_UDI: @@ -8460,9 +9094,9 @@ set_property (GObject *object, guint prop_id, if (g_value_get_string (value)) { g_free (priv->iface); priv->iface = g_value_dup_string (value); - priv->ifindex = nm_platform_link_get_ifindex (priv->iface); + priv->ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, priv->iface); if (priv->ifindex > 0) - priv->up = nm_platform_link_is_up (priv->ifindex); + priv->up = nm_platform_link_is_up (NM_PLATFORM_GET, priv->ifindex); } break; case PROP_DRIVER: @@ -8696,10 +9330,12 @@ nm_device_class_init (NMDeviceClass *klass) klass->act_stage4_ip6_config_timeout = act_stage4_ip6_config_timeout; klass->have_any_ready_slaves = have_any_ready_slaves; + klass->get_type_description = get_type_description; klass->spec_match_list = spec_match_list; klass->can_auto_connect = can_auto_connect; klass->check_connection_compatible = check_connection_compatible; klass->check_connection_available = check_connection_available; + klass->can_unmanaged_external_down = can_unmanaged_external_down; klass->is_up = is_up; klass->bring_up = bring_up; klass->take_down = take_down; diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index e204af84..f1dab9bf 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -130,19 +130,17 @@ typedef struct { void (* link_changed) (NMDevice *self, NMPlatformLink *info); /* Hardware state (IFF_UP) */ - gboolean (*is_up) (NMDevice *self); - gboolean (*bring_up) (NMDevice *self, gboolean *no_firmware); - gboolean (*take_down) (NMDevice *self); + gboolean (*can_unmanaged_external_down) (NMDevice *self); + gboolean (*is_up) (NMDevice *self); + gboolean (*bring_up) (NMDevice *self, gboolean *no_firmware); + gboolean (*take_down) (NMDevice *self); /* Carrier state (IFF_LOWER_UP) */ void (*carrier_changed) (NMDevice *, gboolean carrier); - void (* update_permanent_hw_address) (NMDevice *self); - void (* update_initial_hw_address) (NMDevice *self); - gboolean (* get_ip_iface_identifier) (NMDevice *self, NMUtilsIPv6IfaceId *out_iid); - guint32 (* get_generic_capabilities) (NMDevice *self); + NMDeviceCapabilities (* get_generic_capabilities) (NMDevice *self); gboolean (* is_available) (NMDevice *self, NMDeviceCheckDevAvailableFlags flags); @@ -213,7 +211,9 @@ typedef struct { /* Sync deactivating (in the DISCONNECTED phase) */ void (* deactivate) (NMDevice *self); - gboolean (* spec_match_list) (NMDevice *self, const GSList *specs); + const char *(*get_type_description) (NMDevice *self); + + NMMatchSpecMatchType (* spec_match_list) (NMDevice *self, const GSList *specs); /* Update the connection with currently configured L2 settings */ void (* update_connection) (NMDevice *device, NMConnection *connection); @@ -235,6 +235,21 @@ typedef struct { gboolean (* have_any_ready_slaves) (NMDevice *self, const GSList *slaves); + /** + * component_added: + * @self: the #NMDevice + * @component: the component (device, modem, etc) which was added + * + * Notifies @self that a new component was added to the Manager. This + * may include any kind of %GObject subclass, and the device is expected + * to match only specific components they care about, like %NMModem objects + * or %NMDevice objects. + * + * Returns: %TRUE if the component was claimed exclusively and no further + * devices should be notified of the new component. %FALSE to indicate + * that the component was not exclusively claimed and other devices should + * be notified. + */ gboolean (* component_added) (NMDevice *self, GObject *component); gboolean (* owns_iface) (NMDevice *self, const char *iface); @@ -264,13 +279,16 @@ int nm_device_get_ip_ifindex(NMDevice *dev); const char * nm_device_get_driver (NMDevice *dev); const char * nm_device_get_driver_version (NMDevice *dev); const char * nm_device_get_type_desc (NMDevice *dev); +const char * nm_device_get_type_description (NMDevice *dev); NMDeviceType nm_device_get_device_type (NMDevice *dev); int nm_device_get_priority (NMDevice *dev); guint32 nm_device_get_ip4_route_metric (NMDevice *dev); guint32 nm_device_get_ip6_route_metric (NMDevice *dev); -const char * nm_device_get_hw_address (NMDevice *dev); +const char * nm_device_get_hw_address (NMDevice *dev); +const char * nm_device_get_permanent_hw_address (NMDevice *dev); +const char * nm_device_get_initial_hw_address (NMDevice *dev); NMDhcp4Config * nm_device_get_dhcp4_config (NMDevice *dev); NMDhcp6Config * nm_device_get_dhcp6_config (NMDevice *dev); @@ -342,6 +360,8 @@ RfKillType nm_device_get_rfkill_type (NMDevice *device); * @NM_UNMANAGED_USER: %TRUE when unmanaged by user decision (via unmanaged-specs) * @NM_UNMANAGED_PARENT: %TRUE when unmanaged due to parent device being unmanaged * @NM_UNMANAGED_EXTERNAL_DOWN: %TRUE when unmanaged because !IFF_UP and not created by NM + * @NM_UNMANAGED_PLATFORM_INIT: %TRUE when unmanaged because platform link not + * yet initialized */ typedef enum { NM_UNMANAGED_NONE = 0x00, @@ -350,6 +370,7 @@ typedef enum { NM_UNMANAGED_USER = 0x04, NM_UNMANAGED_PARENT = 0x08, NM_UNMANAGED_EXTERNAL_DOWN = 0x10, + NM_UNMANAGED_PLATFORM_INIT = 0x20, /* Boundary value */ __NM_UNMANAGED_LAST, @@ -371,6 +392,8 @@ void nm_device_set_initial_unmanaged_flag (NMDevice *device, gboolean nm_device_get_is_nm_owned (NMDevice *device); void nm_device_set_nm_owned (NMDevice *device); +gboolean nm_device_has_capability (NMDevice *self, NMDeviceCapabilities caps); + gboolean nm_device_get_autoconnect (NMDevice *device); void nm_device_handle_autoip4_event (NMDevice *self, @@ -387,6 +410,8 @@ void nm_device_queue_state (NMDevice *self, gboolean nm_device_get_firmware_missing (NMDevice *self); +void nm_device_steal_connection (NMDevice *device, NMConnection *connection); + void nm_device_queue_activation (NMDevice *device, NMActRequest *req); gboolean nm_device_supports_vlans (NMDevice *device); diff --git a/src/devices/team/Makefile.in b/src/devices/team/Makefile.in index 29ad0598..342c41ee 100644 --- a/src/devices/team/Makefile.in +++ b/src/devices/team/Makefile.in @@ -227,6 +227,7 @@ ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -340,6 +341,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ +LOG_DRIVER = @LOG_DRIVER@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ diff --git a/src/devices/team/nm-device-team.c b/src/devices/team/nm-device-team.c index 49202c36..23750f35 100644 --- a/src/devices/team/nm-device-team.c +++ b/src/devices/team/nm-device-team.c @@ -39,7 +39,6 @@ #include "nm-dbus-manager.h" #include "nm-enum-types.h" #include "nm-team-enum-types.h" -#include "nm-posix-signals.h" #include "nm-core-internal.h" #include "gsystem-local-alloc.h" @@ -71,18 +70,16 @@ static gboolean teamd_start (NMDevice *device, NMSettingTeam *s_team); /******************************************************************/ -static guint32 +static NMDeviceCapabilities get_generic_capabilities (NMDevice *device) { - return NM_DEVICE_CAP_CARRIER_DETECT; + return NM_DEVICE_CAP_CARRIER_DETECT | NM_DEVICE_CAP_IS_SOFTWARE; } static gboolean is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags) { - if (NM_DEVICE_GET_CLASS (device)->is_up) - return NM_DEVICE_GET_CLASS (device)->is_up (device); - return FALSE; + return TRUE; } static gboolean @@ -424,23 +421,6 @@ teamd_process_watch_cb (GPid pid, gint status, gpointer user_data) } } -static void -teamd_child_setup (gpointer user_data G_GNUC_UNUSED) -{ - /* We are in the child process at this point. - * Give child it's own program group for signal - * separation. - */ - pid_t pid = getpid (); - setpgid (pid, pid); - - /* - * We blocked signals in main(). We need to restore original signal - * mask for avahi-autoipd here so that it can receive signals. - */ - nm_unblock_posix_signals (NULL); -} - static gboolean teamd_kill (NMDeviceTeam *self, const char *teamd_binary, GError **error) { @@ -448,7 +428,7 @@ teamd_kill (NMDeviceTeam *self, const char *teamd_binary, GError **error) gs_free char *tmp_str = NULL; if (!teamd_binary) { - teamd_binary = nm_utils_find_helper ("teamd", NULL, NULL); + teamd_binary = nm_utils_find_helper ("teamd", NULL, error); if (!teamd_binary) { _LOGW (LOGD_TEAM, "Activation: (team) failed to start teamd: teamd binary not found"); return FALSE; @@ -463,7 +443,7 @@ teamd_kill (NMDeviceTeam *self, const char *teamd_binary, GError **error) g_ptr_array_add (argv, NULL); _LOGD (LOGD_TEAM, "running: %s", (tmp_str = g_strjoinv (" ", (gchar **) argv->pdata))); - return g_spawn_sync ("/", (char **) argv->pdata, NULL, 0, nm_unblock_posix_signals, NULL, NULL, NULL, NULL, error); + return g_spawn_sync ("/", (char **) argv->pdata, NULL, 0, NULL, NULL, NULL, NULL, NULL, error); } static gboolean @@ -514,7 +494,7 @@ teamd_start (NMDevice *device, NMSettingTeam *s_team) _LOGD (LOGD_TEAM, "running: %s", (tmp_str = g_strjoinv (" ", (gchar **) argv->pdata))); if (!g_spawn_async ("/", (char **) argv->pdata, NULL, G_SPAWN_DO_NOT_REAP_CHILD, - teamd_child_setup, NULL, &priv->teamd_pid, &error)) { + nm_utils_setpgid, NULL, &priv->teamd_pid, &error)) { _LOGW (LOGD_TEAM, "Activation: (team) failed to start teamd: %s", error->message); teamd_cleanup (device, TRUE); return FALSE; @@ -638,7 +618,8 @@ enslave_slave (NMDevice *device, } } } - success = nm_platform_link_enslave (nm_device_get_ip_ifindex (device), + success = nm_platform_link_enslave (NM_PLATFORM_GET, + nm_device_get_ip_ifindex (device), nm_device_get_ip_ifindex (slave)); nm_device_bring_up (slave, TRUE, &no_firmware); @@ -663,7 +644,8 @@ release_slave (NMDevice *device, gboolean success = TRUE, no_firmware = FALSE; if (configure) { - success = nm_platform_link_release (nm_device_get_ip_ifindex (device), + success = nm_platform_link_release (NM_PLATFORM_GET, + nm_device_get_ip_ifindex (device), nm_device_get_ip_ifindex (slave)); if (success) @@ -694,8 +676,6 @@ release_slave (NMDevice *device, NMDevice * nm_device_team_new (NMPlatformLink *platform_device) { - g_return_val_if_fail (platform_device != NULL, NULL); - return (NMDevice *) g_object_new (NM_TYPE_DEVICE_TEAM, NM_DEVICE_PLATFORM_DEVICE, platform_device, NM_DEVICE_DRIVER, "team", @@ -708,21 +688,18 @@ nm_device_team_new (NMPlatformLink *platform_device) NMDevice * nm_device_team_new_for_connection (NMConnection *connection, GError **error) { - const char *iface; - - g_return_val_if_fail (connection != NULL, NULL); - - iface = nm_connection_get_interface_name (connection); - g_return_val_if_fail (iface != NULL, NULL); - - if ( !nm_platform_team_add (iface) - && nm_platform_get_error () != NM_PLATFORM_ERROR_EXISTS) { - g_set_error (error, - NM_DEVICE_ERROR, - NM_DEVICE_ERROR_CREATION_FAILED, - "failed to create team master interface '%s' for connection '%s': %s", - iface, nm_connection_get_id (connection), - nm_platform_get_error_msg ()); + const char *iface = nm_connection_get_interface_name (connection); + NMPlatformError plerr; + + g_assert (iface); + + plerr = nm_platform_team_add (NM_PLATFORM_GET, iface, NULL); + if (plerr != NM_PLATFORM_ERROR_SUCCESS && plerr != NM_PLATFORM_ERROR_EXISTS) { + g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, + "Failed to create team master interface '%s' for '%s': %s", + iface, + nm_connection_get_id (connection), + nm_platform_error_to_string (plerr)); return NULL; } diff --git a/src/devices/team/nm-team-factory.c b/src/devices/team/nm-team-factory.c index cb887cac..d87919b6 100644 --- a/src/devices/team/nm-team-factory.c +++ b/src/devices/team/nm-team-factory.c @@ -48,11 +48,9 @@ nm_device_factory_create (GError **error) /************************************************************************/ static NMDevice * -new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error) +new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error) { - if (plink->type == NM_LINK_TYPE_TEAM) - return nm_device_team_new (plink); - return NULL; + return nm_device_team_new (plink); } static NMDevice * @@ -61,16 +59,13 @@ create_virtual_device_for_connection (NMDeviceFactory *factory, NMDevice *parent, GError **error) { - if (nm_connection_is_type (connection, NM_SETTING_TEAM_SETTING_NAME)) - return nm_device_team_new_for_connection (connection, error); - return NULL; + return nm_device_team_new_for_connection (connection, error); } -static NMDeviceType -get_device_type (NMDeviceFactory *factory) -{ - return NM_DEVICE_TYPE_TEAM; -} +NM_DEVICE_FACTORY_DECLARE_TYPES ( + NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_TEAM) + NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_TEAM_SETTING_NAME) +) /************************************************************************/ @@ -84,7 +79,7 @@ device_factory_interface_init (NMDeviceFactory *factory_iface) { factory_iface->new_link = new_link; factory_iface->create_virtual_device_for_connection = create_virtual_device_for_connection; - factory_iface->get_device_type = get_device_type; + factory_iface->get_supported_types = get_supported_types; } static void diff --git a/src/devices/wifi/Makefile.in b/src/devices/wifi/Makefile.in index f5e88007..b679bfc2 100644 --- a/src/devices/wifi/Makefile.in +++ b/src/devices/wifi/Makefile.in @@ -267,6 +267,7 @@ ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -380,6 +381,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ +LOG_DRIVER = @LOG_DRIVER@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ diff --git a/src/devices/wifi/nm-device-olpc-mesh.c b/src/devices/wifi/nm-device-olpc-mesh.c index dc3dfbc6..e67d1a20 100644 --- a/src/devices/wifi/nm-device-olpc-mesh.c +++ b/src/devices/wifi/nm-device-olpc-mesh.c @@ -197,8 +197,8 @@ _mesh_set_channel (NMDeviceOlpcMesh *self, guint32 channel) { int ifindex = nm_device_get_ifindex (NM_DEVICE (self)); - if (nm_platform_mesh_get_channel (ifindex) != channel) { - if (nm_platform_mesh_set_channel (ifindex, channel)) + if (nm_platform_mesh_get_channel (NM_PLATFORM_GET, ifindex) != channel) { + if (nm_platform_mesh_set_channel (NM_PLATFORM_GET, ifindex, channel)) g_object_notify (G_OBJECT (self), NM_DEVICE_OLPC_MESH_ACTIVE_CHANNEL); } } @@ -224,7 +224,8 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *reason) _mesh_set_channel (self, channel); ssid = nm_setting_olpc_mesh_get_ssid (s_mesh); - nm_platform_mesh_set_ssid (nm_device_get_ifindex (device), + nm_platform_mesh_set_ssid (NM_PLATFORM_GET, + nm_device_get_ifindex (device), g_bytes_get_data (ssid, NULL), g_bytes_get_size (ssid)); @@ -368,9 +369,9 @@ device_added_cb (NMManager *manager, NMDevice *other, gpointer user_data) NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); if (!priv->companion && check_companion (self, other)) { - nm_device_state_changed (NM_DEVICE (self), - NM_DEVICE_STATE_DISCONNECTED, - NM_DEVICE_STATE_REASON_NONE); + nm_device_queue_recheck_available (NM_DEVICE (self), + NM_DEVICE_STATE_REASON_NONE, + NM_DEVICE_STATE_REASON_NONE); nm_device_remove_pending_action (NM_DEVICE (self), "waiting for companion", TRUE); } } @@ -398,9 +399,9 @@ find_companion (NMDeviceOlpcMesh *self) /* Try to find the companion if it's already known to the NMManager */ for (list = nm_manager_get_devices (nm_manager_get ()); list ; list = g_slist_next (list)) { if (check_companion (self, NM_DEVICE (list->data))) { - nm_device_queue_state (NM_DEVICE (self), - NM_DEVICE_STATE_DISCONNECTED, - NM_DEVICE_STATE_REASON_NONE); + nm_device_queue_recheck_available (NM_DEVICE (self), + NM_DEVICE_STATE_REASON_NONE, + NM_DEVICE_STATE_REASON_NONE); nm_device_remove_pending_action (NM_DEVICE (self), "waiting for companion", TRUE); break; } @@ -453,7 +454,7 @@ constructor (GType type, self = NM_DEVICE_OLPC_MESH (object); - if (!nm_platform_wifi_get_capabilities (nm_device_get_ifindex (NM_DEVICE (self)), &caps)) { + if (!nm_platform_wifi_get_capabilities (NM_PLATFORM_GET, nm_device_get_ifindex (NM_DEVICE (self)), &caps)) { _LOGW (LOGD_HW | LOGD_OLPC, "failed to initialize WiFi driver"); g_object_unref (object); return NULL; @@ -482,7 +483,7 @@ get_property (GObject *object, guint prop_id, g_value_set_boxed (value, "/"); break; case PROP_ACTIVE_CHANNEL: - g_value_set_uint (value, nm_platform_mesh_get_channel (nm_device_get_ifindex (NM_DEVICE (device)))); + g_value_set_uint (value, nm_platform_mesh_get_channel (NM_PLATFORM_GET, nm_device_get_ifindex (NM_DEVICE (device)))); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c index ce9a3f75..2e116f87 100644 --- a/src/devices/wifi/nm-device-wifi.c +++ b/src/devices/wifi/nm-device-wifi.c @@ -26,13 +26,7 @@ #include <dbus/dbus.h> #include <netinet/in.h> #include <string.h> -#include <sys/stat.h> -#include <sys/wait.h> -#include <signal.h> #include <unistd.h> -#include <linux/sockios.h> -#include <linux/ethtool.h> -#include <sys/ioctl.h> #include <errno.h> #include "nm-glib-compat.h" @@ -116,10 +110,7 @@ enum { static guint signals[LAST_SIGNAL] = { 0 }; struct _NMDeviceWifiPrivate { - gboolean disposed; - - char * perm_hw_addr; /* Permanent MAC address */ - char * initial_hw_addr; /* Initial MAC address (as seen when NM starts) */ + gboolean disposed; gint8 invalid_strength_counter; @@ -165,7 +156,7 @@ static void supplicant_iface_state_cb (NMSupplicantInterface *iface, static void supplicant_iface_new_bss_cb (NMSupplicantInterface * iface, const char *object_path, - GHashTable *properties, + GVariant *properties, NMDeviceWifi * self); static void supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface, @@ -213,7 +204,8 @@ constructor (GType type, self = NM_DEVICE_WIFI (object); priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - if (!nm_platform_wifi_get_capabilities (nm_device_get_ifindex (NM_DEVICE (self)), + if (!nm_platform_wifi_get_capabilities (NM_PLATFORM_GET, + nm_device_get_ifindex (NM_DEVICE (self)), &priv->capabilities)) { _LOGW (LOGD_HW | LOGD_WIFI, "failed to initialize WiFi driver"); g_object_unref (object); @@ -362,21 +354,21 @@ find_active_ap (NMDeviceWifi *self, NM80211Mode devmode; guint32 devfreq; - nm_platform_wifi_get_bssid (ifindex, bssid); + nm_platform_wifi_get_bssid (NM_PLATFORM_GET, ifindex, bssid); _LOGD (LOGD_WIFI, "active BSSID: %02x:%02x:%02x:%02x:%02x:%02x", bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]); if (!nm_ethernet_address_is_valid (bssid, ETH_ALEN)) return NULL; - ssid = nm_platform_wifi_get_ssid (ifindex); + ssid = nm_platform_wifi_get_ssid (NM_PLATFORM_GET, ifindex); _LOGD (LOGD_WIFI, "active SSID: %s%s%s", ssid ? "'" : "", ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)", ssid ? "'" : ""); - devmode = nm_platform_wifi_get_mode (ifindex); - devfreq = nm_platform_wifi_get_frequency (ifindex); + devmode = nm_platform_wifi_get_mode (NM_PLATFORM_GET, ifindex); + devfreq = nm_platform_wifi_get_frequency (NM_PLATFORM_GET, ifindex); /* When matching hidden APs, do a second pass that ignores the SSID check, * because NM might not yet know the SSID of the hidden AP in the scan list @@ -591,7 +583,7 @@ periodic_update (NMDeviceWifi *self, NMAccessPoint *ignore_ap) if (priv->current_ap && (nm_ap_get_mode (priv->current_ap) == NM_802_11_MODE_ADHOC)) { guint8 bssid[ETH_ALEN] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; - nm_platform_wifi_get_bssid (ifindex, bssid); + nm_platform_wifi_get_bssid (NM_PLATFORM_GET, ifindex, bssid); /* 0x02 means "locally administered" and should be OR-ed into * the first byte of IBSS BSSIDs. */ @@ -607,7 +599,7 @@ periodic_update (NMDeviceWifi *self, NMAccessPoint *ignore_ap) /* Try to smooth out the strength. Atmel cards, for example, will give no strength * one second and normal strength the next. */ - percent = nm_platform_wifi_get_quality (ifindex); + percent = nm_platform_wifi_get_quality (NM_PLATFORM_GET, ifindex); if (percent >= 0 || ++priv->invalid_strength_counter > 3) { nm_ap_set_strength (new_ap, (gint8) percent); priv->invalid_strength_counter = 0; @@ -639,7 +631,7 @@ periodic_update (NMDeviceWifi *self, NMAccessPoint *ignore_ap) set_current_ap (self, new_ap, TRUE, FALSE); } - new_rate = nm_platform_wifi_get_rate (ifindex); + new_rate = nm_platform_wifi_get_rate (NM_PLATFORM_GET, ifindex); if (new_rate != priv->rate) { priv->rate = new_rate; g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_BITRATE); @@ -738,18 +730,18 @@ deactivate (NMDevice *device) set_current_ap (self, NULL, TRUE, FALSE); /* Clear any critical protocol notification in the Wi-Fi stack */ - nm_platform_wifi_indicate_addressing_running (ifindex, FALSE); + nm_platform_wifi_indicate_addressing_running (NM_PLATFORM_GET, ifindex, FALSE); /* Reset MAC address back to initial address */ - if (priv->initial_hw_addr) - nm_device_set_hw_addr (device, priv->initial_hw_addr, "reset", LOGD_WIFI); + if (nm_device_get_initial_hw_address (device)) + nm_device_set_hw_addr (device, nm_device_get_initial_hw_address (device), "reset", LOGD_WIFI); /* Ensure we're in infrastructure mode after deactivation; some devices * (usually older ones) don't scan well in adhoc mode. */ - if (nm_platform_wifi_get_mode (ifindex) != NM_802_11_MODE_INFRA) { + if (nm_platform_wifi_get_mode (NM_PLATFORM_GET, ifindex) != NM_802_11_MODE_INFRA) { nm_device_take_down (NM_DEVICE (self), TRUE); - nm_platform_wifi_set_mode (ifindex, NM_802_11_MODE_INFRA); + nm_platform_wifi_set_mode (NM_PLATFORM_GET, ifindex, NM_802_11_MODE_INFRA); nm_device_bring_up (NM_DEVICE (self), TRUE, NULL); } @@ -806,6 +798,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection) const char * const *mac_blacklist; int i; const char *mode; + const char *perm_hw_addr; if (!NM_DEVICE_CLASS (nm_device_wifi_parent_class)->check_connection_compatible (device, connection)) return FALSE; @@ -820,21 +813,25 @@ check_connection_compatible (NMDevice *device, NMConnection *connection) if (!s_wireless) return FALSE; + perm_hw_addr = nm_device_get_permanent_hw_address (device); mac = nm_setting_wireless_get_mac_address (s_wireless); - if (mac && !nm_utils_hwaddr_matches (mac, -1, priv->perm_hw_addr, -1)) - return FALSE; - - /* Check for MAC address blacklist */ - mac_blacklist = nm_setting_wireless_get_mac_address_blacklist (s_wireless); - for (i = 0; mac_blacklist[i]; i++) { - if (!nm_utils_hwaddr_valid (mac_blacklist[i], ETH_ALEN)) { - g_warn_if_reached (); + if (perm_hw_addr) { + if (mac && !nm_utils_hwaddr_matches (mac, -1, perm_hw_addr, -1)) return FALSE; - } - if (nm_utils_hwaddr_matches (mac_blacklist[i], -1, priv->perm_hw_addr, -1)) - return FALSE; - } + /* Check for MAC address blacklist */ + mac_blacklist = nm_setting_wireless_get_mac_address_blacklist (s_wireless); + for (i = 0; mac_blacklist[i]; i++) { + if (!nm_utils_hwaddr_valid (mac_blacklist[i], ETH_ALEN)) { + g_warn_if_reached (); + return FALSE; + } + + if (nm_utils_hwaddr_matches (mac_blacklist[i], -1, perm_hw_addr, -1)) + return FALSE; + } + } else if (mac) + return FALSE; if (is_adhoc_wpa (connection)) return FALSE; @@ -970,6 +967,7 @@ complete_connection (NMDevice *device, GBytes *setting_ssid = NULL; GSList *iter; gboolean hidden = FALSE; + const char *perm_hw_addr; s_wifi = nm_connection_get_setting_wireless (connection); s_wsec = nm_connection_get_setting_wireless_security (connection); @@ -1105,29 +1103,31 @@ complete_connection (NMDevice *device, if (hidden) g_object_set (s_wifi, NM_SETTING_WIRELESS_HIDDEN, TRUE, NULL); - 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, priv->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 perm_hw_addr[ETH_ALEN]; + 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 (priv->perm_hw_addr, perm_hw_addr, ETH_ALEN); - if ( !(perm_hw_addr[0] & 0x02) - && !nm_utils_hwaddr_matches (perm_hw_addr, ETH_ALEN, NULL, ETH_ALEN)) { - g_object_set (G_OBJECT (s_wifi), - NM_SETTING_WIRELESS_MAC_ADDRESS, priv->perm_hw_addr, - NULL); + /* 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); + } } } @@ -1832,7 +1832,7 @@ schedule_scanlist_cull (NMDeviceWifi *self) static void supplicant_iface_new_bss_cb (NMSupplicantInterface *iface, const char *object_path, - GHashTable *properties, + GVariant *properties, NMDeviceWifi *self) { NMDeviceState state; @@ -2148,6 +2148,7 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface, NMDevice *device = NM_DEVICE (self); NMDeviceState devstate; gboolean scanning; + gboolean recheck_available = FALSE; if (new_state == old_state) return; @@ -2167,23 +2168,9 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface, switch (new_state) { case NM_SUPPLICANT_INTERFACE_STATE_READY: + _LOGD (LOGD_WIFI_SCAN, "supplicant ready"); + recheck_available = TRUE; priv->scan_interval = SCAN_INTERVAL_MIN; - - /* If the interface can now be activated because the supplicant is now - * available, transition to DISCONNECTED. - */ - if ((devstate == NM_DEVICE_STATE_UNAVAILABLE) && nm_device_is_available (device, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE)) { - nm_device_state_changed (device, - NM_DEVICE_STATE_DISCONNECTED, - NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE); - } - - _LOGD (LOGD_WIFI_SCAN, "supplicant ready, requesting initial scan"); - - /* Request a scan to get latest results */ - cancel_pending_scan (self); - request_wireless_scan (self); - if (old_state < NM_SUPPLICANT_INTERFACE_STATE_READY) nm_device_remove_pending_action (device, "waiting for supplicant", TRUE); break; @@ -2243,6 +2230,7 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface, } break; case NM_SUPPLICANT_INTERFACE_STATE_DOWN: + recheck_available = TRUE; cleanup_association_attempt (self, FALSE); if (old_state < NM_SUPPLICANT_INTERFACE_STATE_READY) @@ -2255,15 +2243,17 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface, */ supplicant_interface_release (self); supplicant_interface_acquire (self); - - nm_device_state_changed (device, - NM_DEVICE_STATE_UNAVAILABLE, - NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); break; default: break; } + if (recheck_available) { + nm_device_queue_recheck_available (NM_DEVICE (device), + NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE, + NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + } + /* Signal scanning state changes */ if ( new_state == NM_SUPPLICANT_INTERFACE_STATE_SCANNING || old_state == NM_SUPPLICANT_INTERFACE_STATE_SCANNING) @@ -2507,62 +2497,6 @@ error: /****************************************************************************/ -static void -update_permanent_hw_address (NMDevice *device) -{ - NMDeviceWifi *self = NM_DEVICE_WIFI (device); - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - struct ifreq req; - struct ethtool_perm_addr *epaddr = NULL; - int fd, ret, errsv; - - g_return_if_fail (priv->perm_hw_addr == NULL); - - fd = socket (PF_INET, SOCK_DGRAM, 0); - if (fd < 0) { - _LOGE (LOGD_HW, "could not open control socket."); - return; - } - - /* Get permanent MAC address */ - memset (&req, 0, sizeof (struct ifreq)); - strncpy (req.ifr_name, nm_device_get_iface (device), IFNAMSIZ); - - epaddr = g_malloc0 (sizeof (struct ethtool_perm_addr) + ETH_ALEN); - epaddr->cmd = ETHTOOL_GPERMADDR; - epaddr->size = ETH_ALEN; - req.ifr_data = (void *) epaddr; - - errno = 0; - ret = ioctl (fd, SIOCETHTOOL, &req); - errsv = errno; - if ((ret < 0) || !nm_ethernet_address_is_valid (epaddr->data, ETH_ALEN)) { - _LOGD (LOGD_HW | LOGD_ETHER, "unable to read permanent MAC address (error %d)", - errsv); - /* Fall back to current address */ - nm_utils_hwaddr_aton (nm_device_get_hw_address (device), epaddr->data, ETH_ALEN); - } - - priv->perm_hw_addr = nm_utils_hwaddr_ntoa (epaddr->data, ETH_ALEN); - - g_free (epaddr); - close (fd); -} - -static void -update_initial_hw_address (NMDevice *device) -{ - NMDeviceWifi *self = NM_DEVICE_WIFI (device); - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - - /* This sets initial MAC address from current MAC address. It should only - * be called from NMDevice constructor() to really get the initial address. - */ - priv->initial_hw_addr = g_strdup (nm_device_get_hw_address (device)); - - _LOGD (LOGD_DEVICE | LOGD_ETHER, "read initial MAC address %s", priv->initial_hw_addr); -} - static NMActStageReturn act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason) { @@ -2687,9 +2621,9 @@ ensure_hotspot_frequency (NMDeviceWifi *self, return; if (g_strcmp0 (band, "a") == 0) - freq = nm_platform_wifi_find_frequency (nm_device_get_ifindex (NM_DEVICE (self)), a_freqs); + freq = nm_platform_wifi_find_frequency (NM_PLATFORM_GET, nm_device_get_ifindex (NM_DEVICE (self)), a_freqs); else - freq = nm_platform_wifi_find_frequency (nm_device_get_ifindex (NM_DEVICE (self)), bg_freqs); + freq = nm_platform_wifi_find_frequency (NM_PLATFORM_GET, nm_device_get_ifindex (NM_DEVICE (self)), bg_freqs); if (!freq) freq = (g_strcmp0 (band, "a") == 0) ? 5180 : 2462; @@ -2823,7 +2757,7 @@ act_stage3_ip4_config_start (NMDevice *device, /* Indicate that a critical protocol is about to start */ if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0) - nm_platform_wifi_indicate_addressing_running (nm_device_get_ifindex (device), TRUE); + nm_platform_wifi_indicate_addressing_running (NM_PLATFORM_GET, nm_device_get_ifindex (device), TRUE); return NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage3_ip4_config_start (device, out_config, reason); } @@ -2846,7 +2780,7 @@ act_stage3_ip6_config_start (NMDevice *device, /* Indicate that a critical protocol is about to start */ if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0 || strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) == 0) - nm_platform_wifi_indicate_addressing_running (nm_device_get_ifindex (device), TRUE); + nm_platform_wifi_indicate_addressing_running (NM_PLATFORM_GET, nm_device_get_ifindex (device), TRUE); return NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage3_ip6_config_start (device, out_config, reason); } @@ -2998,7 +2932,7 @@ activation_success_handler (NMDevice *device) g_assert (connection); /* Clear any critical protocol notification in the wifi stack */ - nm_platform_wifi_indicate_addressing_running (ifindex, FALSE); + nm_platform_wifi_indicate_addressing_running (NM_PLATFORM_GET, ifindex, FALSE); /* Clear wireless secrets tries on success */ g_object_set_data (G_OBJECT (connection), WIRELESS_SECRETS_TRIES, NULL); @@ -3017,16 +2951,16 @@ activation_success_handler (NMDevice *device) * But if activation was successful, the card will know the BSSID. Grab * the BSSID off the card and fill in the BSSID of the activation AP. */ - nm_platform_wifi_get_bssid (ifindex, bssid); + nm_platform_wifi_get_bssid (NM_PLATFORM_GET, ifindex, bssid); if (!nm_ap_get_address (ap)) { char *bssid_str = nm_utils_hwaddr_ntoa (bssid, ETH_ALEN); nm_ap_set_address (ap, bssid_str); g_free (bssid_str); } if (!nm_ap_get_freq (ap)) - nm_ap_set_freq (ap, nm_platform_wifi_get_frequency (ifindex)); + nm_ap_set_freq (ap, nm_platform_wifi_get_frequency (NM_PLATFORM_GET, ifindex)); if (!nm_ap_get_max_bitrate (ap)) - nm_ap_set_max_bitrate (ap, nm_platform_wifi_get_rate (ifindex)); + nm_ap_set_max_bitrate (ap, nm_platform_wifi_get_rate (NM_PLATFORM_GET, ifindex)); tmp_ap = find_active_ap (self, ap, TRUE); if (tmp_ap) { @@ -3076,7 +3010,7 @@ activation_failure_handler (NMDevice *device) g_object_set_data (G_OBJECT (connection), WIRELESS_SECRETS_TRIES, NULL); /* Clear any critical protocol notification in the wifi stack */ - nm_platform_wifi_indicate_addressing_running (nm_device_get_ifindex (device), FALSE); + nm_platform_wifi_indicate_addressing_running (NM_PLATFORM_GET, nm_device_get_ifindex (device), FALSE); } static void @@ -3126,7 +3060,7 @@ device_state_changed (NMDevice *device, break; case NM_DEVICE_STATE_IP_CHECK: /* Clear any critical protocol notification in the wifi stack */ - nm_platform_wifi_indicate_addressing_running (nm_device_get_ifindex (device), FALSE); + nm_platform_wifi_indicate_addressing_running (NM_PLATFORM_GET, nm_device_get_ifindex (device), FALSE); break; case NM_DEVICE_STATE_ACTIVATED: activation_success_handler (device); @@ -3253,12 +3187,6 @@ dispose (GObject *object) static void finalize (GObject *object) { - NMDeviceWifi *self = NM_DEVICE_WIFI (object); - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - - g_free (priv->perm_hw_addr); - g_free (priv->initial_hw_addr); - G_OBJECT_CLASS (nm_device_wifi_parent_class)->finalize (object); } @@ -3273,7 +3201,7 @@ get_property (GObject *object, guint prop_id, switch (prop_id) { case PROP_PERM_HW_ADDRESS: - g_value_set_string (value, priv->perm_hw_addr); + g_value_set_string (value, nm_device_get_permanent_hw_address (NM_DEVICE (device))); break; case PROP_MODE: g_value_set_uint (value, priv->mode); @@ -3332,8 +3260,6 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass) object_class->finalize = finalize; parent_class->bring_up = bring_up; - parent_class->update_permanent_hw_address = update_permanent_hw_address; - parent_class->update_initial_hw_address = update_initial_hw_address; parent_class->can_auto_connect = can_auto_connect; parent_class->is_available = is_available; parent_class->check_connection_compatible = check_connection_compatible; diff --git a/src/devices/wifi/nm-wifi-ap.c b/src/devices/wifi/nm-wifi-ap.c index 66d73563..d70dd9a6 100644 --- a/src/devices/wifi/nm-wifi-ap.c +++ b/src/devices/wifi/nm-wifi-ap.c @@ -30,6 +30,7 @@ #include "nm-utils.h" #include "nm-logging.h" #include "nm-dbus-manager.h" +#include "nm-core-internal.h" #include "nm-setting-wireless.h" #include "nm-glib-compat.h" @@ -330,163 +331,134 @@ nm_ap_new (void) } static NM80211ApSecurityFlags -pair_to_flags (const char *str) +security_from_vardict (GVariant *security) { - g_return_val_if_fail (str != NULL, NM_802_11_AP_SEC_NONE); + NM80211ApSecurityFlags flags = NM_802_11_AP_SEC_NONE; + const char **array, *tmp; - if (strcmp (str, "tkip") == 0) - return NM_802_11_AP_SEC_PAIR_TKIP; - if (strcmp (str, "ccmp") == 0) - return NM_802_11_AP_SEC_PAIR_CCMP; - return NM_802_11_AP_SEC_NONE; -} + g_return_val_if_fail (g_variant_is_of_type (security, G_VARIANT_TYPE_VARDICT), NM_802_11_AP_SEC_NONE); -static NM80211ApSecurityFlags -group_to_flags (const char *str) -{ - g_return_val_if_fail (str != NULL, NM_802_11_AP_SEC_NONE); - - if (strcmp (str, "wep40") == 0) - return NM_802_11_AP_SEC_GROUP_WEP40; - if (strcmp (str, "wep104") == 0) - return NM_802_11_AP_SEC_GROUP_WEP104; - if (strcmp (str, "tkip") == 0) - return NM_802_11_AP_SEC_GROUP_TKIP; - if (strcmp (str, "ccmp") == 0) - return NM_802_11_AP_SEC_GROUP_CCMP; - return NM_802_11_AP_SEC_NONE; -} - -static NM80211ApSecurityFlags -security_from_dict (GHashTable *security) -{ - GValue *value; - NM80211ApSecurityFlags flags = NM_802_11_AP_SEC_NONE; - const char **items, **iter; - - value = g_hash_table_lookup (security, "KeyMgmt"); - if (value) { - items = g_value_get_boxed (value); - for (iter = items; iter && *iter; iter++) { - if (strcmp (*iter, "wpa-psk") == 0) - flags |= NM_802_11_AP_SEC_KEY_MGMT_PSK; - else if (strcmp (*iter, "wpa-eap") == 0) - flags |= NM_802_11_AP_SEC_KEY_MGMT_802_1X; - } + if (g_variant_lookup (security, "KeyMgmt", "^a&s", &array)) { + if (_nm_utils_string_in_list ("wpa-psk", array)) + flags |= NM_802_11_AP_SEC_KEY_MGMT_PSK; + if (_nm_utils_string_in_list ("wpa-eap", array)) + flags |= NM_802_11_AP_SEC_KEY_MGMT_802_1X; + g_free (array); } - value = g_hash_table_lookup (security, "Pairwise"); - if (value) { - items = g_value_get_boxed (value); - for (iter = items; iter && *iter; iter++) - flags |= pair_to_flags (*iter); + if (g_variant_lookup (security, "Pairwise", "^a&s", &array)) { + if (_nm_utils_string_in_list ("tkip", array)) + flags |= NM_802_11_AP_SEC_PAIR_TKIP; + if (_nm_utils_string_in_list ("ccmp", array)) + flags |= NM_802_11_AP_SEC_PAIR_CCMP; + g_free (array); } - value = g_hash_table_lookup (security, "Group"); - if (value) - flags |= group_to_flags (g_value_get_string (value)); + if (g_variant_lookup (security, "Group", "&s", &tmp)) { + if (strcmp (tmp, "wep40") == 0) + flags |= NM_802_11_AP_SEC_GROUP_WEP40; + if (strcmp (tmp, "wep104") == 0) + flags |= NM_802_11_AP_SEC_GROUP_WEP104; + if (strcmp (tmp, "tkip") == 0) + flags |= NM_802_11_AP_SEC_GROUP_TKIP; + if (strcmp (tmp, "ccmp") == 0) + flags |= NM_802_11_AP_SEC_GROUP_CCMP; + } return flags; } -static void -foreach_property_cb (gpointer key, gpointer value, gpointer user_data) -{ - GValue *variant = (GValue *) value; - NMAccessPoint *ap = (NMAccessPoint *) user_data; - - if (G_VALUE_HOLDS_BOXED (variant)) { - GArray *array = g_value_get_boxed (variant); - - if (!strcmp (key, "SSID")) { - guint32 len = MIN (32, array->len); - - /* Stupid ieee80211 layer uses <hidden> */ - if (((len == 8) || (len == 9)) - && (memcmp (array->data, "<hidden>", 8) == 0)) - return; - - if (nm_utils_is_empty_ssid ((const guint8 *) array->data, len)) - return; - - nm_ap_set_ssid (ap, (const guint8 *) array->data, len); - } else if (!strcmp (key, "BSSID")) { - char *addr; - - if (array->len != ETH_ALEN) - return; - addr = nm_utils_hwaddr_ntoa (array->data, array->len); - nm_ap_set_address (ap, addr); - g_free (addr); - } else if (!strcmp (key, "Rates")) { - guint32 maxrate = 0; - int i; - - /* Find the max AP rate */ - for (i = 0; i < array->len; i++) { - guint32 r = g_array_index (array, guint32, i); - - if (r > maxrate) { - maxrate = r; - nm_ap_set_max_bitrate (ap, r / 1000); - } - } - } else if (!strcmp (key, "WPA")) { - NM80211ApSecurityFlags flags = nm_ap_get_wpa_flags (ap); - - flags |= security_from_dict (g_value_get_boxed (variant)); - nm_ap_set_wpa_flags (ap, flags); - } else if (!strcmp (key, "RSN")) { - NM80211ApSecurityFlags flags = nm_ap_get_rsn_flags (ap); - - flags |= security_from_dict (g_value_get_boxed (variant)); - nm_ap_set_rsn_flags (ap, flags); - } - } else if (G_VALUE_HOLDS_UINT (variant)) { - guint32 val = g_value_get_uint (variant); - - if (!strcmp (key, "Frequency")) - nm_ap_set_freq (ap, val); - } else if (G_VALUE_HOLDS_INT (variant)) { - gint val = g_value_get_int (variant); - - if (!strcmp (key, "Signal")) - nm_ap_set_strength (ap, nm_ap_utils_level_to_quality (val)); - } else if (G_VALUE_HOLDS_STRING (variant)) { - const char *val = g_value_get_string (variant); - - if (val && !strcmp (key, "Mode")) { - if (strcmp (val, "infrastructure") == 0) - nm_ap_set_mode (ap, NM_802_11_MODE_INFRA); - else if (strcmp (val, "ad-hoc") == 0) - nm_ap_set_mode (ap, NM_802_11_MODE_ADHOC); - } - } else if (G_VALUE_HOLDS_BOOLEAN (variant)) { - gboolean val = g_value_get_boolean (variant); - - if (strcmp (key, "Privacy") == 0) { - if (val) { - NM80211ApFlags flags = nm_ap_get_flags (ap); - nm_ap_set_flags (ap, flags | NM_802_11_AP_FLAGS_PRIVACY); - } - } - } -} - NMAccessPoint * -nm_ap_new_from_properties (const char *supplicant_path, GHashTable *properties) +nm_ap_new_from_properties (const char *supplicant_path, GVariant *properties) { - NMAccessPoint *ap; - const char *addr; const char bad_bssid1[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; const char bad_bssid2[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + const char *addr; + const guint8 *bytes; + NMAccessPoint *ap; + GVariant *v; + gsize len; + gboolean b = FALSE; + const char *s; + gint16 i16; + guint16 u16; g_return_val_if_fail (properties != NULL, NULL); ap = nm_ap_new (); g_object_freeze_notify (G_OBJECT (ap)); - g_hash_table_foreach (properties, foreach_property_cb, ap); + + if (g_variant_lookup (properties, "Privacy", "b", &b) && b) + nm_ap_set_flags (ap, nm_ap_get_flags (ap) | NM_802_11_AP_FLAGS_PRIVACY); + + if (g_variant_lookup (properties, "Mode", "&s", &s)) { + if (!g_strcmp0 (s, "infrastructure")) + nm_ap_set_mode (ap, NM_802_11_MODE_INFRA); + else if (!g_strcmp0 (s, "ad-hoc")) + nm_ap_set_mode (ap, NM_802_11_MODE_ADHOC); + } + + if (g_variant_lookup (properties, "Signal", "n", &i16)) + nm_ap_set_strength (ap, nm_ap_utils_level_to_quality (i16)); + + if (g_variant_lookup (properties, "Frequency", "q", &u16)) + nm_ap_set_freq (ap, u16); + + v = g_variant_lookup_value (properties, "SSID", G_VARIANT_TYPE_BYTESTRING); + if (v) { + bytes = g_variant_get_fixed_array (v, &len, 1); + len = MIN (32, len); + + /* Stupid ieee80211 layer uses <hidden> */ + if ( bytes && len + && !(((len == 8) || (len == 9)) && !memcmp (bytes, "<hidden>", 8)) + && !nm_utils_is_empty_ssid (bytes, len)) + nm_ap_set_ssid (ap, bytes, len); + + g_variant_unref (v); + } + + v = g_variant_lookup_value (properties, "BSSID", G_VARIANT_TYPE_BYTESTRING); + if (v) { + bytes = g_variant_get_fixed_array (v, &len, 1); + if (len == ETH_ALEN) { + char *a; + + a = nm_utils_hwaddr_ntoa (bytes, len); + nm_ap_set_address (ap, a); + g_free (a); + } + g_variant_unref (v); + } + + v = g_variant_lookup_value (properties, "Rates", G_VARIANT_TYPE ("au")); + if (v) { + const guint32 *rates = g_variant_get_fixed_array (v, &len, sizeof (guint32)); + guint32 maxrate = 0; + int i; + + /* Find the max AP rate */ + for (i = 0; i < len; i++) { + if (rates[i] > maxrate) { + maxrate = rates[i]; + nm_ap_set_max_bitrate (ap, rates[i] / 1000); + } + } + g_variant_unref (v); + } + + v = g_variant_lookup_value (properties, "WPA", G_VARIANT_TYPE_VARDICT); + if (v) { + nm_ap_set_wpa_flags (ap, nm_ap_get_wpa_flags (ap) | security_from_vardict (v)); + g_variant_unref (v); + } + + v = g_variant_lookup_value (properties, "RSN", G_VARIANT_TYPE_VARDICT); + if (v) { + nm_ap_set_rsn_flags (ap, nm_ap_get_rsn_flags (ap) | security_from_vardict (v)); + g_variant_unref (v); + } nm_ap_set_supplicant_path (ap, supplicant_path); diff --git a/src/devices/wifi/nm-wifi-ap.h b/src/devices/wifi/nm-wifi-ap.h index 0abb28fc..8ad9acb7 100644 --- a/src/devices/wifi/nm-wifi-ap.h +++ b/src/devices/wifi/nm-wifi-ap.h @@ -56,7 +56,7 @@ typedef struct { GType nm_ap_get_type (void); NMAccessPoint * nm_ap_new_from_properties (const char *supplicant_path, - GHashTable *properties); + GVariant *properties); NMAccessPoint * nm_ap_new_fake_from_connection (NMConnection *connection); void nm_ap_export_to_dbus (NMAccessPoint *ap); diff --git a/src/devices/wifi/nm-wifi-factory.c b/src/devices/wifi/nm-wifi-factory.c index 4093c612..c4b4042e 100644 --- a/src/devices/wifi/nm-wifi-factory.c +++ b/src/devices/wifi/nm-wifi-factory.c @@ -23,6 +23,8 @@ #include <gmodule.h> #include "nm-device-factory.h" +#include "nm-setting-wireless.h" +#include "nm-setting-olpc-mesh.h" #include "nm-device-wifi.h" #include "nm-device-olpc-mesh.h" #include "nm-settings-connection.h" @@ -57,26 +59,25 @@ nm_device_factory_create (GError **error) /**************************************************************************/ static NMDevice * -new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error) +new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error) { if (plink->type == NM_LINK_TYPE_WIFI) return nm_device_wifi_new (plink); else if (plink->type == NM_LINK_TYPE_OLPC_MESH) return nm_device_olpc_mesh_new (plink); - return NULL; + g_assert_not_reached (); } -static NMDeviceType -get_device_type (NMDeviceFactory *factory) -{ - return NM_DEVICE_TYPE_WIFI; -} +NM_DEVICE_FACTORY_DECLARE_TYPES ( + NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_WIFI, NM_LINK_TYPE_OLPC_MESH) + NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_OLPC_MESH_SETTING_NAME) +) static void device_factory_interface_init (NMDeviceFactory *factory_iface) { factory_iface->new_link = new_link; - factory_iface->get_device_type = get_device_type; + factory_iface->get_supported_types = get_supported_types; } static void diff --git a/src/devices/wifi/tests/Makefile.in b/src/devices/wifi/tests/Makefile.in index e3b7c767..f807ee77 100644 --- a/src/devices/wifi/tests/Makefile.in +++ b/src/devices/wifi/tests/Makefile.in @@ -204,13 +204,196 @@ am__tty_colors = { \ std='[m'; \ fi; \ } +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__recheck_rx = ^[ ]*:recheck:[ ]* +am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* +am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* +# A command that, given a newline-separated list of test names on the +# standard input, print the name of the tests that are to be re-run +# upon "make recheck". +am__list_recheck_tests = $(AWK) '{ \ + recheck = 1; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + { \ + if ((getline line2 < ($$0 ".log")) < 0) \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ + { \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ + { \ + break; \ + } \ + }; \ + if (recheck) \ + print $$0; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# A command that, given a newline-separated list of test names on the +# standard input, create the global log from their .trs and .log files. +am__create_global_log = $(AWK) ' \ +function fatal(msg) \ +{ \ + print "fatal: making $@: " msg | "cat >&2"; \ + exit 1; \ +} \ +function rst_section(header) \ +{ \ + print header; \ + len = length(header); \ + for (i = 1; i <= len; i = i + 1) \ + printf "="; \ + printf "\n\n"; \ +} \ +{ \ + copy_in_global_log = 1; \ + global_test_result = "RUN"; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".trs"); \ + if (line ~ /$(am__global_test_result_rx)/) \ + { \ + sub("$(am__global_test_result_rx)", "", line); \ + sub("[ ]*$$", "", line); \ + global_test_result = line; \ + } \ + else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ + copy_in_global_log = 0; \ + }; \ + if (copy_in_global_log) \ + { \ + rst_section(global_test_result ": " $$0); \ + while ((rc = (getline line < ($$0 ".log"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".log"); \ + print line; \ + }; \ + printf "\n"; \ + }; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# Restructured Text title. +am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } +# Solaris 10 'make', and several other traditional 'make' implementations, +# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it +# by disabling -e (using the XSI extension "set +e") if it's set. +am__sh_e_setup = case $$- in *e*) set +e;; esac +# Default flags passed to test drivers. +am__common_driver_flags = \ + --color-tests "$$am__color_tests" \ + --enable-hard-errors "$$am__enable_hard_errors" \ + --expect-failure "$$am__expect_failure" +# To be inserted before the command running the test. Creates the +# directory for the log if needed. Stores in $dir the directory +# containing $f, in $tst the test, in $log the log. Executes the +# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and +# passes TESTS_ENVIRONMENT. Set up options for the wrapper that +# will run the test scripts (or their associated LOG_COMPILER, if +# thy have one). +am__check_pre = \ +$(am__sh_e_setup); \ +$(am__vpath_adj_setup) $(am__vpath_adj) \ +$(am__tty_colors); \ +srcdir=$(srcdir); export srcdir; \ +case "$@" in \ + */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ + *) am__odir=.;; \ +esac; \ +test "x$$am__odir" = x"." || test -d "$$am__odir" \ + || $(MKDIR_P) "$$am__odir" || exit $$?; \ +if test -f "./$$f"; then dir=./; \ +elif test -f "$$f"; then dir=; \ +else dir="$(srcdir)/"; fi; \ +tst=$$dir$$f; log='$@'; \ +if test -n '$(DISABLE_HARD_ERRORS)'; then \ + am__enable_hard_errors=no; \ +else \ + am__enable_hard_errors=yes; \ +fi; \ +case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ + am__expect_failure=yes;; \ + *) \ + am__expect_failure=no;; \ +esac; \ +$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) +# A shell command to get the names of the tests scripts with any registered +# extension removed (i.e., equivalently, the names of the test logs, with +# the '.log' extension removed). The result is saved in the shell variable +# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, +# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", +# since that might cause problem with VPATH rewrites for suffix-less tests. +# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. +am__set_TESTS_bases = \ + bases='$(TEST_LOGS)'; \ + bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ + bases=`echo $$bases` +RECHECK_LOGS = $(TEST_LOGS) +AM_RECURSIVE_TARGETS = check recheck +TEST_SUITE_LOG = test-suite.log +TEST_EXTENSIONS = @EXEEXT@ .test +LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) +am__set_b = \ + case '$@' in \ + */*) \ + case '$*' in \ + */*) b='$*';; \ + *) b=`echo '$@' | sed 's/\.log$$//'`; \ + esac;; \ + *) \ + b='$*';; \ + esac +am__test_logs1 = $(TESTS:=.log) +am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) +TEST_LOGS = $(am__test_logs2:.test.log=.log) +TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/build-aux/test-driver +TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ + $(TEST_LOG_FLAGS) am__DIST_COMMON = $(srcdir)/Makefile.in \ - $(top_srcdir)/build-aux/depcomp + $(top_srcdir)/build-aux/depcomp \ + $(top_srcdir)/build-aux/test-driver DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -324,6 +507,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ +LOG_DRIVER = @LOG_DRIVER@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ @@ -492,7 +676,7 @@ test_wifi_ap_utils_LDADD = $(top_builddir)/src/libNetworkManager.la all: all-am .SUFFIXES: -.SUFFIXES: .c .lo .o .obj +.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ @@ -653,98 +837,168 @@ cscopelist-am: $(am__tagged_files) distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -check-TESTS: $(TESTS) - @failed=0; all=0; xfail=0; xpass=0; skip=0; \ - srcdir=$(srcdir); export srcdir; \ - list=' $(TESTS) '; \ - $(am__tty_colors); \ - if test -n "$$list"; then \ - for tst in $$list; do \ - if test -f ./$$tst; then dir=./; \ - elif test -f $$tst; then dir=; \ - else dir="$(srcdir)/"; fi; \ - if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \ - all=`expr $$all + 1`; \ - case " $(XFAIL_TESTS) " in \ - *[\ \ ]$$tst[\ \ ]*) \ - xpass=`expr $$xpass + 1`; \ - failed=`expr $$failed + 1`; \ - col=$$red; res=XPASS; \ - ;; \ - *) \ - col=$$grn; res=PASS; \ - ;; \ - esac; \ - elif test $$? -ne 77; then \ - all=`expr $$all + 1`; \ - case " $(XFAIL_TESTS) " in \ - *[\ \ ]$$tst[\ \ ]*) \ - xfail=`expr $$xfail + 1`; \ - col=$$lgn; res=XFAIL; \ - ;; \ - *) \ - failed=`expr $$failed + 1`; \ - col=$$red; res=FAIL; \ - ;; \ - esac; \ - else \ - skip=`expr $$skip + 1`; \ - col=$$blu; res=SKIP; \ - fi; \ - echo "$${col}$$res$${std}: $$tst"; \ - done; \ - if test "$$all" -eq 1; then \ - tests="test"; \ - All=""; \ - else \ - tests="tests"; \ - All="All "; \ +# Recover from deleted '.trs' file; this should ensure that +# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create +# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells +# to avoid problems with "make -n". +.log.trs: + rm -f $< $@ + $(MAKE) $(AM_MAKEFLAGS) $< + +# Leading 'am--fnord' is there to ensure the list of targets does not +# expand to empty, as could happen e.g. with make check TESTS=''. +am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) +am--force-recheck: + @: + +$(TEST_SUITE_LOG): $(TEST_LOGS) + @$(am__set_TESTS_bases); \ + am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ + redo_bases=`for i in $$bases; do \ + am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ + done`; \ + if test -n "$$redo_bases"; then \ + redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ + redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ + if $(am__make_dryrun); then :; else \ + rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ fi; \ - if test "$$failed" -eq 0; then \ - if test "$$xfail" -eq 0; then \ - banner="$$All$$all $$tests passed"; \ - else \ - if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ - banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ - fi; \ - else \ - if test "$$xpass" -eq 0; then \ - banner="$$failed of $$all $$tests failed"; \ + fi; \ + if test -n "$$am__remaking_logs"; then \ + echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ + "recursion detected" >&2; \ + elif test -n "$$redo_logs"; then \ + am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ + fi; \ + if $(am__make_dryrun); then :; else \ + st=0; \ + errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ + for i in $$redo_bases; do \ + test -f $$i.trs && test -r $$i.trs \ + || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ + test -f $$i.log && test -r $$i.log \ + || { echo "$$errmsg $$i.log" >&2; st=1; }; \ + done; \ + test $$st -eq 0 || exit 1; \ + fi + @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ + ws='[ ]'; \ + results=`for b in $$bases; do echo $$b.trs; done`; \ + test -n "$$results" || results=/dev/null; \ + all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ + pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ + fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ + skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ + xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ + xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ + error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ + if test `expr $$fail + $$xpass + $$error` -eq 0; then \ + success=true; \ + else \ + success=false; \ + fi; \ + br='==================='; br=$$br$$br$$br$$br; \ + result_count () \ + { \ + if test x"$$1" = x"--maybe-color"; then \ + maybe_colorize=yes; \ + elif test x"$$1" = x"--no-color"; then \ + maybe_colorize=no; \ else \ - if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ - banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ + echo "$@: invalid 'result_count' usage" >&2; exit 4; \ fi; \ - fi; \ - dashes="$$banner"; \ - skipped=""; \ - if test "$$skip" -ne 0; then \ - if test "$$skip" -eq 1; then \ - skipped="($$skip test was not run)"; \ + shift; \ + desc=$$1 count=$$2; \ + if test $$maybe_colorize = yes && test $$count -gt 0; then \ + color_start=$$3 color_end=$$std; \ else \ - skipped="($$skip tests were not run)"; \ + color_start= color_end=; \ fi; \ - test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ - dashes="$$skipped"; \ - fi; \ - report=""; \ - if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ - report="Please report to $(PACKAGE_BUGREPORT)"; \ - test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ - dashes="$$report"; \ - fi; \ - dashes=`echo "$$dashes" | sed s/./=/g`; \ - if test "$$failed" -eq 0; then \ - col="$$grn"; \ - else \ - col="$$red"; \ - fi; \ - echo "$${col}$$dashes$${std}"; \ - echo "$${col}$$banner$${std}"; \ - test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \ - test -z "$$report" || echo "$${col}$$report$${std}"; \ - echo "$${col}$$dashes$${std}"; \ - test "$$failed" -eq 0; \ - else :; fi + echo "$${color_start}# $$desc $$count$${color_end}"; \ + }; \ + create_testsuite_report () \ + { \ + result_count $$1 "TOTAL:" $$all "$$brg"; \ + result_count $$1 "PASS: " $$pass "$$grn"; \ + result_count $$1 "SKIP: " $$skip "$$blu"; \ + result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ + result_count $$1 "FAIL: " $$fail "$$red"; \ + result_count $$1 "XPASS:" $$xpass "$$red"; \ + result_count $$1 "ERROR:" $$error "$$mgn"; \ + }; \ + { \ + echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ + $(am__rst_title); \ + create_testsuite_report --no-color; \ + echo; \ + echo ".. contents:: :depth: 2"; \ + echo; \ + for b in $$bases; do echo $$b; done \ + | $(am__create_global_log); \ + } >$(TEST_SUITE_LOG).tmp || exit 1; \ + mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ + if $$success; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ + fi; \ + echo "$${col}$$br$${std}"; \ + echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \ + echo "$${col}$$br$${std}"; \ + create_testsuite_report --maybe-color; \ + echo "$$col$$br$$std"; \ + if $$success; then :; else \ + echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ + if test -n "$(PACKAGE_BUGREPORT)"; then \ + echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ + fi; \ + echo "$$col$$br$$std"; \ + fi; \ + $$success || exit 1 + +check-TESTS: + @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list + @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + trs_list=`for i in $$bases; do echo $$i.trs; done`; \ + log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ + exit $$?; +recheck: all + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + bases=`for i in $$bases; do echo $$i; done \ + | $(am__list_recheck_tests)` || exit 1; \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + log_list=`echo $$log_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ + am__force_recheck=am--force-recheck \ + TEST_LOGS="$$log_list"; \ + exit $$? +test-wifi-ap-utils.log: test-wifi-ap-utils$(EXEEXT) + @p='test-wifi-ap-utils$(EXEEXT)'; \ + b='test-wifi-ap-utils'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +.test.log: + @p='$<'; \ + $(am__set_b); \ + $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +@am__EXEEXT_TRUE@.test$(EXEEXT).log: +@am__EXEEXT_TRUE@ @p='$<'; \ +@am__EXEEXT_TRUE@ $(am__set_b); \ +@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ +@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ +@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ +@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ @@ -801,6 +1055,9 @@ install-strip: "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: + -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) + -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) + -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) clean-generic: @@ -896,7 +1153,7 @@ uninstall-am: installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ - tags tags-am uninstall uninstall-am + recheck tags tags-am uninstall uninstall-am .PRECIOUS: Makefile diff --git a/src/devices/wimax/Makefile.in b/src/devices/wimax/Makefile.in index 32800fb7..8ad4b4e2 100644 --- a/src/devices/wimax/Makefile.in +++ b/src/devices/wimax/Makefile.in @@ -221,6 +221,7 @@ ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -334,6 +335,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ +LOG_DRIVER = @LOG_DRIVER@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ diff --git a/src/devices/wimax/nm-wimax-factory.c b/src/devices/wimax/nm-wimax-factory.c index 39e9a287..7dc580fc 100644 --- a/src/devices/wimax/nm-wimax-factory.c +++ b/src/devices/wimax/nm-wimax-factory.c @@ -25,6 +25,7 @@ #include "nm-device-factory.h" #include "nm-device-wimax.h" #include "nm-platform.h" +#include "nm-setting-wimax.h" #define NM_TYPE_WIMAX_FACTORY (nm_wimax_factory_get_type ()) #define NM_WIMAX_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_WIMAX_FACTORY, NMWimaxFactory)) @@ -55,7 +56,7 @@ nm_device_factory_create (GError **error) /**************************************************************************/ static NMDevice * -new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error) +new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error) { /* FIXME: check udev 'DEVTYPE' instead; but since we only support Intel * WiMAX devices for now this is appropriate. @@ -66,17 +67,16 @@ new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error) return (NMDevice *) nm_device_wimax_new (plink); } -static NMDeviceType -get_device_type (NMDeviceFactory *factory) -{ - return NM_DEVICE_TYPE_WIMAX; -} +NM_DEVICE_FACTORY_DECLARE_TYPES ( + NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_WIMAX) + NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_WIMAX_SETTING_NAME) +) static void device_factory_interface_init (NMDeviceFactory *factory_iface) { factory_iface->new_link = new_link; - factory_iface->get_device_type = get_device_type; + factory_iface->get_supported_types = get_supported_types; } static void diff --git a/src/devices/wimax/nm-wimax-util.h b/src/devices/wimax/nm-wimax-util.h index a1852d55..5fedc6e8 100644 --- a/src/devices/wimax/nm-wimax-util.h +++ b/src/devices/wimax/nm-wimax-util.h @@ -23,7 +23,7 @@ #include <glib.h> -#include "nm-utils-internal.h" +#include "nm-macros-internal.h" NM_PRAGMA_WARNING_DISABLE("-Wstrict-prototypes") #include <WiMaxType.h> diff --git a/src/devices/wwan/Makefile.in b/src/devices/wwan/Makefile.in index 1a8aef3b..227c622f 100644 --- a/src/devices/wwan/Makefile.in +++ b/src/devices/wwan/Makefile.in @@ -232,6 +232,7 @@ ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -345,6 +346,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ +LOG_DRIVER = @LOG_DRIVER@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ diff --git a/src/devices/wwan/nm-device-modem.c b/src/devices/wwan/nm-device-modem.c index f819d1d7..ca724c04 100644 --- a/src/devices/wwan/nm-device-modem.c +++ b/src/devices/wwan/nm-device-modem.c @@ -300,19 +300,9 @@ modem_state_cb (NMModem *modem, nm_device_recheck_available_connections (device); } - if ((dev_state >= NM_DEVICE_STATE_DISCONNECTED) && !nm_device_is_available (device, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE)) { - nm_device_state_changed (device, - NM_DEVICE_STATE_UNAVAILABLE, - NM_DEVICE_STATE_REASON_MODEM_FAILED); - return; - } - - if ((dev_state == NM_DEVICE_STATE_UNAVAILABLE) && nm_device_is_available (device, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE)) { - nm_device_state_changed (device, - NM_DEVICE_STATE_DISCONNECTED, - NM_DEVICE_STATE_REASON_MODEM_AVAILABLE); - return; - } + nm_device_queue_recheck_available (device, + NM_DEVICE_STATE_REASON_MODEM_AVAILABLE, + NM_DEVICE_STATE_REASON_MODEM_FAILED); } static void @@ -376,12 +366,24 @@ device_state_changed (NMDevice *device, } } -static guint32 +static NMDeviceCapabilities get_generic_capabilities (NMDevice *device) { return NM_DEVICE_CAP_IS_NON_KERNEL; } +static const char * +get_type_description (NMDevice *device) +{ + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device); + + if (NM_FLAGS_HAS (priv->current_caps, NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS)) + return "gsm"; + if (NM_FLAGS_HAS (priv->current_caps, NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO)) + return "cdma"; + return NM_DEVICE_CLASS (nm_device_modem_parent_class)->get_type_description (device); +} + static gboolean check_connection_compatible (NMDevice *device, NMConnection *connection) { @@ -751,6 +753,7 @@ nm_device_modem_class_init (NMDeviceModemClass *mclass) object_class->constructed = constructed; device_class->get_generic_capabilities = get_generic_capabilities; + device_class->get_type_description = get_type_description; device_class->check_connection_compatible = check_connection_compatible; device_class->check_connection_available = check_connection_available; device_class->complete_connection = complete_connection; diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c index 69eaddf3..3385a6fd 100644 --- a/src/devices/wwan/nm-modem.c +++ b/src/devices/wwan/nm-modem.c @@ -32,6 +32,7 @@ #include "nm-device-private.h" #include "nm-dbus-glib-types.h" #include "nm-modem-enum-types.h" +#include "nm-route-manager.h" G_DEFINE_TYPE (NMModem, nm_modem, G_TYPE_OBJECT) @@ -615,7 +616,7 @@ nm_modem_ip4_pre_commit (NMModem *modem, g_assert (address); if (address->plen == 32) - nm_platform_link_set_noarp (nm_device_get_ip_ifindex (device)); + nm_platform_link_set_noarp (NM_PLATFORM_GET, nm_device_get_ip_ifindex (device)); } } @@ -912,9 +913,9 @@ deactivate_cleanup (NMModem *self, NMDevice *device) priv->ip6_method == NM_MODEM_IP_METHOD_AUTO) { ifindex = nm_device_get_ip_ifindex (device); if (ifindex > 0) { - nm_platform_route_flush (ifindex); - nm_platform_address_flush (ifindex); - nm_platform_link_set_down (ifindex); + nm_route_manager_route_flush (nm_route_manager_get (), ifindex); + nm_platform_address_flush (NM_PLATFORM_GET, ifindex); + nm_platform_link_set_down (NM_PLATFORM_GET, ifindex); } } } diff --git a/src/devices/wwan/nm-wwan-factory.c b/src/devices/wwan/nm-wwan-factory.c index 2b9c3d4d..49b0b0af 100644 --- a/src/devices/wwan/nm-wwan-factory.c +++ b/src/devices/wwan/nm-wwan-factory.c @@ -25,9 +25,12 @@ #include "nm-device-factory.h" #include "nm-wwan-factory.h" +#include "nm-setting-gsm.h" +#include "nm-setting-cdma.h" #include "nm-modem-manager.h" #include "nm-device-modem.h" #include "nm-logging.h" +#include "nm-platform.h" static GType nm_wwan_factory_get_type (void); @@ -87,10 +90,17 @@ modem_added_cb (NMModemManager *manager, } -static NMDeviceType -get_device_type (NMDeviceFactory *factory) +NM_DEVICE_FACTORY_DECLARE_TYPES ( + NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_WWAN_ETHERNET) + NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_GSM_SETTING_NAME, NM_SETTING_CDMA_SETTING_NAME) +) + +static NMDevice * +new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error) { - return NM_DEVICE_TYPE_MODEM; + g_warn_if_fail (plink->type == NM_LINK_TYPE_WWAN_ETHERNET); + *out_ignore = TRUE; + return NULL; } static void @@ -115,7 +125,8 @@ nm_wwan_factory_init (NMWwanFactory *self) static void device_factory_interface_init (NMDeviceFactory *factory_iface) { - factory_iface->get_device_type = get_device_type; + factory_iface->get_supported_types = get_supported_types; + factory_iface->new_link = new_link; factory_iface->start = start; } diff --git a/src/dhcp-manager/Makefile.in b/src/dhcp-manager/Makefile.in index a609e8c6..8c6e059c 100644 --- a/src/dhcp-manager/Makefile.in +++ b/src/dhcp-manager/Makefile.in @@ -218,6 +218,7 @@ ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -331,6 +332,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ +LOG_DRIVER = @LOG_DRIVER@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ diff --git a/src/dhcp-manager/nm-dhcp-client.c b/src/dhcp-manager/nm-dhcp-client.c index a39ffcca..eb317330 100644 --- a/src/dhcp-manager/nm-dhcp-client.c +++ b/src/dhcp-manager/nm-dhcp-client.c @@ -590,9 +590,10 @@ nm_dhcp_client_stop_existing (const char *pid_file, const char *binary_name) if ((errno == 0) && (tmp > 1)) { guint64 start_time; const char *exe; + pid_t ppid; /* Ensure the process is a DHCP client */ - start_time = nm_utils_get_start_time_for_pid (tmp); + start_time = nm_utils_get_start_time_for_pid (tmp, NULL, &ppid); proc_path = g_strdup_printf ("/proc/%ld/cmdline", tmp); if ( start_time && g_file_get_contents (proc_path, &proc_contents, NULL, NULL)) { @@ -602,9 +603,15 @@ nm_dhcp_client_stop_existing (const char *pid_file, const char *binary_name) else exe = proc_contents; - if (!strcmp (exe, binary_name)) - nm_utils_kill_process_sync (tmp, start_time, SIGTERM, LOGD_DHCP, - "dhcp-client", 1000 / 2, 1000 / 20); + if (!strcmp (exe, binary_name)) { + if (ppid == getpid ()) { + /* the process is our own child. */ + nm_utils_kill_child_sync (tmp, SIGTERM, LOGD_DHCP, "dhcp-client", NULL, 1000 / 2, 1000 / 20); + } else { + nm_utils_kill_process_sync (tmp, start_time, SIGTERM, LOGD_DHCP, + "dhcp-client", 1000 / 2, 1000 / 20, 2000); + } + } } } diff --git a/src/dhcp-manager/nm-dhcp-dhclient-utils.c b/src/dhcp-manager/nm-dhcp-dhclient-utils.c index d1fe0c30..41ce5354 100644 --- a/src/dhcp-manager/nm-dhcp-dhclient-utils.c +++ b/src/dhcp-manager/nm-dhcp-dhclient-utils.c @@ -32,7 +32,7 @@ #include "nm-platform.h" #include "NetworkManagerUtils.h" #include "gsystem-local-alloc.h" -#include "nm-utils-internal.h" +#include "nm-macros-internal.h" #define CLIENTID_TAG "send dhcp-client-identifier" diff --git a/src/dhcp-manager/nm-dhcp-dhclient.c b/src/dhcp-manager/nm-dhcp-dhclient.c index 504e8d5d..91515dda 100644 --- a/src/dhcp-manager/nm-dhcp-dhclient.c +++ b/src/dhcp-manager/nm-dhcp-dhclient.c @@ -41,7 +41,6 @@ #include "nm-logging.h" #include "nm-dhcp-dhclient-utils.h" #include "nm-dhcp-manager.h" -#include "nm-posix-signals.h" #include "NetworkManagerUtils.h" #include "nm-dhcp-listener.h" #include "gsystem-local-alloc.h" @@ -301,20 +300,6 @@ create_dhclient_config (const char *iface, } -static void -dhclient_child_setup (gpointer user_data G_GNUC_UNUSED) -{ - /* We are in the child process at this point */ - pid_t pid = getpid (); - setpgid (pid, pid); - - /* - * We blocked signals in main(). We need to restore original signal - * mask for dhclient here so that it can receive signals. - */ - nm_unblock_posix_signals (NULL); -} - static gboolean dhclient_start (NMDhcpClient *client, const char *mode_opt, @@ -457,7 +442,7 @@ dhclient_start (NMDhcpClient *client, if (g_spawn_async (NULL, (char **) argv->pdata, NULL, G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL, - &dhclient_child_setup, NULL, &pid, &error)) { + nm_utils_setpgid, NULL, &pid, &error)) { g_assert (pid > 0); nm_log_info (log_domain, "dhclient started with pid %d", pid); if (release == FALSE) diff --git a/src/dhcp-manager/nm-dhcp-dhcpcd.c b/src/dhcp-manager/nm-dhcp-dhcpcd.c index e5a40c2f..86aba4da 100644 --- a/src/dhcp-manager/nm-dhcp-dhcpcd.c +++ b/src/dhcp-manager/nm-dhcp-dhcpcd.c @@ -37,7 +37,6 @@ #include "nm-dhcp-manager.h" #include "nm-utils.h" #include "nm-logging.h" -#include "nm-posix-signals.h" #include "NetworkManagerUtils.h" #include "nm-dhcp-listener.h" @@ -59,20 +58,6 @@ nm_dhcp_dhcpcd_get_path (void) return path; } -static void -dhcpcd_child_setup (gpointer user_data G_GNUC_UNUSED) -{ - /* We are in the child process at this point */ - pid_t pid = getpid (); - setpgid (pid, pid); - - /* - * We blocked signals in main(). We need to restore original signal - * mask for dhcpcd here so that it can receive signals. - */ - nm_unblock_posix_signals (NULL); -} - static gboolean ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last_ip4_address) { @@ -143,7 +128,7 @@ ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last if (g_spawn_async (NULL, (char **) argv->pdata, NULL, G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL, - &dhcpcd_child_setup, NULL, &pid, &error)) { + nm_utils_setpgid, NULL, &pid, &error)) { g_assert (pid > 0); nm_log_info (LOGD_DHCP4, "dhcpcd started with pid %d", pid); nm_dhcp_client_watch_child (client, pid); diff --git a/src/dhcp-manager/nm-dhcp-listener.c b/src/dhcp-manager/nm-dhcp-listener.c index b995c348..ca57ae59 100644 --- a/src/dhcp-manager/nm-dhcp-listener.c +++ b/src/dhcp-manager/nm-dhcp-listener.c @@ -31,6 +31,7 @@ #include <unistd.h> #include "nm-dhcp-listener.h" +#include "nm-core-internal.h" #include "nm-logging.h" #include "nm-dbus-manager.h" #include "nm-dbus-glib-types.h" @@ -132,7 +133,7 @@ handle_event (DBusGProxy *proxy, } pid_str = get_option (options, "pid"); - pid = nm_utils_ascii_str_to_int64 (pid_str, 10, 0, G_MAXINT32, -1); + pid = _nm_utils_ascii_str_to_int64 (pid_str, 10, 0, G_MAXINT32, -1); if (pid == -1) { nm_log_warn (LOGD_DHCP, "DHCP event: couldn't convert PID '%s' to an integer", pid_str ? pid_str : "(null)"); goto out; @@ -193,16 +194,7 @@ dis_connection_cb (NMDBusManager *mgr, /***************************************************/ -NMDhcpListener * -nm_dhcp_listener_get (void) -{ - static NMDhcpListener *singleton = NULL; - - if (G_UNLIKELY (singleton == NULL)) - singleton = g_object_new (NM_TYPE_DHCP_LISTENER, NULL); - g_assert (singleton); - return singleton; -} +NM_DEFINE_SINGLETON_GETTER (NMDhcpListener, nm_dhcp_listener_get, NM_TYPE_DHCP_LISTENER); static void nm_dhcp_listener_init (NMDhcpListener *self) diff --git a/src/dhcp-manager/systemd-dhcp/nm-sd-adapt.h b/src/dhcp-manager/systemd-dhcp/nm-sd-adapt.h index c316fb46..f8856a1b 100644 --- a/src/dhcp-manager/systemd-dhcp/nm-sd-adapt.h +++ b/src/dhcp-manager/systemd-dhcp/nm-sd-adapt.h @@ -38,6 +38,11 @@ #include "nm-logging.h" +/* Missing in Linux 3.2.0, in Ubuntu 12.04 */ +#ifndef BPF_XOR +#define BPF_XOR 0xa0 +#endif + static inline guint32 _slog_level_to_nm (int slevel) { @@ -96,6 +101,15 @@ G_STMT_START { \ #define noreturn G_GNUC_NORETURN +/* + * Some toolchains (E.G. uClibc 0.9.33 and earlier) don't export + * CLOCK_BOOTTIME even though the kernel supports it, so provide a + * local definition + */ +#ifndef CLOCK_BOOTTIME +#define CLOCK_BOOTTIME 7 +#endif + #include "sd-id128.h" #include "sparse-endian.h" #include "async.h" diff --git a/src/dhcp-manager/tests/Makefile.in b/src/dhcp-manager/tests/Makefile.in index e0a155f1..6125aea8 100644 --- a/src/dhcp-manager/tests/Makefile.in +++ b/src/dhcp-manager/tests/Makefile.in @@ -209,13 +209,196 @@ am__tty_colors = { \ std='[m'; \ fi; \ } +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__recheck_rx = ^[ ]*:recheck:[ ]* +am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* +am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* +# A command that, given a newline-separated list of test names on the +# standard input, print the name of the tests that are to be re-run +# upon "make recheck". +am__list_recheck_tests = $(AWK) '{ \ + recheck = 1; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + { \ + if ((getline line2 < ($$0 ".log")) < 0) \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ + { \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ + { \ + break; \ + } \ + }; \ + if (recheck) \ + print $$0; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# A command that, given a newline-separated list of test names on the +# standard input, create the global log from their .trs and .log files. +am__create_global_log = $(AWK) ' \ +function fatal(msg) \ +{ \ + print "fatal: making $@: " msg | "cat >&2"; \ + exit 1; \ +} \ +function rst_section(header) \ +{ \ + print header; \ + len = length(header); \ + for (i = 1; i <= len; i = i + 1) \ + printf "="; \ + printf "\n\n"; \ +} \ +{ \ + copy_in_global_log = 1; \ + global_test_result = "RUN"; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".trs"); \ + if (line ~ /$(am__global_test_result_rx)/) \ + { \ + sub("$(am__global_test_result_rx)", "", line); \ + sub("[ ]*$$", "", line); \ + global_test_result = line; \ + } \ + else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ + copy_in_global_log = 0; \ + }; \ + if (copy_in_global_log) \ + { \ + rst_section(global_test_result ": " $$0); \ + while ((rc = (getline line < ($$0 ".log"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".log"); \ + print line; \ + }; \ + printf "\n"; \ + }; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# Restructured Text title. +am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } +# Solaris 10 'make', and several other traditional 'make' implementations, +# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it +# by disabling -e (using the XSI extension "set +e") if it's set. +am__sh_e_setup = case $$- in *e*) set +e;; esac +# Default flags passed to test drivers. +am__common_driver_flags = \ + --color-tests "$$am__color_tests" \ + --enable-hard-errors "$$am__enable_hard_errors" \ + --expect-failure "$$am__expect_failure" +# To be inserted before the command running the test. Creates the +# directory for the log if needed. Stores in $dir the directory +# containing $f, in $tst the test, in $log the log. Executes the +# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and +# passes TESTS_ENVIRONMENT. Set up options for the wrapper that +# will run the test scripts (or their associated LOG_COMPILER, if +# thy have one). +am__check_pre = \ +$(am__sh_e_setup); \ +$(am__vpath_adj_setup) $(am__vpath_adj) \ +$(am__tty_colors); \ +srcdir=$(srcdir); export srcdir; \ +case "$@" in \ + */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ + *) am__odir=.;; \ +esac; \ +test "x$$am__odir" = x"." || test -d "$$am__odir" \ + || $(MKDIR_P) "$$am__odir" || exit $$?; \ +if test -f "./$$f"; then dir=./; \ +elif test -f "$$f"; then dir=; \ +else dir="$(srcdir)/"; fi; \ +tst=$$dir$$f; log='$@'; \ +if test -n '$(DISABLE_HARD_ERRORS)'; then \ + am__enable_hard_errors=no; \ +else \ + am__enable_hard_errors=yes; \ +fi; \ +case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ + am__expect_failure=yes;; \ + *) \ + am__expect_failure=no;; \ +esac; \ +$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) +# A shell command to get the names of the tests scripts with any registered +# extension removed (i.e., equivalently, the names of the test logs, with +# the '.log' extension removed). The result is saved in the shell variable +# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, +# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", +# since that might cause problem with VPATH rewrites for suffix-less tests. +# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. +am__set_TESTS_bases = \ + bases='$(TEST_LOGS)'; \ + bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ + bases=`echo $$bases` +RECHECK_LOGS = $(TEST_LOGS) +AM_RECURSIVE_TARGETS = check recheck +TEST_SUITE_LOG = test-suite.log +TEST_EXTENSIONS = @EXEEXT@ .test +LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) +am__set_b = \ + case '$@' in \ + */*) \ + case '$*' in \ + */*) b='$*';; \ + *) b=`echo '$@' | sed 's/\.log$$//'`; \ + esac;; \ + *) \ + b='$*';; \ + esac +am__test_logs1 = $(TESTS:=.log) +am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) +TEST_LOGS = $(am__test_logs2:.test.log=.log) +TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/build-aux/test-driver +TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ + $(TEST_LOG_FLAGS) am__DIST_COMMON = $(srcdir)/Makefile.in \ - $(top_srcdir)/build-aux/depcomp + $(top_srcdir)/build-aux/depcomp \ + $(top_srcdir)/build-aux/test-driver DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -329,6 +512,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ +LOG_DRIVER = @LOG_DRIVER@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ @@ -516,7 +700,7 @@ EXTRA_DIST = \ all: all-am .SUFFIXES: -.SUFFIXES: .c .lo .o .obj +.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ @@ -682,98 +866,175 @@ cscopelist-am: $(am__tagged_files) distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -check-TESTS: $(TESTS) - @failed=0; all=0; xfail=0; xpass=0; skip=0; \ - srcdir=$(srcdir); export srcdir; \ - list=' $(TESTS) '; \ - $(am__tty_colors); \ - if test -n "$$list"; then \ - for tst in $$list; do \ - if test -f ./$$tst; then dir=./; \ - elif test -f $$tst; then dir=; \ - else dir="$(srcdir)/"; fi; \ - if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \ - all=`expr $$all + 1`; \ - case " $(XFAIL_TESTS) " in \ - *[\ \ ]$$tst[\ \ ]*) \ - xpass=`expr $$xpass + 1`; \ - failed=`expr $$failed + 1`; \ - col=$$red; res=XPASS; \ - ;; \ - *) \ - col=$$grn; res=PASS; \ - ;; \ - esac; \ - elif test $$? -ne 77; then \ - all=`expr $$all + 1`; \ - case " $(XFAIL_TESTS) " in \ - *[\ \ ]$$tst[\ \ ]*) \ - xfail=`expr $$xfail + 1`; \ - col=$$lgn; res=XFAIL; \ - ;; \ - *) \ - failed=`expr $$failed + 1`; \ - col=$$red; res=FAIL; \ - ;; \ - esac; \ - else \ - skip=`expr $$skip + 1`; \ - col=$$blu; res=SKIP; \ - fi; \ - echo "$${col}$$res$${std}: $$tst"; \ - done; \ - if test "$$all" -eq 1; then \ - tests="test"; \ - All=""; \ - else \ - tests="tests"; \ - All="All "; \ +# Recover from deleted '.trs' file; this should ensure that +# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create +# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells +# to avoid problems with "make -n". +.log.trs: + rm -f $< $@ + $(MAKE) $(AM_MAKEFLAGS) $< + +# Leading 'am--fnord' is there to ensure the list of targets does not +# expand to empty, as could happen e.g. with make check TESTS=''. +am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) +am--force-recheck: + @: + +$(TEST_SUITE_LOG): $(TEST_LOGS) + @$(am__set_TESTS_bases); \ + am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ + redo_bases=`for i in $$bases; do \ + am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ + done`; \ + if test -n "$$redo_bases"; then \ + redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ + redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ + if $(am__make_dryrun); then :; else \ + rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ fi; \ - if test "$$failed" -eq 0; then \ - if test "$$xfail" -eq 0; then \ - banner="$$All$$all $$tests passed"; \ - else \ - if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ - banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ - fi; \ - else \ - if test "$$xpass" -eq 0; then \ - banner="$$failed of $$all $$tests failed"; \ + fi; \ + if test -n "$$am__remaking_logs"; then \ + echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ + "recursion detected" >&2; \ + elif test -n "$$redo_logs"; then \ + am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ + fi; \ + if $(am__make_dryrun); then :; else \ + st=0; \ + errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ + for i in $$redo_bases; do \ + test -f $$i.trs && test -r $$i.trs \ + || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ + test -f $$i.log && test -r $$i.log \ + || { echo "$$errmsg $$i.log" >&2; st=1; }; \ + done; \ + test $$st -eq 0 || exit 1; \ + fi + @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ + ws='[ ]'; \ + results=`for b in $$bases; do echo $$b.trs; done`; \ + test -n "$$results" || results=/dev/null; \ + all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ + pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ + fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ + skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ + xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ + xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ + error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ + if test `expr $$fail + $$xpass + $$error` -eq 0; then \ + success=true; \ + else \ + success=false; \ + fi; \ + br='==================='; br=$$br$$br$$br$$br; \ + result_count () \ + { \ + if test x"$$1" = x"--maybe-color"; then \ + maybe_colorize=yes; \ + elif test x"$$1" = x"--no-color"; then \ + maybe_colorize=no; \ else \ - if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ - banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ + echo "$@: invalid 'result_count' usage" >&2; exit 4; \ fi; \ - fi; \ - dashes="$$banner"; \ - skipped=""; \ - if test "$$skip" -ne 0; then \ - if test "$$skip" -eq 1; then \ - skipped="($$skip test was not run)"; \ + shift; \ + desc=$$1 count=$$2; \ + if test $$maybe_colorize = yes && test $$count -gt 0; then \ + color_start=$$3 color_end=$$std; \ else \ - skipped="($$skip tests were not run)"; \ + color_start= color_end=; \ fi; \ - test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ - dashes="$$skipped"; \ - fi; \ - report=""; \ - if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ - report="Please report to $(PACKAGE_BUGREPORT)"; \ - test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ - dashes="$$report"; \ - fi; \ - dashes=`echo "$$dashes" | sed s/./=/g`; \ - if test "$$failed" -eq 0; then \ - col="$$grn"; \ - else \ - col="$$red"; \ - fi; \ - echo "$${col}$$dashes$${std}"; \ - echo "$${col}$$banner$${std}"; \ - test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \ - test -z "$$report" || echo "$${col}$$report$${std}"; \ - echo "$${col}$$dashes$${std}"; \ - test "$$failed" -eq 0; \ - else :; fi + echo "$${color_start}# $$desc $$count$${color_end}"; \ + }; \ + create_testsuite_report () \ + { \ + result_count $$1 "TOTAL:" $$all "$$brg"; \ + result_count $$1 "PASS: " $$pass "$$grn"; \ + result_count $$1 "SKIP: " $$skip "$$blu"; \ + result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ + result_count $$1 "FAIL: " $$fail "$$red"; \ + result_count $$1 "XPASS:" $$xpass "$$red"; \ + result_count $$1 "ERROR:" $$error "$$mgn"; \ + }; \ + { \ + echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ + $(am__rst_title); \ + create_testsuite_report --no-color; \ + echo; \ + echo ".. contents:: :depth: 2"; \ + echo; \ + for b in $$bases; do echo $$b; done \ + | $(am__create_global_log); \ + } >$(TEST_SUITE_LOG).tmp || exit 1; \ + mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ + if $$success; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ + fi; \ + echo "$${col}$$br$${std}"; \ + echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \ + echo "$${col}$$br$${std}"; \ + create_testsuite_report --maybe-color; \ + echo "$$col$$br$$std"; \ + if $$success; then :; else \ + echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ + if test -n "$(PACKAGE_BUGREPORT)"; then \ + echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ + fi; \ + echo "$$col$$br$$std"; \ + fi; \ + $$success || exit 1 + +check-TESTS: + @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list + @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + trs_list=`for i in $$bases; do echo $$i.trs; done`; \ + log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ + exit $$?; +recheck: all + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + bases=`for i in $$bases; do echo $$i; done \ + | $(am__list_recheck_tests)` || exit 1; \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + log_list=`echo $$log_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ + am__force_recheck=am--force-recheck \ + TEST_LOGS="$$log_list"; \ + exit $$? +test-dhcp-dhclient.log: test-dhcp-dhclient$(EXEEXT) + @p='test-dhcp-dhclient$(EXEEXT)'; \ + b='test-dhcp-dhclient'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +test-dhcp-utils.log: test-dhcp-utils$(EXEEXT) + @p='test-dhcp-utils$(EXEEXT)'; \ + b='test-dhcp-utils'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +.test.log: + @p='$<'; \ + $(am__set_b); \ + $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +@am__EXEEXT_TRUE@.test$(EXEEXT).log: +@am__EXEEXT_TRUE@ @p='$<'; \ +@am__EXEEXT_TRUE@ $(am__set_b); \ +@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ +@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ +@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ +@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ @@ -830,6 +1091,9 @@ install-strip: "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: + -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) + -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) + -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) clean-generic: @@ -925,7 +1189,7 @@ uninstall-am: installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ - tags tags-am uninstall uninstall-am + recheck tags tags-am uninstall uninstall-am .PRECIOUS: Makefile diff --git a/src/dhcp-manager/tests/test-dhcp-dhclient.c b/src/dhcp-manager/tests/test-dhcp-dhclient.c index e1a96cab..459915e3 100644 --- a/src/dhcp-manager/tests/test-dhcp-dhclient.c +++ b/src/dhcp-manager/tests/test-dhcp-dhclient.c @@ -32,7 +32,7 @@ #include "nm-utils.h" #include "nm-ip4-config.h" #include "nm-platform.h" -#include "nm-utils-internal.h" +#include "nm-macros-internal.h" #define DEBUG 1 diff --git a/src/dns-manager/nm-dns-manager.c b/src/dns-manager/nm-dns-manager.c index 2f8ceab5..f041c29e 100644 --- a/src/dns-manager/nm-dns-manager.c +++ b/src/dns-manager/nm-dns-manager.c @@ -43,7 +43,6 @@ #include "nm-ip6-config.h" #include "nm-logging.h" #include "NetworkManagerUtils.h" -#include "nm-posix-signals.h" #include "nm-config.h" #include "nm-dns-plugin.h" @@ -87,6 +86,8 @@ typedef struct { NMDnsManagerResolvConfMode resolv_conf_mode; NMDnsPlugin *plugin; + NMConfig *config; + gboolean dns_touched; } NMDnsManagerPrivate; @@ -129,7 +130,7 @@ add_string_item (GPtrArray *array, const char *str) static void merge_one_ip4_config (NMResolvConfData *rc, NMIP4Config *src) { - guint32 num, i; + guint32 num, num_domains, num_searches, i; num = nm_ip4_config_get_num_nameservers (src); for (i = 0; i < num; i++) { @@ -137,18 +138,10 @@ merge_one_ip4_config (NMResolvConfData *rc, NMIP4Config *src) nm_utils_inet4_ntop (nm_ip4_config_get_nameserver (src, i), NULL)); } - num = nm_ip4_config_get_num_domains (src); - for (i = 0; i < num; i++) { - const char *domain; - - domain = nm_ip4_config_get_domain (src, i); - if (!DOMAIN_IS_VALID (domain)) - continue; - add_string_item (rc->searches, domain); - } + num_domains = nm_ip4_config_get_num_domains (src); + num_searches = nm_ip4_config_get_num_searches (src); - num = nm_ip4_config_get_num_searches (src); - for (i = 0; i < num; i++) { + for (i = 0; i < num_searches; i++) { const char *search; search = nm_ip4_config_get_search (src, i); @@ -157,6 +150,17 @@ merge_one_ip4_config (NMResolvConfData *rc, NMIP4Config *src) add_string_item (rc->searches, search); } + if (num_domains > 1 || !num_searches) { + for (i = 0; i < num_domains; i++) { + const char *domain; + + domain = nm_ip4_config_get_domain (src, i); + if (!DOMAIN_IS_VALID (domain)) + continue; + add_string_item (rc->searches, domain); + } + } + /* NIS stuff */ num = nm_ip4_config_get_num_nis_servers (src); for (i = 0; i < num; i++) { @@ -174,7 +178,7 @@ merge_one_ip4_config (NMResolvConfData *rc, NMIP4Config *src) static void merge_one_ip6_config (NMResolvConfData *rc, NMIP6Config *src) { - guint32 num, i; + guint32 num, num_domains, num_searches, i; const char *iface; iface = g_object_get_data (G_OBJECT (src), IP_CONFIG_IFACE_TAG); @@ -199,18 +203,10 @@ merge_one_ip6_config (NMResolvConfData *rc, NMIP6Config *src) add_string_item (rc->nameservers, buf); } - num = nm_ip6_config_get_num_domains (src); - for (i = 0; i < num; i++) { - const char *domain; + num_domains = nm_ip6_config_get_num_domains (src); + num_searches = nm_ip6_config_get_num_searches (src); - domain = nm_ip6_config_get_domain (src, i); - if (!DOMAIN_IS_VALID (domain)) - continue; - add_string_item (rc->searches, domain); - } - - num = nm_ip6_config_get_num_searches (src); - for (i = 0; i < num; i++) { + for (i = 0; i < num_searches; i++) { const char *search; search = nm_ip6_config_get_search (src, i); @@ -218,6 +214,17 @@ merge_one_ip6_config (NMResolvConfData *rc, NMIP6Config *src) continue; add_string_item (rc->searches, search); } + + if (num_domains > 1 || !num_searches) { + for (i = 0; i < num_domains; i++) { + const char *domain; + + domain = nm_ip6_config_get_domain (src, i); + if (!DOMAIN_IS_VALID (domain)) + continue; + add_string_item (rc->searches, domain); + } + } } @@ -225,19 +232,6 @@ merge_one_ip6_config (NMResolvConfData *rc, NMIP6Config *src) /**********************************/ /* SUSE */ -static void -netconfig_child_setup (gpointer user_data G_GNUC_UNUSED) -{ - pid_t pid = getpid (); - setpgid (pid, pid); - - /* - * We blocked signals in main(). We need to restore original signal - * mask for netconfig here so that it can receive signals. - */ - nm_unblock_posix_signals (NULL); -} - static GPid run_netconfig (GError **error, gint *stdin_fd) { @@ -255,7 +249,7 @@ run_netconfig (GError **error, gint *stdin_fd) nm_log_dbg (LOGD_DNS, "spawning '%s'", tmp); g_free (tmp); - if (!g_spawn_async_with_pipes (NULL, argv, NULL, 0, netconfig_child_setup, + if (!g_spawn_async_with_pipes (NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, stdin_fd, NULL, NULL, error)) return -1; @@ -284,10 +278,10 @@ dispatch_netconfig (char **searches, char *str; GPid pid; gint fd; - int ret = 1; + int status; pid = run_netconfig (error, &fd); - if (pid < 0) + if (pid <= 0) return FALSE; /* NM is writing already-merged DNS information to netconfig, so it @@ -320,24 +314,22 @@ dispatch_netconfig (char **searches, close (fd); /* Wait until the process exits */ + if (!nm_utils_kill_child_sync (pid, 0, LOGD_DNS, "netconfig", &status, 1000, 0)) { + int errsv = errno; - again: - - if (waitpid (pid, NULL, 0) < 0) { - if (errno == EINTR) - goto again; - else if (errno == ECHILD) { - /* child already exited */ - ret = pid; - } else { - g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED, - "Error waiting for netconfig to exit: %s", - strerror (errno)); - ret = 0; - } + g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED, + "Error waiting for netconfig to exit: %s", + strerror (errsv)); + return FALSE; } - - return ret > 0; + if (!WIFEXITED (status) || WEXITSTATUS (status) != EXIT_SUCCESS) { + g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED, + "Error calling netconfig: %s %d", + WIFEXITED (status) ? "exited with status" : (WIFSIGNALED (status) ? "exited with signal" : "exited with unknown reason"), + WIFEXITED (status) ? WEXITSTATUS (status) : (WIFSIGNALED (status) ? WTERMSIG (status) : status)); + return FALSE; + } + return TRUE; } #endif @@ -782,13 +774,16 @@ update_dns (NMDnsManager *self, #ifdef NETCONFIG_PATH if (success == FALSE) { + g_clear_error (error); success = dispatch_netconfig (searches, nameservers, nis_domain, nis_servers, error); } #endif - if (success == FALSE) + if (success == FALSE) { + g_clear_error (error); success = update_resolv_conf (searches, nameservers, error); + } /* signal that resolv.conf was changed */ if (success) @@ -1099,6 +1094,8 @@ init_resolv_conf_mode (NMDnsManager *self) const char *mode; int fd, flags; + g_clear_object (&priv->plugin); + fd = open (_PATH_RESCONF, O_RDONLY); if (fd != -1) { if (ioctl (fd, FS_IOC_GETFLAGS, &flags) == -1) @@ -1112,7 +1109,7 @@ init_resolv_conf_mode (NMDnsManager *self) } } - mode = nm_config_get_dns_mode (nm_config_get ()); + mode = nm_config_data_get_dns_mode (nm_config_get_data (priv->config)); if (!g_strcmp0 (mode, "none")) { priv->resolv_conf_mode = NM_DNS_MANAGER_RESOLV_CONF_UNMANAGED; nm_log_info (LOGD_DNS, "DNS: not managing " _PATH_RESCONF); @@ -1127,6 +1124,34 @@ init_resolv_conf_mode (NMDnsManager *self) if (mode && g_strcmp0 (mode, "default") != 0) nm_log_warn (LOGD_DNS, "Unknown DNS mode '%s'", mode); } + + if (priv->plugin) { + nm_log_info (LOGD_DNS, "DNS: loaded plugin %s", nm_dns_plugin_get_name (priv->plugin)); + g_signal_connect (priv->plugin, NM_DNS_PLUGIN_FAILED, G_CALLBACK (plugin_failed), self); + g_signal_connect (priv->plugin, NM_DNS_PLUGIN_CHILD_QUIT, G_CALLBACK (plugin_child_quit), self); + } +} + +static void +config_changed_cb (NMConfig *config, + NMConfigData *config_data, + NMConfigChangeFlags changes, + NMConfigData *old_data, + NMDnsManager *self) +{ + GError *error = NULL; + + if (NM_FLAGS_HAS (changes, NM_CONFIG_CHANGE_DNS_MODE)) + init_resolv_conf_mode (self); + + if (NM_FLAGS_ANY (changes, NM_CONFIG_CHANGE_SIGHUP | + NM_CONFIG_CHANGE_SIGUSR1 | + NM_CONFIG_CHANGE_DNS_MODE)) { + if (!update_dns (self, TRUE, &error)) { + nm_log_warn (LOGD_DNS, "could not commit DNS changes: %s", error->message); + g_clear_error (&error); + } + } } static void @@ -1137,13 +1162,12 @@ nm_dns_manager_init (NMDnsManager *self) /* Set the initial hash */ compute_hash (self, NM_DNS_MANAGER_GET_PRIVATE (self)->hash); + priv->config = g_object_ref (nm_config_get ()); + g_signal_connect (G_OBJECT (priv->config), + NM_CONFIG_SIGNAL_CONFIG_CHANGED, + G_CALLBACK (config_changed_cb), + self); init_resolv_conf_mode (self); - - if (priv->plugin) { - nm_log_info (LOGD_DNS, "DNS: loaded plugin %s", nm_dns_plugin_get_name (priv->plugin)); - g_signal_connect (priv->plugin, NM_DNS_PLUGIN_FAILED, G_CALLBACK (plugin_failed), self); - g_signal_connect (priv->plugin, NM_DNS_PLUGIN_CHILD_QUIT, G_CALLBACK (plugin_child_quit), self); - } } static void @@ -1170,6 +1194,11 @@ dispose (GObject *object) priv->dns_touched = FALSE; } + if (priv->config) { + g_signal_handlers_disconnect_by_func (priv->config, config_changed_cb, self); + g_clear_object (&priv->config); + } + g_slist_free_full (priv->configs, g_object_unref); priv->configs = NULL; diff --git a/src/dns-manager/nm-dns-plugin.c b/src/dns-manager/nm-dns-plugin.c index c684464e..5b95d1c5 100644 --- a/src/dns-manager/nm-dns-plugin.c +++ b/src/dns-manager/nm-dns-plugin.c @@ -28,7 +28,6 @@ #include "nm-dns-plugin.h" #include "nm-logging.h" -#include "nm-posix-signals.h" #include "NetworkManagerUtils.h" typedef struct { @@ -136,20 +135,6 @@ watch_cb (GPid pid, gint status, gpointer user_data) g_signal_emit (self, signals[CHILD_QUIT], 0, status); } -static void -child_setup (gpointer user_data G_GNUC_UNUSED) -{ - /* We are in the child process at this point */ - pid_t pid = getpid (); - setpgid (pid, pid); - - /* - * We blocked signals in main(). We need to restore original signal - * mask for DNS plugin here so that it can receive signals. - */ - nm_unblock_posix_signals (NULL); -} - GPid nm_dns_plugin_child_spawn (NMDnsPlugin *self, const char **argv, @@ -183,8 +168,8 @@ nm_dns_plugin_child_spawn (NMDnsPlugin *self, priv->pid = 0; if (g_spawn_async (NULL, (char **) argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, - child_setup, - NULL, &priv->pid, + nm_utils_setpgid, NULL, + &priv->pid, &error)) { nm_log_dbg (LOGD_DNS, "%s started with pid %d", priv->progname, priv->pid); priv->watch_id = g_child_watch_add (priv->pid, (GChildWatchFunc) watch_cb, self); diff --git a/src/dnsmasq-manager/nm-dnsmasq-manager.c b/src/dnsmasq-manager/nm-dnsmasq-manager.c index 2f38ea71..a85631bc 100644 --- a/src/dnsmasq-manager/nm-dnsmasq-manager.c +++ b/src/dnsmasq-manager/nm-dnsmasq-manager.c @@ -32,7 +32,6 @@ #include "nm-dnsmasq-utils.h" #include "nm-logging.h" #include "nm-glib-compat.h" -#include "nm-posix-signals.h" #include "nm-utils.h" #include "NetworkManagerUtils.h" @@ -297,20 +296,6 @@ create_dm_cmd_line (const char *iface, } static void -dm_child_setup (gpointer user_data G_GNUC_UNUSED) -{ - /* We are in the child process at this point */ - pid_t pid = getpid (); - setpgid (pid, pid); - - /* - * We blocked signals in main(). We need to restore original signal - * mask for dnsmasq here so that it can receive signals. - */ - nm_unblock_posix_signals (NULL); -} - -static void kill_existing_for_iface (const char *iface, const char *pidfile) { char *contents = NULL; @@ -374,9 +359,9 @@ nm_dnsmasq_manager_start (NMDnsMasqManager *manager, priv->pid = 0; if (!g_spawn_async (NULL, (char **) dm_cmd->array->pdata, NULL, - G_SPAWN_DO_NOT_REAP_CHILD, - dm_child_setup, - NULL, &priv->pid, error)) { + G_SPAWN_DO_NOT_REAP_CHILD, + nm_utils_setpgid, NULL, + &priv->pid, error)) { goto out; } diff --git a/src/dnsmasq-manager/tests/Makefile.in b/src/dnsmasq-manager/tests/Makefile.in index c21629db..8e6bb12e 100644 --- a/src/dnsmasq-manager/tests/Makefile.in +++ b/src/dnsmasq-manager/tests/Makefile.in @@ -203,13 +203,196 @@ am__tty_colors = { \ std='[m'; \ fi; \ } +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__recheck_rx = ^[ ]*:recheck:[ ]* +am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* +am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* +# A command that, given a newline-separated list of test names on the +# standard input, print the name of the tests that are to be re-run +# upon "make recheck". +am__list_recheck_tests = $(AWK) '{ \ + recheck = 1; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + { \ + if ((getline line2 < ($$0 ".log")) < 0) \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ + { \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ + { \ + break; \ + } \ + }; \ + if (recheck) \ + print $$0; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# A command that, given a newline-separated list of test names on the +# standard input, create the global log from their .trs and .log files. +am__create_global_log = $(AWK) ' \ +function fatal(msg) \ +{ \ + print "fatal: making $@: " msg | "cat >&2"; \ + exit 1; \ +} \ +function rst_section(header) \ +{ \ + print header; \ + len = length(header); \ + for (i = 1; i <= len; i = i + 1) \ + printf "="; \ + printf "\n\n"; \ +} \ +{ \ + copy_in_global_log = 1; \ + global_test_result = "RUN"; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".trs"); \ + if (line ~ /$(am__global_test_result_rx)/) \ + { \ + sub("$(am__global_test_result_rx)", "", line); \ + sub("[ ]*$$", "", line); \ + global_test_result = line; \ + } \ + else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ + copy_in_global_log = 0; \ + }; \ + if (copy_in_global_log) \ + { \ + rst_section(global_test_result ": " $$0); \ + while ((rc = (getline line < ($$0 ".log"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".log"); \ + print line; \ + }; \ + printf "\n"; \ + }; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# Restructured Text title. +am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } +# Solaris 10 'make', and several other traditional 'make' implementations, +# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it +# by disabling -e (using the XSI extension "set +e") if it's set. +am__sh_e_setup = case $$- in *e*) set +e;; esac +# Default flags passed to test drivers. +am__common_driver_flags = \ + --color-tests "$$am__color_tests" \ + --enable-hard-errors "$$am__enable_hard_errors" \ + --expect-failure "$$am__expect_failure" +# To be inserted before the command running the test. Creates the +# directory for the log if needed. Stores in $dir the directory +# containing $f, in $tst the test, in $log the log. Executes the +# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and +# passes TESTS_ENVIRONMENT. Set up options for the wrapper that +# will run the test scripts (or their associated LOG_COMPILER, if +# thy have one). +am__check_pre = \ +$(am__sh_e_setup); \ +$(am__vpath_adj_setup) $(am__vpath_adj) \ +$(am__tty_colors); \ +srcdir=$(srcdir); export srcdir; \ +case "$@" in \ + */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ + *) am__odir=.;; \ +esac; \ +test "x$$am__odir" = x"." || test -d "$$am__odir" \ + || $(MKDIR_P) "$$am__odir" || exit $$?; \ +if test -f "./$$f"; then dir=./; \ +elif test -f "$$f"; then dir=; \ +else dir="$(srcdir)/"; fi; \ +tst=$$dir$$f; log='$@'; \ +if test -n '$(DISABLE_HARD_ERRORS)'; then \ + am__enable_hard_errors=no; \ +else \ + am__enable_hard_errors=yes; \ +fi; \ +case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ + am__expect_failure=yes;; \ + *) \ + am__expect_failure=no;; \ +esac; \ +$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) +# A shell command to get the names of the tests scripts with any registered +# extension removed (i.e., equivalently, the names of the test logs, with +# the '.log' extension removed). The result is saved in the shell variable +# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, +# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", +# since that might cause problem with VPATH rewrites for suffix-less tests. +# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. +am__set_TESTS_bases = \ + bases='$(TEST_LOGS)'; \ + bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ + bases=`echo $$bases` +RECHECK_LOGS = $(TEST_LOGS) +AM_RECURSIVE_TARGETS = check recheck +TEST_SUITE_LOG = test-suite.log +TEST_EXTENSIONS = @EXEEXT@ .test +LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) +am__set_b = \ + case '$@' in \ + */*) \ + case '$*' in \ + */*) b='$*';; \ + *) b=`echo '$@' | sed 's/\.log$$//'`; \ + esac;; \ + *) \ + b='$*';; \ + esac +am__test_logs1 = $(TESTS:=.log) +am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) +TEST_LOGS = $(am__test_logs2:.test.log=.log) +TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/build-aux/test-driver +TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ + $(TEST_LOG_FLAGS) am__DIST_COMMON = $(srcdir)/Makefile.in \ - $(top_srcdir)/build-aux/depcomp + $(top_srcdir)/build-aux/depcomp \ + $(top_srcdir)/build-aux/test-driver DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -323,6 +506,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ +LOG_DRIVER = @LOG_DRIVER@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ @@ -488,7 +672,7 @@ test_dnsmasq_utils_LDADD = \ all: all-am .SUFFIXES: -.SUFFIXES: .c .lo .o .obj +.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ @@ -619,98 +803,168 @@ cscopelist-am: $(am__tagged_files) distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -check-TESTS: $(TESTS) - @failed=0; all=0; xfail=0; xpass=0; skip=0; \ - srcdir=$(srcdir); export srcdir; \ - list=' $(TESTS) '; \ - $(am__tty_colors); \ - if test -n "$$list"; then \ - for tst in $$list; do \ - if test -f ./$$tst; then dir=./; \ - elif test -f $$tst; then dir=; \ - else dir="$(srcdir)/"; fi; \ - if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \ - all=`expr $$all + 1`; \ - case " $(XFAIL_TESTS) " in \ - *[\ \ ]$$tst[\ \ ]*) \ - xpass=`expr $$xpass + 1`; \ - failed=`expr $$failed + 1`; \ - col=$$red; res=XPASS; \ - ;; \ - *) \ - col=$$grn; res=PASS; \ - ;; \ - esac; \ - elif test $$? -ne 77; then \ - all=`expr $$all + 1`; \ - case " $(XFAIL_TESTS) " in \ - *[\ \ ]$$tst[\ \ ]*) \ - xfail=`expr $$xfail + 1`; \ - col=$$lgn; res=XFAIL; \ - ;; \ - *) \ - failed=`expr $$failed + 1`; \ - col=$$red; res=FAIL; \ - ;; \ - esac; \ - else \ - skip=`expr $$skip + 1`; \ - col=$$blu; res=SKIP; \ - fi; \ - echo "$${col}$$res$${std}: $$tst"; \ - done; \ - if test "$$all" -eq 1; then \ - tests="test"; \ - All=""; \ - else \ - tests="tests"; \ - All="All "; \ +# Recover from deleted '.trs' file; this should ensure that +# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create +# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells +# to avoid problems with "make -n". +.log.trs: + rm -f $< $@ + $(MAKE) $(AM_MAKEFLAGS) $< + +# Leading 'am--fnord' is there to ensure the list of targets does not +# expand to empty, as could happen e.g. with make check TESTS=''. +am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) +am--force-recheck: + @: + +$(TEST_SUITE_LOG): $(TEST_LOGS) + @$(am__set_TESTS_bases); \ + am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ + redo_bases=`for i in $$bases; do \ + am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ + done`; \ + if test -n "$$redo_bases"; then \ + redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ + redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ + if $(am__make_dryrun); then :; else \ + rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ fi; \ - if test "$$failed" -eq 0; then \ - if test "$$xfail" -eq 0; then \ - banner="$$All$$all $$tests passed"; \ - else \ - if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ - banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ - fi; \ - else \ - if test "$$xpass" -eq 0; then \ - banner="$$failed of $$all $$tests failed"; \ + fi; \ + if test -n "$$am__remaking_logs"; then \ + echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ + "recursion detected" >&2; \ + elif test -n "$$redo_logs"; then \ + am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ + fi; \ + if $(am__make_dryrun); then :; else \ + st=0; \ + errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ + for i in $$redo_bases; do \ + test -f $$i.trs && test -r $$i.trs \ + || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ + test -f $$i.log && test -r $$i.log \ + || { echo "$$errmsg $$i.log" >&2; st=1; }; \ + done; \ + test $$st -eq 0 || exit 1; \ + fi + @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ + ws='[ ]'; \ + results=`for b in $$bases; do echo $$b.trs; done`; \ + test -n "$$results" || results=/dev/null; \ + all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ + pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ + fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ + skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ + xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ + xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ + error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ + if test `expr $$fail + $$xpass + $$error` -eq 0; then \ + success=true; \ + else \ + success=false; \ + fi; \ + br='==================='; br=$$br$$br$$br$$br; \ + result_count () \ + { \ + if test x"$$1" = x"--maybe-color"; then \ + maybe_colorize=yes; \ + elif test x"$$1" = x"--no-color"; then \ + maybe_colorize=no; \ else \ - if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ - banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ + echo "$@: invalid 'result_count' usage" >&2; exit 4; \ fi; \ - fi; \ - dashes="$$banner"; \ - skipped=""; \ - if test "$$skip" -ne 0; then \ - if test "$$skip" -eq 1; then \ - skipped="($$skip test was not run)"; \ + shift; \ + desc=$$1 count=$$2; \ + if test $$maybe_colorize = yes && test $$count -gt 0; then \ + color_start=$$3 color_end=$$std; \ else \ - skipped="($$skip tests were not run)"; \ + color_start= color_end=; \ fi; \ - test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ - dashes="$$skipped"; \ - fi; \ - report=""; \ - if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ - report="Please report to $(PACKAGE_BUGREPORT)"; \ - test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ - dashes="$$report"; \ - fi; \ - dashes=`echo "$$dashes" | sed s/./=/g`; \ - if test "$$failed" -eq 0; then \ - col="$$grn"; \ - else \ - col="$$red"; \ - fi; \ - echo "$${col}$$dashes$${std}"; \ - echo "$${col}$$banner$${std}"; \ - test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \ - test -z "$$report" || echo "$${col}$$report$${std}"; \ - echo "$${col}$$dashes$${std}"; \ - test "$$failed" -eq 0; \ - else :; fi + echo "$${color_start}# $$desc $$count$${color_end}"; \ + }; \ + create_testsuite_report () \ + { \ + result_count $$1 "TOTAL:" $$all "$$brg"; \ + result_count $$1 "PASS: " $$pass "$$grn"; \ + result_count $$1 "SKIP: " $$skip "$$blu"; \ + result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ + result_count $$1 "FAIL: " $$fail "$$red"; \ + result_count $$1 "XPASS:" $$xpass "$$red"; \ + result_count $$1 "ERROR:" $$error "$$mgn"; \ + }; \ + { \ + echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ + $(am__rst_title); \ + create_testsuite_report --no-color; \ + echo; \ + echo ".. contents:: :depth: 2"; \ + echo; \ + for b in $$bases; do echo $$b; done \ + | $(am__create_global_log); \ + } >$(TEST_SUITE_LOG).tmp || exit 1; \ + mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ + if $$success; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ + fi; \ + echo "$${col}$$br$${std}"; \ + echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \ + echo "$${col}$$br$${std}"; \ + create_testsuite_report --maybe-color; \ + echo "$$col$$br$$std"; \ + if $$success; then :; else \ + echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ + if test -n "$(PACKAGE_BUGREPORT)"; then \ + echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ + fi; \ + echo "$$col$$br$$std"; \ + fi; \ + $$success || exit 1 + +check-TESTS: + @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list + @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + trs_list=`for i in $$bases; do echo $$i.trs; done`; \ + log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ + exit $$?; +recheck: all + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + bases=`for i in $$bases; do echo $$i; done \ + | $(am__list_recheck_tests)` || exit 1; \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + log_list=`echo $$log_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ + am__force_recheck=am--force-recheck \ + TEST_LOGS="$$log_list"; \ + exit $$? +test-dnsmasq-utils.log: test-dnsmasq-utils$(EXEEXT) + @p='test-dnsmasq-utils$(EXEEXT)'; \ + b='test-dnsmasq-utils'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +.test.log: + @p='$<'; \ + $(am__set_b); \ + $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +@am__EXEEXT_TRUE@.test$(EXEEXT).log: +@am__EXEEXT_TRUE@ @p='$<'; \ +@am__EXEEXT_TRUE@ $(am__set_b); \ +@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ +@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ +@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ +@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ @@ -767,6 +1021,9 @@ install-strip: "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: + -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) + -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) + -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) clean-generic: @@ -862,7 +1119,7 @@ uninstall-am: installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ - tags tags-am uninstall uninstall-am + recheck tags tags-am uninstall uninstall-am .PRECIOUS: Makefile diff --git a/src/main-utils.c b/src/main-utils.c index c4d0eeb9..b0d8df3e 100644 --- a/src/main-utils.c +++ b/src/main-utils.c @@ -31,103 +31,63 @@ #include <glib.h> #include <glib/gi18n.h> +#include <glib-unix.h> #include <gmodule.h> +#include "nm-glib-compat.h" + #include "gsystem-local-alloc.h" #include "main-utils.h" -#include "nm-posix-signals.h" #include "NetworkManagerUtils.h" #include "nm-logging.h" -static sigset_t signal_set; -static gboolean *quit_early = NULL; +static gboolean +sighup_handler (gpointer user_data) +{ + nm_main_config_reload (GPOINTER_TO_INT (user_data)); + return G_SOURCE_CONTINUE; +} -/* - * Thread function waiting for signals and processing them. - * Wait for signals in signal set. The semantics of sigwait() require that all - * threads (including the thread calling sigwait()) have the signal masked, for - * reliable operation. Otherwise, a signal that arrives while this thread is - * not blocked in sigwait() might be delivered to another thread. - */ -static void * -signal_handling_thread (void *arg) +static gboolean +sigint_handler (gpointer user_data) { - GMainLoop *main_loop = arg; - int signo; - - while (1) { - sigwait (&signal_set, &signo); - - switch (signo) { - case SIGINT: - case SIGTERM: - nm_log_info (LOGD_CORE, "caught signal %d, shutting down normally.", signo); - *quit_early = TRUE; /* for quitting before entering the main loop */ - g_main_loop_quit (main_loop); - break; - case SIGHUP: - /* Reread config stuff like system config files, VPN service files, etc */ - nm_log_info (LOGD_CORE, "caught signal %d, not supported yet.", signo); - break; - case SIGPIPE: - /* silently ignore signal */ - break; - default: - nm_log_err (LOGD_CORE, "caught unexpected signal %d", signo); - break; - } - } - return NULL; + GMainLoop *main_loop = user_data; + + nm_log_info (LOGD_CORE, "caught SIGINT, shutting down normally."); + g_main_loop_quit (main_loop); + + return G_SOURCE_REMOVE; +} + +static gboolean +sigterm_handler (gpointer user_data) +{ + GMainLoop *main_loop = user_data; + + nm_log_info (LOGD_CORE, "caught SIGTERM, shutting down normally."); + g_main_loop_quit (main_loop); + + return G_SOURCE_REMOVE; } /** * nm_main_utils_setup_signals: * @main_loop: the #GMainLoop to quit when SIGINT or SIGTERM is received - * @quit_early: location of a variable that will be set to TRUE when - * SIGINT or SIGTERM is received * - * Mask the signals we are interested in and create a signal handling thread. - * Because all threads inherit the signal mask from their creator, all threads - * in the process will have the signals masked. That's why setup_signals() has - * to be called before creating other threads. - * - * Returns: %TRUE on success + * Sets up signal handling for NetworkManager. */ -gboolean -nm_main_utils_setup_signals (GMainLoop *main_loop, gboolean *quit_early_ptr) +void +nm_main_utils_setup_signals (GMainLoop *main_loop) { - pthread_t signal_thread_id; - sigset_t old_sig_mask; - int status; - - g_return_val_if_fail (main_loop != NULL, FALSE); - g_return_val_if_fail (quit_early_ptr != NULL, FALSE); - - quit_early = quit_early_ptr; - - sigemptyset (&signal_set); - sigaddset (&signal_set, SIGHUP); - sigaddset (&signal_set, SIGINT); - sigaddset (&signal_set, SIGTERM); - sigaddset (&signal_set, SIGPIPE); + g_return_if_fail (main_loop != NULL); - /* Block all signals of interest. */ - status = pthread_sigmask (SIG_BLOCK, &signal_set, &old_sig_mask); - if (status != 0) { - fprintf (stderr, _("Failed to set signal mask: %d"), status); - return FALSE; - } - /* Save original mask so that we could use it for child processes. */ - nm_save_original_signal_mask (old_sig_mask); - - /* Create the signal handling thread. */ - status = pthread_create (&signal_thread_id, NULL, signal_handling_thread, main_loop); - if (status != 0) { - fprintf (stderr, _("Failed to create signal handling thread: %d"), status); - return FALSE; - } + signal (SIGPIPE, SIG_IGN); - return TRUE; + g_unix_signal_add (SIGHUP, sighup_handler, GINT_TO_POINTER (SIGHUP)); + g_unix_signal_add (SIGUSR1, sighup_handler, GINT_TO_POINTER (SIGUSR1)); + g_unix_signal_add (SIGUSR2, sighup_handler, GINT_TO_POINTER (SIGUSR2)); + g_unix_signal_add (SIGINT, sigint_handler, main_loop); + g_unix_signal_add (SIGTERM, sigterm_handler, main_loop); } gboolean @@ -231,7 +191,8 @@ nm_main_utils_early_setup (const char *progname, int *argc, char **argv[], GOptionEntry *options, - GOptionEntry *more_options, + void (*option_context_hook) (gpointer user_data, GOptionContext *opt_ctx), + gpointer option_context_hook_data, const char *summary) { GOptionContext *opt_ctx = NULL; @@ -276,9 +237,9 @@ nm_main_utils_early_setup (const char *progname, g_option_context_set_ignore_unknown_options (opt_ctx, FALSE); g_option_context_set_help_enabled (opt_ctx, TRUE); g_option_context_add_main_entries (opt_ctx, options, NULL); - if (more_options) - g_option_context_add_main_entries (opt_ctx, more_options, NULL); g_option_context_set_summary (opt_ctx, summary); + if (option_context_hook) + option_context_hook (option_context_hook_data, opt_ctx); success = g_option_context_parse (opt_ctx, argc, argv, &error); if (!success) { diff --git a/src/main-utils.h b/src/main-utils.h index 1fceb350..49550295 100644 --- a/src/main-utils.h +++ b/src/main-utils.h @@ -25,7 +25,7 @@ void nm_main_utils_ensure_root (void); -gboolean nm_main_utils_setup_signals (GMainLoop *main_loop, gboolean *quit_early_ptr); +void nm_main_utils_setup_signals (GMainLoop *main_loop); void nm_main_utils_ensure_rundir (void); @@ -37,7 +37,13 @@ gboolean nm_main_utils_early_setup (const char *progname, int *argc, char **argv[], GOptionEntry *options, - GOptionEntry *more_options, + void (*option_context_hook) (gpointer user_data, GOptionContext *opt_ctx), + gpointer option_context_hook_data, const char *summary); +/* The following functions are not implemented inside nm-main-utils.c, instead + * main.c and nm-iface-helper.c */ + +void nm_main_config_reload (int signal); + #endif /* __MAIN_UTILS_H__ */ diff --git a/src/main.c b/src/main.c index 53e829cd..dfc307b5 100644 --- a/src/main.c +++ b/src/main.c @@ -54,7 +54,6 @@ #include "nm-vpn-manager.h" #include "nm-logging.h" #include "nm-config.h" -#include "nm-posix-signals.h" #include "nm-session-monitor.h" #include "nm-dispatcher.h" #include "nm-settings.h" @@ -69,7 +68,7 @@ #define NM_DEFAULT_SYSTEM_STATE_FILE NMSTATEDIR "/NetworkManager.state" static GMainLoop *main_loop = NULL; -static gboolean quit_early = FALSE; +static gboolean configure_and_quit = FALSE; static struct { gboolean show_version; @@ -164,7 +163,7 @@ parse_state_file (const char *filename, } static void -_set_g_fatal_warnings () +_set_g_fatal_warnings (void) { GLogLevelFlags fatal_mask; @@ -208,16 +207,29 @@ _init_nm_debug (const char *debug) _set_g_fatal_warnings (); } +void +nm_main_config_reload (int signal) +{ + nm_log_info (LOGD_CORE, "reload configuration (signal %s)...", strsignal (signal)); + /* The signal handler thread is only installed after + * creating NMConfig instance, and on shut down we + * no longer run the mainloop (to reach this point). + * + * Hence, a NMConfig singleton instance must always be + * available. */ + nm_config_reload (nm_config_get (), signal); +} + static void manager_configure_quit (NMManager *manager, gpointer user_data) { nm_log_info (LOGD_CORE, "quitting now that startup is complete"); g_main_loop_quit (main_loop); - quit_early = TRUE; + configure_and_quit = TRUE; } static void -do_early_setup (int *argc, char **argv[]) +do_early_setup (int *argc, char **argv[], NMConfigCmdLineOptions *config_cli) { GOptionEntry options[] = { { "version", 'V', 0, G_OPTION_ARG_NONE, &global_opt.show_version, N_("Print NetworkManager version and exit"), NULL }, @@ -228,17 +240,19 @@ do_early_setup (int *argc, char **argv[]) N_("Log domains separated by ',': any combination of [%s]"), "PLATFORM,RFKILL,WIFI" }, { "g-fatal-warnings", 0, 0, G_OPTION_ARG_NONE, &global_opt.g_fatal_warnings, N_("Make all warnings fatal"), NULL }, - { "pid-file", 'p', 0, G_OPTION_ARG_FILENAME, &global_opt.pidfile, N_("Specify the location of a PID file"), N_("filename") }, - { "state-file", 0, 0, G_OPTION_ARG_FILENAME, &global_opt.state_file, N_("State file location"), N_("/path/to/state.file") }, + { "pid-file", 'p', 0, G_OPTION_ARG_FILENAME, &global_opt.pidfile, N_("Specify the location of a PID file"), N_(NM_DEFAULT_PID_FILE) }, + { "state-file", 0, 0, G_OPTION_ARG_FILENAME, &global_opt.state_file, N_("State file location"), N_(NM_DEFAULT_SYSTEM_STATE_FILE) }, { "run-from-build-dir", 0, 0, G_OPTION_ARG_NONE, &global_opt.run_from_build_dir, "Run from build directory", NULL }, {NULL} }; + config_cli = nm_config_cmd_line_options_new (); if (!nm_main_utils_early_setup ("NetworkManager", argc, argv, options, - nm_config_get_options (), + (void (*)(gpointer, GOptionContext *)) nm_config_cmd_line_options_add_to_entries, + config_cli, _("NetworkManager monitors all network connections and automatically\nchooses the best connection to use. It also allows the user to\nspecify wireless access points which wireless cards in the computer\nshould associate with."))) exit (1); @@ -267,6 +281,7 @@ main (int argc, char *argv[]) GError *error = NULL; gboolean wrote_pidfile = FALSE; char *bad_domains = NULL; + NMConfigCmdLineOptions *config_cli; #if !GLIB_CHECK_VERSION (2, 35, 0) g_type_init (); @@ -276,7 +291,8 @@ main (int argc, char *argv[]) main_loop = g_main_loop_new (NULL, FALSE); - do_early_setup (&argc, &argv); + config_cli = nm_config_cmd_line_options_new (); + do_early_setup (&argc, &argv, config_cli); if (global_opt.g_fatal_warnings) _set_g_fatal_warnings (); @@ -338,7 +354,9 @@ main (int argc, char *argv[]) } /* Read the config file and CLI overrides */ - config = nm_config_new (&error); + config = nm_config_setup (config_cli, &error); + nm_config_cmd_line_options_free (config_cli); + config_cli = NULL; if (config == NULL) { fprintf (stderr, _("Failed to read configuration: (%d) %s\n"), error ? error->code : -1, @@ -381,8 +399,7 @@ main (int argc, char *argv[]) } /* Set up unix signal handling - before creating threads, but after daemonizing! */ - if (!nm_main_utils_setup_signals (main_loop, &quit_early)) - exit (1); + nm_main_utils_setup_signals (main_loop); nm_logging_syslog_openlog (global_opt.debug); @@ -405,8 +422,9 @@ main (int argc, char *argv[]) */ dbus_glib_global_set_disable_legacy_property_access (); - nm_log_info (LOGD_CORE, "Read config: %s", nm_config_get_description (config)); - nm_log_info (LOGD_CORE, "WEXT support is %s", + nm_log_info (LOGD_CORE, "Read config: %s", nm_config_data_get_config_description (nm_config_get_data (config))); + nm_config_data_log (nm_config_get_data (config), "CONFIG: "); + nm_log_dbg (LOGD_CORE, "WEXT support is %s", #if HAVE_WEXT "enabled" #else @@ -493,12 +511,11 @@ main (int argc, char *argv[]) * physical interfaces. */ nm_log_dbg (LOGD_CORE, "setting up local loopback"); - nm_platform_link_set_up (nm_platform_link_get_ifindex ("lo")); + nm_platform_link_set_up (NM_PLATFORM_GET, 1, NULL); success = TRUE; - /* Told to quit before getting to the mainloop by the signal handler */ - if (!quit_early) + if (configure_and_quit == FALSE) g_main_loop_run (main_loop); nm_manager_stop (manager); diff --git a/src/nm-activation-request.c b/src/nm-activation-request.c index 30ce7515..2c084b87 100644 --- a/src/nm-activation-request.c +++ b/src/nm-activation-request.c @@ -35,7 +35,6 @@ #include "nm-device.h" #include "nm-active-connection.h" #include "nm-settings-connection.h" -#include "nm-posix-signals.h" #include "nm-auth-subject.h" G_DEFINE_TYPE (NMActRequest, nm_act_request, NM_TYPE_ACTIVE_CONNECTION) @@ -196,16 +195,6 @@ clear_share_rules (NMActRequest *req) priv->share_rules = NULL; } -static void -share_child_setup (gpointer user_data G_GNUC_UNUSED) -{ - /* We are in the child process at this point */ - pid_t pid = getpid (); - setpgid (pid, pid); - - nm_unblock_posix_signals (NULL); -} - void nm_act_request_set_shared (NMActRequest *req, gboolean shared) { @@ -243,7 +232,7 @@ nm_act_request_set_shared (NMActRequest *req, gboolean shared) nm_log_info (LOGD_SHARING, "Executing: %s", cmd); if (!g_spawn_sync ("/", argv, envp, G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL, - share_child_setup, NULL, NULL, NULL, &status, &error)) { + NULL, NULL, NULL, NULL, &status, &error)) { nm_log_warn (LOGD_SHARING, "Error executing command: (%d) %s", error ? error->code : -1, (error && error->message) ? error->message : "(unknown)"); diff --git a/src/nm-active-connection.c b/src/nm-active-connection.c index d0b5b875..d40d571e 100644 --- a/src/nm-active-connection.c +++ b/src/nm-active-connection.c @@ -515,6 +515,7 @@ master_state_cb (NMActiveConnection *master, gpointer user_data) { NMActiveConnection *self = NM_ACTIVE_CONNECTION (user_data); + NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self); NMActiveConnectionState master_state = nm_active_connection_get_state (master); check_master_ready (self); @@ -522,8 +523,8 @@ master_state_cb (NMActiveConnection *master, nm_log_dbg (LOGD_DEVICE, "(%p): master ActiveConnection [%p] state now '%s' (%d)", self, master, state_to_string (master_state), master_state); - if (master_state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATING && - nm_active_connection_get_device (master) == NULL) { + if ( master_state >= NM_ACTIVE_CONNECTION_STATE_DEACTIVATING + && !priv->master_ready) { /* Master failed without ever creating its device */ if (NM_ACTIVE_CONNECTION_GET_CLASS (self)->master_failed) NM_ACTIVE_CONNECTION_GET_CLASS (self)->master_failed (self); diff --git a/src/nm-auth-subject.c b/src/nm-auth-subject.c index 13133f27..c67424e7 100644 --- a/src/nm-auth-subject.c +++ b/src/nm-auth-subject.c @@ -360,7 +360,7 @@ constructed (GObject *object) if (!priv->unix_process.dbus_sender || !*priv->unix_process.dbus_sender) break; - priv->unix_process.start_time = nm_utils_get_start_time_for_pid (priv->unix_process.pid); + priv->unix_process.start_time = nm_utils_get_start_time_for_pid (priv->unix_process.pid, NULL, NULL); if (!priv->unix_process.start_time) { /* could not detect the process start time. The subject is invalid, but don't diff --git a/src/nm-config-data.c b/src/nm-config-data.c new file mode 100644 index 00000000..dc20064b --- /dev/null +++ b/src/nm-config-data.c @@ -0,0 +1,744 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2011 Red Hat, Inc. + * Copyright (C) 2013 Thomas Bechtold <thomasbechtold@jpberlin.de> + */ + +#include "nm-config-data.h" + +#include <string.h> + +#include "nm-config.h" +#include "nm-device.h" +#include "gsystem-local-alloc.h" +#include "nm-core-internal.h" +#include "nm-keyfile-internal.h" +#include "nm-macros-internal.h" +#include "nm-logging.h" + +typedef struct { + char *group_name; + gboolean stop_match; + struct { + /* have a separate boolean field @has, because a @spec with + * value %NULL does not necessarily mean, that the property + * "match-device" was unspecified. */ + gboolean has; + GSList *spec; + } match_device; +} ConnectionInfo; + +typedef struct { + char *config_main_file; + char *config_description; + + GKeyFile *keyfile; + + /* A zero-terminated list of pre-processed information from the + * [connection] sections. This is to speed up lookup. */ + ConnectionInfo *connection_infos; + + struct { + char *uri; + char *response; + guint interval; + } connectivity; + + struct { + char **arr; + GSList *specs; + GSList *specs_config; + } no_auto_default; + + GSList *ignore_carrier; + GSList *assume_ipv6ll_only; + + char *dns_mode; +} NMConfigDataPrivate; + + +enum { + PROP_0, + PROP_CONFIG_MAIN_FILE, + PROP_CONFIG_DESCRIPTION, + PROP_KEYFILE, + PROP_CONNECTIVITY_URI, + PROP_CONNECTIVITY_INTERVAL, + PROP_CONNECTIVITY_RESPONSE, + PROP_NO_AUTO_DEFAULT, + + LAST_PROP +}; + +G_DEFINE_TYPE (NMConfigData, nm_config_data, G_TYPE_OBJECT) + +#define NM_CONFIG_DATA_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_CONFIG_DATA, NMConfigDataPrivate)) + +/************************************************************************/ + +const char * +nm_config_data_get_config_main_file (const NMConfigData *self) +{ + g_return_val_if_fail (self, NULL); + + return NM_CONFIG_DATA_GET_PRIVATE (self)->config_main_file; +} + +const char * +nm_config_data_get_config_description (const NMConfigData *self) +{ + g_return_val_if_fail (self, NULL); + + return NM_CONFIG_DATA_GET_PRIVATE (self)->config_description; +} + +gboolean +nm_config_data_has_group (const NMConfigData *self, const char *group) +{ + g_return_val_if_fail (NM_IS_CONFIG_DATA (self), FALSE); + g_return_val_if_fail (group && *group, FALSE); + + return g_key_file_has_group (NM_CONFIG_DATA_GET_PRIVATE (self)->keyfile, group); +} + +char * +nm_config_data_get_value (const NMConfigData *self, const char *group, const char *key, NMConfigGetValueFlags flags) +{ + g_return_val_if_fail (NM_IS_CONFIG_DATA (self), NULL); + g_return_val_if_fail (group && *group, NULL); + g_return_val_if_fail (key && *key, NULL); + + return nm_config_keyfile_get_value (NM_CONFIG_DATA_GET_PRIVATE (self)->keyfile, group, key, flags); +} + +gboolean +nm_config_data_has_value (const NMConfigData *self, const char *group, const char *key, NMConfigGetValueFlags flags) +{ + gs_free char *value = NULL; + + g_return_val_if_fail (NM_IS_CONFIG_DATA (self), FALSE); + g_return_val_if_fail (group && *group, FALSE); + g_return_val_if_fail (key && *key, FALSE); + + value = nm_config_keyfile_get_value (NM_CONFIG_DATA_GET_PRIVATE (self)->keyfile, group, key, flags); + return !!value; +} + +gint +nm_config_data_get_value_boolean (const NMConfigData *self, const char *group, const char *key, gint default_value) +{ + char *str; + gint value = default_value; + + g_return_val_if_fail (NM_IS_CONFIG_DATA (self), default_value); + g_return_val_if_fail (group && *group, default_value); + g_return_val_if_fail (key && *key, default_value); + + /* when parsing the boolean, base it on the raw value from g_key_file_get_value(). */ + str = g_key_file_get_value (NM_CONFIG_DATA_GET_PRIVATE (self)->keyfile, group, key, NULL); + if (str) { + value = nm_config_parse_boolean (str, default_value); + g_free (str); + } + return value; +} + +const char * +nm_config_data_get_connectivity_uri (const NMConfigData *self) +{ + g_return_val_if_fail (self, NULL); + + return NM_CONFIG_DATA_GET_PRIVATE (self)->connectivity.uri; +} + +const guint +nm_config_data_get_connectivity_interval (const NMConfigData *self) +{ + g_return_val_if_fail (self, 0); + + return NM_CONFIG_DATA_GET_PRIVATE (self)->connectivity.interval; +} + +const char * +nm_config_data_get_connectivity_response (const NMConfigData *self) +{ + g_return_val_if_fail (self != NULL, NULL); + + return NM_CONFIG_DATA_GET_PRIVATE (self)->connectivity.response; +} + +const char *const* +nm_config_data_get_no_auto_default (const NMConfigData *self) +{ + g_return_val_if_fail (self, FALSE); + + return (const char *const*) NM_CONFIG_DATA_GET_PRIVATE (self)->no_auto_default.arr; +} + +gboolean +nm_config_data_get_no_auto_default_for_device (const NMConfigData *self, NMDevice *device) +{ + NMConfigDataPrivate *priv; + + g_return_val_if_fail (NM_IS_CONFIG_DATA (self), FALSE); + g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); + + priv = NM_CONFIG_DATA_GET_PRIVATE (self); + return nm_device_spec_match_list (device, priv->no_auto_default.specs) + || nm_device_spec_match_list (device, priv->no_auto_default.specs_config); +} + +const char * +nm_config_data_get_dns_mode (const NMConfigData *self) +{ + g_return_val_if_fail (self, NULL); + + return NM_CONFIG_DATA_GET_PRIVATE (self)->dns_mode; +} + +gboolean +nm_config_data_get_ignore_carrier (const NMConfigData *self, NMDevice *device) +{ + g_return_val_if_fail (NM_IS_CONFIG_DATA (self), FALSE); + g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); + + return nm_device_spec_match_list (device, NM_CONFIG_DATA_GET_PRIVATE (self)->ignore_carrier); +} + +gboolean +nm_config_data_get_assume_ipv6ll_only (const NMConfigData *self, NMDevice *device) +{ + g_return_val_if_fail (NM_IS_CONFIG_DATA (self), FALSE); + g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); + + return nm_device_spec_match_list (device, NM_CONFIG_DATA_GET_PRIVATE (self)->assume_ipv6ll_only); +} + +/************************************************************************/ + +/** + * nm_config_data_get_groups: + * @self: the #NMConfigData instance + * + * Returns: (transfer-full): the list of groups in the configuration. The order + * of the section is undefined, as the configuration gets merged from multiple + * sources. + */ +char ** +nm_config_data_get_groups (const NMConfigData *self) +{ + g_return_val_if_fail (NM_IS_CONFIG_DATA (self), NULL); + + return g_key_file_get_groups (NM_CONFIG_DATA_GET_PRIVATE (self)->keyfile, NULL); +} + +char ** +nm_config_data_get_keys (const NMConfigData *self, const char *group) +{ + g_return_val_if_fail (NM_IS_CONFIG_DATA (self), NULL); + g_return_val_if_fail (group && *group, NULL); + + return g_key_file_get_keys (NM_CONFIG_DATA_GET_PRIVATE (self)->keyfile, group, NULL, NULL); +} + +/************************************************************************/ + +static int +_nm_config_data_log_sort (const char **pa, const char **pb, gpointer dummy) +{ + gboolean a_is_connection, b_is_connection; + const char *a = *pa; + const char *b = *pb; + + /* we sort connection groups before intern groups (to the end). */ + a_is_connection = a && g_str_has_prefix (a, NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION); + b_is_connection = b && g_str_has_prefix (b, NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION); + + if (a_is_connection && b_is_connection) { + /* if both are connection groups, we want the explicit [connection] group first. */ + a_is_connection = a[STRLEN (NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION)] == '\0'; + b_is_connection = b[STRLEN (NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION)] == '\0'; + + if (a_is_connection != b_is_connection) { + if (a_is_connection) + return -1; + return 1; + } + /* the sections are ordered lowest-priority first. Reverse their order. */ + return pa < pb ? 1 : -1; + } + if (a_is_connection && !b_is_connection) + return 1; + if (b_is_connection && !a_is_connection) + return -1; + + /* no reordering. */ + return 0; +} + +void +nm_config_data_log (const NMConfigData *self, const char *prefix) +{ + NMConfigDataPrivate *priv; + gs_strfreev char **groups = NULL; + gsize ngroups; + guint g, k; + + g_return_if_fail (NM_IS_CONFIG_DATA (self)); + + if (!nm_logging_enabled (LOGL_DEBUG, LOGD_CORE)) + return; + + if (!prefix) + prefix = ""; + +#define _LOG(...) _nm_log (LOGL_DEBUG, LOGD_CORE, 0, "%s"_NM_UTILS_MACRO_FIRST(__VA_ARGS__), prefix _NM_UTILS_MACRO_REST (__VA_ARGS__)) + + priv = NM_CONFIG_DATA_GET_PRIVATE (self); + + groups = g_key_file_get_groups (priv->keyfile, &ngroups); + if (!groups) + ngroups = 0; + + if (groups && groups[0]) { + g_qsort_with_data (groups, ngroups, + sizeof (char *), + (GCompareDataFunc) _nm_config_data_log_sort, + NULL); + } + + _LOG ("config-data[%p]: %lu groups", self, (unsigned long) ngroups); + + for (g = 0; g < ngroups; g++) { + const char *group = groups[g]; + gs_strfreev char **keys = NULL; + + _LOG (""); + _LOG ("[%s]", group); + + keys = g_key_file_get_keys (priv->keyfile, group, NULL, NULL); + for (k = 0; keys && keys[k]; k++) { + const char *key = keys[k]; + gs_free char *value = NULL; + + value = g_key_file_get_value (priv->keyfile, group, key, NULL); + _LOG (" %s=%s", key, value); + } + } + +#undef _LOG +} + +/************************************************************************/ + +char * +nm_config_data_get_connection_default (const NMConfigData *self, + const char *property, + NMDevice *device) +{ + NMConfigDataPrivate *priv; + const ConnectionInfo *connection_info; + + g_return_val_if_fail (self, NULL); + g_return_val_if_fail (property && *property, NULL); + g_return_val_if_fail (strchr (property, '.'), NULL); + + priv = NM_CONFIG_DATA_GET_PRIVATE (self); + + if (!priv->connection_infos) + return NULL; + + for (connection_info = &priv->connection_infos[0]; connection_info->group_name; connection_info++) { + char *value; + gboolean match; + + /* FIXME: Here we use g_key_file_get_string(). This should be in sync with what keyfile-reader + * does. + * + * Unfortunately that is currently not possible because keyfile-reader does the two steps + * string_to_value(keyfile_to_string(keyfile)) in one. Optimally, keyfile library would + * expose both functions, and we would return here keyfile_to_string(keyfile). + * The caller then could convert the string to the proper value via string_to_value(value). */ + value = g_key_file_get_string (priv->keyfile, connection_info->group_name, property, NULL); + if (!value && !connection_info->stop_match) + continue; + + match = TRUE; + if (connection_info->match_device.has) + match = device && nm_device_spec_match_list (device, connection_info->match_device.spec); + + if (match) + return value; + g_free (value); + } + return NULL; +} + +static void +_get_connection_info_init (ConnectionInfo *connection_info, GKeyFile *keyfile, char *group) +{ + /* pass ownership of @group on... */ + connection_info->group_name = group; + + connection_info->match_device.spec = nm_config_get_device_match_spec (keyfile, + group, + "match-device", + &connection_info->match_device.has); + connection_info->stop_match = nm_config_keyfile_get_boolean (keyfile, group, "stop-match", FALSE); +} + +static ConnectionInfo * +_get_connection_infos (GKeyFile *keyfile) +{ + char **groups; + gsize i, j, ngroups; + char *connection_tag = NULL; + ConnectionInfo *connection_infos = NULL; + + /* get the list of existing [connection.\+] sections that we consider + * for nm_config_data_get_connection_default(). + * + * We expect the sections in their right order, with lowest priority + * first. Only exception is the (literal) [connection] section, which + * we will always reorder to the end. */ + groups = g_key_file_get_groups (keyfile, &ngroups); + if (!groups) + ngroups = 0; + else if (ngroups > 0) { + for (i = 0, j = 0; i < ngroups; i++) { + if (g_str_has_prefix (groups[i], NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION)) { + if (groups[i][STRLEN (NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION)] == '\0') + connection_tag = groups[i]; + else + groups[j++] = groups[i]; + } else + g_free (groups[i]); + } + ngroups = j; + } + + connection_infos = g_new0 (ConnectionInfo, ngroups + 1 + (connection_tag ? 1 : 0)); + for (i = 0; i < ngroups; i++) { + /* pass ownership of @group on... */ + _get_connection_info_init (&connection_infos[i], keyfile, groups[ngroups - i - 1]); + } + if (connection_tag) { + /* pass ownership of @connection_tag on... */ + _get_connection_info_init (&connection_infos[i], keyfile, connection_tag); + } + g_free (groups); + + return connection_infos; +} + +/************************************************************************/ + +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) +{ + NMConfigChangeFlags changes = NM_CONFIG_CHANGE_NONE; + NMConfigDataPrivate *priv_old, *priv_new; + + g_return_val_if_fail (NM_IS_CONFIG_DATA (old_data), NM_CONFIG_CHANGE_NONE); + g_return_val_if_fail (NM_IS_CONFIG_DATA (new_data), NM_CONFIG_CHANGE_NONE); + + priv_old = NM_CONFIG_DATA_GET_PRIVATE (old_data); + priv_new = NM_CONFIG_DATA_GET_PRIVATE (new_data); + + if (!_nm_keyfile_equals (priv_old->keyfile, priv_new->keyfile, TRUE)) + changes |= NM_CONFIG_CHANGE_VALUES; + + if ( g_strcmp0 (nm_config_data_get_config_main_file (old_data), nm_config_data_get_config_main_file (new_data)) != 0 + || g_strcmp0 (nm_config_data_get_config_description (old_data), nm_config_data_get_config_description (new_data)) != 0) + changes |= NM_CONFIG_CHANGE_CONFIG_FILES; + + if ( nm_config_data_get_connectivity_interval (old_data) != nm_config_data_get_connectivity_interval (new_data) + || g_strcmp0 (nm_config_data_get_connectivity_uri (old_data), nm_config_data_get_connectivity_uri (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)) + 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))) + changes |= NM_CONFIG_CHANGE_DNS_MODE; + + return changes; +} + +/************************************************************************/ + +static void +get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NMConfigData *self = NM_CONFIG_DATA (object); + + switch (prop_id) { + case PROP_CONFIG_MAIN_FILE: + g_value_set_string (value, nm_config_data_get_config_main_file (self)); + break; + case PROP_CONFIG_DESCRIPTION: + g_value_set_string (value, nm_config_data_get_config_description (self)); + break; + case PROP_CONNECTIVITY_URI: + g_value_set_string (value, nm_config_data_get_connectivity_uri (self)); + break; + case PROP_CONNECTIVITY_INTERVAL: + g_value_set_uint (value, nm_config_data_get_connectivity_interval (self)); + break; + case PROP_CONNECTIVITY_RESPONSE: + g_value_set_string (value, nm_config_data_get_connectivity_response (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) +{ + NMConfigData *self = NM_CONFIG_DATA (object); + NMConfigDataPrivate *priv = NM_CONFIG_DATA_GET_PRIVATE (self); + + /* This type is immutable. All properties are construct only. */ + switch (prop_id) { + case PROP_CONFIG_MAIN_FILE: + priv->config_main_file = g_value_dup_string (value); + break; + case PROP_CONFIG_DESCRIPTION: + priv->config_description = g_value_dup_string (value); + break; + case PROP_KEYFILE: + priv->keyfile = g_value_dup_boxed (value); + if (!priv->keyfile) + priv->keyfile = nm_config_create_keyfile (); + break; + case PROP_NO_AUTO_DEFAULT: + { + 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])); + } + } + priv->no_auto_default.arr[j++] = NULL; + priv->no_auto_default.specs = g_slist_reverse (priv->no_auto_default.specs); + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +dispose (GObject *object) +{ +} + +static void +finalize (GObject *gobject) +{ + NMConfigDataPrivate *priv = NM_CONFIG_DATA_GET_PRIVATE (gobject); + guint i; + + g_free (priv->config_main_file); + g_free (priv->config_description); + + g_free (priv->connectivity.uri); + g_free (priv->connectivity.response); + + g_slist_free_full (priv->no_auto_default.specs, g_free); + g_slist_free_full (priv->no_auto_default.specs_config, g_free); + g_strfreev (priv->no_auto_default.arr); + + g_free (priv->dns_mode); + + g_slist_free_full (priv->ignore_carrier, g_free); + g_slist_free_full (priv->assume_ipv6ll_only, g_free); + + if (priv->connection_infos) { + for (i = 0; priv->connection_infos[i].group_name; i++) { + g_free (priv->connection_infos[i].group_name); + g_slist_free_full (priv->connection_infos[i].match_device.spec, g_free); + } + g_free (priv->connection_infos); + } + + g_key_file_unref (priv->keyfile); + + G_OBJECT_CLASS (nm_config_data_parent_class)->finalize (gobject); +} + +static void +nm_config_data_init (NMConfigData *self) +{ +} + +static void +constructed (GObject *object) +{ + NMConfigData *self = NM_CONFIG_DATA (object); + NMConfigDataPrivate *priv = NM_CONFIG_DATA_GET_PRIVATE (self); + char *interval; + + priv->connection_infos = _get_connection_infos (priv->keyfile); + + priv->connectivity.uri = nm_strstrip (g_key_file_get_string (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "uri", NULL)); + priv->connectivity.response = g_key_file_get_string (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "response", NULL); + + /* On missing config value, fallback to 300. On invalid value, disable connectivity checking by setting + * the interval to zero. */ + interval = g_key_file_get_string (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "interval", NULL); + priv->connectivity.interval = interval + ? _nm_utils_ascii_str_to_int64 (interval, 10, 0, G_MAXUINT, 0) + : NM_CONFIG_DEFAULT_CONNECTIVITY_INTERVAL; + g_free (interval); + + priv->dns_mode = nm_strstrip (g_key_file_get_string (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "dns", NULL)); + + priv->ignore_carrier = nm_config_get_device_match_spec (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "ignore-carrier", NULL); + priv->assume_ipv6ll_only = nm_config_get_device_match_spec (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "assume-ipv6ll-only", NULL); + + priv->no_auto_default.specs_config = nm_config_get_device_match_spec (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "no-auto-default", NULL); + + G_OBJECT_CLASS (nm_config_data_parent_class)->constructed (object); +} + +NMConfigData * +nm_config_data_new (const char *config_main_file, + const char *config_description, + const char *const*no_auto_default, + GKeyFile *keyfile) +{ + return g_object_new (NM_TYPE_CONFIG_DATA, + NM_CONFIG_DATA_CONFIG_MAIN_FILE, config_main_file, + NM_CONFIG_DATA_CONFIG_DESCRIPTION, config_description, + NM_CONFIG_DATA_KEYFILE, keyfile, + NM_CONFIG_DATA_NO_AUTO_DEFAULT, no_auto_default, + NULL); +} + +NMConfigData * +nm_config_data_new_update_no_auto_default (const NMConfigData *base, + const char *const*no_auto_default) +{ + NMConfigDataPrivate *priv = NM_CONFIG_DATA_GET_PRIVATE (base); + + return g_object_new (NM_TYPE_CONFIG_DATA, + NM_CONFIG_DATA_CONFIG_MAIN_FILE, priv->config_main_file, + NM_CONFIG_DATA_CONFIG_DESCRIPTION, priv->config_description, + NM_CONFIG_DATA_KEYFILE, priv->keyfile, /* the keyfile is unchanged. It's safe to share it. */ + NM_CONFIG_DATA_NO_AUTO_DEFAULT, no_auto_default, + NULL); +} + +static void +nm_config_data_class_init (NMConfigDataClass *config_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (config_class); + + g_type_class_add_private (config_class, sizeof (NMConfigDataPrivate)); + + object_class->constructed = constructed; + object_class->dispose = dispose; + object_class->finalize = finalize; + object_class->get_property = get_property; + object_class->set_property = set_property; + + g_object_class_install_property + (object_class, PROP_CONFIG_MAIN_FILE, + g_param_spec_string (NM_CONFIG_DATA_CONFIG_MAIN_FILE, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property + (object_class, PROP_CONFIG_DESCRIPTION, + g_param_spec_string (NM_CONFIG_DATA_CONFIG_DESCRIPTION, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property + (object_class, PROP_KEYFILE, + g_param_spec_boxed (NM_CONFIG_DATA_KEYFILE, "", "", + G_TYPE_KEY_FILE, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property + (object_class, PROP_CONNECTIVITY_URI, + g_param_spec_string (NM_CONFIG_DATA_CONNECTIVITY_URI, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property + (object_class, PROP_CONNECTIVITY_INTERVAL, + g_param_spec_uint (NM_CONFIG_DATA_CONNECTIVITY_INTERVAL, "", "", + 0, G_MAXUINT, 0, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property + (object_class, PROP_CONNECTIVITY_RESPONSE, + g_param_spec_string (NM_CONFIG_DATA_CONNECTIVITY_RESPONSE, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property + (object_class, PROP_NO_AUTO_DEFAULT, + g_param_spec_boxed (NM_CONFIG_DATA_NO_AUTO_DEFAULT, "", "", + G_TYPE_STRV, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + +} + diff --git a/src/nm-config-data.h b/src/nm-config-data.h new file mode 100644 index 00000000..cc7f95e2 --- /dev/null +++ b/src/nm-config-data.h @@ -0,0 +1,132 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2014 Red Hat, Inc. + */ + +#ifndef NM_CONFIG_DATA_H +#define NM_CONFIG_DATA_H + +#include <glib.h> +#include <glib-object.h> + +#include "nm-types.h" + +G_BEGIN_DECLS + +#define NM_TYPE_CONFIG_DATA (nm_config_data_get_type ()) +#define NM_CONFIG_DATA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_CONFIG_DATA, NMConfigData)) +#define NM_CONFIG_DATA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_CONFIG_DATA, NMConfigDataClass)) +#define NM_IS_CONFIG_DATA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_CONFIG_DATA)) +#define NM_IS_CONFIG_DATA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_CONFIG_DATA)) +#define NM_CONFIG_DATA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_CONFIG_DATA, NMConfigDataClass)) + + +#define NM_CONFIG_DATA_CONFIG_MAIN_FILE "config-main-file" +#define NM_CONFIG_DATA_CONFIG_DESCRIPTION "config-description" +#define NM_CONFIG_DATA_KEYFILE "keyfile" +#define NM_CONFIG_DATA_CONNECTIVITY_URI "connectivity-uri" +#define NM_CONFIG_DATA_CONNECTIVITY_INTERVAL "connectivity-interval" +#define NM_CONFIG_DATA_CONNECTIVITY_RESPONSE "connectivity-response" +#define NM_CONFIG_DATA_NO_AUTO_DEFAULT "no-auto-default" +#define NM_CONFIG_DATA_DNS_MODE "dns" + +typedef enum { /*<flags >*/ + NM_CONFIG_GET_VALUE_NONE = 0, + + /* use g_key_file_get_value() instead of g_key_file_get_string(). */ + NM_CONFIG_GET_VALUE_RAW = (1LL << 0), + + /* strip whitespaces */ + NM_CONFIG_GET_VALUE_STRIP = (1LL << 1), + + /* if the returned string would be the empty word, return NULL. */ + NM_CONFIG_GET_VALUE_NO_EMPTY = (1LL << 2), + + /* special flag to read device spec. You want to use this before passing the + * value to nm_match_spec_split(). */ + NM_CONFIG_GET_VALUE_TYPE_SPEC = NM_CONFIG_GET_VALUE_RAW, +} NMConfigGetValueFlags; + +typedef enum { /*< flags >*/ + NM_CONFIG_CHANGE_NONE = 0, + + NM_CONFIG_CHANGE_SIGHUP = (1L << 0), + NM_CONFIG_CHANGE_SIGUSR1 = (1L << 1), + NM_CONFIG_CHANGE_SIGUSR2 = (1L << 2), + + NM_CONFIG_CHANGE_CONFIG_FILES = (1L << 3), + NM_CONFIG_CHANGE_VALUES = (1L << 4), + NM_CONFIG_CHANGE_CONNECTIVITY = (1L << 5), + NM_CONFIG_CHANGE_NO_AUTO_DEFAULT = (1L << 6), + NM_CONFIG_CHANGE_DNS_MODE = (1L << 7), + + _NM_CONFIG_CHANGE_LAST, + NM_CONFIG_CHANGE_ALL = ((_NM_CONFIG_CHANGE_LAST - 1) << 1) - 1, +} NMConfigChangeFlags; + +struct _NMConfigData { + GObject parent; +}; + +typedef struct { + GObjectClass parent; +} NMConfigDataClass; + +GType nm_config_data_get_type (void); + +NMConfigData *nm_config_data_new (const char *config_main_file, + const char *config_description, + const char *const*no_auto_default, + GKeyFile *keyfile); +NMConfigData *nm_config_data_new_update_no_auto_default (const NMConfigData *base, const char *const*no_auto_default); + +NMConfigChangeFlags nm_config_data_diff (NMConfigData *old_data, NMConfigData *new_data); + +void nm_config_data_log (const NMConfigData *config_data, const char *prefix); + +const char *nm_config_data_get_config_main_file (const NMConfigData *config_data); +const char *nm_config_data_get_config_description (const NMConfigData *config_data); + +gboolean nm_config_data_has_group (const NMConfigData *self, const char *group); +gboolean nm_config_data_has_value (const NMConfigData *self, const char *group, const char *key, NMConfigGetValueFlags flags); +char *nm_config_data_get_value (const NMConfigData *config_data, const char *group, const char *key, NMConfigGetValueFlags flags); +gint nm_config_data_get_value_boolean (const NMConfigData *self, const char *group, const char *key, gint default_value); + +const char *nm_config_data_get_connectivity_uri (const NMConfigData *config_data); +const guint nm_config_data_get_connectivity_interval (const NMConfigData *config_data); +const char *nm_config_data_get_connectivity_response (const NMConfigData *config_data); + +const char *const*nm_config_data_get_no_auto_default (const NMConfigData *config_data); +gboolean nm_config_data_get_no_auto_default_for_device (const NMConfigData *self, NMDevice *device); + +const char *nm_config_data_get_dns_mode (const NMConfigData *self); + +gboolean nm_config_data_get_ignore_carrier (const NMConfigData *self, NMDevice *device); +gboolean nm_config_data_get_assume_ipv6ll_only (const NMConfigData *self, NMDevice *device); + +char *nm_config_data_get_connection_default (const NMConfigData *self, + const char *property, + NMDevice *device); + +char **nm_config_data_get_groups (const NMConfigData *self); +char **nm_config_data_get_keys (const NMConfigData *self, const char *group); + +G_END_DECLS + +#endif /* NM_CONFIG_DATA_H */ + diff --git a/src/nm-config.c b/src/nm-config.c index 4626ae26..86b3d929 100644 --- a/src/nm-config.c +++ b/src/nm-config.c @@ -29,100 +29,213 @@ #include "nm-utils.h" #include "nm-glib-compat.h" #include "nm-device.h" +#include "NetworkManagerUtils.h" +#include "gsystem-local-alloc.h" +#include "nm-enum-types.h" +#include "nm-core-internal.h" #include <gio/gio.h> #include <glib/gi18n.h> -#define NM_DEFAULT_SYSTEM_CONF_FILE NMCONFDIR "/NetworkManager.conf" -#define NM_DEFAULT_SYSTEM_CONF_DIR NMCONFDIR "/conf.d" -#define NM_OLD_SYSTEM_CONF_FILE NMCONFDIR "/nm-system-settings.conf" -#define NM_NO_AUTO_DEFAULT_STATE_FILE NMSTATEDIR "/no-auto-default.state" +#define DEFAULT_CONFIG_MAIN_FILE NMCONFDIR "/NetworkManager.conf" +#define DEFAULT_CONFIG_DIR NMCONFDIR "/conf.d" +#define DEFAULT_CONFIG_MAIN_FILE_OLD NMCONFDIR "/nm-system-settings.conf" +#define DEFAULT_NO_AUTO_DEFAULT_FILE NMSTATEDIR "/no-auto-default.state" + +struct NMConfigCmdLineOptions { + char *config_main_file; + char *config_dir; + char *no_auto_default_file; + char *plugins; + gboolean configure_and_quit; + char *connectivity_uri; + + /* We store interval as signed internally to track whether it's + * set or not via GOptionEntry + */ + int connectivity_interval; + char *connectivity_response; +}; typedef struct { - char *nm_conf_path; + NMConfigCmdLineOptions cli; + + NMConfigData *config_data; + NMConfigData *config_data_orig; + char *config_dir; - char *config_description; char *no_auto_default_file; - GKeyFile *keyfile; char **plugins; gboolean monitor_connection_files; gboolean auth_polkit; char *dhcp_client; - char *dns_mode; char *log_level; char *log_domains; char *debug; - char *connectivity_uri; - gint connectivity_interval; - char *connectivity_response; - - char **no_auto_default; - char **ignore_carrier; - gboolean configure_and_quit; } NMConfigPrivate; -static NMConfig *singleton = NULL; +enum { + PROP_0, + PROP_CMD_LINE_OPTIONS, + LAST_PROP, +}; + +enum { + SIGNAL_CONFIG_CHANGED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +static void nm_config_initable_iface_init (GInitableIface *iface); + +G_DEFINE_TYPE_WITH_CODE (NMConfig, nm_config, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, nm_config_initable_iface_init); + ) -G_DEFINE_TYPE (NMConfig, nm_config, G_TYPE_OBJECT) #define NM_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_CONFIG, NMConfigPrivate)) /************************************************************************/ -static gboolean -_get_bool_value (GKeyFile *keyfile, - const char *section, - const char *key, - gboolean default_value) +static void _set_config_data (NMConfig *self, NMConfigData *new_data, int signal); + +/************************************************************************/ + +gint +nm_config_parse_boolean (const char *str, + gint default_value) { - gboolean value = default_value; - char *str; + gsize len; + char *s = NULL; + + if (!str) + return default_value; + + while (str[0] && g_ascii_isspace (str[0])) + str++; + + if (!str[0]) + return default_value; + + len = strlen (str); + if (g_ascii_isspace (str[len - 1])) { + s = g_strdup (str); + g_strchomp (s); + str = s; + } + + if (!g_ascii_strcasecmp (str, "true") || !g_ascii_strcasecmp (str, "yes") || !g_ascii_strcasecmp (str, "on") || !g_ascii_strcasecmp (str, "1")) + default_value = TRUE; + else if (!g_ascii_strcasecmp (str, "false") || !g_ascii_strcasecmp (str, "no") || !g_ascii_strcasecmp (str, "off") || !g_ascii_strcasecmp (str, "0")) + default_value = FALSE; + if (s) + g_free (s); + return default_value; +} + +gint +nm_config_keyfile_get_boolean (GKeyFile *keyfile, + const char *section, + const char *key, + gint default_value) +{ + gs_free char *str = NULL; g_return_val_if_fail (keyfile != NULL, default_value); g_return_val_if_fail (section != NULL, default_value); g_return_val_if_fail (key != NULL, default_value); str = g_key_file_get_value (keyfile, section, key, NULL); - if (!str) - return default_value; + return nm_config_parse_boolean (str, default_value); +} - g_strstrip (str); - if (str[0]) { - if (!g_ascii_strcasecmp (str, "true") || !g_ascii_strcasecmp (str, "yes") || !g_ascii_strcasecmp (str, "on") || !g_ascii_strcasecmp (str, "1")) - value = TRUE; - else if (!g_ascii_strcasecmp (str, "false") || !g_ascii_strcasecmp (str, "no") || !g_ascii_strcasecmp (str, "off") || !g_ascii_strcasecmp (str, "0")) - value = FALSE; - else { - nm_log_warn (LOGD_CORE, "Unrecognized value for %s.%s: '%s'. Assuming '%s'", - section, key, str, default_value ? "true" : "false"); - } +char * +nm_config_keyfile_get_value (GKeyFile *keyfile, + const char *section, + const char *key, + NMConfigGetValueFlags flags) +{ + char *value; + + if (NM_FLAGS_HAS (flags, NM_CONFIG_GET_VALUE_RAW)) + value = g_key_file_get_value (keyfile, section, key, NULL); + else + value = g_key_file_get_string (keyfile, section, key, NULL); + + if (!value) + return NULL; + + if (NM_FLAGS_HAS (flags, NM_CONFIG_GET_VALUE_STRIP)) + g_strstrip (value); + + if ( NM_FLAGS_HAS (flags, NM_CONFIG_GET_VALUE_NO_EMPTY) + && !*value) { + g_free (value); + return NULL; } - g_free (str); return value; } +void +nm_config_keyfile_set_string_list (GKeyFile *keyfile, + const char *group, + const char *key, + const char *const* strv, + gssize len) +{ + gsize l; + char *new_value; + + if (len < 0) + len = strv ? g_strv_length ((char **) strv) : 0; + + g_key_file_set_string_list (keyfile, group, key, strv, len); + + /* g_key_file_set_string_list() appends a trailing separator to the value. + * We don't like that, get rid of it. */ + + new_value = g_key_file_get_value (keyfile, group, key, NULL); + if (!new_value) + return; + + l = strlen (new_value); + if (l > 0 && new_value[l - 1] == NM_CONFIG_KEYFILE_LIST_SEPARATOR) { + /* Maybe we should check that value doesn't end with "\\,", i.e. + * with an escaped separator. But the way g_key_file_set_string_list() + * is implemented (currently), it always adds a trailing separator. */ + new_value[l - 1] = '\0'; + g_key_file_set_value (keyfile, group, key, new_value); + } + g_free (new_value); +} + /************************************************************************/ -const char * -nm_config_get_path (NMConfig *config) +NMConfigData * +nm_config_get_data (NMConfig *config) { g_return_val_if_fail (config != NULL, NULL); - return NM_CONFIG_GET_PRIVATE (config)->nm_conf_path; + return NM_CONFIG_GET_PRIVATE (config)->config_data; } -const char * -nm_config_get_description (NMConfig *config) +/* The NMConfigData instance is reloadable and will be swapped on reload. + * nm_config_get_data_orig() returns the original configuration, when the NMConfig + * instance was created. */ +NMConfigData * +nm_config_get_data_orig (NMConfig *config) { g_return_val_if_fail (config != NULL, NULL); - return NM_CONFIG_GET_PRIVATE (config)->config_description; + return NM_CONFIG_GET_PRIVATE (config)->config_data_orig; } const char ** @@ -158,14 +271,6 @@ nm_config_get_dhcp_client (NMConfig *config) } const char * -nm_config_get_dns_mode (NMConfig *config) -{ - g_return_val_if_fail (config != NULL, NULL); - - return NM_CONFIG_GET_PRIVATE (config)->dns_mode; -} - -const char * nm_config_get_log_level (NMConfig *config) { g_return_val_if_fail (config != NULL, NULL); @@ -189,102 +294,32 @@ nm_config_get_debug (NMConfig *config) return NM_CONFIG_GET_PRIVATE (config)->debug; } -const char * -nm_config_get_connectivity_uri (NMConfig *config) -{ - g_return_val_if_fail (config != NULL, NULL); - - return NM_CONFIG_GET_PRIVATE (config)->connectivity_uri; -} - -guint -nm_config_get_connectivity_interval (NMConfig *config) -{ - g_return_val_if_fail (config != NULL, 0); - - /* We store interval as signed internally to track whether it's - * set or not, but report as unsigned to callers. - */ - return MAX (NM_CONFIG_GET_PRIVATE (config)->connectivity_interval, 0); -} - -const char * -nm_config_get_connectivity_response (NMConfig *config) -{ - g_return_val_if_fail (config != NULL, NULL); - - return NM_CONFIG_GET_PRIVATE (config)->connectivity_response; -} - gboolean nm_config_get_configure_and_quit (NMConfig *config) { return NM_CONFIG_GET_PRIVATE (config)->configure_and_quit; } -char * -nm_config_get_value (NMConfig *config, const char *group, const char *key, GError **error) -{ - NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config); - - return g_key_file_get_string (priv->keyfile, group, key, error); -} - -gboolean -nm_config_get_ignore_carrier (NMConfig *config, NMDevice *device) -{ - NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config); - GSList *specs = NULL; - int i; - gboolean match; - - if (!priv->ignore_carrier) - return FALSE; - - for (i = 0; priv->ignore_carrier[i]; i++) - specs = g_slist_prepend (specs, priv->ignore_carrier[i]); - - match = nm_device_spec_match_list (device, specs); - - g_slist_free (specs); - return match; -} - /************************************************************************/ -static void -merge_no_auto_default_state (NMConfig *config) +static char ** +no_auto_default_from_file (const char *no_auto_default_file) { - NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config); - GPtrArray *updated; + GPtrArray *no_auto_default_new; char **list; - int i, j; + guint i; char *data; - /* If the config already matches everything, we don't need to do anything else. */ - if (priv->no_auto_default && !g_strcmp0 (priv->no_auto_default[0], "*")) - return; + no_auto_default_new = g_ptr_array_new (); - updated = g_ptr_array_new (); - if (priv->no_auto_default) { - for (i = 0; priv->no_auto_default[i]; i++) - g_ptr_array_add (updated, priv->no_auto_default[i]); - g_free (priv->no_auto_default); - } - - if (g_file_get_contents (priv->no_auto_default_file, &data, NULL, NULL)) { + 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]) { - g_free (list[i]); - continue; - } - for (j = 0; j < updated->len; j++) { - if (!strcmp (list[i], updated->pdata[j])) - break; - } - if (j == updated->len) - g_ptr_array_add (updated, list[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]); } @@ -292,99 +327,240 @@ merge_no_auto_default_state (NMConfig *config) g_free (data); } - g_ptr_array_add (updated, NULL); - priv->no_auto_default = (char **) g_ptr_array_free (updated, FALSE); + g_ptr_array_add (no_auto_default_new, NULL); + return (char **) g_ptr_array_free (no_auto_default_new, FALSE); } -gboolean -nm_config_get_ethernet_can_auto_default (NMConfig *config, NMDevice *device) +static gboolean +no_auto_default_to_file (const char *no_auto_default_file, const char *const*no_auto_default, GError **error) { - NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config); - GSList *specs = NULL; - int i; - gboolean match; - - for (i = 0; priv->no_auto_default[i]; i++) - specs = g_slist_prepend (specs, priv->no_auto_default[i]); + GString *data; + gboolean success; + guint i; + + data = g_string_new (""); + for (i = 0; no_auto_default && no_auto_default[i]; i++) { + g_string_append (data, no_auto_default[i]); + 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; +} - match = nm_device_spec_match_list (device, specs); +gboolean +nm_config_get_no_auto_default_for_device (NMConfig *self, NMDevice *device) +{ + g_return_val_if_fail (NM_IS_CONFIG (self), FALSE); - g_slist_free (specs); - return !match; + return nm_config_data_get_no_auto_default_for_device (NM_CONFIG_GET_PRIVATE (self)->config_data, device); } void -nm_config_set_ethernet_no_auto_default (NMConfig *config, NMDevice *device) +nm_config_set_no_auto_default_for_device (NMConfig *self, NMDevice *device) { - NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config); - char *current; - GString *updated; + NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (self); GError *error = NULL; + NMConfigData *new_data = NULL; + const char *hw_address; + const char *const*no_auto_default_current; + GPtrArray *no_auto_default_new = NULL; + guint i; - if (!nm_config_get_ethernet_can_auto_default (config, device)) - return; + g_return_if_fail (NM_IS_CONFIG (self)); + g_return_if_fail (NM_IS_DEVICE (device)); + + hw_address = nm_device_get_hw_address (device); - updated = g_string_new (NULL); - if (g_file_get_contents (priv->no_auto_default_file, ¤t, NULL, NULL)) { - g_string_append (updated, current); - g_free (current); - if (updated->str[updated->len - 1] != '\n') - g_string_append_c (updated, '\n'); + 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. + * Maybe we should write to no_auto_default_file anew, but let's save that too. */ + return; } - g_string_append (updated, nm_device_get_hw_address (device)); - g_string_append_c (updated, '\n'); + 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); - if (!g_file_set_contents (priv->no_auto_default_file, updated->str, updated->len, &error)) { + if (!no_auto_default_to_file (priv->no_auto_default_file, (const char *const*) no_auto_default_new->pdata, &error)) { nm_log_warn (LOGD_SETTINGS, "Could not update no-auto-default.state file: %s", error->message); g_error_free (error); } - g_string_free (updated, TRUE); + new_data = nm_config_data_new_update_no_auto_default (priv->config_data, (const char *const*) no_auto_default_new->pdata); - merge_no_auto_default_state (config); + /* 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); + + _set_config_data (self, new_data, 0); } /************************************************************************/ -static char *cli_config_path; -static char *cli_config_dir; -static char *cli_no_auto_default_file; -static char *cli_plugins; -static char *cli_connectivity_uri; -static int cli_connectivity_interval = -1; -static char *cli_connectivity_response; - -static GOptionEntry config_options[] = { - { "config", 0, 0, G_OPTION_ARG_FILENAME, &cli_config_path, N_("Config file location"), N_("/path/to/config.file") }, - { "config-dir", 0, 0, G_OPTION_ARG_FILENAME, &cli_config_dir, N_("Config directory location"), N_("/path/to/config/dir") }, - { "no-auto-default", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_FILENAME, &cli_no_auto_default_file, "no-auto-default.state location", NULL }, - { "plugins", 0, 0, G_OPTION_ARG_STRING, &cli_plugins, N_("List of plugins separated by ','"), N_("plugin1,plugin2") }, - - /* These three are hidden for now, and should eventually just go away. */ - { "connectivity-uri", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &cli_connectivity_uri, N_("An http(s) address for checking internet connectivity"), "http://example.com" }, - { "connectivity-interval", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_INT, &cli_connectivity_interval, N_("The interval between connectivity checks (in seconds)"), "60" }, - { "connectivity-response", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &cli_connectivity_response, N_("The expected start of the response"), N_("Bingo!") }, - {NULL} -}; -GOptionEntry * -nm_config_get_options (void) +static void +_nm_config_cmd_line_options_clear (NMConfigCmdLineOptions *cli) +{ + g_clear_pointer (&cli->config_main_file, g_free); + g_clear_pointer (&cli->config_dir, g_free); + g_clear_pointer (&cli->no_auto_default_file, g_free); + g_clear_pointer (&cli->plugins, g_free); + cli->configure_and_quit = FALSE; + g_clear_pointer (&cli->connectivity_uri, g_free); + g_clear_pointer (&cli->connectivity_response, g_free); + cli->connectivity_interval = -1; +} + +static void +_nm_config_cmd_line_options_copy (const NMConfigCmdLineOptions *cli, NMConfigCmdLineOptions *dst) +{ + g_return_if_fail (cli); + g_return_if_fail (dst); + g_return_if_fail (cli != dst); + + _nm_config_cmd_line_options_clear (dst); + dst->config_dir = g_strdup (cli->config_dir); + dst->config_main_file = g_strdup (cli->config_main_file); + dst->no_auto_default_file = g_strdup (cli->no_auto_default_file); + dst->plugins = g_strdup (cli->plugins); + dst->configure_and_quit = cli->configure_and_quit; + dst->connectivity_uri = g_strdup (cli->connectivity_uri); + dst->connectivity_response = g_strdup (cli->connectivity_response); + dst->connectivity_interval = cli->connectivity_interval; +} + +NMConfigCmdLineOptions * +nm_config_cmd_line_options_new () +{ + NMConfigCmdLineOptions *cli = g_new0 (NMConfigCmdLineOptions, 1); + + _nm_config_cmd_line_options_clear (cli); + return cli; +} + +void +nm_config_cmd_line_options_free (NMConfigCmdLineOptions *cli) +{ + g_return_if_fail (cli); + + _nm_config_cmd_line_options_clear (cli); + g_free (cli); +} + +void +nm_config_cmd_line_options_add_to_entries (NMConfigCmdLineOptions *cli, + GOptionContext *opt_ctx) { - return config_options; + g_return_if_fail (opt_ctx); + g_return_if_fail (cli); + + { + GOptionEntry config_options[] = { + { "config", 0, 0, G_OPTION_ARG_FILENAME, &cli->config_main_file, N_("Config file location"), N_(DEFAULT_CONFIG_MAIN_FILE) }, + { "config-dir", 0, 0, G_OPTION_ARG_FILENAME, &cli->config_dir, N_("Config directory location"), N_(DEFAULT_CONFIG_DIR) }, + { "no-auto-default", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_FILENAME, &cli->no_auto_default_file, N_("State file for no-auto-default devices"), N_(DEFAULT_NO_AUTO_DEFAULT_FILE) }, + { "plugins", 0, 0, G_OPTION_ARG_STRING, &cli->plugins, N_("List of plugins separated by ','"), N_(CONFIG_PLUGINS_DEFAULT) }, + { "configure-and-quit", 0, 0, G_OPTION_ARG_NONE, &cli->configure_and_quit, N_("Quit after initial configuration"), NULL }, + + /* These three are hidden for now, and should eventually just go away. */ + { "connectivity-uri", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &cli->connectivity_uri, N_("An http(s) address for checking internet connectivity"), "http://example.com" }, + { "connectivity-interval", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_INT, &cli->connectivity_interval, N_("The interval between connectivity checks (in seconds)"), G_STRINGIFY (NM_CONFIG_DEFAULT_CONNECTIVITY_INTERVAL) }, + { "connectivity-response", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &cli->connectivity_response, N_("The expected start of the response"), N_(NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE) }, + { 0 }, + }; + + g_option_context_add_main_entries (opt_ctx, config_options, NULL); + } } /************************************************************************/ +GKeyFile * +nm_config_create_keyfile () +{ + GKeyFile *keyfile; + + keyfile = g_key_file_new (); + g_key_file_set_list_separator (keyfile, NM_CONFIG_KEYFILE_LIST_SEPARATOR); + return keyfile; +} + +static int +_sort_groups_cmp (const char **pa, const char **pb, gpointer dummy) +{ + const char *a, *b; + gboolean a_is_connection, b_is_connection; + + /* basic NULL checking... */ + if (pa == pb) + return 0; + if (!pa) + return -1; + if (!pb) + return 1; + + a = *pa; + b = *pb; + + a_is_connection = g_str_has_prefix (a, NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION); + b_is_connection = g_str_has_prefix (b, NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION); + + if (a_is_connection != b_is_connection) { + /* one is a [connection*] entry, the other not. We sort [connection*] entires + * after. */ + if (a_is_connection) + return 1; + return -1; + } + if (!a_is_connection) { + /* both are non-connection entries. Don't reorder. */ + return 0; + } + + /* both are [connection.\+] entires. Reverse their order. + * One of the sections might be literally [connection]. That section + * is special and it's order will be fixed later. It doesn't actually + * matter here how it compares with [connection.\+] sections. */ + return pa > pb ? -1 : 1; +} + static gboolean -read_config (NMConfig *config, const char *path, GError **error) +_setting_is_device_spec (const char *group, const char *key) +{ +#define _IS(group_v, key_v) (strcmp (group, (""group_v)) == 0 && strcmp (key, (""key_v)) == 0) + return _IS (NM_CONFIG_KEYFILE_GROUP_MAIN, "no-auto-default") + || _IS (NM_CONFIG_KEYFILE_GROUP_MAIN, "ignore-carrier") + || _IS (NM_CONFIG_KEYFILE_GROUP_MAIN, "assume-ipv6ll-only") + || _IS (NM_CONFIG_KEYFILE_GROUP_KEYFILE, "unmanaged-devices") + || (g_str_has_prefix (group, NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION) && !strcmp (key, "match-device")); +} + +static gboolean +_setting_is_string_list (const char *group, const char *key) +{ + return _IS (NM_CONFIG_KEYFILE_GROUP_MAIN, "plugins") + || _IS (NM_CONFIG_KEYFILE_GROUP_MAIN, "debug") + || _IS (NM_CONFIG_KEYFILE_GROUP_LOGGING, "domains") + || g_str_has_prefix (group, NM_CONFIG_KEYFILE_GROUPPREFIX_TEST_APPEND_STRINGLIST); +#undef _IS +} + +static gboolean +read_config (GKeyFile *keyfile, const char *path, GError **error) { - NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config); GKeyFile *kf; char **groups, **keys; gsize ngroups, nkeys; int g, k; + g_return_val_if_fail (keyfile, FALSE); + g_return_val_if_fail (path, FALSE); + g_return_val_if_fail (!error || !*error, FALSE); + if (g_file_test (path, G_FILE_TEST_EXISTS) == FALSE) { g_set_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND, "file %s not found", path); return FALSE; @@ -392,8 +568,7 @@ read_config (NMConfig *config, const char *path, GError **error) nm_log_dbg (LOGD_SETTINGS, "Reading config file '%s'", path); - kf = g_key_file_new (); - g_key_file_set_list_separator (kf, ','); + kf = nm_config_create_keyfile (); if (!g_key_file_load_from_file (kf, path, G_KEY_FILE_NONE, error)) { g_key_file_free (kf); return FALSE; @@ -401,36 +576,116 @@ read_config (NMConfig *config, const char *path, GError **error) /* Override the current settings with the new ones */ groups = g_key_file_get_groups (kf, &ngroups); - for (g = 0; groups[g]; g++) { - keys = g_key_file_get_keys (kf, groups[g], &nkeys, NULL); + if (!groups) + ngroups = 0; + + /* Within one file we reverse the order of the '[connection.\+] sections. + * Here we merge the current file (@kf) into @keyfile. As we merge multiple + * files, earlier sections (with lower priority) will be added first. + * But within one file, we want a top-to-bottom order. This means we + * must reverse the order within each file. + * At the very end, we will revert the order of all sections again and + * get thus the right behavior. This final reversing is done in + * NMConfigData:_get_connection_infos(). */ + if (ngroups > 1) { + g_qsort_with_data (groups, + ngroups, + sizeof (char *), + (GCompareDataFunc) _sort_groups_cmp, + NULL); + } + + for (g = 0; groups && groups[g]; g++) { + const char *group = groups[g]; + + keys = g_key_file_get_keys (kf, group, &nkeys, NULL); if (!keys) continue; for (k = 0; keys[k]; k++) { - int len = strlen (keys[k]); - char *v; - - if (keys[k][len - 1] == '+') { - char *base_key = g_strndup (keys[k], len - 1); - char *old_val = g_key_file_get_value (priv->keyfile, groups[g], base_key, NULL); - char *new_val = g_key_file_get_value (kf, groups[g], keys[k], NULL); - - if (old_val && *old_val) { - char *combined = g_strconcat (old_val, ",", new_val, NULL); - - g_key_file_set_value (priv->keyfile, groups[g], base_key, combined); - g_free (combined); - } else - g_key_file_set_value (priv->keyfile, groups[g], base_key, new_val); - - g_free (base_key); - g_free (old_val); - g_free (new_val); + const char *key; + char *new_value; + char last_char; + gsize key_len; + + key = keys[k]; + g_assert (key && *key); + key_len = strlen (key); + last_char = key[key_len - 1]; + if ( key_len > 1 + && (last_char == '+' || last_char == '-')) { + gs_free char *base_key = g_strndup (key, key_len - 1); + gboolean is_string_list; + + is_string_list = _setting_is_string_list (group, base_key); + + if ( is_string_list + || _setting_is_device_spec (group, base_key)) { + gs_unref_ptrarray GPtrArray *new = g_ptr_array_new_with_free_func (g_free); + char **iter_val; + gs_strfreev char **old_val = NULL; + gs_free char **new_val = NULL; + + if (is_string_list) { + old_val = g_key_file_get_string_list (keyfile, group, base_key, NULL, NULL); + new_val = g_key_file_get_string_list (kf, group, key, NULL, NULL); + } else { + gs_free char *old_sval = nm_config_keyfile_get_value (keyfile, group, base_key, NM_CONFIG_GET_VALUE_TYPE_SPEC); + gs_free char *new_sval = nm_config_keyfile_get_value (kf, group, key, NM_CONFIG_GET_VALUE_TYPE_SPEC); + gs_free_slist GSList *old_specs = nm_match_spec_split (old_sval); + gs_free_slist GSList *new_specs = nm_match_spec_split (new_sval); + + /* the key is a device spec. This is a special kind of string-list, that + * we must split differently. */ + old_val = _nm_utils_slist_to_strv (old_specs, FALSE); + new_val = _nm_utils_slist_to_strv (new_specs, FALSE); + } + + /* merge the string lists, by omiting duplicates. */ + + for (iter_val = old_val; iter_val && *iter_val; iter_val++) { + if ( last_char != '-' + || _nm_utils_strv_find_first (new_val, -1, *iter_val) < 0) + g_ptr_array_add (new, g_strdup (*iter_val)); + } + for (iter_val = new_val; iter_val && *iter_val; iter_val++) { + /* don't add duplicates. That means an "option=a,b"; "option+=a,c" results in "option=a,b,c" */ + if ( last_char == '+' + && _nm_utils_strv_find_first (old_val, -1, *iter_val) < 0) + g_ptr_array_add (new, *iter_val); + else + g_free (*iter_val); + } + + if (new->len > 0) { + if (is_string_list) + nm_config_keyfile_set_string_list (keyfile, group, base_key, (const char *const*) new->pdata, new->len); + else { + gs_free_slist GSList *specs = NULL; + gs_free char *specs_joined = NULL; + + g_ptr_array_add (new, NULL); + specs = _nm_utils_strv_to_slist ((char **) new->pdata, FALSE); + + specs_joined = nm_match_spec_join (specs); + + g_key_file_set_value (keyfile, group, base_key, specs_joined); + } + } else { + if (is_string_list) + g_key_file_remove_key (keyfile, group, base_key, NULL); + else + g_key_file_set_value (keyfile, group, base_key, ""); + } + } else { + /* For any other settings we don't support extending the option with +/-. + * Just drop the key. */ + } continue; } - g_key_file_set_value (priv->keyfile, groups[g], keys[k], - v = g_key_file_get_value (kf, groups[g], keys[k], NULL)); - g_free (v); + new_value = g_key_file_get_value (kf, group, key, NULL); + g_key_file_set_value (keyfile, group, key, new_value); + g_free (new_value); } g_strfreev (keys); } @@ -441,16 +696,22 @@ read_config (NMConfig *config, const char *path, GError **error) } static gboolean -find_base_config (NMConfig *config, GError **error) +read_base_config (GKeyFile *keyfile, + const char *cli_config_main_file, + char **out_config_main_file, + GError **error) { - NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config); GError *my_error = NULL; + g_return_val_if_fail (keyfile, FALSE); + g_return_val_if_fail (out_config_main_file && !*out_config_main_file, FALSE); + g_return_val_if_fail (!error || !*error, FALSE); + /* Try a user-specified config file first */ - if (cli_config_path) { + if (cli_config_main_file) { /* Bad user-specific config file path is a hard error */ - if (read_config (config, cli_config_path, error)) { - priv->nm_conf_path = g_strdup (cli_config_path); + if (read_config (keyfile, cli_config_main_file, error)) { + *out_config_main_file = g_strdup (cli_config_main_file); return TRUE; } else return FALSE; @@ -464,27 +725,27 @@ find_base_config (NMConfig *config, GError **error) */ /* Try deprecated nm-system-settings.conf first */ - if (read_config (config, NM_OLD_SYSTEM_CONF_FILE, &my_error)) { - priv->nm_conf_path = g_strdup (NM_OLD_SYSTEM_CONF_FILE); + if (read_config (keyfile, DEFAULT_CONFIG_MAIN_FILE_OLD, &my_error)) { + *out_config_main_file = g_strdup (DEFAULT_CONFIG_MAIN_FILE_OLD); return TRUE; } if (!g_error_matches (my_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND)) { nm_log_warn (LOGD_CORE, "Old default config file %s invalid: %s\n", - NM_OLD_SYSTEM_CONF_FILE, + DEFAULT_CONFIG_MAIN_FILE_OLD, my_error->message); } g_clear_error (&my_error); /* Try the standard config file location next */ - if (read_config (config, NM_DEFAULT_SYSTEM_CONF_FILE, &my_error)) { - priv->nm_conf_path = g_strdup (NM_DEFAULT_SYSTEM_CONF_FILE); + if (read_config (keyfile, DEFAULT_CONFIG_MAIN_FILE, &my_error)) { + *out_config_main_file = g_strdup (DEFAULT_CONFIG_MAIN_FILE); return TRUE; } if (!g_error_matches (my_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND)) { nm_log_warn (LOGD_CORE, "Default config file %s invalid: %s\n", - NM_DEFAULT_SYSTEM_CONF_FILE, + DEFAULT_CONFIG_MAIN_FILE, my_error->message); g_propagate_error (error, my_error); return FALSE; @@ -494,21 +755,12 @@ find_base_config (NMConfig *config, GError **error) /* If for some reason no config file exists, use the default * config file path. */ - priv->nm_conf_path = g_strdup (NM_DEFAULT_SYSTEM_CONF_FILE); + *out_config_main_file = g_strdup (DEFAULT_CONFIG_MAIN_FILE); nm_log_info (LOGD_CORE, "No config file found or given; using %s\n", - NM_DEFAULT_SYSTEM_CONF_FILE); + DEFAULT_CONFIG_MAIN_FILE); return TRUE; } -/************************************************************************/ - -NMConfig * -nm_config_get (void) -{ - g_assert (singleton); - return singleton; -} - static int sort_asciibetically (gconstpointer a, gconstpointer b) { @@ -518,114 +770,374 @@ sort_asciibetically (gconstpointer a, gconstpointer b) return strcmp (s1, s2); } -/* call this function only once! */ -NMConfig * -nm_config_new (GError **error) +static GPtrArray * +_get_config_dir_files (const char *config_main_file, + const char *config_dir, + char **out_config_description) { - NMConfigPrivate *priv = NULL; GFile *dir; GFileEnumerator *direnum; GFileInfo *info; GPtrArray *confs; - const char *name; - int i; GString *config_description; + const char *name; + guint i; - g_assert (!singleton); - singleton = NM_CONFIG (g_object_new (NM_TYPE_CONFIG, NULL)); - priv = NM_CONFIG_GET_PRIVATE (singleton); - - /* First read the base config file */ - if (!find_base_config (singleton, error)) { - g_object_unref (singleton); - singleton = NULL; - return NULL; - } - - /* Now read the overrides in the config dir */ - if (cli_config_dir) - priv->config_dir = g_strdup (cli_config_dir); - else - priv->config_dir = g_strdup (NM_DEFAULT_SYSTEM_CONF_DIR); + g_return_val_if_fail (config_main_file, NULL); + g_return_val_if_fail (config_dir, NULL); + g_return_val_if_fail (out_config_description && !*out_config_description, NULL); confs = g_ptr_array_new_with_free_func (g_free); - config_description = g_string_new (priv->nm_conf_path); - dir = g_file_new_for_path (priv->config_dir); + config_description = g_string_new (config_main_file); + dir = g_file_new_for_path (config_dir); direnum = g_file_enumerate_children (dir, G_FILE_ATTRIBUTE_STANDARD_NAME, 0, NULL, NULL); if (direnum) { while ((info = g_file_enumerator_next_file (direnum, NULL, NULL))) { name = g_file_info_get_name (info); - if (g_str_has_suffix (name, ".conf")) { - g_ptr_array_add (confs, g_build_filename (priv->config_dir, name, NULL)); - if (confs->len == 1) - g_string_append (config_description, " and conf.d: "); - else - g_string_append (config_description, ", "); - g_string_append (config_description, name); - } + if (g_str_has_suffix (name, ".conf")) + g_ptr_array_add (confs, g_strdup (name)); g_object_unref (info); } g_object_unref (direnum); } g_object_unref (dir); - g_ptr_array_sort (confs, sort_asciibetically); - priv->config_description = g_string_free (config_description, FALSE); + if (confs->len > 0) { + g_ptr_array_sort (confs, sort_asciibetically); + g_string_append (config_description, " and conf.d: "); + for (i = 0; i < confs->len; i++) { + char *n = confs->pdata[i]; + + if (i > 0) + g_string_append (config_description, ", "); + g_string_append (config_description, n); + confs->pdata[i] = g_build_filename (config_dir, n, NULL); + g_free (n); + } + } + + *out_config_description = g_string_free (config_description, FALSE); + return confs; +} + +static GKeyFile * +read_entire_config (const NMConfigCmdLineOptions *cli, + const char *config_dir, + char **out_config_main_file, + char **out_config_description, + GError **error) +{ + GKeyFile *keyfile = nm_config_create_keyfile (); + GPtrArray *confs; + guint i; + char *o_config_main_file = NULL; + char *o_config_description = NULL; + char **plugins_tmp; + + g_return_val_if_fail (config_dir, NULL); + g_return_val_if_fail (out_config_main_file && !*out_config_main_file, FALSE); + g_return_val_if_fail (out_config_description && !*out_config_description, NULL); + g_return_val_if_fail (!error || !*error, FALSE); + + /* First read the base config file */ + if (!read_base_config (keyfile, cli ? cli->config_main_file : NULL, &o_config_main_file, error)) { + g_key_file_free (keyfile); + return NULL; + } + + g_assert (o_config_main_file); + + confs = _get_config_dir_files (o_config_main_file, config_dir, &o_config_description); for (i = 0; i < confs->len; i++) { - if (!read_config (singleton, confs->pdata[i], error)) { - g_object_unref (singleton); - singleton = NULL; - break; + if (!read_config (keyfile, confs->pdata[i], error)) { + g_key_file_free (keyfile); + g_free (o_config_main_file); + g_free (o_config_description); + g_ptr_array_unref (confs); + return NULL; } } g_ptr_array_unref (confs); - if (!singleton) + + /* Merge settings from command line. They overwrite everything read from + * config files. */ + + if (cli && cli->plugins && cli->plugins[0]) + g_key_file_set_value (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "plugins", cli->plugins); + plugins_tmp = g_key_file_get_string_list (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "plugins", NULL, NULL); + if (!plugins_tmp) { + if (STRLEN (CONFIG_PLUGINS_DEFAULT) > 0) + g_key_file_set_value (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "plugins", CONFIG_PLUGINS_DEFAULT); + } else + g_strfreev (plugins_tmp); + + if (cli && cli->configure_and_quit) + g_key_file_set_boolean (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "configure-and-quit", TRUE); + + if (cli && cli->connectivity_uri && cli->connectivity_uri[0]) + g_key_file_set_string (keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "uri", cli->connectivity_uri); + if (cli && cli->connectivity_interval >= 0) + g_key_file_set_integer (keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "interval", cli->connectivity_interval); + if (cli && cli->connectivity_response && cli->connectivity_response[0]) + g_key_file_set_string (keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "response", cli->connectivity_response); + + *out_config_main_file = o_config_main_file; + *out_config_description = o_config_description; + return keyfile; +} + +GSList * +nm_config_get_device_match_spec (const GKeyFile *keyfile, const char *group, const char *key, gboolean *out_has_key) +{ + gs_free char *value = NULL; + + /* nm_match_spec_split() already supports full escaping and is basically + * a modified version of g_key_file_parse_value_as_string(). So we first read + * the raw value (g_key_file_get_value()), and do the parsing ourselves. */ + value = g_key_file_get_value ((GKeyFile *) keyfile, group, key, NULL); + if (out_has_key) + *out_has_key = !!value; + return nm_match_spec_split (value); +} + +/************************************************************************/ + +void +nm_config_reload (NMConfig *self, int signal) +{ + NMConfigPrivate *priv; + GError *error = NULL; + GKeyFile *keyfile; + NMConfigData *new_data = NULL; + char *config_main_file = NULL; + char *config_description = NULL; + gs_strfreev char **no_auto_default = NULL; + + g_return_if_fail (NM_IS_CONFIG (self)); + + priv = NM_CONFIG_GET_PRIVATE (self); + + if (signal != SIGHUP) { + _set_config_data (self, NULL, signal); + return; + } + + /* pass on the original command line options. This means, that + * options specified at command line cannot ever be reloaded from + * file. That seems desirable. + */ + keyfile = read_entire_config (&priv->cli, + priv->config_dir, + &config_main_file, + &config_description, + &error); + if (!keyfile) { + nm_log_err (LOGD_CORE, "Failed to reload the configuration: %s", error->message); + g_clear_error (&error); + _set_config_data (self, NULL, signal); + return; + } + no_auto_default = no_auto_default_from_file (priv->no_auto_default_file); + + new_data = nm_config_data_new (config_main_file, config_description, (const char *const*) no_auto_default, keyfile); + g_free (config_main_file); + g_free (config_description); + g_key_file_unref (keyfile); + + _set_config_data (self, new_data, signal); +} + +static const char * +_change_flags_one_to_string (NMConfigChangeFlags flag) +{ + switch (flag) { + case NM_CONFIG_CHANGE_SIGHUP: + return "SIGHUP"; + case NM_CONFIG_CHANGE_SIGUSR1: + return "SIGUSR1"; + case NM_CONFIG_CHANGE_SIGUSR2: + return "SIGUSR2"; + case NM_CONFIG_CHANGE_CONFIG_FILES: + return "config-files"; + case NM_CONFIG_CHANGE_VALUES: + return "values"; + case NM_CONFIG_CHANGE_CONNECTIVITY: + return "connectivity"; + case NM_CONFIG_CHANGE_NO_AUTO_DEFAULT: + return "no-auto-default"; + case NM_CONFIG_CHANGE_DNS_MODE: + return "dns-mode"; + default: + g_return_val_if_reached ("unknown"); + } +} + +char * +nm_config_change_flags_to_string (NMConfigChangeFlags flags) +{ + GString *str = g_string_new (""); + NMConfigChangeFlags s = 0x01; + + while (flags) { + if (NM_FLAGS_HAS (flags, s)) { + if (str->len) + g_string_append_c (str, ','); + g_string_append (str, _change_flags_one_to_string (s)); + } + flags = flags & ~s; + s <<= 1; + } + return g_string_free (str, FALSE); +} + +static void +_set_config_data (NMConfig *self, NMConfigData *new_data, int signal) +{ + NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (self); + NMConfigData *old_data = priv->config_data; + NMConfigChangeFlags changes, changes_diff; + gs_free char *log_str = NULL; + gboolean had_new_data = !!new_data; + + switch (signal) { + case SIGHUP: + changes = NM_CONFIG_CHANGE_SIGHUP; + break; + case SIGUSR1: + changes = NM_CONFIG_CHANGE_SIGUSR1; + break; + case SIGUSR2: + changes = NM_CONFIG_CHANGE_SIGUSR2; + break; + default: + changes = NM_CONFIG_CHANGE_NONE; + break; + } + + if (new_data) { + changes_diff = nm_config_data_diff (old_data, new_data); + if (changes_diff == NM_CONFIG_CHANGE_NONE) + g_clear_object (&new_data); + else + changes |= changes_diff; + } + + if (changes == NM_CONFIG_CHANGE_NONE) + return; + + if (new_data) { + nm_log_info (LOGD_CORE, "config: update %s (%s)", nm_config_data_get_config_description (new_data), + (log_str = nm_config_change_flags_to_string (changes))); + nm_config_data_log (new_data, "CONFIG: "); + priv->config_data = new_data; + } else if (had_new_data) + nm_log_info (LOGD_CORE, "config: signal %s (no changes from disk)", (log_str = nm_config_change_flags_to_string (changes))); + else + nm_log_info (LOGD_CORE, "config: signal %s", (log_str = nm_config_change_flags_to_string (changes))); + g_signal_emit (self, signals[SIGNAL_CONFIG_CHANGED], 0, + new_data ? new_data : old_data, + changes, old_data); + if (new_data) + g_object_unref (old_data); +} + +NM_DEFINE_SINGLETON_DESTRUCTOR (NMConfig); +NM_DEFINE_SINGLETON_WEAK_REF (NMConfig); + +NMConfig * +nm_config_get (void) +{ + g_assert (singleton_instance); + return singleton_instance; +} + +NMConfig * +nm_config_setup (const NMConfigCmdLineOptions *cli, GError **error) +{ + g_assert (!singleton_instance); + + singleton_instance = nm_config_new (cli, error); + if (singleton_instance) + nm_singleton_instance_weak_ref_register (); + return singleton_instance; +} + +static gboolean +init_sync (GInitable *initable, GCancellable *cancellable, GError **error) +{ + NMConfig *self = NM_CONFIG (initable); + NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (self); + GKeyFile *keyfile; + char *config_main_file = NULL; + char *config_description = NULL; + gs_strfreev char **no_auto_default = NULL; + + if (priv->config_dir) { + /* Object is already initialized. */ + if (priv->config_data) + return TRUE; + g_set_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND, "unspecified error"); + return FALSE; + } + + if (priv->cli.config_dir) + priv->config_dir = g_strdup (priv->cli.config_dir); + else + priv->config_dir = g_strdup (DEFAULT_CONFIG_DIR); + + keyfile = read_entire_config (&priv->cli, + priv->config_dir, + &config_main_file, + &config_description, + error); + if (!keyfile) return FALSE; - /* Handle no-auto-default key and state file */ - priv->no_auto_default = g_key_file_get_string_list (priv->keyfile, "main", "no-auto-default", NULL, NULL); - if (cli_no_auto_default_file) - priv->no_auto_default_file = g_strdup (cli_no_auto_default_file); + /* Initialize read only private members */ + + if (priv->cli.no_auto_default_file) + priv->no_auto_default_file = g_strdup (priv->cli.no_auto_default_file); else - priv->no_auto_default_file = g_strdup (NM_NO_AUTO_DEFAULT_STATE_FILE); - merge_no_auto_default_state (singleton); + priv->no_auto_default_file = g_strdup (DEFAULT_NO_AUTO_DEFAULT_FILE); - /* Now let command-line options override the config files, and fill in priv. */ - if (cli_plugins && cli_plugins[0]) - g_key_file_set_value (priv->keyfile, "main", "plugins", cli_plugins); - priv->plugins = g_key_file_get_string_list (priv->keyfile, "main", "plugins", NULL, NULL); - if (!priv->plugins && STRLEN (CONFIG_PLUGINS_DEFAULT) > 0) - priv->plugins = g_strsplit (CONFIG_PLUGINS_DEFAULT, ",", -1); + priv->plugins = _nm_utils_strv_cleanup (g_key_file_get_string_list (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "plugins", NULL, NULL), + TRUE, TRUE, TRUE); + if (!priv->plugins) + priv->plugins = g_new0 (char *, 1); - priv->monitor_connection_files = _get_bool_value (priv->keyfile, "main", "monitor-connection-files", FALSE); + priv->monitor_connection_files = nm_config_keyfile_get_boolean (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "monitor-connection-files", FALSE); - priv->auth_polkit = _get_bool_value (priv->keyfile, "main", "auth-polkit", NM_CONFIG_DEFAULT_AUTH_POLKIT); + priv->auth_polkit = nm_config_keyfile_get_boolean (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "auth-polkit", NM_CONFIG_DEFAULT_AUTH_POLKIT); - priv->dhcp_client = g_key_file_get_value (priv->keyfile, "main", "dhcp", NULL); - priv->dns_mode = g_key_file_get_value (priv->keyfile, "main", "dns", NULL); + priv->dhcp_client = nm_strstrip (g_key_file_get_string (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "dhcp", NULL)); - priv->log_level = g_key_file_get_value (priv->keyfile, "logging", "level", NULL); - priv->log_domains = g_key_file_get_value (priv->keyfile, "logging", "domains", NULL); + priv->log_level = nm_strstrip (g_key_file_get_string (keyfile, NM_CONFIG_KEYFILE_GROUP_LOGGING, "level", NULL)); + priv->log_domains = nm_strstrip (g_key_file_get_string (keyfile, NM_CONFIG_KEYFILE_GROUP_LOGGING, "domains", NULL)); - priv->debug = g_key_file_get_value (priv->keyfile, "main", "debug", NULL); + priv->debug = g_key_file_get_string (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "debug", NULL); - if (cli_connectivity_uri && cli_connectivity_uri[0]) - g_key_file_set_value (priv->keyfile, "connectivity", "uri", cli_connectivity_uri); - priv->connectivity_uri = g_key_file_get_value (priv->keyfile, "connectivity", "uri", NULL); + priv->configure_and_quit = nm_config_keyfile_get_boolean (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "configure-and-quit", FALSE); - if (cli_connectivity_interval >= 0) - g_key_file_set_integer (priv->keyfile, "connectivity", "interval", cli_connectivity_interval); - priv->connectivity_interval = g_key_file_get_integer (priv->keyfile, "connectivity", "interval", NULL); + no_auto_default = no_auto_default_from_file (priv->no_auto_default_file); - if (cli_connectivity_response && cli_connectivity_response[0]) - g_key_file_set_value (priv->keyfile, "connectivity", "response", cli_connectivity_response); - priv->connectivity_response = g_key_file_get_value (priv->keyfile, "connectivity", "response", NULL); + priv->config_data_orig = nm_config_data_new (config_main_file, config_description, (const char *const*) no_auto_default, keyfile); - priv->ignore_carrier = g_key_file_get_string_list (priv->keyfile, "main", "ignore-carrier", NULL, NULL); + priv->config_data = g_object_ref (priv->config_data_orig); - priv->configure_and_quit = _get_bool_value (priv->keyfile, "main", "configure-and-quit", FALSE); + g_free (config_main_file); + g_free (config_description); + g_key_file_unref (keyfile); + return TRUE; +} - return singleton; +NMConfig * +nm_config_new (const NMConfigCmdLineOptions *cli, GError **error) +{ + return NM_CONFIG (g_initable_new (NM_TYPE_CONFIG, + NULL, + error, + NM_CONFIG_CMD_LINE_OPTIONS, cli, + NULL)); } static void @@ -634,11 +1146,6 @@ nm_config_init (NMConfig *config) NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config); priv->auth_polkit = NM_CONFIG_DEFAULT_AUTH_POLKIT; - - priv->keyfile = g_key_file_new (); - g_key_file_set_list_separator (priv->keyfile, ','); - - priv->connectivity_interval = -1; } static void @@ -646,34 +1153,44 @@ finalize (GObject *gobject) { NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (gobject); - g_free (priv->nm_conf_path); g_free (priv->config_dir); - g_free (priv->config_description); g_free (priv->no_auto_default_file); - g_clear_pointer (&priv->keyfile, g_key_file_unref); g_strfreev (priv->plugins); g_free (priv->dhcp_client); - g_free (priv->dns_mode); g_free (priv->log_level); g_free (priv->log_domains); g_free (priv->debug); - g_free (priv->connectivity_uri); - g_free (priv->connectivity_response); - g_strfreev (priv->no_auto_default); - g_strfreev (priv->ignore_carrier); - singleton = NULL; + _nm_config_cmd_line_options_clear (&priv->cli); - g_clear_pointer (&cli_config_path, g_free); - g_clear_pointer (&cli_config_dir, g_free); - g_clear_pointer (&cli_no_auto_default_file, g_free); - g_clear_pointer (&cli_plugins, g_free); - g_clear_pointer (&cli_connectivity_uri, g_free); - g_clear_pointer (&cli_connectivity_response, g_free); + g_clear_object (&priv->config_data); + g_clear_object (&priv->config_data_orig); G_OBJECT_CLASS (nm_config_parent_class)->finalize (gobject); } +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMConfig *self = NM_CONFIG (object); + NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (self); + NMConfigCmdLineOptions *cli; + + switch (prop_id) { + case PROP_CMD_LINE_OPTIONS: + /* construct only */ + cli = g_value_get_pointer (value); + if (!cli) + _nm_config_cmd_line_options_clear (&priv->cli); + else + _nm_config_cmd_line_options_copy (cli, &priv->cli); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} static void nm_config_class_init (NMConfigClass *config_class) @@ -682,5 +1199,27 @@ nm_config_class_init (NMConfigClass *config_class) g_type_class_add_private (config_class, sizeof (NMConfigPrivate)); object_class->finalize = finalize; + object_class->set_property = set_property; + + g_object_class_install_property + (object_class, PROP_CMD_LINE_OPTIONS, + g_param_spec_pointer (NM_CONFIG_CMD_LINE_OPTIONS, "", "", + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + signals[SIGNAL_CONFIG_CHANGED] = + g_signal_new (NM_CONFIG_SIGNAL_CONFIG_CHANGED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMConfigClass, config_changed), + NULL, NULL, NULL, + G_TYPE_NONE, 3, NM_TYPE_CONFIG_DATA, NM_TYPE_CONFIG_CHANGE_FLAGS, NM_TYPE_CONFIG_DATA); +} + +static void +nm_config_initable_iface_init (GInitableIface *iface) +{ + iface->init = init_sync; } diff --git a/src/nm-config.h b/src/nm-config.h index 56f75fb5..5b096f16 100644 --- a/src/nm-config.h +++ b/src/nm-config.h @@ -26,6 +26,7 @@ #include <glib-object.h> #include "nm-types.h" +#include "nm-config-data.h" G_BEGIN_DECLS @@ -36,43 +37,96 @@ G_BEGIN_DECLS #define NM_IS_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_CONFIG)) #define NM_CONFIG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_CONFIG, NMConfigClass)) -typedef struct { +/* Properties */ +#define NM_CONFIG_CMD_LINE_OPTIONS "cmd-line-options" + +/* Signals */ +#define NM_CONFIG_SIGNAL_CONFIG_CHANGED "config-changed" + +#define NM_CONFIG_DEFAULT_CONNECTIVITY_INTERVAL 300 +#define NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE "NetworkManager is online" /* NOT LOCALIZED */ + +#define NM_CONFIG_KEYFILE_LIST_SEPARATOR ',' + +#define NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION "connection" +#define NM_CONFIG_KEYFILE_GROUPPREFIX_TEST_APPEND_STRINGLIST ".test-append-stringlist" + +#define NM_CONFIG_KEYFILE_GROUP_MAIN "main" +#define NM_CONFIG_KEYFILE_GROUP_LOGGING "logging" +#define NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY "connectivity" + +#define NM_CONFIG_KEYFILE_GROUP_KEYFILE "keyfile" +#define NM_CONFIG_KEYFILE_GROUP_IFUPDOWN "ifupdown" +#define NM_CONFIG_KEYFILE_GROUP_IFNET "ifnet" + +#define NM_CONFIG_KEYFILE_KEY_IFNET_AUTO_REFRESH "auto_refresh" +#define NM_CONFIG_KEYFILE_KEY_IFNET_MANAGED "managed" +#define NM_CONFIG_KEYFILE_KEY_IFUPDOWN_MANAGED "managed" + +typedef struct NMConfigCmdLineOptions NMConfigCmdLineOptions; + +struct _NMConfig { GObject parent; -} NMConfig; +}; typedef struct { GObjectClass parent; + + /* Signals */ + void (*config_changed) (NMConfig *config, GHashTable *changes, NMConfigData *old_data); } NMConfigClass; GType nm_config_get_type (void); NMConfig *nm_config_get (void); -const char *nm_config_get_path (NMConfig *config); -const char *nm_config_get_description (NMConfig *config); +char *nm_config_change_flags_to_string (NMConfigChangeFlags flags); + +NMConfigData *nm_config_get_data (NMConfig *config); +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 ())) + const char **nm_config_get_plugins (NMConfig *config); gboolean nm_config_get_monitor_connection_files (NMConfig *config); gboolean nm_config_get_auth_polkit (NMConfig *config); const char *nm_config_get_dhcp_client (NMConfig *config); -const char *nm_config_get_dns_mode (NMConfig *config); const char *nm_config_get_log_level (NMConfig *config); const char *nm_config_get_log_domains (NMConfig *config); const char *nm_config_get_debug (NMConfig *config); -const char *nm_config_get_connectivity_uri (NMConfig *config); -guint nm_config_get_connectivity_interval (NMConfig *config); -const char *nm_config_get_connectivity_response (NMConfig *config); gboolean nm_config_get_configure_and_quit (NMConfig *config); -gboolean nm_config_get_ethernet_can_auto_default (NMConfig *config, NMDevice *device); -void nm_config_set_ethernet_no_auto_default (NMConfig *config, NMDevice *device); - -gboolean nm_config_get_ignore_carrier (NMConfig *config, NMDevice *device); - -char *nm_config_get_value (NMConfig *config, const char *group, const char *key, GError **error); - /* for main.c only */ -GOptionEntry *nm_config_get_options (void); -NMConfig *nm_config_new (GError **error); +NMConfigCmdLineOptions *nm_config_cmd_line_options_new (void); +void nm_config_cmd_line_options_free (NMConfigCmdLineOptions *cli); +void nm_config_cmd_line_options_add_to_entries (NMConfigCmdLineOptions *cli, + GOptionContext *opt_ctx); + +gboolean nm_config_get_no_auto_default_for_device (NMConfig *config, NMDevice *device); +void nm_config_set_no_auto_default_for_device (NMConfig *config, NMDevice *device); + +NMConfig *nm_config_new (const NMConfigCmdLineOptions *cli, GError **error); +NMConfig *nm_config_setup (const NMConfigCmdLineOptions *cli, GError **error); +void nm_config_reload (NMConfig *config, int signal); + +gint nm_config_parse_boolean (const char *str, gint default_value); + +GKeyFile *nm_config_create_keyfile (void); +gint nm_config_keyfile_get_boolean (GKeyFile *keyfile, + const char *section, + const char *key, + gint default_value); +char *nm_config_keyfile_get_value (GKeyFile *keyfile, + const char *section, + const char *key, + NMConfigGetValueFlags flags); +void nm_config_keyfile_set_string_list (GKeyFile *keyfile, + const char *group, + const char *key, + const char *const* strv, + gssize len); +GSList *nm_config_get_device_match_spec (const GKeyFile *keyfile, const char *group, const char *key, gboolean *out_has_key); G_END_DECLS diff --git a/src/nm-connectivity.c b/src/nm-connectivity.c index 85bd70ae..81ac8f9d 100644 --- a/src/nm-connectivity.c +++ b/src/nm-connectivity.c @@ -26,25 +26,43 @@ #include <libsoup/soup.h> #endif +#include "nm-glib-compat.h" + #include "nm-connectivity.h" -#include "nm-logging.h" #include "nm-config.h" +#include "nm-logging.h" G_DEFINE_TYPE (NMConnectivity, nm_connectivity, G_TYPE_OBJECT) #define NM_CONNECTIVITY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_CONNECTIVITY, NMConnectivityPrivate)) -#define DEFAULT_RESPONSE "NetworkManager is online" /* NOT LOCALIZED */ +#define _LOG_DEFAULT_DOMAIN LOGD_CONCHECK + +#define _LOG(level, domain, ...) \ + G_STMT_START { \ + nm_log ((level), (domain), \ + "%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + "connectivity: " \ + _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } G_STMT_END + +#define _LOGT(...) _LOG (LOGL_TRACE, _LOG_DEFAULT_DOMAIN, __VA_ARGS__) +#define _LOGD(...) _LOG (LOGL_DEBUG, _LOG_DEFAULT_DOMAIN, __VA_ARGS__) +#define _LOGI(...) _LOG (LOGL_INFO, _LOG_DEFAULT_DOMAIN, __VA_ARGS__) +#define _LOGW(...) _LOG (LOGL_WARN, _LOG_DEFAULT_DOMAIN, __VA_ARGS__) +#define _LOGE(...) _LOG (LOGL_ERR, _LOG_DEFAULT_DOMAIN, __VA_ARGS__) + typedef struct { char *uri; char *response; guint interval; + gboolean online; /* whether periodic connectivity checking is enabled. */ #if WITH_CONCHECK SoupSession *soup_session; - guint pending_checks; + gboolean initial_check_obsoleted; guint check_id; #endif @@ -69,8 +87,8 @@ nm_connectivity_get_state (NMConnectivity *connectivity) return NM_CONNECTIVITY_GET_PRIVATE (connectivity)->state; } -static const char * -state_name (NMConnectivityState state) +const char * +nm_connectivity_state_to_string (NMConnectivityState state) { switch (state) { case NM_CONNECTIVITY_UNKNOWN: @@ -84,7 +102,7 @@ state_name (NMConnectivityState state) case NM_CONNECTIVITY_FULL: return "FULL"; default: - return "???"; + g_return_val_if_reached ("???"); } } @@ -94,31 +112,41 @@ update_state (NMConnectivity *self, NMConnectivityState state) NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); if (priv->state != state) { - nm_log_dbg (LOGD_CONCHECK, "Connectivity state changed from %s to %s", - state_name (priv->state), state_name (state)); + _LOGD ("state changed from %s to %s", + nm_connectivity_state_to_string (priv->state), + nm_connectivity_state_to_string (state)); priv->state = state; g_object_notify (G_OBJECT (self), NM_CONNECTIVITY_STATE); } } #if WITH_CONCHECK +typedef struct { + GSimpleAsyncResult *simple; + char *uri; + char *response; + guint check_id_when_scheduled; +} ConCheckCbData; + static void nm_connectivity_check_cb (SoupSession *session, SoupMessage *msg, gpointer user_data) { - GSimpleAsyncResult *simple = user_data; NMConnectivity *self; NMConnectivityPrivate *priv; + ConCheckCbData *cb_data = user_data; + GSimpleAsyncResult *simple = cb_data->simple; NMConnectivityState new_state; const char *nm_header; + const char *uri = cb_data->uri; + const char *response = cb_data->response ? cb_data->response : NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE; self = NM_CONNECTIVITY (g_async_result_get_source_object (G_ASYNC_RESULT (simple))); + /* it is safe to unref @self here, @simple holds yet another reference. */ g_object_unref (self); priv = NM_CONNECTIVITY_GET_PRIVATE (self); - priv->pending_checks--; if (SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code)) { - nm_log_info (LOGD_CONCHECK, "Connectivity check for uri '%s' failed with '%s'.", - priv->uri, msg->reason_phrase); + _LOGI ("check for uri '%s' failed with '%s'", uri, msg->reason_phrase); new_state = NM_CONNECTIVITY_LIMITED; goto done; } @@ -126,33 +154,48 @@ nm_connectivity_check_cb (SoupSession *session, SoupMessage *msg, gpointer user_ /* Check headers; if we find the NM-specific one we're done */ nm_header = soup_message_headers_get_one (msg->response_headers, "X-NetworkManager-Status"); if (g_strcmp0 (nm_header, "online") == 0) { - nm_log_dbg (LOGD_CONCHECK, "Connectivity check for uri '%s' with Status header successful.", priv->uri); + _LOGD ("check for uri '%s' with Status header successful.", uri); new_state = NM_CONNECTIVITY_FULL; } else if (msg->status_code == SOUP_STATUS_OK) { /* check response */ - if (msg->response_body->data && (g_str_has_prefix (msg->response_body->data, priv->response))) { - nm_log_dbg (LOGD_CONCHECK, "Connectivity check for uri '%s' successful.", - priv->uri); + if (msg->response_body->data && g_str_has_prefix (msg->response_body->data, response)) { + _LOGD ("check for uri '%s' successful.", uri); new_state = NM_CONNECTIVITY_FULL; } else { - nm_log_info (LOGD_CONCHECK, "Connectivity check for uri '%s' did not match expected response '%s'; assuming captive portal.", - priv->uri, priv->response); + _LOGI ("check for uri '%s' did not match expected response '%s'; assuming captive portal.", + uri, response); new_state = NM_CONNECTIVITY_PORTAL; } } else { - nm_log_info (LOGD_CONCHECK, "Connectivity check for uri '%s' returned status '%d %s'; assuming captive portal.", - priv->uri, msg->status_code, msg->reason_phrase); + _LOGI ("check for uri '%s' returned status '%d %s'; assuming captive portal.", + uri, msg->status_code, msg->reason_phrase); new_state = NM_CONNECTIVITY_PORTAL; } done: - update_state (self, new_state); + /* Only update the state, if the call was done from external, or if the periodic check + * is still the one that called this async check. */ + if (!cb_data->check_id_when_scheduled || cb_data->check_id_when_scheduled == priv->check_id) { + /* Only update the state, if the URI and response parameters did not change + * since invocation. + * The interval does not matter for exernal calls, and for internal calls + * we don't reach this line if the interval changed. */ + if ( !g_strcmp0 (cb_data->uri, priv->uri) + && !g_strcmp0 (cb_data->response, priv->response)) + update_state (self, new_state); + } g_simple_async_result_set_op_res_gssize (simple, new_state); g_simple_async_result_complete (simple); g_object_unref (simple); + + g_free (cb_data->uri); + g_free (cb_data->response); + g_slice_free (ConCheckCbData, cb_data); } +#define IS_PERIODIC_CHECK(callback) (callback == run_check_complete) + static void run_check_complete (GObject *object, GAsyncResult *result, @@ -163,7 +206,7 @@ run_check_complete (GObject *object, nm_connectivity_check_finish (self, result, &error); if (error) { - nm_log_err (LOGD_CONCHECK, "Connectivity check failed: %s", error->message); + _LOGE ("check failed: %s", error->message); g_error_free (error); } } @@ -184,39 +227,54 @@ idle_start_periodic_checks (gpointer user_data) NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); priv->check_id = g_timeout_add_seconds (priv->interval, run_check, self); - if (!priv->pending_checks) + if (!priv->initial_check_obsoleted) run_check (self); return FALSE; } #endif -void -nm_connectivity_set_online (NMConnectivity *self, - gboolean online) +static void +_reschedule_periodic_checks (NMConnectivity *self, gboolean force_reschedule) { -#if WITH_CONCHECK NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); -#endif - - nm_log_dbg (LOGD_CONCHECK, "nm_connectivity_set_online(%s)", online ? "TRUE" : "FALSE"); #if WITH_CONCHECK - if (online && priv->uri && priv->interval) { - if (!priv->check_id) + if (priv->online && priv->uri && priv->interval) { + if (force_reschedule || !priv->check_id) { + if (priv->check_id) + g_source_remove (priv->check_id); priv->check_id = g_timeout_add (0, idle_start_periodic_checks, self); - - return; - } else if (priv->check_id) { - g_source_remove (priv->check_id); - priv->check_id = 0; + priv->initial_check_obsoleted = FALSE; + } + } else { + if (priv->check_id) { + g_source_remove (priv->check_id); + priv->check_id = 0; + } } + if (priv->check_id) + return; #endif /* Either @online is %TRUE but we aren't checking connectivity, or * @online is %FALSE. Either way we can update our status immediately. */ - update_state (self, online ? NM_CONNECTIVITY_FULL : NM_CONNECTIVITY_NONE); + update_state (self, priv->online ? NM_CONNECTIVITY_FULL : NM_CONNECTIVITY_NONE); +} + +void +nm_connectivity_set_online (NMConnectivity *self, + gboolean online) +{ + NMConnectivityPrivate *priv= NM_CONNECTIVITY_GET_PRIVATE (self); + + online = !!online; + if (priv->online != online) { + _LOGD ("set %s", online ? "online" : "offline"); + priv->online = online; + _reschedule_periodic_checks (self, FALSE); + } } void @@ -225,38 +283,44 @@ nm_connectivity_check_async (NMConnectivity *self, gpointer user_data) { NMConnectivityPrivate *priv; -#if WITH_CONCHECK - SoupMessage *msg; -#endif GSimpleAsyncResult *simple; g_return_if_fail (NM_IS_CONNECTIVITY (self)); priv = NM_CONNECTIVITY_GET_PRIVATE (self); -#if WITH_CONCHECK - if (callback == run_check_complete) - nm_log_dbg (LOGD_CONCHECK, "Periodic connectivity check started with uri '%s'.", priv->uri); - else -#endif - nm_log_dbg (LOGD_CONCHECK, "Connectivity check started with uri '%s'.", priv->uri); - simple = g_simple_async_result_new (G_OBJECT (self), callback, user_data, nm_connectivity_check_async); #if WITH_CONCHECK if (priv->uri && priv->interval) { + SoupMessage *msg; + ConCheckCbData *cb_data = g_slice_new (ConCheckCbData); + msg = soup_message_new ("GET", priv->uri); soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT); /* Disable HTTP/1.1 keepalive; the connection should not persist */ soup_message_headers_append (msg->request_headers, "Connection", "close"); + cb_data->simple = simple; + cb_data->uri = g_strdup (priv->uri); + cb_data->response = g_strdup (priv->response); + + /* For internal calls (periodic), remember the check-id at time of scheduling. */ + cb_data->check_id_when_scheduled = IS_PERIODIC_CHECK (callback) ? priv->check_id : 0; + soup_session_queue_message (priv->soup_session, msg, nm_connectivity_check_cb, - simple); - priv->pending_checks++; + cb_data); + priv->initial_check_obsoleted = TRUE; + _LOGD ("check: send %srequest to '%s'", IS_PERIODIC_CHECK (callback) ? "periodic " : "", priv->uri); return; + } else { + g_warn_if_fail (!IS_PERIODIC_CHECK (callback)); + _LOGD ("check: faking request. Connectivity check disabled"); } +#else + _LOGD ("check: faking request. Compiled without connectivity-check support"); #endif g_simple_async_result_set_op_res_gssize (simple, priv->state); @@ -279,38 +343,18 @@ nm_connectivity_check_finish (NMConnectivity *self, return (NMConnectivityState) g_simple_async_result_get_op_res_gssize (simple); } +/**************************************************************************/ NMConnectivity * -nm_connectivity_new (void) +nm_connectivity_new (const char *uri, + guint interval, + const char *response) { - NMConnectivity *self; - NMConfig *config; - const char *check_response; - - config = nm_config_get (); - check_response = nm_config_get_connectivity_response (config); - - self = g_object_new (NM_TYPE_CONNECTIVITY, - NM_CONNECTIVITY_URI, nm_config_get_connectivity_uri (config), - NM_CONNECTIVITY_INTERVAL, nm_config_get_connectivity_interval (config), - NM_CONNECTIVITY_RESPONSE, check_response ? check_response : DEFAULT_RESPONSE, + return g_object_new (NM_TYPE_CONNECTIVITY, + NM_CONNECTIVITY_URI, uri, + NM_CONNECTIVITY_INTERVAL, interval, + NM_CONNECTIVITY_RESPONSE, response, NULL); - g_return_val_if_fail (self != NULL, NULL); - update_state (self, NM_CONNECTIVITY_NONE); - - return self; -} - -static char * -get_non_empty_string_value (const GValue *val) -{ - const char *s; - - s = g_value_get_string (val); - if (s && s[0]) - return g_strdup (s); - else - return NULL; } static void @@ -319,32 +363,48 @@ set_property (GObject *object, guint property_id, { NMConnectivity *self = NM_CONNECTIVITY (object); NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); + const char *uri, *response; + guint interval; switch (property_id) { case PROP_URI: - g_free (priv->uri); - priv->uri = get_non_empty_string_value (value); - + uri = g_value_get_string (value); + if (uri && !*uri) + uri = NULL; #if WITH_CONCHECK - if (priv->uri) { - SoupURI *uri = soup_uri_new (priv->uri); + if (uri) { + SoupURI *soup_uri = soup_uri_new (uri); - if (!uri || !SOUP_URI_VALID_FOR_HTTP (uri)) { - nm_log_err (LOGD_CONCHECK, "Invalid uri '%s' for connectivity check.", priv->uri); - g_free (priv->uri); - priv->uri = NULL; + if (!soup_uri || !SOUP_URI_VALID_FOR_HTTP (soup_uri)) { + _LOGE ("invalid uri '%s' for connectivity check.", uri); + uri = NULL; } - if (uri) - soup_uri_free (uri); + if (soup_uri) + soup_uri_free (soup_uri); } #endif + if (g_strcmp0 (uri, priv->uri) != 0) { + g_free (priv->uri); + priv->uri = g_strdup (uri); + _reschedule_periodic_checks (self, TRUE); + } break; case PROP_INTERVAL: - priv->interval = g_value_get_uint (value); + interval = g_value_get_uint (value); + if (priv->interval != interval) { + priv->interval = interval; + _reschedule_periodic_checks (self, TRUE); + } break; case PROP_RESPONSE: - g_free (priv->response); - priv->response = get_non_empty_string_value (value); + response = g_value_get_string (value); + if (g_strcmp0 (response, priv->response) != 0) { + /* a response %NULL means, NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE. Any other response + * (including "") is accepted. */ + g_free (priv->response); + priv->response = g_strdup (response); + _reschedule_periodic_checks (self, TRUE); + } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -367,7 +427,10 @@ get_property (GObject *object, guint property_id, g_value_set_uint (value, priv->interval); break; case PROP_RESPONSE: - g_value_set_string (value, priv->response); + if (priv->response) + g_value_set_string (value, priv->response); + else + g_value_set_static_string (value, NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE); break; case PROP_STATE: g_value_set_uint (value, priv->state); @@ -382,11 +445,12 @@ get_property (GObject *object, guint property_id, static void nm_connectivity_init (NMConnectivity *self) { -#if WITH_CONCHECK NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); +#if WITH_CONCHECK priv->soup_session = soup_session_async_new_with_options (SOUP_SESSION_TIMEOUT, 15, NULL); #endif + priv->state = NM_CONNECTIVITY_NONE; } @@ -396,8 +460,8 @@ dispose (GObject *object) NMConnectivity *self = NM_CONNECTIVITY (object); NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); - g_free (priv->uri); - g_free (priv->response); + g_clear_pointer (&priv->uri, g_free); + g_clear_pointer (&priv->response, g_free); #if WITH_CONCHECK if (priv->soup_session) { @@ -426,31 +490,34 @@ nm_connectivity_class_init (NMConnectivityClass *klass) /* properties */ g_object_class_install_property - (object_class, PROP_URI, - g_param_spec_string (NM_CONNECTIVITY_URI, "", "", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + (object_class, PROP_URI, + g_param_spec_string (NM_CONNECTIVITY_URI, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property - (object_class, PROP_INTERVAL, - g_param_spec_uint (NM_CONNECTIVITY_INTERVAL, "", "", - 0, G_MAXUINT, 300, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT | - G_PARAM_STATIC_STRINGS)); + (object_class, PROP_INTERVAL, + g_param_spec_uint (NM_CONNECTIVITY_INTERVAL, "", "", + 0, G_MAXUINT, NM_CONFIG_DEFAULT_CONNECTIVITY_INTERVAL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property - (object_class, PROP_RESPONSE, - g_param_spec_string (NM_CONNECTIVITY_RESPONSE, "", "", - DEFAULT_RESPONSE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT | - G_PARAM_STATIC_STRINGS)); + (object_class, PROP_RESPONSE, + g_param_spec_string (NM_CONNECTIVITY_RESPONSE, "", "", + NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property - (object_class, PROP_STATE, - g_param_spec_uint (NM_CONNECTIVITY_STATE, "", "", - NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_UNKNOWN, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); + (object_class, PROP_STATE, + g_param_spec_uint (NM_CONNECTIVITY_STATE, "", "", + NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_UNKNOWN, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); } diff --git a/src/nm-connectivity.h b/src/nm-connectivity.h index 771abdc2..fc584d5f 100644 --- a/src/nm-connectivity.h +++ b/src/nm-connectivity.h @@ -51,7 +51,11 @@ typedef struct { GType nm_connectivity_get_type (void); -NMConnectivity *nm_connectivity_new (void); +const char *nm_connectivity_state_to_string (NMConnectivityState state); + +NMConnectivity *nm_connectivity_new (const char *uri, + guint interval, + const char *response); void nm_connectivity_set_online (NMConnectivity *self, gboolean online); diff --git a/src/nm-dbus-manager.c b/src/nm-dbus-manager.c index f57eeb8e..7e20b2c1 100644 --- a/src/nm-dbus-manager.c +++ b/src/nm-dbus-manager.c @@ -36,6 +36,7 @@ #include <dbus/dbus-glib-lowlevel.h> #include <string.h> #include "nm-logging.h" +#include "NetworkManagerUtils.h" #define PRIV_SOCK_PATH NMRUNDIR "/private" #define PRIV_SOCK_TAG "private" @@ -490,7 +491,7 @@ private_server_setup (NMDBusManager *self) NMDBusManagerPrivate *priv = NM_DBUS_MANAGER_GET_PRIVATE (self); /* Skip this step if this is just a test program */ - if (g_test_initialized ()) + if (nm_utils_get_testing ()) return; /* Set up our main private DBus socket */ diff --git a/src/nm-dcb.c b/src/nm-dcb.c index 6faec5c5..8bf3858a 100644 --- a/src/nm-dcb.c +++ b/src/nm-dcb.c @@ -27,7 +27,6 @@ #include "nm-dcb.h" #include "nm-platform.h" #include "NetworkManagerUtils.h" -#include "nm-posix-signals.h" #include "nm-logging.h" static const char *helper_names[] = { "dcbtool", "fcoeadm" }; @@ -305,7 +304,7 @@ run_helper (char **argv, guint which, gpointer user_data, GError **error) nm_log_dbg (LOGD_DCB, "%s", cmdline); success = g_spawn_sync ("/", argv, NULL, 0 /*G_SPAWN_DEFAULT*/, - nm_unblock_posix_signals, NULL, + NULL, NULL, &outmsg, &errmsg, &exit_status, error); /* Log any stderr output */ if (success && WIFEXITED (exit_status) && WEXITSTATUS (exit_status) && (errmsg || outmsg)) { @@ -355,7 +354,7 @@ carrier_wait (const char *iface, guint secs, gboolean up) g_return_if_fail (iface != NULL); - ifindex = nm_platform_link_get_ifindex (iface); + ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, iface); if (ifindex > 0) { /* To work around driver quirks and lldpad handling of carrier status, * we must wait a short period of time to see if the carrier goes @@ -366,9 +365,9 @@ carrier_wait (const char *iface, guint secs, gboolean up) nm_log_dbg (LOGD_DCB, "(%s): cleanup waiting for carrier %s", iface, up ? "up" : "down"); g_usleep (G_USEC_PER_SEC / 4); - while (nm_platform_link_is_connected (ifindex) != up && count-- > 0) { + while (nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex) != up && count-- > 0) { g_usleep (G_USEC_PER_SEC / 10); - nm_platform_link_refresh (ifindex); + nm_platform_link_refresh (NM_PLATFORM_GET, ifindex); } } } diff --git a/src/nm-default-route-manager.c b/src/nm-default-route-manager.c index 153c1de6..fbb07ce3 100644 --- a/src/nm-default-route-manager.c +++ b/src/nm-default-route-manager.c @@ -60,7 +60,7 @@ typedef struct { G_DEFINE_TYPE (NMDefaultRouteManager, nm_default_route_manager, G_TYPE_OBJECT) -static NMDefaultRouteManager *singleton_instance; +NM_DEFINE_SINGLETON_GETTER (NMDefaultRouteManager, nm_default_route_manager_get, NM_TYPE_DEFAULT_ROUTE_MANAGER); #define _LOG(level, addr_family, ...) \ G_STMT_START { \ @@ -87,14 +87,14 @@ static NMDefaultRouteManager *singleton_instance; #define _LOGW(addr_family, ...) _LOG (LOGL_WARN , addr_family, __VA_ARGS__) #define _LOGE(addr_family, ...) _LOG (LOGL_ERR , addr_family, __VA_ARGS__) -#define LOG_ENTRY_FMT "entry[%u/%s:%p:%s:%c%c]" +#define LOG_ENTRY_FMT "entry[%u/%s:%p:%s:%c:%csync]" #define LOG_ENTRY_ARGS(entry_idx, entry) \ (entry_idx), \ NM_IS_DEVICE ((entry)->source.pointer) ? "dev" : "vpn", \ (entry)->source.pointer, \ NM_IS_DEVICE ((entry)->source.pointer) ? nm_device_get_iface ((entry)->source.device) : nm_vpn_connection_get_connection_id ((entry)->source.vpn), \ - ((entry)->never_default ? 'N' : 'n'), \ - ((entry)->synced ? 'S' : 's') + ((entry)->never_default ? '0' : '1'), \ + ((entry)->synced ? '+' : '-') /***********************************************************************************/ @@ -127,7 +127,7 @@ typedef struct { * to indicate that the ifindex is managed but has no default-route. * Missing entries also indicate that a certain ifindex has no default-route. * The difference is that missing entries are considered assumed while on - * (synced && never_default) entires the absence of the default route + * (synced && never_default) entries the absence of the default route * is enforced. NMDefaultRouteManager will actively remove any default * route on such ifindexes. * Also, for VPN sources in addition we track them so that a never-default @@ -256,7 +256,8 @@ _platform_route_sync_add (const VTableIP *vtable, NMDefaultRouteManager *self, g return FALSE; if (vtable->vt->is_ip4) { - success = nm_platform_ip4_route_add (entry->route.rx.ifindex, + success = nm_platform_ip4_route_add (NM_PLATFORM_GET, + entry->route.rx.ifindex, entry->route.rx.source, 0, 0, @@ -265,7 +266,8 @@ _platform_route_sync_add (const VTableIP *vtable, NMDefaultRouteManager *self, g entry->effective_metric, entry->route.rx.mss); } else { - success = nm_platform_ip6_route_add (entry->route.rx.ifindex, + success = nm_platform_ip6_route_add (NM_PLATFORM_GET, + entry->route.rx.ifindex, entry->route.rx.source, in6addr_any, 0, @@ -290,7 +292,7 @@ _platform_route_sync_flush (const VTableIP *vtable, NMDefaultRouteManager *self, gboolean changed = FALSE; /* prune all other default routes from this device. */ - routes = vtable->vt->route_get_all (0, NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT); + routes = vtable->vt->route_get_all (NM_PLATFORM_GET, 0, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT); for (i = 0; i < routes->len; i++) { const NMPlatformIPRoute *route; @@ -299,7 +301,7 @@ _platform_route_sync_flush (const VTableIP *vtable, NMDefaultRouteManager *self, route = _vt_route_index (vtable, routes, i); - /* look at all entires and see if the route for this ifindex pair is + /* look at all entries and see if the route for this ifindex pair is * a known entry. */ for (j = 0; j < entries->len; j++) { Entry *e = g_ptr_array_index (entries, j); @@ -322,7 +324,7 @@ _platform_route_sync_flush (const VTableIP *vtable, NMDefaultRouteManager *self, */ if ( !entry && (has_ifindex_synced || ifindex_to_flush == route->ifindex)) { - vtable->vt->route_delete_default (route->ifindex, route->metric); + vtable->vt->route_delete_default (NM_PLATFORM_GET, route->ifindex, route->metric); changed = TRUE; } } @@ -409,6 +411,30 @@ _get_assumed_interface_metrics (const VTableIP *vtable, NMDefaultRouteManager *s g_hash_table_add (result, GUINT_TO_POINTER (vtable->vt->metric_normalize (route->metric))); } + /* also add all non-synced metrics from our entries list. We might have there some metrics that + * we track as non-synced but that are no longer part of platform routes. Anyway, for now + * we still want to treat them as assumed. */ + for (i = 0; i < entries->len; i++) { + gboolean ifindex_has_synced_entry = FALSE; + Entry *e_i = g_ptr_array_index (entries, i); + + if (e_i->synced) + continue; + + for (j = 0; j < entries->len; j++) { + Entry *e_j = g_ptr_array_index (entries, j); + + if ( j != i + && (e_j->synced && e_j->route.rx.ifindex == e_i->route.rx.ifindex)) { + ifindex_has_synced_entry = TRUE; + break; + } + } + + if (!ifindex_has_synced_entry) + g_hash_table_add (result, GUINT_TO_POINTER (vtable->vt->metric_normalize (e_i->route.rx.metric))); + } + return result; } @@ -452,7 +478,7 @@ _resync_all (const VTableIP *vtable, NMDefaultRouteManager *self, const Entry *c entries = vtable->get_entries (priv); - routes = vtable->vt->route_get_all (0, NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT); + routes = vtable->vt->route_get_all (NM_PLATFORM_GET, 0, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT); assumed_metrics = _get_assumed_interface_metrics (vtable, self, routes); @@ -522,23 +548,23 @@ _resync_all (const VTableIP *vtable, NMDefaultRouteManager *self, const Entry *c * or none. Hence, we only have to remember what is going to change. */ g_array_append_val (changed_metrics, expected_metric); if (old_entry) { - _LOGD (vtable->vt->addr_family, LOG_ENTRY_FMT": update %s (%u -> %u)", LOG_ENTRY_ARGS (i, entry), + _LOGD (vtable->vt->addr_family, LOG_ENTRY_FMT": sync:update %s (%u -> %u)", LOG_ENTRY_ARGS (i, entry), vtable->vt->route_to_string (&entry->route), (guint) old_entry->effective_metric, (guint) expected_metric); } else { - _LOGD (vtable->vt->addr_family, LOG_ENTRY_FMT": add %s (%u)", LOG_ENTRY_ARGS (i, entry), + _LOGD (vtable->vt->addr_family, LOG_ENTRY_FMT": sync:add %s (%u)", LOG_ENTRY_ARGS (i, entry), vtable->vt->route_to_string (&entry->route), (guint) expected_metric); } } else if (entry->effective_metric != expected_metric) { g_array_append_val (changed_metrics, entry->effective_metric); g_array_append_val (changed_metrics, expected_metric); - _LOGD (vtable->vt->addr_family, LOG_ENTRY_FMT": resync metric %s (%u -> %u)", LOG_ENTRY_ARGS (i, entry), + _LOGD (vtable->vt->addr_family, LOG_ENTRY_FMT": sync:metric %s (%u -> %u)", LOG_ENTRY_ARGS (i, entry), vtable->vt->route_to_string (&entry->route), (guint) entry->effective_metric, (guint) expected_metric); } else { if (!_vt_routes_has_entry (vtable, routes, entry)) { g_array_append_val (changed_metrics, entry->effective_metric); - _LOGD (vtable->vt->addr_family, LOG_ENTRY_FMT": readd route %s (%u -> %u)", LOG_ENTRY_ARGS (i, entry), + _LOGD (vtable->vt->addr_family, LOG_ENTRY_FMT": sync:re-add %s (%u -> %u)", LOG_ENTRY_ARGS (i, entry), vtable->vt->route_to_string (&entry->route), (guint) entry->effective_metric, (guint) entry->effective_metric); } @@ -603,10 +629,11 @@ _entry_at_idx_update (const VTableIP *vtable, NMDefaultRouteManager *self, guint if (!entry->synced && !entry->never_default) entry->effective_metric = entry->route.rx.metric; - _LOGD (vtable->vt->addr_family, LOG_ENTRY_FMT": %s %s", + _LOGD (vtable->vt->addr_family, LOG_ENTRY_FMT": %s %s (%"G_GUINT32_FORMAT")", LOG_ENTRY_ARGS (entry_idx, entry), - old_entry ? "update" : "add", - vtable->vt->route_to_string (&entry->route)); + old_entry ? "record:update" : "record:add ", + vtable->vt->route_to_string (&entry->route), + entry->effective_metric); g_ptr_array_sort_with_data (entries, _sort_entries_cmp, NULL); @@ -626,7 +653,7 @@ _entry_at_idx_remove (const VTableIP *vtable, NMDefaultRouteManager *self, guint entry = g_ptr_array_index (entries, entry_idx); - _LOGD (vtable->vt->addr_family, LOG_ENTRY_FMT": remove %s (%u)", LOG_ENTRY_ARGS (entry_idx, entry), + _LOGD (vtable->vt->addr_family, LOG_ENTRY_FMT": record:remove %s (%u)", LOG_ENTRY_ARGS (entry_idx, entry), vtable->vt->route_to_string (&entry->route), (guint) entry->effective_metric); /* Remove the entry from the list (but don't free it yet) */ @@ -740,7 +767,7 @@ _ipx_update_default_route (const VTableIP *vtable, NMDefaultRouteManager *self, never_default = nm_ip4_config_get_never_default (vpn_config); rt.r4.ifindex = ip_ifindex; rt.r4.source = NM_IP_CONFIG_SOURCE_VPN; - rt.r4.gateway = nm_vpn_connection_get_ip4_internal_gateway (vpn); + rt.r4.gateway = nm_ip4_config_get_gateway (vpn_config); rt.r4.metric = nm_vpn_connection_get_ip4_route_metric (vpn); rt.r4.mss = nm_ip4_config_get_mss (vpn_config); default_route = &rt.rx; @@ -750,7 +777,7 @@ _ipx_update_default_route (const VTableIP *vtable, NMDefaultRouteManager *self, vpn_config = nm_vpn_connection_get_ip6_config (vpn); if (vpn_config) { - const struct in6_addr *int_gw = nm_vpn_connection_get_ip6_internal_gateway (vpn); + const struct in6_addr *int_gw = nm_ip6_config_get_gateway (vpn_config); never_default = nm_ip6_config_get_never_default (vpn_config); rt.r6.ifindex = ip_ifindex; @@ -762,11 +789,24 @@ _ipx_update_default_route (const VTableIP *vtable, NMDefaultRouteManager *self, } } } - synced = TRUE; + if (nm_vpn_connection_get_ip_ifindex (vpn) > 0) + synced = TRUE; + else { + /* a VPN connection without tunnel device cannot have a non-synced, missing default route. + * Either it has a default route (which is synced), or it has no entry. */ + synced = default_route && !never_default; + } } } + g_assert (!default_route || default_route->plen == 0); + if (!synced && never_default) { + /* having a non-synced, never-default entry is non-sensical. Unset + * @default_route so that we don't add such an entry below. */ + default_route = NULL; + } + if (!entry && !default_route) /* nothing to do */; else if (!entry) { @@ -830,48 +870,60 @@ nm_default_route_manager_ip6_update_default_route (NMDefaultRouteManager *self, /***********************************************************************************/ static gboolean -_ipx_connection_has_default_route (const VTableIP *vtable, NMDefaultRouteManager *self, NMConnection *connection) +_ipx_connection_has_default_route (const VTableIP *vtable, NMDefaultRouteManager *self, NMConnection *connection, gboolean *out_is_never_default) { const char *method; NMSettingIPConfig *s_ip; + gboolean is_never_default = FALSE; + gboolean has_default_route = FALSE; g_return_val_if_fail (NM_IS_DEFAULT_ROUTE_MANAGER (self), FALSE); - g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); + + if (!connection) + goto out; if (vtable->vt->is_ip4) s_ip = nm_connection_get_setting_ip4_config (connection); else s_ip = nm_connection_get_setting_ip6_config (connection); - if (!s_ip || nm_setting_ip_config_get_never_default (s_ip)) - return FALSE; + if (!s_ip) + goto out; + if (nm_setting_ip_config_get_never_default (s_ip)) { + is_never_default = TRUE; + goto out; + } if (vtable->vt->is_ip4) { method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); if ( !method || !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) || !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) - return FALSE; + goto out; } else { method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); if ( !method || !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) || !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) - return FALSE; + goto out; } - return TRUE; + has_default_route = TRUE; +out: + if (out_is_never_default) + *out_is_never_default = is_never_default; + return has_default_route; } gboolean -nm_default_route_manager_ip4_connection_has_default_route (NMDefaultRouteManager *self, NMConnection *connection) +nm_default_route_manager_ip4_connection_has_default_route (NMDefaultRouteManager *self, NMConnection *connection, gboolean *out_is_never_default) { - return _ipx_connection_has_default_route (&vtable_ip4, self, connection); + return _ipx_connection_has_default_route (&vtable_ip4, self, connection, out_is_never_default); } gboolean -nm_default_route_manager_ip6_connection_has_default_route (NMDefaultRouteManager *self, NMConnection *connection) +nm_default_route_manager_ip6_connection_has_default_route (NMDefaultRouteManager *self, NMConnection *connection, gboolean *out_is_never_default) { - return _ipx_connection_has_default_route (&vtable_ip6, self, connection); + return _ipx_connection_has_default_route (&vtable_ip6, self, connection, out_is_never_default); } /***********************************************************************************/ @@ -970,7 +1022,7 @@ _ipx_get_best_activating_device (const VTableIP *vtable, NMDefaultRouteManager * || state >= NM_DEVICE_STATE_DEACTIVATING) continue; - if (!_ipx_connection_has_default_route (vtable, self, nm_device_get_connection (device))) + if (!_ipx_connection_has_default_route (vtable, self, nm_device_get_connection (device), NULL)) continue; prio = nm_device_get_ip4_route_metric (device); @@ -1171,18 +1223,6 @@ static const VTableIP vtable_ip6 = { /***********************************************************************************/ -NMDefaultRouteManager * -nm_default_route_manager_get () -{ - if (G_UNLIKELY (!singleton_instance)) { - singleton_instance = NM_DEFAULT_ROUTE_MANAGER (g_object_new (NM_TYPE_DEFAULT_ROUTE_MANAGER, NULL)); - g_object_add_weak_pointer (G_OBJECT (singleton_instance), (gpointer *) &singleton_instance); - } - return singleton_instance; -} - -/***********************************************************************************/ - static gboolean _resync_idle_now (NMDefaultRouteManager *self) { @@ -1239,8 +1279,8 @@ _resync_idle_reschedule (NMDefaultRouteManager *self) { NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self); - /* since we react on external changes and readd/remove default routes for - * the interfaces we manage, there could be the erronous situation where two applications + /* since we react on external changes and re-add/remove default routes for + * the interfaces we manage, there could be the erroneous situation where two applications * fight over a certain default route. * Avoid this, by increasingly wait longer to touch the system (backoff wait time). */ @@ -1286,47 +1326,30 @@ _platform_ipx_route_changed_cb (const VTableIP *vtable, } static void -_platform_ip4_address_changed_cb (NMPlatform *platform, - int ifindex, - gpointer platform_object, - NMPlatformSignalChangeType change_type, - NMPlatformReason reason, - NMDefaultRouteManager *self) -{ - _platform_ipx_route_changed_cb (&vtable_ip4, self, NULL); -} - -static void -_platform_ip6_address_changed_cb (NMPlatform *platform, - int ifindex, - gpointer platform_object, - NMPlatformSignalChangeType change_type, - NMPlatformReason reason, - NMDefaultRouteManager *self) -{ - _platform_ipx_route_changed_cb (&vtable_ip6, self, NULL); -} - -static void -_platform_ip4_route_changed_cb (NMPlatform *platform, - int ifindex, - gpointer platform_object, - NMPlatformSignalChangeType change_type, - NMPlatformReason reason, - NMDefaultRouteManager *self) -{ - _platform_ipx_route_changed_cb (&vtable_ip4, self, platform_object); -} - -static void -_platform_ip6_route_changed_cb (NMPlatform *platform, - int ifindex, - gpointer platform_object, - NMPlatformSignalChangeType change_type, - NMPlatformReason reason, - NMDefaultRouteManager *self) +_platform_changed_cb (NMPlatform *platform, + NMPObjectType obj_type, + int ifindex, + gpointer platform_object, + NMPlatformSignalChangeType change_type, + NMPlatformReason reason, + NMDefaultRouteManager *self) { - _platform_ipx_route_changed_cb (&vtable_ip6, self, platform_object); + switch (obj_type) { + case NMP_OBJECT_TYPE_IP4_ADDRESS: + _platform_ipx_route_changed_cb (&vtable_ip4, self, NULL); + break; + case NMP_OBJECT_TYPE_IP6_ADDRESS: + _platform_ipx_route_changed_cb (&vtable_ip6, self, NULL); + break; + case NMP_OBJECT_TYPE_IP4_ROUTE: + _platform_ipx_route_changed_cb (&vtable_ip4, self, (const NMPlatformIPRoute *) platform_object); + break; + case NMP_OBJECT_TYPE_IP6_ROUTE: + _platform_ipx_route_changed_cb (&vtable_ip6, self, (const NMPlatformIPRoute *) platform_object); + break; + default: + g_return_if_reached (); + } } /***********************************************************************************/ @@ -1340,10 +1363,10 @@ nm_default_route_manager_init (NMDefaultRouteManager *self) priv->entries_ip6 = g_ptr_array_new_full (0, (GDestroyNotify) _entry_free); priv->platform = g_object_ref (nm_platform_get ()); - g_signal_connect (priv->platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, G_CALLBACK (_platform_ip4_address_changed_cb), self); - g_signal_connect (priv->platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, G_CALLBACK (_platform_ip6_address_changed_cb), self); - g_signal_connect (priv->platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (_platform_ip4_route_changed_cb), self); - g_signal_connect (priv->platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (_platform_ip6_route_changed_cb), self); + g_signal_connect (priv->platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, G_CALLBACK (_platform_changed_cb), self); + g_signal_connect (priv->platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, G_CALLBACK (_platform_changed_cb), self); + g_signal_connect (priv->platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (_platform_changed_cb), self); + g_signal_connect (priv->platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (_platform_changed_cb), self); } static void @@ -1355,7 +1378,7 @@ dispose (GObject *object) priv->disposed = TRUE; if (priv->platform) { - g_signal_handlers_disconnect_by_data (priv->platform, self); + g_signal_handlers_disconnect_by_func (priv->platform, G_CALLBACK (_platform_changed_cb), self); g_clear_object (&priv->platform); } diff --git a/src/nm-default-route-manager.h b/src/nm-default-route-manager.h index d8e42273..7fc27bbd 100644 --- a/src/nm-default-route-manager.h +++ b/src/nm-default-route-manager.h @@ -51,8 +51,8 @@ NMDefaultRouteManager *nm_default_route_manager_get (void); void nm_default_route_manager_ip4_update_default_route (NMDefaultRouteManager *manager, gpointer source); void nm_default_route_manager_ip6_update_default_route (NMDefaultRouteManager *manager, gpointer source); -gboolean nm_default_route_manager_ip4_connection_has_default_route (NMDefaultRouteManager *manager, NMConnection *connection); -gboolean nm_default_route_manager_ip6_connection_has_default_route (NMDefaultRouteManager *manager, NMConnection *connection); +gboolean nm_default_route_manager_ip4_connection_has_default_route (NMDefaultRouteManager *manager, NMConnection *connection, gboolean *out_is_never_default); +gboolean nm_default_route_manager_ip6_connection_has_default_route (NMDefaultRouteManager *manager, NMConnection *connection, gboolean *out_is_never_default); NMDevice *nm_default_route_manager_ip4_get_best_device (NMDefaultRouteManager *manager, const GSList *devices, gboolean fully_activated, NMDevice *preferred_device); NMDevice *nm_default_route_manager_ip6_get_best_device (NMDefaultRouteManager *manager, const GSList *devices, gboolean fully_activated, NMDevice *preferred_device); diff --git a/src/nm-dispatcher.c b/src/nm-dispatcher.c index 4e200266..13352ded 100644 --- a/src/nm-dispatcher.c +++ b/src/nm-dispatcher.c @@ -372,7 +372,7 @@ static const char *action_table[] = { static const char * action_to_string (DispatcherAction action) { - g_assert (action >= 0 && action < G_N_ELEMENTS (action_table)); + g_assert ((gsize) action < G_N_ELEMENTS (action_table)); return action_table[action]; } diff --git a/src/nm-enum-types.c b/src/nm-enum-types.c index c3f15f38..f92158d3 100644 --- a/src/nm-enum-types.c +++ b/src/nm-enum-types.c @@ -39,6 +39,8 @@ #include "nm-fake-platform.h" #include "nm-linux-platform.h" #include "nm-platform.h" +#include "nm-platform-utils.h" +#include "nmp-object.h" #include "wifi-utils-nl80211.h" #include "wifi-utils-private.h" #include "wifi-utils.h" @@ -65,17 +67,18 @@ #include "nm-supplicant-manager.h" #include "nm-supplicant-settings-verify.h" #include "nm-supplicant-types.h" -#include "nm-call-store.h" #include "nm-vpn-connection.h" #include "nm-vpn-manager.h" #include "nm-vpn-service.h" #include "nm-activation-request.h" #include "nm-active-connection.h" #include "nm-config.h" +#include "nm-config-data.h" #include "nm-connection-provider.h" #include "nm-connectivity.h" #include "nm-dbus-manager.h" #include "nm-dcb.h" +#include "nm-route-manager.h" #include "nm-default-route-manager.h" #include "nm-dhcp4-config.h" #include "nm-dhcp6-config.h" @@ -88,8 +91,8 @@ #include "nm-auth-subject.h" #include "nm-auth-utils.h" #include "nm-manager.h" +#include "nm-multi-index.h" #include "nm-policy.h" -#include "nm-posix-signals.h" #include "nm-properties-changed-signal.h" #include "nm-rfkill-manager.h" #include "nm-session-monitor.h" @@ -133,6 +136,7 @@ nm_unmanaged_flags_get_type (void) { NM_UNMANAGED_USER, "NM_UNMANAGED_USER", "nm-unmanaged-user" }, { NM_UNMANAGED_PARENT, "NM_UNMANAGED_PARENT", "nm-unmanaged-parent" }, { NM_UNMANAGED_EXTERNAL_DOWN, "NM_UNMANAGED_EXTERNAL_DOWN", "nm-unmanaged-external-down" }, + { NM_UNMANAGED_PLATFORM_INIT, "NM_UNMANAGED_PLATFORM_INIT", "nm-unmanaged-platform-init" }, { __NM_UNMANAGED_LAST, "__NM_UNMANAGED_LAST", "--nm-unmanaged-last" }, { NM_UNMANAGED_LAST, "NM_UNMANAGED_LAST", "nm-unmanaged-last" }, { NM_UNMANAGED_ALL, "NM_UNMANAGED_ALL", "nm-unmanaged-all" }, @@ -231,29 +235,6 @@ nm_dns_masq_status_get_type (void) return g_define_type_id__volatile; } GType -nm_platform_error_get_type (void) -{ - static volatile gsize g_define_type_id__volatile = 0; - - if (g_once_init_enter (&g_define_type_id__volatile)) - { - static const GEnumValue values[] = { - { NM_PLATFORM_ERROR_NONE, "NM_PLATFORM_ERROR_NONE", "none" }, - { NM_PLATFORM_ERROR_NOT_FOUND, "NM_PLATFORM_ERROR_NOT_FOUND", "not-found" }, - { NM_PLATFORM_ERROR_EXISTS, "NM_PLATFORM_ERROR_EXISTS", "exists" }, - { NM_PLATFORM_ERROR_WRONG_TYPE, "NM_PLATFORM_ERROR_WRONG_TYPE", "wrong-type" }, - { NM_PLATFORM_ERROR_NOT_SLAVE, "NM_PLATFORM_ERROR_NOT_SLAVE", "not-slave" }, - { NM_PLATFORM_ERROR_NO_FIRMWARE, "NM_PLATFORM_ERROR_NO_FIRMWARE", "no-firmware" }, - { 0, NULL, NULL } - }; - GType g_define_type_id = - g_enum_register_static (g_intern_static_string ("NMPlatformError"), values); - g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); - } - - return g_define_type_id__volatile; -} -GType nm_platform_reason_get_type (void) { static volatile gsize g_define_type_id__volatile = 0; @@ -283,6 +264,7 @@ nm_platform_signal_change_type_get_type (void) if (g_once_init_enter (&g_define_type_id__volatile)) { static const GEnumValue values[] = { + { NM_PLATFORM_SIGNAL_NONE, "NM_PLATFORM_SIGNAL_NONE", "none" }, { NM_PLATFORM_SIGNAL_ADDED, "NM_PLATFORM_SIGNAL_ADDED", "added" }, { NM_PLATFORM_SIGNAL_CHANGED, "NM_PLATFORM_SIGNAL_CHANGED", "changed" }, { NM_PLATFORM_SIGNAL_REMOVED, "NM_PLATFORM_SIGNAL_REMOVED", "removed" }, @@ -296,26 +278,6 @@ nm_platform_signal_change_type_get_type (void) return g_define_type_id__volatile; } GType -nm_platform_get_route_mode_get_type (void) -{ - static volatile gsize g_define_type_id__volatile = 0; - - if (g_once_init_enter (&g_define_type_id__volatile)) - { - static const GEnumValue values[] = { - { NM_PLATFORM_GET_ROUTE_MODE_ALL, "NM_PLATFORM_GET_ROUTE_MODE_ALL", "all" }, - { NM_PLATFORM_GET_ROUTE_MODE_NO_DEFAULT, "NM_PLATFORM_GET_ROUTE_MODE_NO_DEFAULT", "no-default" }, - { NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT, "NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT", "only-default" }, - { 0, NULL, NULL } - }; - GType g_define_type_id = - g_enum_register_static (g_intern_static_string ("NMPlatformGetRouteMode"), values); - g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); - } - - return g_define_type_id__volatile; -} -GType nm_rdisc_dhcp_level_get_type (void) { static volatile gsize g_define_type_id__volatile = 0; @@ -522,6 +484,56 @@ nm_opt_type_get_type (void) return g_define_type_id__volatile; } GType +nm_config_get_value_flags_get_type (void) +{ + static volatile gsize g_define_type_id__volatile = 0; + + if (g_once_init_enter (&g_define_type_id__volatile)) + { + static const GFlagsValue values[] = { + { NM_CONFIG_GET_VALUE_NONE, "NM_CONFIG_GET_VALUE_NONE", "none" }, + { NM_CONFIG_GET_VALUE_RAW, "NM_CONFIG_GET_VALUE_RAW", "raw" }, + { NM_CONFIG_GET_VALUE_STRIP, "NM_CONFIG_GET_VALUE_STRIP", "strip" }, + { NM_CONFIG_GET_VALUE_NO_EMPTY, "NM_CONFIG_GET_VALUE_NO_EMPTY", "no-empty" }, + { NM_CONFIG_GET_VALUE_TYPE_SPEC, "NM_CONFIG_GET_VALUE_TYPE_SPEC", "type-spec" }, + { 0, NULL, NULL } + }; + GType g_define_type_id = + g_flags_register_static (g_intern_static_string ("NMConfigGetValueFlags"), values); + g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); + } + + return g_define_type_id__volatile; +} +GType +nm_config_change_flags_get_type (void) +{ + static volatile gsize g_define_type_id__volatile = 0; + + if (g_once_init_enter (&g_define_type_id__volatile)) + { + static const GFlagsValue values[] = { + { NM_CONFIG_CHANGE_NONE, "NM_CONFIG_CHANGE_NONE", "nm-config-change-none" }, + { NM_CONFIG_CHANGE_SIGHUP, "NM_CONFIG_CHANGE_SIGHUP", "nm-config-change-sighup" }, + { NM_CONFIG_CHANGE_SIGUSR1, "NM_CONFIG_CHANGE_SIGUSR1", "nm-config-change-sigusr1" }, + { NM_CONFIG_CHANGE_SIGUSR2, "NM_CONFIG_CHANGE_SIGUSR2", "nm-config-change-sigusr2" }, + { NM_CONFIG_CHANGE_CONFIG_FILES, "NM_CONFIG_CHANGE_CONFIG_FILES", "nm-config-change-config-files" }, + { NM_CONFIG_CHANGE_VALUES, "NM_CONFIG_CHANGE_VALUES", "nm-config-change-values" }, + { NM_CONFIG_CHANGE_CONNECTIVITY, "NM_CONFIG_CHANGE_CONNECTIVITY", "nm-config-change-connectivity" }, + { NM_CONFIG_CHANGE_NO_AUTO_DEFAULT, "NM_CONFIG_CHANGE_NO_AUTO_DEFAULT", "nm-config-change-no-auto-default" }, + { NM_CONFIG_CHANGE_DNS_MODE, "NM_CONFIG_CHANGE_DNS_MODE", "nm-config-change-dns-mode" }, + { _NM_CONFIG_CHANGE_LAST, "_NM_CONFIG_CHANGE_LAST", "-nm-config-change-last" }, + { NM_CONFIG_CHANGE_ALL, "NM_CONFIG_CHANGE_ALL", "nm-config-change-all" }, + { 0, NULL, NULL } + }; + GType g_define_type_id = + g_flags_register_static (g_intern_static_string ("NMConfigChangeFlags"), values); + g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); + } + + return g_define_type_id__volatile; +} +GType nm_dispatcher_action_get_type (void) { static volatile gsize g_define_type_id__volatile = 0; @@ -640,16 +652,18 @@ nm_ip_config_source_get_type (void) if (g_once_init_enter (&g_define_type_id__volatile)) { static const GEnumValue values[] = { - { NM_IP_CONFIG_SOURCE_UNKNOWN, "NM_IP_CONFIG_SOURCE_UNKNOWN", "unknown" }, - { NM_IP_CONFIG_SOURCE_KERNEL, "NM_IP_CONFIG_SOURCE_KERNEL", "kernel" }, - { NM_IP_CONFIG_SOURCE_SHARED, "NM_IP_CONFIG_SOURCE_SHARED", "shared" }, - { NM_IP_CONFIG_SOURCE_IP4LL, "NM_IP_CONFIG_SOURCE_IP4LL", "ip4ll" }, - { NM_IP_CONFIG_SOURCE_PPP, "NM_IP_CONFIG_SOURCE_PPP", "ppp" }, - { NM_IP_CONFIG_SOURCE_WWAN, "NM_IP_CONFIG_SOURCE_WWAN", "wwan" }, - { NM_IP_CONFIG_SOURCE_VPN, "NM_IP_CONFIG_SOURCE_VPN", "vpn" }, - { NM_IP_CONFIG_SOURCE_DHCP, "NM_IP_CONFIG_SOURCE_DHCP", "dhcp" }, - { NM_IP_CONFIG_SOURCE_RDISC, "NM_IP_CONFIG_SOURCE_RDISC", "rdisc" }, - { NM_IP_CONFIG_SOURCE_USER, "NM_IP_CONFIG_SOURCE_USER", "user" }, + { NM_IP_CONFIG_SOURCE_UNKNOWN, "NM_IP_CONFIG_SOURCE_UNKNOWN", "nm-ip-config-source-unknown" }, + { _NM_IP_CONFIG_SOURCE_RTM_F_CLONED, "_NM_IP_CONFIG_SOURCE_RTM_F_CLONED", "-nm-ip-config-source-rtm-f-cloned" }, + { NM_IP_CONFIG_SOURCE_RTPROT_KERNEL, "NM_IP_CONFIG_SOURCE_RTPROT_KERNEL", "nm-ip-config-source-rtprot-kernel" }, + { NM_IP_CONFIG_SOURCE_KERNEL, "NM_IP_CONFIG_SOURCE_KERNEL", "nm-ip-config-source-kernel" }, + { NM_IP_CONFIG_SOURCE_SHARED, "NM_IP_CONFIG_SOURCE_SHARED", "nm-ip-config-source-shared" }, + { NM_IP_CONFIG_SOURCE_IP4LL, "NM_IP_CONFIG_SOURCE_IP4LL", "nm-ip-config-source-ip4ll" }, + { NM_IP_CONFIG_SOURCE_PPP, "NM_IP_CONFIG_SOURCE_PPP", "nm-ip-config-source-ppp" }, + { NM_IP_CONFIG_SOURCE_WWAN, "NM_IP_CONFIG_SOURCE_WWAN", "nm-ip-config-source-wwan" }, + { NM_IP_CONFIG_SOURCE_VPN, "NM_IP_CONFIG_SOURCE_VPN", "nm-ip-config-source-vpn" }, + { NM_IP_CONFIG_SOURCE_DHCP, "NM_IP_CONFIG_SOURCE_DHCP", "nm-ip-config-source-dhcp" }, + { NM_IP_CONFIG_SOURCE_RDISC, "NM_IP_CONFIG_SOURCE_RDISC", "nm-ip-config-source-rdisc" }, + { NM_IP_CONFIG_SOURCE_USER, "NM_IP_CONFIG_SOURCE_USER", "nm-ip-config-source-user" }, { 0, NULL, NULL } }; GType g_define_type_id = @@ -688,6 +702,7 @@ nm_link_type_get_type (void) { NM_LINK_TYPE_VETH, "NM_LINK_TYPE_VETH", "veth" }, { NM_LINK_TYPE_VLAN, "NM_LINK_TYPE_VLAN", "vlan" }, { NM_LINK_TYPE_VXLAN, "NM_LINK_TYPE_VXLAN", "vxlan" }, + { NM_LINK_TYPE_BNEP, "NM_LINK_TYPE_BNEP", "bnep" }, { NM_LINK_TYPE_BRIDGE, "NM_LINK_TYPE_BRIDGE", "bridge" }, { NM_LINK_TYPE_BOND, "NM_LINK_TYPE_BOND", "bond" }, { NM_LINK_TYPE_TEAM, "NM_LINK_TYPE_TEAM", "team" }, @@ -700,6 +715,74 @@ nm_link_type_get_type (void) return g_define_type_id__volatile; } +GType +nm_pobject_type_get_type (void) +{ + static volatile gsize g_define_type_id__volatile = 0; + + if (g_once_init_enter (&g_define_type_id__volatile)) + { + static const GEnumValue values[] = { + { NMP_OBJECT_TYPE_UNKNOWN, "NMP_OBJECT_TYPE_UNKNOWN", "nmp-object-type-unknown" }, + { NMP_OBJECT_TYPE_LINK, "NMP_OBJECT_TYPE_LINK", "nmp-object-type-link" }, + { NMP_OBJECT_TYPE_IP4_ADDRESS, "NMP_OBJECT_TYPE_IP4_ADDRESS", "nmp-object-type-ip4-address" }, + { NMP_OBJECT_TYPE_IP6_ADDRESS, "NMP_OBJECT_TYPE_IP6_ADDRESS", "nmp-object-type-ip6-address" }, + { NMP_OBJECT_TYPE_IP4_ROUTE, "NMP_OBJECT_TYPE_IP4_ROUTE", "nmp-object-type-ip4-route" }, + { NMP_OBJECT_TYPE_IP6_ROUTE, "NMP_OBJECT_TYPE_IP6_ROUTE", "nmp-object-type-ip6-route" }, + { __NMP_OBJECT_TYPE_LAST, "__NMP_OBJECT_TYPE_LAST", "--nmp-object-type-last" }, + { NMP_OBJECT_TYPE_MAX, "NMP_OBJECT_TYPE_MAX", "nmp-object-type-max" }, + { 0, NULL, NULL } + }; + GType g_define_type_id = + g_enum_register_static (g_intern_static_string ("NMPObjectType"), values); + g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); + } + + return g_define_type_id__volatile; +} +GType +nm_match_spec_match_type_get_type (void) +{ + static volatile gsize g_define_type_id__volatile = 0; + + if (g_once_init_enter (&g_define_type_id__volatile)) + { + static const GEnumValue values[] = { + { NM_MATCH_SPEC_NO_MATCH, "NM_MATCH_SPEC_NO_MATCH", "no-match" }, + { NM_MATCH_SPEC_MATCH, "NM_MATCH_SPEC_MATCH", "match" }, + { NM_MATCH_SPEC_NEG_MATCH, "NM_MATCH_SPEC_NEG_MATCH", "neg-match" }, + { 0, NULL, NULL } + }; + GType g_define_type_id = + g_enum_register_static (g_intern_static_string ("NMMatchSpecMatchType"), values); + g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); + } + + return g_define_type_id__volatile; +} +GType +nm_utils_test_flags_get_type (void) +{ + static volatile gsize g_define_type_id__volatile = 0; + + if (g_once_init_enter (&g_define_type_id__volatile)) + { + static const GFlagsValue values[] = { + { NM_UTILS_TEST_NONE, "NM_UTILS_TEST_NONE", "nm-utils-test-none" }, + { _NM_UTILS_TEST_INITIALIZED, "_NM_UTILS_TEST_INITIALIZED", "-nm-utils-test-initialized" }, + { _NM_UTILS_TEST_GENERAL, "_NM_UTILS_TEST_GENERAL", "-nm-utils-test-general" }, + { NM_UTILS_TEST_NO_KEYFILE_OWNER_CHECK, "NM_UTILS_TEST_NO_KEYFILE_OWNER_CHECK", "nm-utils-test-no-keyfile-owner-check" }, + { _NM_UTILS_TEST_LAST, "_NM_UTILS_TEST_LAST", "-nm-utils-test-last" }, + { NM_UTILS_TEST_ALL, "NM_UTILS_TEST_ALL", "nm-utils-test-all" }, + { 0, NULL, NULL } + }; + GType g_define_type_id = + g_flags_register_static (g_intern_static_string ("NMUtilsTestFlags"), values); + g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); + } + + return g_define_type_id__volatile; +} diff --git a/src/nm-enum-types.h b/src/nm-enum-types.h index 01d7bb44..86f17f04 100644 --- a/src/nm-enum-types.h +++ b/src/nm-enum-types.h @@ -22,14 +22,10 @@ GType nm_dns_manager_resolv_conf_mode_get_type (void) G_GNUC_CONST; #define NM_TYPE_DNS_MANAGER_RESOLV_CONF_MODE (nm_dns_manager_resolv_conf_mode_get_type ()) GType nm_dns_masq_status_get_type (void) G_GNUC_CONST; #define NM_TYPE_DNS_MASQ_STATUS (nm_dns_masq_status_get_type ()) -GType nm_platform_error_get_type (void) G_GNUC_CONST; -#define NM_TYPE_PLATFORM_ERROR (nm_platform_error_get_type ()) GType nm_platform_reason_get_type (void) G_GNUC_CONST; #define NM_TYPE_PLATFORM_REASON (nm_platform_reason_get_type ()) GType nm_platform_signal_change_type_get_type (void) G_GNUC_CONST; #define NM_TYPE_PLATFORM_SIGNAL_CHANGE_TYPE (nm_platform_signal_change_type_get_type ()) -GType nm_platform_get_route_mode_get_type (void) G_GNUC_CONST; -#define NM_TYPE_PLATFORM_GET_ROUTE_MODE (nm_platform_get_route_mode_get_type ()) GType nm_rdisc_dhcp_level_get_type (void) G_GNUC_CONST; #define NM_TYPE_RDISC_DHCP_LEVEL (nm_rdisc_dhcp_level_get_type ()) GType nm_rdisc_preference_get_type (void) G_GNUC_CONST; @@ -48,6 +44,10 @@ GType nm_ap_support_get_type (void) G_GNUC_CONST; #define NM_TYPE_AP_SUPPORT (nm_ap_support_get_type ()) GType nm_opt_type_get_type (void) G_GNUC_CONST; #define NM_TYPE_OPT_TYPE (nm_opt_type_get_type ()) +GType nm_config_get_value_flags_get_type (void) G_GNUC_CONST; +#define NM_TYPE_CONFIG_GET_VALUE_FLAGS (nm_config_get_value_flags_get_type ()) +GType nm_config_change_flags_get_type (void) G_GNUC_CONST; +#define NM_TYPE_CONFIG_CHANGE_FLAGS (nm_config_change_flags_get_type ()) GType nm_dispatcher_action_get_type (void) G_GNUC_CONST; #define NM_TYPE_DISPATCHER_ACTION (nm_dispatcher_action_get_type ()) GType nm_auth_subject_type_get_type (void) G_GNUC_CONST; @@ -62,6 +62,12 @@ GType nm_ip_config_source_get_type (void) G_GNUC_CONST; #define NM_TYPE_IP_CONFIG_SOURCE (nm_ip_config_source_get_type ()) GType nm_link_type_get_type (void) G_GNUC_CONST; #define NM_TYPE_LINK_TYPE (nm_link_type_get_type ()) +GType nm_pobject_type_get_type (void) G_GNUC_CONST; +#define NM_TYPE_POBJECT_TYPE (nm_pobject_type_get_type ()) +GType nm_match_spec_match_type_get_type (void) G_GNUC_CONST; +#define NM_TYPE_MATCH_SPEC_MATCH_TYPE (nm_match_spec_match_type_get_type ()) +GType nm_utils_test_flags_get_type (void) G_GNUC_CONST; +#define NM_TYPE_UTILS_TEST_FLAGS (nm_utils_test_flags_get_type ()) G_END_DECLS #endif /* __NM_ENUM_TYPES_H__ */ diff --git a/src/nm-iface-helper.c b/src/nm-iface-helper.c index 97e1708e..2192f36b 100644 --- a/src/nm-iface-helper.c +++ b/src/nm-iface-helper.c @@ -104,7 +104,7 @@ dhcp4_state_changed (NMDhcpClient *client, nm_ip4_config_subtract (existing, last_config); nm_ip4_config_merge (existing, ip4_config); - if (!nm_ip4_config_commit (existing, ifindex, global_opt.priority_v4)) + if (!nm_ip4_config_commit (existing, ifindex, TRUE, global_opt.priority_v4)) nm_log_warn (LOGD_DHCP4, "(%s): failed to apply DHCPv4 config", global_opt.ifname); if (last_config) @@ -145,7 +145,7 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, gpointer user_da * from adding a prefix route for this address. **/ system_support = nm_platform_check_support_libnl_extended_ifa_flags () && - nm_platform_check_support_kernel_extended_ifa_flags (); + nm_platform_check_support_kernel_extended_ifa_flags (NM_PLATFORM_GET); } if (system_support) @@ -227,13 +227,13 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, gpointer user_da } if (changed & NM_RDISC_CONFIG_HOP_LIMIT) - nm_platform_sysctl_set_ip6_hop_limit_safe (global_opt.ifname, rdisc->hop_limit); + nm_platform_sysctl_set_ip6_hop_limit_safe (NM_PLATFORM_GET, global_opt.ifname, rdisc->hop_limit); if (changed & NM_RDISC_CONFIG_MTU) { char val[16]; g_snprintf (val, sizeof (val), "%d", rdisc->mtu); - nm_platform_sysctl_set (nm_utils_ip6_property_path (global_opt.ifname, "mtu"), val); + nm_platform_sysctl_set (NM_PLATFORM_GET, nm_utils_ip6_property_path (global_opt.ifname, "mtu"), val); } existing = nm_ip6_config_capture (ifindex, FALSE, global_opt.tempaddr); @@ -241,7 +241,7 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, gpointer user_da nm_ip6_config_subtract (existing, last_config); nm_ip6_config_merge (existing, ip6_config); - if (!nm_ip6_config_commit (existing, ifindex)) + if (!nm_ip6_config_commit (existing, ifindex, TRUE)) nm_log_warn (LOGD_IP6, "(%s): failed to apply IPv6 config", global_opt.ifname); if (last_config) @@ -263,24 +263,16 @@ rdisc_ra_timeout (NMRDisc *rdisc, gpointer user_data) static gboolean quit_handler (gpointer user_data) { - gboolean *quit_early_ptr = user_data; - - *quit_early_ptr = TRUE; g_main_loop_quit (main_loop); - return G_SOURCE_CONTINUE; + return G_SOURCE_REMOVE; } static void -setup_signals (gboolean *quit_early_ptr) +setup_signals (void) { - sigset_t sigmask; - - sigemptyset (&sigmask); - pthread_sigmask (SIG_SETMASK, &sigmask, NULL); - signal (SIGPIPE, SIG_IGN); - g_unix_signal_add (SIGINT, quit_handler, quit_early_ptr); - g_unix_signal_add (SIGTERM, quit_handler, quit_early_ptr); + g_unix_signal_add (SIGINT, quit_handler, NULL); + g_unix_signal_add (SIGTERM, quit_handler, NULL); } static void @@ -320,6 +312,7 @@ do_early_setup (int *argc, char **argv[]) argv, options, NULL, + NULL, _("nm-iface-helper is a small, standalone process that manages a single network interface."))) exit (1); @@ -337,7 +330,6 @@ main (int argc, char *argv[]) GError *error = NULL; gboolean wrote_pidfile = FALSE; gs_free char *pidfile = NULL; - gboolean quit_early = FALSE; gs_unref_object NMDhcpClient *dhcp4_client = NULL; gs_unref_object NMRDisc *rdisc = NULL; GByteArray *hwaddr = NULL; @@ -414,7 +406,7 @@ main (int argc, char *argv[]) /* Set up unix signal handling - before creating threads, but after daemonizing! */ main_loop = g_main_loop_new (NULL, FALSE); - setup_signals (&quit_early); + setup_signals (); nm_logging_syslog_openlog (global_opt.debug); @@ -423,7 +415,7 @@ main (int argc, char *argv[]) /* Set up platform interaction layer */ nm_linux_platform_setup (); - tmp = nm_platform_link_get_address (ifindex, &hwaddr_len); + tmp = nm_platform_link_get_address (NM_PLATFORM_GET, ifindex, &hwaddr_len); if (tmp) { hwaddr = g_byte_array_sized_new (hwaddr_len); g_byte_array_append (hwaddr, tmp, hwaddr_len); @@ -442,7 +434,7 @@ main (int argc, char *argv[]) } if (global_opt.dhcp4_address) { - nm_platform_sysctl_set (nm_utils_ip4_property_path (global_opt.ifname, "promote_secondaries"), "1"); + nm_platform_sysctl_set (NM_PLATFORM_GET, nm_utils_ip4_property_path (global_opt.ifname, "promote_secondaries"), "1"); /* Initialize DHCP manager */ dhcp_mgr = nm_dhcp_manager_get (); @@ -468,7 +460,7 @@ main (int argc, char *argv[]) } if (global_opt.slaac) { - nm_platform_link_set_user_ipv6ll_enabled (ifindex, TRUE); + nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, ifindex, TRUE); rdisc = nm_lndp_rdisc_new (ifindex, global_opt.ifname); g_assert (rdisc); @@ -476,10 +468,10 @@ main (int argc, char *argv[]) if (iid) nm_rdisc_set_iid (rdisc, *iid); - nm_platform_sysctl_set (nm_utils_ip6_property_path (global_opt.ifname, "accept_ra"), "1"); - nm_platform_sysctl_set (nm_utils_ip6_property_path (global_opt.ifname, "accept_ra_defrtr"), "0"); - nm_platform_sysctl_set (nm_utils_ip6_property_path (global_opt.ifname, "accept_ra_pinfo"), "0"); - nm_platform_sysctl_set (nm_utils_ip6_property_path (global_opt.ifname, "accept_ra_rtr_pref"), "0"); + nm_platform_sysctl_set (NM_PLATFORM_GET, nm_utils_ip6_property_path (global_opt.ifname, "accept_ra"), "1"); + nm_platform_sysctl_set (NM_PLATFORM_GET, nm_utils_ip6_property_path (global_opt.ifname, "accept_ra_defrtr"), "0"); + nm_platform_sysctl_set (NM_PLATFORM_GET, nm_utils_ip6_property_path (global_opt.ifname, "accept_ra_pinfo"), "0"); + nm_platform_sysctl_set (NM_PLATFORM_GET, nm_utils_ip6_property_path (global_opt.ifname, "accept_ra_rtr_pref"), "0"); g_signal_connect (rdisc, NM_RDISC_CONFIG_CHANGED, @@ -492,8 +484,7 @@ main (int argc, char *argv[]) nm_rdisc_start (rdisc); } - if (!quit_early) - g_main_loop_run (main_loop); + g_main_loop_run (main_loop); g_clear_pointer (&hwaddr, g_byte_array_unref); @@ -509,6 +500,12 @@ main (int argc, char *argv[]) /*******************************************************/ /* Stub functions */ +void +nm_main_config_reload (int signal) +{ + nm_log_info (LOGD_CORE, "reloading configuration not supported"); +} + gconstpointer nm_config_get (void); const char *nm_config_get_dhcp_client (gpointer unused); gboolean nm_config_get_configure_and_quit (gpointer unused); diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c index db2ef158..db8c4b43 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -33,6 +33,8 @@ #include "nm-ip4-config-glue.h" #include "NetworkManagerUtils.h" #include "nm-core-internal.h" +#include "nm-route-manager.h" +#include "gsystem-local-alloc.h" G_DEFINE_TYPE (NMIP4Config, nm_ip4_config, G_TYPE_OBJECT) @@ -186,7 +188,7 @@ nm_ip4_config_capture (int ifindex, gboolean capture_resolv_conf) gboolean has_gateway = FALSE; /* Slaves have no IP configuration */ - if (nm_platform_link_get_master (ifindex) > 0) + if (nm_platform_link_get_master (NM_PLATFORM_GET, ifindex) > 0) return NULL; config = nm_ip4_config_new (); @@ -195,12 +197,12 @@ nm_ip4_config_capture (int ifindex, gboolean capture_resolv_conf) g_array_unref (priv->addresses); g_array_unref (priv->routes); - priv->addresses = nm_platform_ip4_address_get_all (ifindex); - priv->routes = nm_platform_ip4_route_get_all (ifindex, NM_PLATFORM_GET_ROUTE_MODE_ALL); + priv->addresses = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); + priv->routes = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); /* Extract gateway from default route */ old_gateway = priv->gateway; - for (i = 0; i < priv->routes->len; i++) { + for (i = 0; i < priv->routes->len; ) { const NMPlatformIP4Route *route = &g_array_index (priv->routes, NMPlatformIP4Route, i); if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) { @@ -210,9 +212,10 @@ nm_ip4_config_capture (int ifindex, gboolean capture_resolv_conf) } has_gateway = TRUE; /* Remove the default route from the list */ - g_array_remove_index (priv->routes, i); - i--; + g_array_remove_index_fast (priv->routes, i); + continue; } + i++; } /* If there is a host route to the gateway, ignore that route. It is @@ -251,26 +254,59 @@ nm_ip4_config_capture (int ifindex, gboolean capture_resolv_conf) } gboolean -nm_ip4_config_commit (const NMIP4Config *config, int ifindex, guint32 default_route_metric) +nm_ip4_config_commit (const NMIP4Config *config, int ifindex, gboolean routes_full_sync, gint64 default_route_metric) { NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); - guint32 mtu = nm_ip4_config_get_mtu (config); int i; + gs_unref_ptrarray GPtrArray *added_addresses = NULL; g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (config != NULL, FALSE); /* Addresses */ - nm_platform_ip4_address_sync (ifindex, priv->addresses, default_route_metric); + nm_platform_ip4_address_sync (NM_PLATFORM_GET, ifindex, priv->addresses, + default_route_metric >= 0 ? &added_addresses : NULL); /* Routes */ { int count = nm_ip4_config_get_num_routes (config); GArray *routes = g_array_sized_new (FALSE, FALSE, sizeof (NMPlatformIP4Route), count); - const NMPlatformIP4Route *route; gboolean success; + gs_unref_array GArray *device_route_purge_list = NULL; + + if ( default_route_metric >= 0 + && added_addresses) { + /* For IPv6, we explicitly add the device-routes (onlink) to NMIP6Config. + * As we don't do that for IPv4, add it here shortly before syncing + * the routes. For NMRouteManager these routes are very much important. */ + for (i = 0; i < added_addresses->len; i++) { + const NMPlatformIP4Address *addr = added_addresses->pdata[i]; + NMPlatformIP4Route route = { 0 }; + + if (addr->plen == 0) + continue; + + route.ifindex = ifindex; + route.source = NM_IP_CONFIG_SOURCE_KERNEL; + route.network = nm_utils_ip4_address_clear_host_address (addr->address, addr->plen); + route.plen = addr->plen; + route.pref_src = addr->address; + route.metric = default_route_metric; + + g_array_append_val (routes, route); + + if (default_route_metric != NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE) { + if (!device_route_purge_list) + device_route_purge_list = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route)); + route.metric = NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE; + g_array_append_val (device_route_purge_list, route); + } + } + } for (i = 0; i < count; i++) { + const NMPlatformIP4Route *route; + route = nm_ip4_config_get_route (config, i); /* Don't add the route if it's more specific than one of the subnets @@ -280,19 +316,19 @@ nm_ip4_config_commit (const NMIP4Config *config, int ifindex, guint32 default_ro && nm_ip4_config_destination_is_direct (config, route->network, route->plen)) continue; + /* duplicates in @routes are no problem as route-manager handles them + * gracefully (by ignoring them). */ g_array_append_vals (routes, route, 1); } - success = nm_platform_ip4_route_sync (ifindex, routes); + nm_route_manager_ip4_route_register_device_route_purge_list (nm_route_manager_get (), device_route_purge_list); + + success = nm_route_manager_ip4_route_sync (nm_route_manager_get (), ifindex, routes, default_route_metric < 0, routes_full_sync); g_array_unref (routes); if (!success) return FALSE; } - /* MTU */ - if (mtu && mtu != nm_platform_link_get_mtu (ifindex)) - nm_platform_link_set_mtu (ifindex, mtu); - return TRUE; } @@ -503,7 +539,7 @@ nm_ip4_config_merge (NMIP4Config *dst, const NMIP4Config *src) nm_ip4_config_add_nameserver (dst, nm_ip4_config_get_nameserver (src, i)); /* default gateway */ - if (!nm_ip4_config_get_gateway (dst)) + if (nm_ip4_config_get_gateway (src)) nm_ip4_config_set_gateway (dst, nm_ip4_config_get_gateway (src)); /* routes */ @@ -519,11 +555,11 @@ nm_ip4_config_merge (NMIP4Config *dst, const NMIP4Config *src) nm_ip4_config_add_search (dst, nm_ip4_config_get_search (src, i)); /* MSS */ - if (!nm_ip4_config_get_mss (dst)) + if (nm_ip4_config_get_mss (src)) nm_ip4_config_set_mss (dst, nm_ip4_config_get_mss (src)); /* MTU */ - if (!nm_ip4_config_get_mtu (dst)) + if (nm_ip4_config_get_mtu (src)) nm_ip4_config_set_mtu (dst, nm_ip4_config_get_mtu (src), nm_ip4_config_get_mtu_source (src)); @@ -541,6 +577,117 @@ nm_ip4_config_merge (NMIP4Config *dst, const NMIP4Config *src) g_object_thaw_notify (G_OBJECT (dst)); } +/*******************************************************************************/ + +static int +_addresses_get_index (const NMIP4Config *self, const NMPlatformIP4Address *addr) +{ + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); + guint i; + + for (i = 0; i < priv->addresses->len; i++) { + const NMPlatformIP4Address *a = &g_array_index (priv->addresses, NMPlatformIP4Address, i); + + if (addr->address == a->address && + addr->plen == a->plen) + return (int) i; + } + return -1; +} + +static int +_nameservers_get_index (const NMIP4Config *self, guint32 ns) +{ + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); + guint i; + + for (i = 0; i < priv->nameservers->len; i++) { + guint32 n = g_array_index (priv->nameservers, guint32, i); + + if (ns == n) + return (int) i; + } + return -1; +} + +static int +_routes_get_index (const NMIP4Config *self, const NMPlatformIP4Route *route) +{ + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); + guint i; + + for (i = 0; i < priv->routes->len; i++) { + const NMPlatformIP4Route *r = &g_array_index (priv->routes, NMPlatformIP4Route, i); + + if ( route->network == r->network + && route->plen == r->plen) + return (int) i; + } + return -1; +} + +static int +_domains_get_index (const NMIP4Config *self, const char *domain) +{ + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); + guint i; + + for (i = 0; i < priv->domains->len; i++) { + const char *d = g_ptr_array_index (priv->domains, i); + + if (g_strcmp0 (domain, d) == 0) + return (int) i; + } + return -1; +} + +static int +_searches_get_index (const NMIP4Config *self, const char *search) +{ + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); + guint i; + + for (i = 0; i < priv->searches->len; i++) { + const char *s = g_ptr_array_index (priv->searches, i); + + if (g_strcmp0 (search, s) == 0) + return (int) i; + } + return -1; +} + +static int +_nis_servers_get_index (const NMIP4Config *self, guint32 nis_server) +{ + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); + guint i; + + for (i = 0; i < priv->nis->len; i++) { + guint32 n = g_array_index (priv->nis, guint32, i); + + if (n == nis_server) + return (int) i; + } + return -1; +} + +static int +_wins_get_index (const NMIP4Config *self, guint32 wins_server) +{ + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self); + guint i; + + for (i = 0; i < priv->wins->len; i++) { + guint32 n = g_array_index (priv->wins, guint32, i); + + if (n == wins_server) + return (int) i; + } + return -1; +} + +/*******************************************************************************/ + /** * nm_ip4_config_subtract: * @dst: config from which to remove everything in @src @@ -551,7 +698,8 @@ nm_ip4_config_merge (NMIP4Config *dst, const NMIP4Config *src) void nm_ip4_config_subtract (NMIP4Config *dst, const NMIP4Config *src) { - guint32 i, j; + guint32 i; + gint idx; g_return_if_fail (src != NULL); g_return_if_fail (dst != NULL); @@ -560,31 +708,16 @@ nm_ip4_config_subtract (NMIP4Config *dst, const NMIP4Config *src) /* addresses */ for (i = 0; i < nm_ip4_config_get_num_addresses (src); i++) { - const NMPlatformIP4Address *src_addr = nm_ip4_config_get_address (src, i); - - for (j = 0; j < nm_ip4_config_get_num_addresses (dst); j++) { - const NMPlatformIP4Address *dst_addr = nm_ip4_config_get_address (dst, j); - - if (src_addr->address == dst_addr->address && - src_addr->plen == dst_addr->plen) { - nm_ip4_config_del_address (dst, j); - break; - } - } + idx = _addresses_get_index (dst, nm_ip4_config_get_address (src, i)); + if (idx >= 0) + nm_ip4_config_del_address (dst, idx); } /* nameservers */ for (i = 0; i < nm_ip4_config_get_num_nameservers (src); i++) { - guint32 src_ns = nm_ip4_config_get_nameserver (src, i); - - for (j = 0; j < nm_ip4_config_get_num_nameservers (dst); j++) { - guint32 dst_ns = nm_ip4_config_get_nameserver (dst, j); - - if (dst_ns == src_ns) { - nm_ip4_config_del_nameserver (dst, j); - break; - } - } + idx = _nameservers_get_index (dst, nm_ip4_config_get_nameserver (src, i)); + if (idx >= 0) + nm_ip4_config_del_nameserver (dst, idx); } /* default gateway */ @@ -596,44 +729,23 @@ nm_ip4_config_subtract (NMIP4Config *dst, const NMIP4Config *src) /* routes */ for (i = 0; i < nm_ip4_config_get_num_routes (src); i++) { - const NMPlatformIP4Route *src_route = nm_ip4_config_get_route (src, i); - - for (j = 0; j < nm_ip4_config_get_num_routes (dst); j++) { - const NMPlatformIP4Route *dst_route = nm_ip4_config_get_route (dst, j); - - if (src_route->network == dst_route->network && src_route->plen == dst_route->plen) { - nm_ip4_config_del_route (dst, j); - break; - } - } + idx = _routes_get_index (dst, nm_ip4_config_get_route (src, i)); + if (idx >= 0) + nm_ip4_config_del_route (dst, idx); } /* domains */ for (i = 0; i < nm_ip4_config_get_num_domains (src); i++) { - const char *src_domain = nm_ip4_config_get_domain (src, i); - - for (j = 0; j < nm_ip4_config_get_num_domains (dst); j++) { - const char *dst_domain = nm_ip4_config_get_domain (dst, j); - - if (g_strcmp0 (src_domain, dst_domain) == 0) { - nm_ip4_config_del_domain (dst, j); - break; - } - } + idx = _domains_get_index (dst, nm_ip4_config_get_domain (src, i)); + if (idx >= 0) + nm_ip4_config_del_domain (dst, idx); } /* dns searches */ for (i = 0; i < nm_ip4_config_get_num_searches (src); i++) { - const char *src_search = nm_ip4_config_get_search (src, i); - - for (j = 0; j < nm_ip4_config_get_num_searches (dst); j++) { - const char *dst_search = nm_ip4_config_get_search (dst, j); - - if (g_strcmp0 (src_search, dst_search) == 0) { - nm_ip4_config_del_search (dst, j); - break; - } - } + idx = _searches_get_index (dst, nm_ip4_config_get_search (src, i)); + if (idx >= 0) + nm_ip4_config_del_search (dst, idx); } /* MSS */ @@ -646,16 +758,9 @@ nm_ip4_config_subtract (NMIP4Config *dst, const NMIP4Config *src) /* NIS */ for (i = 0; i < nm_ip4_config_get_num_nis_servers (src); i++) { - guint32 src_nis = nm_ip4_config_get_nis_server (src, i); - - for (j = 0; j < nm_ip4_config_get_num_nis_servers (dst); j++) { - guint32 dst_nis = nm_ip4_config_get_nis_server (dst, j); - - if (dst_nis == src_nis) { - nm_ip4_config_del_nis_server (dst, j); - break; - } - } + idx = _nis_servers_get_index (dst, nm_ip4_config_get_nis_server (src, i)); + if (idx >= 0) + nm_ip4_config_del_nis_server (dst, idx); } if (g_strcmp0 (nm_ip4_config_get_nis_domain (src), nm_ip4_config_get_nis_domain (dst)) == 0) @@ -663,18 +768,55 @@ nm_ip4_config_subtract (NMIP4Config *dst, const NMIP4Config *src) /* WINS */ for (i = 0; i < nm_ip4_config_get_num_wins (src); i++) { - guint32 src_wins = nm_ip4_config_get_wins (src, i); + idx = _wins_get_index (dst, nm_ip4_config_get_wins (src, i)); + if (idx >= 0) + nm_ip4_config_del_wins (dst, idx); + } - for (j = 0; j < nm_ip4_config_get_num_wins (dst); j++) { - guint32 dst_wins = nm_ip4_config_get_wins (dst, j); + g_object_thaw_notify (G_OBJECT (dst)); +} - if (dst_wins == src_wins) { - nm_ip4_config_del_wins (dst, j); - break; - } - } +void +nm_ip4_config_intersect (NMIP4Config *dst, const NMIP4Config *src) +{ + guint32 i; + gint idx; + + g_return_if_fail (src != NULL); + g_return_if_fail (dst != NULL); + + g_object_freeze_notify (G_OBJECT (dst)); + + /* addresses */ + for (i = 0; i < nm_ip4_config_get_num_addresses (dst); ) { + idx = _addresses_get_index (src, nm_ip4_config_get_address (dst, i)); + if (idx < 0) + nm_ip4_config_del_address (dst, i); + else + i++; } + /* ignore nameservers */ + + /* default gateway */ + if ( !nm_ip4_config_get_num_addresses (dst) + || (nm_ip4_config_get_gateway (src) != nm_ip4_config_get_gateway (dst))) + nm_ip4_config_set_gateway (dst, 0); + + /* routes */ + for (i = 0; i < nm_ip4_config_get_num_routes (dst); ) { + idx = _routes_get_index (src, nm_ip4_config_get_route (dst, i)); + if (idx < 0) + nm_ip4_config_del_route (dst, i); + else + i++; + } + + /* ignore domains */ + /* ignore dns searches */ + /* ignore NIS */ + /* ignore WINS */ + g_object_thaw_notify (G_OBJECT (dst)); } diff --git a/src/nm-ip4-config.h b/src/nm-ip4-config.h index 5ac455d8..46367524 100644 --- a/src/nm-ip4-config.h +++ b/src/nm-ip4-config.h @@ -64,13 +64,14 @@ const char * nm_ip4_config_get_dbus_path (const NMIP4Config *config); /* Integration with nm-platform and nm-setting */ NMIP4Config *nm_ip4_config_capture (int ifindex, gboolean capture_resolv_conf); -gboolean nm_ip4_config_commit (const NMIP4Config *config, int ifindex, guint32 default_route_metric); +gboolean nm_ip4_config_commit (const NMIP4Config *config, int ifindex, gboolean routes_full_sync, gint64 default_route_metric); void nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIPConfig *setting, guint32 default_route_metric); NMSetting *nm_ip4_config_create_setting (const NMIP4Config *config); /* Utility functions */ void nm_ip4_config_merge (NMIP4Config *dst, const NMIP4Config *src); void nm_ip4_config_subtract (NMIP4Config *dst, const NMIP4Config *src); +void nm_ip4_config_intersect (NMIP4Config *dst, const NMIP4Config *src); gboolean nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relevant_changes); gboolean nm_ip4_config_destination_is_direct (const NMIP4Config *config, guint32 dest, int plen); void nm_ip4_config_dump (const NMIP4Config *config, const char *detail); diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c index cf8d74be..48aeb64a 100644 --- a/src/nm-ip6-config.c +++ b/src/nm-ip6-config.c @@ -32,6 +32,7 @@ #include "nm-dbus-manager.h" #include "nm-dbus-glib-types.h" #include "nm-ip6-config-glue.h" +#include "nm-route-manager.h" #include "NetworkManagerUtils.h" G_DEFINE_TYPE (NMIP6Config, nm_ip6_config, G_TYPE_OBJECT) @@ -298,7 +299,7 @@ nm_ip6_config_capture (int ifindex, gboolean capture_resolv_conf, NMSettingIP6Co gboolean notify_nameservers = FALSE; /* Slaves have no IP configuration */ - if (nm_platform_link_get_master (ifindex) > 0) + if (nm_platform_link_get_master (NM_PLATFORM_GET, ifindex) > 0) return NULL; config = nm_ip6_config_new (); @@ -307,12 +308,12 @@ nm_ip6_config_capture (int ifindex, gboolean capture_resolv_conf, NMSettingIP6Co g_array_unref (priv->addresses); g_array_unref (priv->routes); - priv->addresses = nm_platform_ip6_address_get_all (ifindex); - priv->routes = nm_platform_ip6_route_get_all (ifindex, NM_PLATFORM_GET_ROUTE_MODE_ALL); + priv->addresses = nm_platform_ip6_address_get_all (NM_PLATFORM_GET, ifindex); + priv->routes = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); /* Extract gateway from default route */ old_gateway = priv->gateway; - for (i = 0; i < priv->routes->len; i++) { + for (i = 0; i < priv->routes->len; ) { const NMPlatformIP6Route *route = &g_array_index (priv->routes, NMPlatformIP6Route, i); if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) { @@ -322,9 +323,10 @@ nm_ip6_config_capture (int ifindex, gboolean capture_resolv_conf, NMSettingIP6Co } has_gateway = TRUE; /* Remove the default route from the list */ - g_array_remove_index (priv->routes, i); - i--; + g_array_remove_index_fast (priv->routes, i); + continue; } + i++; } /* If there is a host route to the gateway, ignore that route. It is @@ -365,7 +367,7 @@ nm_ip6_config_capture (int ifindex, gboolean capture_resolv_conf, NMSettingIP6Co } gboolean -nm_ip6_config_commit (const NMIP6Config *config, int ifindex) +nm_ip6_config_commit (const NMIP6Config *config, int ifindex, gboolean routes_full_sync) { NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config); int i; @@ -375,7 +377,7 @@ nm_ip6_config_commit (const NMIP6Config *config, int ifindex) g_return_val_if_fail (config != NULL, FALSE); /* Addresses */ - nm_platform_ip6_address_sync (ifindex, priv->addresses, TRUE); + nm_platform_ip6_address_sync (NM_PLATFORM_GET, ifindex, priv->addresses, TRUE); /* Routes */ { @@ -396,7 +398,7 @@ nm_ip6_config_commit (const NMIP6Config *config, int ifindex) g_array_append_vals (routes, route, 1); } - success = nm_platform_ip6_route_sync (ifindex, routes); + success = nm_route_manager_ip6_route_sync (nm_route_manager_get (), ifindex, routes, TRUE, routes_full_sync); g_array_unref (routes); } @@ -615,7 +617,7 @@ nm_ip6_config_merge (NMIP6Config *dst, const NMIP6Config *src) nm_ip6_config_add_nameserver (dst, nm_ip6_config_get_nameserver (src, i)); /* default gateway */ - if (!nm_ip6_config_get_gateway (dst)) + if (nm_ip6_config_get_gateway (src)) nm_ip6_config_set_gateway (dst, nm_ip6_config_get_gateway (src)); /* routes */ @@ -630,7 +632,7 @@ nm_ip6_config_merge (NMIP6Config *dst, const NMIP6Config *src) for (i = 0; i < nm_ip6_config_get_num_searches (src); i++) nm_ip6_config_add_search (dst, nm_ip6_config_get_search (src, i)); - if (!nm_ip6_config_get_mss (dst)) + if (nm_ip6_config_get_mss (src)) nm_ip6_config_set_mss (dst, nm_ip6_config_get_mss (src)); g_object_thaw_notify (G_OBJECT (dst)); @@ -653,6 +655,85 @@ nm_ip6_config_destination_is_direct (const NMIP6Config *config, const struct in6 return FALSE; } +/*******************************************************************************/ + +static int +_addresses_get_index (const NMIP6Config *self, const NMPlatformIP6Address *addr) +{ + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); + guint i; + + for (i = 0; i < priv->addresses->len; i++) { + const NMPlatformIP6Address *a = &g_array_index (priv->addresses, NMPlatformIP6Address, i); + + if (IN6_ARE_ADDR_EQUAL (&addr->address, &a->address)) + return (int) i; + } + return -1; +} + +static int +_nameservers_get_index (const NMIP6Config *self, const struct in6_addr *ns) +{ + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); + guint i; + + for (i = 0; i < priv->nameservers->len; i++) { + const struct in6_addr *n = &g_array_index (priv->nameservers, struct in6_addr, i); + + if (IN6_ARE_ADDR_EQUAL (ns, n)) + return (int) i; + } + return -1; +} + +static int +_routes_get_index (const NMIP6Config *self, const NMPlatformIP6Route *route) +{ + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); + guint i; + + for (i = 0; i < priv->routes->len; i++) { + const NMPlatformIP6Route *r = &g_array_index (priv->routes, NMPlatformIP6Route, i); + + if (routes_are_duplicate (route, r, FALSE)) + return (int) i; + } + return -1; +} + +static int +_domains_get_index (const NMIP6Config *self, const char *domain) +{ + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); + guint i; + + for (i = 0; i < priv->domains->len; i++) { + const char *d = g_ptr_array_index (priv->domains, i); + + if (g_strcmp0 (domain, d) == 0) + return (int) i; + } + return -1; +} + +static int +_searches_get_index (const NMIP6Config *self, const char *search) +{ + NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self); + guint i; + + for (i = 0; i < priv->searches->len; i++) { + const char *s = g_ptr_array_index (priv->searches, i); + + if (g_strcmp0 (search, s) == 0) + return (int) i; + } + return -1; +} + +/*******************************************************************************/ + /** * nm_ip6_config_subtract: * @dst: config from which to remove everything in @src @@ -663,7 +744,8 @@ nm_ip6_config_destination_is_direct (const NMIP6Config *config, const struct in6 void nm_ip6_config_subtract (NMIP6Config *dst, const NMIP6Config *src) { - guint32 i, j; + guint i; + gint idx; const struct in6_addr *dst_tmp, *src_tmp; g_return_if_fail (src != NULL); @@ -673,30 +755,16 @@ nm_ip6_config_subtract (NMIP6Config *dst, const NMIP6Config *src) /* addresses */ for (i = 0; i < nm_ip6_config_get_num_addresses (src); i++) { - const NMPlatformIP6Address *src_addr = nm_ip6_config_get_address (src, i); - - for (j = 0; j < nm_ip6_config_get_num_addresses (dst); j++) { - const NMPlatformIP6Address *dst_addr = nm_ip6_config_get_address (dst, j); - - if (IN6_ARE_ADDR_EQUAL (&src_addr->address, &dst_addr->address)) { - nm_ip6_config_del_address (dst, j); - break; - } - } + idx = _addresses_get_index (dst, nm_ip6_config_get_address (src, i)); + if (idx >= 0) + nm_ip6_config_del_address (dst, idx); } /* nameservers */ for (i = 0; i < nm_ip6_config_get_num_nameservers (src); i++) { - const struct in6_addr *src_ns = nm_ip6_config_get_nameserver (src, i); - - for (j = 0; j < nm_ip6_config_get_num_nameservers (dst); j++) { - const struct in6_addr *dst_ns = nm_ip6_config_get_nameserver (dst, j); - - if (IN6_ARE_ADDR_EQUAL (src_ns, dst_ns)) { - nm_ip6_config_del_nameserver (dst, j); - break; - } - } + idx = _nameservers_get_index (dst, nm_ip6_config_get_nameserver (src, i)); + if (idx >= 0) + nm_ip6_config_del_nameserver (dst, idx); } /* default gateway */ @@ -710,44 +778,23 @@ nm_ip6_config_subtract (NMIP6Config *dst, const NMIP6Config *src) /* routes */ for (i = 0; i < nm_ip6_config_get_num_routes (src); i++) { - const NMPlatformIP6Route *src_route = nm_ip6_config_get_route (src, i); - - for (j = 0; j < nm_ip6_config_get_num_routes (dst); j++) { - const NMPlatformIP6Route *dst_route = nm_ip6_config_get_route (dst, j); - - if (routes_are_duplicate (src_route, dst_route, FALSE)) { - nm_ip6_config_del_route (dst, j); - break; - } - } + idx = _routes_get_index (dst, nm_ip6_config_get_route (src, i)); + if (idx >= 0) + nm_ip6_config_del_route (dst, idx); } /* domains */ for (i = 0; i < nm_ip6_config_get_num_domains (src); i++) { - const char *src_domain = nm_ip6_config_get_domain (src, i); - - for (j = 0; j < nm_ip6_config_get_num_domains (dst); j++) { - const char *dst_domain = nm_ip6_config_get_domain (dst, j); - - if (g_strcmp0 (src_domain, dst_domain) == 0) { - nm_ip6_config_del_domain (dst, j); - break; - } - } + idx = _domains_get_index (dst, nm_ip6_config_get_domain (src, i)); + if (idx >= 0) + nm_ip6_config_del_domain (dst, idx); } /* dns searches */ for (i = 0; i < nm_ip6_config_get_num_searches (src); i++) { - const char *src_search = nm_ip6_config_get_search (src, i); - - for (j = 0; j < nm_ip6_config_get_num_searches (dst); j++) { - const char *dst_search = nm_ip6_config_get_search (dst, j); - - if (g_strcmp0 (src_search, dst_search) == 0) { - nm_ip6_config_del_search (dst, j); - break; - } - } + idx = _searches_get_index (dst, nm_ip6_config_get_search (src, i)); + if (idx >= 0) + nm_ip6_config_del_search (dst, idx); } if (nm_ip6_config_get_mss (src) == nm_ip6_config_get_mss (dst)) @@ -756,6 +803,54 @@ nm_ip6_config_subtract (NMIP6Config *dst, const NMIP6Config *src) g_object_thaw_notify (G_OBJECT (dst)); } +void +nm_ip6_config_intersect (NMIP6Config *dst, const NMIP6Config *src) +{ + guint i; + gint idx; + const struct in6_addr *dst_tmp, *src_tmp; + + g_return_if_fail (src != NULL); + g_return_if_fail (dst != NULL); + + g_object_freeze_notify (G_OBJECT (dst)); + + /* addresses */ + for (i = 0; i < nm_ip6_config_get_num_addresses (dst); ) { + idx = _addresses_get_index (src, nm_ip6_config_get_address (dst, i)); + if (idx < 0) + nm_ip6_config_del_address (dst, i); + else + i++; + } + + /* ignore nameservers */ + + /* default gateway */ + dst_tmp = nm_ip6_config_get_gateway (dst); + if (dst_tmp) { + src_tmp = nm_ip6_config_get_gateway (src); + if ( !nm_ip6_config_get_num_addresses (dst) + || !src_tmp + || !IN6_ARE_ADDR_EQUAL (src_tmp, dst_tmp)) + nm_ip6_config_set_gateway (dst, NULL); + } + + /* routes */ + for (i = 0; i < nm_ip6_config_get_num_routes (dst); ) { + idx = _routes_get_index (src, nm_ip6_config_get_route (dst, i)); + if (idx < 0) + nm_ip6_config_del_route (dst, i); + else + i++; + } + + /* ignore domains */ + /* ignore dns searches */ + + g_object_thaw_notify (G_OBJECT (dst)); +} + /** * nm_ip6_config_replace: * @dst: config which will be replaced with everything in @src diff --git a/src/nm-ip6-config.h b/src/nm-ip6-config.h index f1d2dc8d..66f15888 100644 --- a/src/nm-ip6-config.h +++ b/src/nm-ip6-config.h @@ -64,13 +64,14 @@ const char * nm_ip6_config_get_dbus_path (const NMIP6Config *config); /* Integration with nm-platform and nm-setting */ NMIP6Config *nm_ip6_config_capture (int ifindex, gboolean capture_resolv_conf, NMSettingIP6ConfigPrivacy use_temporary); -gboolean nm_ip6_config_commit (const NMIP6Config *config, int ifindex); +gboolean nm_ip6_config_commit (const NMIP6Config *config, int ifindex, gboolean routes_full_sync); void nm_ip6_config_merge_setting (NMIP6Config *config, NMSettingIPConfig *setting, guint32 default_route_metric); NMSetting *nm_ip6_config_create_setting (const NMIP6Config *config); /* Utility functions */ void nm_ip6_config_merge (NMIP6Config *dst, const NMIP6Config *src); void nm_ip6_config_subtract (NMIP6Config *dst, const NMIP6Config *src); +void nm_ip6_config_intersect (NMIP6Config *dst, const NMIP6Config *src); gboolean nm_ip6_config_replace (NMIP6Config *dst, const NMIP6Config *src, gboolean *relevant_changes); int nm_ip6_config_destination_is_direct (const NMIP6Config *config, const struct in6_addr *dest, int plen); void nm_ip6_config_dump (const NMIP6Config *config, const char *detail); diff --git a/src/nm-logging.h b/src/nm-logging.h index 00d429b0..82a4e71f 100644 --- a/src/nm-logging.h +++ b/src/nm-logging.h @@ -29,7 +29,7 @@ #include <glib.h> #include <glib-object.h> -#include "nm-utils-internal.h" +#include "nm-macros-internal.h" /* Log domains */ typedef enum { /*< skip >*/ diff --git a/src/nm-manager.c b/src/nm-manager.c index 0fa88d1d..080cdb8b 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -26,8 +26,6 @@ #include <errno.h> #include <string.h> #include <unistd.h> -#include <sys/types.h> -#include <sys/stat.h> #include <dbus/dbus-glib-lowlevel.h> #include <dbus/dbus-glib.h> #include <gio/gio.h> @@ -129,8 +127,6 @@ static NMActiveConnection *_new_active_connection (NMManager *self, static void policy_activating_device_changed (GObject *object, GParamSpec *pspec, gpointer user_data); -static NMDevice *find_device_by_ip_iface (NMManager *self, const gchar *iface); - static void rfkill_change (const char *desc, RfKillType rtype, gboolean enabled); static gboolean find_master (NMManager *self, @@ -161,25 +157,22 @@ typedef struct { char *state_file; GSList *active_connections; + GSList *authorizing_connections; guint ac_cleanup_id; NMActiveConnection *primary_connection; NMActiveConnection *activating_connection; GSList *devices; NMState state; + NMConfig *config; NMConnectivity *connectivity; - int ignore_link_added_cb; - NMPolicy *policy; NMDBusManager *dbus_mgr; gboolean prop_filter_added; NMRfkillManager *rfkill_mgr; - /* List of NMDeviceFactoryFunc pointers sorted in priority order */ - GSList *factories; - NMSettings *settings; char *hostname; @@ -285,7 +278,8 @@ active_connection_remove (NMManager *self, NMActiveConnection *active) g_object_unref (active); - if (connection) { + if ( connection + && nm_settings_has_connection (priv->settings, connection)) { nm_log_dbg (LOGD_DEVICE, "Assumed connection disconnected. Deleting generated connection '%s' (%s)", nm_connection_get_id (connection), nm_connection_get_uuid (connection)); nm_settings_connection_delete (NM_SETTINGS_CONNECTION (connection), NULL, NULL); @@ -463,20 +457,18 @@ active_connection_get_by_path (NMManager *manager, const char *path) /************************************************************************/ -static NMDevice * -nm_manager_get_device_by_udi (NMManager *manager, const char *udi) +static void +_config_changed_cb (NMConfig *config, NMConfigData *config_data, NMConfigChangeFlags changes, NMConfigData *old_data, NMManager *self) { - GSList *iter; - - g_return_val_if_fail (udi != NULL, NULL); - - for (iter = NM_MANAGER_GET_PRIVATE (manager)->devices; iter; iter = iter->next) { - if (!strcmp (nm_device_get_udi (NM_DEVICE (iter->data)), udi)) - return NM_DEVICE (iter->data); - } - return NULL; + g_object_set (NM_MANAGER_GET_PRIVATE (self)->connectivity, + NM_CONNECTIVITY_URI, nm_config_data_get_connectivity_uri (config_data), + NM_CONNECTIVITY_INTERVAL, nm_config_data_get_connectivity_interval (config_data), + NM_CONNECTIVITY_RESPONSE, nm_config_data_get_connectivity_response (config_data), + NULL); } +/************************************************************************/ + static NMDevice * nm_manager_get_device_by_path (NMManager *manager, const char *path) { @@ -506,6 +498,50 @@ nm_manager_get_device_by_ifindex (NMManager *manager, int ifindex) return NULL; } +static NMDevice * +find_device_by_hw_addr (NMManager *manager, const char *hwaddr) +{ + GSList *iter; + const char *device_addr; + + g_return_val_if_fail (hwaddr != NULL, NULL); + + if (nm_utils_hwaddr_valid (hwaddr, -1)) { + for (iter = NM_MANAGER_GET_PRIVATE (manager)->devices; iter; iter = iter->next) { + device_addr = nm_device_get_hw_address (NM_DEVICE (iter->data)); + if (device_addr && nm_utils_hwaddr_matches (hwaddr, -1, device_addr, -1)) + return NM_DEVICE (iter->data); + } + } + return NULL; +} + +static NMDevice * +find_device_by_ip_iface (NMManager *self, const gchar *iface) +{ + GSList *iter; + + g_return_val_if_fail (iface != NULL, NULL); + + for (iter = NM_MANAGER_GET_PRIVATE (self)->devices; iter; iter = g_slist_next (iter)) { + if (g_strcmp0 (nm_device_get_ip_iface (NM_DEVICE (iter->data)), iface) == 0) + return NM_DEVICE (iter->data); + } + return NULL; +} + +static NMDevice * +find_device_by_iface (NMManager *self, const gchar *iface) +{ + GSList *iter; + + for (iter = NM_MANAGER_GET_PRIVATE (self)->devices; iter; iter = g_slist_next (iter)) { + if (g_strcmp0 (nm_device_get_iface (NM_DEVICE (iter->data)), iface) == 0) + return NM_DEVICE (iter->data); + } + return NULL; +} + static gboolean manager_sleeping (NMManager *self) { @@ -834,109 +870,56 @@ nm_manager_get_state (NMManager *manager) return NM_MANAGER_GET_PRIVATE (manager)->state; } -/*******************************************************************/ -/* Settings stuff via NMSettings */ -/*******************************************************************/ +/***************************/ static NMDevice * -get_device_from_hwaddr (NMManager *self, const char *setting_mac) +find_parent_device_for_connection (NMManager *self, NMConnection *connection) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - const char *device_mac; + NMDeviceFactory *factory; + const char *parent_name = NULL; + NMConnection *parent_connection; + NMDevice *parent, *first_compatible = NULL; GSList *iter; - if (!setting_mac) + factory = nm_device_factory_manager_find_factory_for_connection (connection); + if (!factory) return NULL; - for (iter = priv->devices; iter; iter = g_slist_next (iter)) { - NMDevice *device = iter->data; - - device_mac = nm_device_get_hw_address (iter->data); - if (!device_mac) - continue; - if (nm_utils_hwaddr_matches (setting_mac, -1, device_mac, -1)) - return device; - } - return NULL; -} - -static NMDevice * -find_vlan_parent (NMManager *self, - NMConnection *connection) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - NMSettingVlan *s_vlan; - NMSettingWired *s_wired; - NMConnection *parent_connection; - const char *parent_iface; - NMDevice *parent = NULL; - const char *setting_mac; - GSList *iter; + parent_name = nm_device_factory_get_connection_parent (factory, connection); + if (!parent_name) + return NULL; - /* The 'parent' property could be given by an interface name, a - * connection UUID, or the MAC address of an NMSettingWired. - */ - s_vlan = nm_connection_get_setting_vlan (connection); - g_return_val_if_fail (s_vlan != NULL, NULL); - - s_wired = nm_connection_get_setting_wired (connection); - setting_mac = s_wired ? nm_setting_wired_get_mac_address (s_wired) : NULL; - - parent_iface = nm_setting_vlan_get_parent (s_vlan); - if (parent_iface) { - parent = find_device_by_ip_iface (self, parent_iface); - if (parent) - return parent; - - if (nm_utils_is_uuid (parent_iface)) { - /* Try as a connection UUID */ - parent_connection = (NMConnection *) nm_settings_get_connection_by_uuid (priv->settings, parent_iface); - if (parent_connection) { - /* Check if the parent connection is activated on some device already */ - for (iter = priv->devices; iter; iter = g_slist_next (iter)) { - NMActRequest *req; - NMConnection *candidate; - - req = nm_device_get_act_request (NM_DEVICE (iter->data)); - if (req) { - candidate = nm_active_connection_get_connection (NM_ACTIVE_CONNECTION (req)); - if (candidate == parent_connection) - return NM_DEVICE (iter->data); - } - } + /* Try as an interface name */ + parent = find_device_by_ip_iface (self, parent_name); + if (parent) + return parent; - /* Check the hardware address of the parent connection */ - return get_device_from_hwaddr (self, setting_mac); - } - return NULL; - } - } + /* Maybe a hardware address */ + parent = find_device_by_hw_addr (self, parent_name); + if (parent) + return parent; - /* Try the hardware address from the VLAN connection's hardware setting */ - return get_device_from_hwaddr (self, setting_mac); -} + /* Maybe a connection UUID */ + parent_connection = (NMConnection *) nm_settings_get_connection_by_uuid (priv->settings, parent_name); + if (!parent_connection) + return NULL; -static NMDevice * -find_infiniband_parent (NMManager *self, - NMConnection *connection) -{ - NMSettingInfiniband *s_infiniband; - const char *parent_iface; - NMDevice *parent = NULL; - const char *setting_mac; + /* Check if the parent connection is currently activated or is comaptible + * with some known device. + */ + for (iter = priv->devices; iter; iter = iter->next) { + NMDevice *candidate = iter->data; - s_infiniband = nm_connection_get_setting_infiniband (connection); - g_return_val_if_fail (s_infiniband != NULL, NULL); + if (nm_device_get_connection (candidate) == parent_connection) + return candidate; - parent_iface = nm_setting_infiniband_get_parent (s_infiniband); - if (parent_iface) { - parent = find_device_by_ip_iface (self, parent_iface); - if (parent) - return parent; + if ( !first_compatible + && nm_device_check_connection_compatible (candidate, parent_connection)) + first_compatible = candidate; } - setting_mac = nm_setting_infiniband_get_mac_address (s_infiniband); - return get_device_from_hwaddr (self, setting_mac); + return first_compatible; } /** @@ -944,86 +927,69 @@ find_infiniband_parent (NMManager *self, * @self: the #NMManager * @connection: the #NMConnection representing a virtual interface * @out_parent: on success, the parent device if any + * @error: an error if determining the virtual interface name failed * * Given @connection, returns the interface name that the connection - * would represent. If the interface name is not given by the connection, - * this may require constructing it based on information in the connection - * and existing network interfaces. + * would represent if it is a virtual connection. %NULL is returned and + * @error is set if the connection is not virtual, or if the name could + * not be determined. * * Returns: the expected interface name (caller takes ownership), or %NULL */ static char * get_virtual_iface_name (NMManager *self, NMConnection *connection, - NMDevice **out_parent) + NMDevice **out_parent, + GError **error) { + NMDeviceFactory *factory; + char *iface = NULL; NMDevice *parent = NULL; - const char *ifname; if (out_parent) *out_parent = NULL; - if (!nm_connection_is_virtual (connection)) + if (!nm_connection_is_virtual (connection)) { + g_set_error (error, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_FAILED, + "NetworkManager plugin for '%s' unavailable", + nm_connection_get_connection_type (connection)); return NULL; - - ifname = nm_connection_get_interface_name (connection); - - if (nm_connection_is_type (connection, NM_SETTING_VLAN_SETTING_NAME)) { - NMSettingVlan *s_vlan; - char *vname; - - s_vlan = nm_connection_get_setting_vlan (connection); - g_return_val_if_fail (s_vlan != NULL, NULL); - - parent = find_vlan_parent (self, connection); - if (!parent) - return NULL; - - if (!nm_device_supports_vlans (parent)) { - nm_log_warn (LOGD_DEVICE, "(%s): No support for VLANs on interface %s of type %s", - ifname ? ifname : nm_connection_get_id (connection), - nm_device_get_ip_iface (parent), - nm_device_get_type_desc (parent)); - return NULL; - } - - /* If the connection doesn't specify the interface name for the VLAN - * device, we create one for it using the VLAN ID and the parent - * interface's name. - */ - if (ifname) - vname = g_strdup (ifname); - else { - vname = nm_utils_new_vlan_name (nm_device_get_ip_iface (parent), - nm_setting_vlan_get_id (s_vlan)); - } - if (out_parent) - *out_parent = parent; - return vname; } - if (nm_connection_is_type (connection, NM_SETTING_INFINIBAND_SETTING_NAME)) { - NMSettingInfiniband *s_infiniband; - - parent = find_infiniband_parent (self, connection); - if (!parent) - return NULL; + factory = nm_device_factory_manager_find_factory_for_connection (connection); + if (!factory) { + nm_log_warn (LOGD_DEVICE, "(%s) NetworkManager plugin for '%s' unavailable", + nm_connection_get_id (connection), + nm_connection_get_connection_type (connection)); + g_set_error (error, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_FAILED, + "NetworkManager plugin for '%s' unavailable", + nm_connection_get_connection_type (connection)); + return NULL; + } - s_infiniband = nm_connection_get_setting_infiniband (connection); - if (out_parent) - *out_parent = parent; - return g_strdup (nm_setting_infiniband_get_virtual_interface_name (s_infiniband)); + parent = find_parent_device_for_connection (self, connection); + iface = nm_device_factory_get_virtual_iface_name (factory, + connection, + parent ? nm_device_get_ip_iface (parent) : NULL); + if (!iface) { + nm_log_warn (LOGD_DEVICE, "(%s) failed to determine virtual interface name", + nm_connection_get_id (connection)); + g_set_error_literal (error, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_UNKNOWN_DEVICE, + "failed to determine virtual interface name"); + return NULL; } - /* For any other virtual connection, NMSettingConnection:interface-name is - * the virtual device name. - */ - g_return_val_if_fail (ifname != NULL, NULL); - return g_strdup (ifname); + if (out_parent) + *out_parent = parent; + return iface; } -/***************************/ - /** * system_create_virtual_device: * @self: the #NMManager @@ -1039,7 +1005,7 @@ static NMDevice * system_create_virtual_device (NMManager *self, NMConnection *connection, GError **error) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - GError *local_err = NULL; + NMDeviceFactory *factory; GSList *iter; char *iface = NULL; NMDevice *device = NULL, *parent = NULL; @@ -1049,16 +1015,9 @@ system_create_virtual_device (NMManager *self, NMConnection *connection, GError g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); - iface = get_virtual_iface_name (self, connection, &parent); - if (!iface) { - nm_log_dbg (LOGD_DEVICE, "(%s) failed to determine virtual interface name", - nm_connection_get_id (connection)); - g_set_error_literal (error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_FAILED, - "failed to determine virtual interface name"); + iface = get_virtual_iface_name (self, connection, &parent, error); + if (!iface) return NULL; - } /* Make sure we didn't create a device for this connection already */ for (iter = priv->devices; iter; iter = g_slist_next (iter)) { @@ -1076,32 +1035,25 @@ system_create_virtual_device (NMManager *self, NMConnection *connection, GError } } - /* Block notification of link added since we're creating the device - * explicitly here, otherwise adding the platform/kernel device would - * create it before this function can do the rest of the setup. - */ - priv->ignore_link_added_cb++; - - nm_owned = !nm_platform_link_exists (iface); - - for (iter = priv->factories; iter; iter = iter->next) { - device = nm_device_factory_create_virtual_device_for_connection (NM_DEVICE_FACTORY (iter->data), - connection, - parent, - &local_err); - if (device || local_err) { - if (device) - g_assert_no_error (local_err); - else { - nm_log_err (LOGD_DEVICE, "(%s) failed to create virtual device: %s", - nm_connection_get_id (connection), - local_err ? local_err->message : "(unknown error)"); - g_propagate_error (error, local_err); - } - break; - } + factory = nm_device_factory_manager_find_factory_for_connection (connection); + if (!factory) { + nm_log_err (LOGD_DEVICE, "(%s:%s) NetworkManager plugin for '%s' unavailable", + nm_connection_get_id (connection), iface, + nm_connection_get_connection_type (connection)); + g_set_error (error, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_FAILED, + "NetworkManager plugin for '%s' unavailable", + nm_connection_get_connection_type (connection)); + goto out; } + nm_owned = !nm_platform_link_get_by_ifname (NM_PLATFORM_GET, iface); + + device = nm_device_factory_create_virtual_device_for_connection (factory, + connection, + parent, + error); if (device) { if (nm_owned) nm_device_set_nm_owned (device); @@ -1112,20 +1064,8 @@ system_create_virtual_device (NMManager *self, NMConnection *connection, GError add_device (self, device, !nm_owned); g_object_unref (device); - } else { - if (error && !*error) - nm_log_err (LOGD_DEVICE, "(%s:%s) NetworkManager plugin for '%s' unavailable", - nm_connection_get_id (connection), iface, - nm_connection_get_connection_type (connection)); - g_set_error (error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_FAILED, - "NetworkManager plugin for '%s' unavailable", - nm_connection_get_connection_type (connection)); } - priv->ignore_link_added_cb--; - out: g_free (iface); return device; @@ -1565,13 +1505,13 @@ get_existing_connection (NMManager *manager, NMDevice *device, gboolean *out_gen nm_device_capture_initial_config (device); if (ifindex) { - int master_ifindex = nm_platform_link_get_master (ifindex); + int master_ifindex = nm_platform_link_get_master (NM_PLATFORM_GET, ifindex); if (master_ifindex) { master = nm_manager_get_device_by_ifindex (manager, master_ifindex); if (!master) { nm_log_dbg (LOGD_DEVICE, "(%s): cannot generate connection for slave before its master (%s/%d)", - nm_device_get_iface (device), nm_platform_link_get_name (master_ifindex), master_ifindex); + nm_device_get_iface (device), nm_platform_link_get_name (NM_PLATFORM_GET, master_ifindex), master_ifindex); return NULL; } if (!nm_device_get_act_request (master)) { @@ -1694,10 +1634,7 @@ recheck_assume_connection (NMDevice *device, gpointer user_data) if (manager_sleeping (self)) return FALSE; - if (nm_device_get_unmanaged_flag (device, NM_UNMANAGED_USER) || - nm_device_get_unmanaged_flag (device, NM_UNMANAGED_INTERNAL) || - nm_device_get_unmanaged_flag (device, NM_UNMANAGED_EXTERNAL_DOWN) || - nm_device_get_unmanaged_flag (device, NM_UNMANAGED_PARENT)) + if (nm_device_get_unmanaged_flag (device, NM_UNMANAGED_ALL & ~NM_UNMANAGED_DEFAULT)) return FALSE; state = nm_device_get_state (device); @@ -1768,6 +1705,18 @@ device_ip_iface_changed (NMDevice *device, } } +static gboolean +notify_component_added (NMManager *self, GObject *component) +{ + GSList *iter; + + for (iter = NM_MANAGER_GET_PRIVATE (self)->devices; iter; iter = iter->next) { + if (nm_device_notify_component_added (NM_DEVICE (iter->data), component)) + return TRUE; + } + return FALSE; +} + /** * add_device: * @self: the #NMManager @@ -1788,9 +1737,13 @@ add_device (NMManager *self, NMDevice *device, gboolean try_assume) RfKillType rtype; GSList *iter, *remove = NULL; gboolean connection_assumed = FALSE; + int ifindex; /* No duplicates */ - if (nm_manager_get_device_by_udi (self, nm_device_get_udi (device))) + ifindex = nm_device_get_ifindex (device); + if (ifindex > 0 && nm_manager_get_device_by_ifindex (self, ifindex)) + return; + if (find_device_by_iface (self, nm_device_get_iface (device))) return; /* Remove existing devices owned by the new device; eg remove ethernet @@ -1852,8 +1805,12 @@ add_device (NMManager *self, NMDevice *device, gboolean try_assume) driver = nm_device_get_driver (device); if (!driver) driver = "unknown"; - nm_log_info (LOGD_HW, "(%s): new %s device (driver: '%s' ifindex: %d)", - iface, type_desc, driver, nm_device_get_ifindex (device)); + nm_log_info (LOGD_HW, "(%s): new %s device (carrier: %s, driver: '%s', ifindex: %d)", + iface, type_desc, + nm_device_has_capability (device, NM_DEVICE_CAP_CARRIER_DETECT) + ? (nm_device_has_carrier (device) ? "ON" : "OFF") + : "UNKNOWN", + driver, nm_device_get_ifindex (device)); unmanaged_specs = nm_settings_get_unmanaged_specs (priv->settings); user_unmanaged = nm_device_spec_match_list (device, unmanaged_specs); @@ -1881,27 +1838,14 @@ add_device (NMManager *self, NMDevice *device, gboolean try_assume) g_signal_emit (self, signals[DEVICE_ADDED], 0, device); g_object_notify (G_OBJECT (self), NM_MANAGER_DEVICES); + notify_component_added (self, G_OBJECT (device)); + /* New devices might be master interfaces for virtual interfaces; so we may * need to create new virtual interfaces now. */ system_create_virtual_devices (self); } -static NMDevice * -find_device_by_ip_iface (NMManager *self, const gchar *iface) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - GSList *iter; - - for (iter = priv->devices; iter; iter = g_slist_next (iter)) { - NMDevice *candidate = iter->data; - - if (g_strcmp0 (nm_device_get_ip_iface (candidate), iface) == 0) - return candidate; - } - return NULL; -} - /*******************************************************************/ static void @@ -1917,134 +1861,13 @@ factory_component_added_cb (NMDeviceFactory *factory, GObject *component, gpointer user_data) { - NMManager *self = NM_MANAGER (user_data); - GSList *iter; - - for (iter = NM_MANAGER_GET_PRIVATE (self)->devices; iter; iter = iter->next) { - if (nm_device_notify_component_added (NM_DEVICE (iter->data), component)) - return TRUE; - } - return FALSE; -} - -#define PLUGIN_PREFIX "libnm-device-plugin-" -#define PLUGIN_PATH_TAG "NMManager-plugin-path" - -struct read_device_factory_paths_data { - char *path; - struct stat st; -}; - -static gint -read_device_factory_paths_sort_fcn (gconstpointer a, gconstpointer b) -{ - const struct read_device_factory_paths_data *da = a; - const struct read_device_factory_paths_data *db = b; - time_t ta, tb; - - ta = MAX (da->st.st_mtime, da->st.st_ctime); - tb = MAX (db->st.st_mtime, db->st.st_ctime); - - if (ta < tb) - return 1; - if (ta > tb) - return -1; - return 0; + return notify_component_added (NM_MANAGER (user_data), component); } -static char** -read_device_factory_paths (void) -{ - GDir *dir; - GError *error = NULL; - const char *item; - GArray *paths; - char **result; - guint i; - - dir = g_dir_open (NMPLUGINDIR, 0, &error); - if (!dir) { - nm_log_warn (LOGD_HW, "device plugin: failed to open directory %s: %s", - NMPLUGINDIR, - (error && error->message) ? error->message : "(unknown)"); - g_clear_error (&error); - return NULL; - } - - paths = g_array_new (FALSE, FALSE, sizeof (struct read_device_factory_paths_data)); - - while ((item = g_dir_read_name (dir))) { - int errsv; - struct read_device_factory_paths_data data; - - if (!g_str_has_prefix (item, PLUGIN_PREFIX)) - continue; - if (g_str_has_suffix (item, ".la")) - continue; - - data.path = g_build_filename (NMPLUGINDIR, item, NULL); - - if (stat (data.path, &data.st) != 0) { - errsv = errno; - nm_log_warn (LOGD_HW, "device plugin: skip invalid file %s (error during stat: %s)", data.path, strerror (errsv)); - goto NEXT; - } - if (!S_ISREG (data.st.st_mode)) - goto NEXT; - if (data.st.st_uid != 0) { - nm_log_warn (LOGD_HW, "device plugin: skip invalid file %s (file must be owned by root)", data.path); - goto NEXT; - } - if (data.st.st_mode & (S_IWGRP | S_IWOTH | S_ISUID)) { - nm_log_warn (LOGD_HW, "device plugin: skip invalid file %s (invalid file permissions)", data.path); - goto NEXT; - } - - g_array_append_val (paths, data); - continue; -NEXT: - g_free (data.path); - } - g_dir_close (dir); - - /* sort filenames by modification time. */ - g_array_sort (paths, read_device_factory_paths_sort_fcn); - - result = g_new (char *, paths->len + 1); - for (i = 0; i < paths->len; i++) - result[i] = g_array_index (paths, struct read_device_factory_paths_data, i).path; - result[i] = NULL; - - g_array_free (paths, TRUE); - return result; -} - -static gboolean -_register_device_factory (NMManager *self, - NMDeviceFactory *factory, - gboolean duplicate_check, - const char *path, - GError **error) +static void +_register_device_factory (NMDeviceFactory *factory, gpointer user_data) { - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - NMDeviceType ftype; - GSList *iter; - - if (duplicate_check) { - /* Make sure we don't double-register factories */ - ftype = nm_device_factory_get_device_type (factory); - for (iter = priv->factories; iter; iter = iter->next) { - if (ftype == nm_device_factory_get_device_type (iter->data)) { - g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED, - "multiple plugins for same type (using '%s' instead of '%s')", - (char *) g_object_get_data (G_OBJECT (iter->data), PLUGIN_PATH_TAG), - path); - return FALSE; - } - } - } - - priv->factories = g_slist_append (priv->factories, factory); + NMManager *self = NM_MANAGER (user_data); g_signal_connect (factory, NM_DEVICE_FACTORY_DEVICE_ADDED, @@ -2054,80 +1877,6 @@ _register_device_factory (NMManager *self, NM_DEVICE_FACTORY_COMPONENT_ADDED, G_CALLBACK (factory_component_added_cb), self); - g_object_set_data_full (G_OBJECT (factory), PLUGIN_PATH_TAG, - g_strdup (path), g_free); - return TRUE; -} - -static void -load_device_factories (NMManager *self) -{ - NMDeviceFactory *factory; - const GSList *iter; - GError *error = NULL; - char **path, **paths; - - /* Register internal factories first */ - for (iter = nm_device_factory_get_internal_factory_types (); iter; iter = iter->next) { - GType ftype = (GType) GPOINTER_TO_SIZE (iter->data); - - factory = (NMDeviceFactory *) g_object_new (ftype, NULL); - g_assert (factory); - if (_register_device_factory (self, factory, FALSE, "internal", &error)) { - nm_log_dbg (LOGD_HW, "Loaded device plugin: %s", g_type_name (ftype)); - } else { - nm_log_warn (LOGD_HW, "Loading device plugin failed: %s", error->message); - g_object_unref (factory); - g_clear_error (&error); - } - } - - paths = read_device_factory_paths (); - if (!paths) - return; - - for (path = paths; *path; path++) { - GModule *plugin; - NMDeviceFactoryCreateFunc create_func; - const char *item; - - item = strrchr (*path, '/'); - g_assert (item); - - plugin = g_module_open (*path, G_MODULE_BIND_LOCAL); - - if (!plugin) { - nm_log_warn (LOGD_HW, "(%s): failed to load plugin: %s", item, g_module_error ()); - continue; - } - - if (!g_module_symbol (plugin, "nm_device_factory_create", (gpointer) &create_func)) { - nm_log_warn (LOGD_HW, "(%s): failed to find device factory creator: %s", item, g_module_error ()); - g_module_close (plugin); - continue; - } - - factory = create_func (&error); - if (!factory) { - nm_log_warn (LOGD_HW, "(%s): failed to initialize device factory: %s", - item, error ? error->message : "unknown"); - g_clear_error (&error); - g_module_close (plugin); - continue; - } - g_clear_error (&error); - - if (_register_device_factory (self, factory, TRUE, g_module_name (plugin), &error)) { - nm_log_info (LOGD_HW, "Loaded device plugin: %s", g_module_name (plugin)); - g_module_make_resident (plugin); - } else { - nm_log_warn (LOGD_HW, "Loading device plugin failed: %s", error->message); - g_object_unref (factory); - g_module_close (plugin); - g_clear_error (&error); - } - } - g_strfreev (paths); } /*******************************************************************/ @@ -2135,63 +1884,43 @@ load_device_factories (NMManager *self) static void platform_link_added (NMManager *self, int ifindex, - NMPlatformLink *plink, - NMPlatformReason reason) + NMPlatformLink *plink) { - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMDeviceFactory *factory; NMDevice *device = NULL; - GSList *iter; GError *error = NULL; g_return_if_fail (ifindex > 0); - if (priv->ignore_link_added_cb > 0) - return; - if (nm_manager_get_device_by_ifindex (self, ifindex)) return; /* Try registered device factories */ - for (iter = priv->factories; iter; iter = iter->next) { - NMDeviceFactory *factory = NM_DEVICE_FACTORY (iter->data); - - device = nm_device_factory_new_link (factory, plink, &error); - if (device && NM_IS_DEVICE (device)) { - g_assert_no_error (error); - break; /* success! */ - } + factory = nm_device_factory_manager_find_factory_for_link_type (plink->type); + if (factory) { + gboolean ignore = FALSE; - if (error) { - nm_log_warn (LOGD_HW, "%s: factory failed to create device: (%d) %s", - plink->udi, - error ? error->code : -1, - error ? error->message : "(unknown)"); - g_clear_error (&error); + device = nm_device_factory_new_link (factory, plink, &ignore, &error); + if (!device) { + if (!ignore) { + nm_log_warn (LOGD_HW, "%s: factory failed to create device: %s", + plink->name, error->message); + g_clear_error (&error); + } return; } } - /* Ignore Bluetooth PAN interfaces; they are handled by their NMDeviceBt - * parent and don't get a separate interface. - */ - if (!strncmp (plink->name, "bnep", STRLEN ("bnep"))) - return; - if (device == NULL) { switch (plink->type) { - case NM_LINK_TYPE_WWAN_ETHERNET: - /* WWAN pseudo-ethernet interfaces are handled automatically by - * their NMDeviceModem and don't get a separate NMDevice object. - */ - break; - + case NM_LINK_TYPE_BNEP: case NM_LINK_TYPE_OLPC_MESH: case NM_LINK_TYPE_TEAM: case NM_LINK_TYPE_WIFI: case NM_LINK_TYPE_WIMAX: nm_log_info (LOGD_HW, "(%s): '%s' plugin not available; creating generic device", - plink->name, plink->type_name); + plink->name, nm_link_type_to_string (plink->type)); /* fall through */ default: device = nm_device_generic_new (plink); @@ -2205,30 +1934,76 @@ platform_link_added (NMManager *self, } } +typedef struct { + NMManager *self; + int ifindex; +} PlatformLinkCbData; + +static gboolean +_platform_link_cb_idle (PlatformLinkCbData *data) +{ + NMManager *self = data->self; + + if (self) { + const NMPlatformLink *l; + + l = nm_platform_link_get (NM_PLATFORM_GET, data->ifindex); + if (l) { + NMPlatformLink pllink; + + pllink = *l; /* make a copy of the link instance */ + platform_link_added (self, data->ifindex, &pllink); + } else { + NMDevice *device; + + device = nm_manager_get_device_by_ifindex (self, data->ifindex); + if (device) + remove_device (self, device, FALSE, TRUE); + } + g_object_remove_weak_pointer (G_OBJECT (self), (gpointer *) &data->self); + } + g_slice_free (PlatformLinkCbData, data); + return G_SOURCE_REMOVE; +} + static void platform_link_cb (NMPlatform *platform, + NMPObjectType obj_type, int ifindex, NMPlatformLink *plink, NMPlatformSignalChangeType change_type, NMPlatformReason reason, gpointer user_data) { + PlatformLinkCbData *data; + switch (change_type) { case NM_PLATFORM_SIGNAL_ADDED: - platform_link_added (NM_MANAGER (user_data), ifindex, plink, reason); - break; - case NM_PLATFORM_SIGNAL_REMOVED: { - NMManager *self = NM_MANAGER (user_data); - NMDevice *device; - - device = nm_manager_get_device_by_ifindex (self, ifindex); - if (device) - remove_device (self, device, FALSE, TRUE); + case NM_PLATFORM_SIGNAL_REMOVED: + data = g_slice_new (PlatformLinkCbData); + data->self = NM_MANAGER (user_data); + data->ifindex = ifindex; + g_object_add_weak_pointer (G_OBJECT (data->self), (gpointer *) &data->self); + g_idle_add ((GSourceFunc) _platform_link_cb_idle, data); break; - } - default: + default: break; - } + } +} + +static void +platform_query_devices (NMManager *self) +{ + GArray *links_array; + NMPlatformLink *links; + int i; + + links_array = nm_platform_link_get_all (NM_PLATFORM_GET); + links = (NMPlatformLink *) links_array->data; + for (i = 0; i < links_array->len; i++) + platform_link_added (self, links[i].ifindex, &links[i]); + + g_array_unref (links_array); } static void @@ -2248,6 +2023,40 @@ nm_manager_get_devices (NMManager *manager) return NM_MANAGER_GET_PRIVATE (manager)->devices; } +static NMDevice * +nm_manager_get_connection_device (NMManager *self, + NMConnection *connection) +{ + NMActiveConnection *ac = find_ac_for_connection (self, connection); + if (ac == NULL) + return NULL; + + return nm_active_connection_get_device (ac); +} + +static NMDevice * +nm_manager_get_best_device_for_connection (NMManager *self, + NMConnection *connection) +{ + const GSList *devices, *iter; + NMDevice *act_device = nm_manager_get_connection_device (self, connection); + + if (act_device) + return act_device; + + /* Pick the first device that's compatible with the connection. */ + devices = nm_manager_get_devices (self); + for (iter = devices; iter; iter = g_slist_next (iter)) { + NMDevice *device = NM_DEVICE (iter->data); + + if (nm_device_check_connection_available (device, connection, NM_DEVICE_CHECK_CON_AVAILABLE_NONE, NULL)) + return device; + } + + /* No luck. :( */ + return NULL; +} + static gboolean impl_manager_get_devices (NMManager *manager, GPtrArray **devices, GError **err) { @@ -2411,13 +2220,10 @@ find_master (NMManager *self, NMConnection *candidate = iter->data; char *vname; - if (nm_connection_is_virtual (candidate)) { - vname = get_virtual_iface_name (self, candidate, NULL); - if ( g_strcmp0 (master, vname) == 0 - && is_compatible_with_slave (candidate, connection)) - master_connection = candidate; - g_free (vname); - } + vname = get_virtual_iface_name (self, candidate, NULL, NULL); + if (g_strcmp0 (master, vname) == 0 && is_compatible_with_slave (candidate, connection)) + master_connection = candidate; + g_free (vname); } g_slist_free (connections); } @@ -2615,6 +2421,127 @@ ensure_master_active_connection (NMManager *self, return NULL; } +/** + * find_slaves: + * @manager: #NMManager object + * @connection: the master #NMConnection to find slave connections for + * @device: the master #NMDevice for the @connection + * + * Given an #NMConnection, attempts to find its slaves. If @connection is not + * master, or has not any slaves, this will return %NULL. + * + * Returns: list of slave connections for given master @connection, or %NULL + **/ +static GSList * +find_slaves (NMManager *manager, + NMConnection *connection, + NMDevice *device) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + GSList *all_connections, *iter; + GSList *slaves = NULL; + NMSettingConnection *s_con; + const char *master; + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + master = nm_setting_connection_get_master (s_con); + + if (master != NULL) + return NULL; /* connection is not master */ + + /* Search through all connections, not only inactive ones, because + * even if a slave was already active, it might be deactivated during + * master reactivation. + */ + all_connections = nm_settings_get_connections (priv->settings); + for (iter = all_connections; iter; iter = iter->next) { + NMConnection *master_connection = NULL; + NMDevice *master_device = NULL; + NMConnection *candidate = iter->data; + + find_master (manager, candidate, NULL, &master_connection, &master_device, NULL, NULL); + if ( (master_connection && master_connection == connection) + || (master_device && master_device == device)) { + slaves = g_slist_prepend (slaves, candidate); + } + } + g_slist_free (all_connections); + + return g_slist_reverse (slaves); +} + +static gboolean +should_connect_slaves (NMConnection *connection, NMDevice *device) +{ + NMSettingConnection *s_con; + NMSettingConnectionAutoconnectSlaves autoconnect_slaves; + gs_free char *value = NULL; + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + + /* Check autoconnect-slaves property */ + autoconnect_slaves = nm_setting_connection_get_autoconnect_slaves (s_con); + if (autoconnect_slaves != NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_DEFAULT) + goto out; + + /* Check configuration default for autoconnect-slaves property */ + value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, + "connection.autoconnect-slaves", device); + if (value) + autoconnect_slaves = _nm_utils_ascii_str_to_int64 (value, 10, 0, 1, -1); + +out: + if (autoconnect_slaves == NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_NO) + return FALSE; + if (autoconnect_slaves == NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_YES) + return TRUE; + return FALSE; +} + +static gboolean +autoconnect_slaves (NMManager *manager, + NMConnection *master_connection, + NMDevice *master_device, + NMAuthSubject *subject) +{ + GError *local_err = NULL; + gboolean ret = FALSE; + + if (should_connect_slaves (master_connection, master_device)) { + GSList *slaves, *iter; + + iter = slaves = find_slaves (manager, master_connection, master_device); + ret = slaves != NULL; + + while (iter) { + NMConnection *slave_connection = iter->data; + + iter = iter->next; + nm_log_dbg (LOGD_CORE, "will activate slave connection '%s' (%s) as a dependency for master '%s' (%s)", + nm_connection_get_id (slave_connection), + nm_connection_get_uuid (slave_connection), + nm_connection_get_id (master_connection), + nm_connection_get_uuid (master_connection)); + + /* Schedule slave activation */ + nm_manager_activate_connection (manager, + slave_connection, + NULL, + nm_manager_get_best_device_for_connection (manager, slave_connection), + subject, + &local_err); + if (local_err) { + nm_log_warn (LOGD_CORE, "Slave connection activation failed: %s", local_err->message); + g_error_free (local_err); + } + } + g_slist_free (slaves); + } + return ret; +} + static gboolean _internal_activate_vpn (NMManager *self, NMActiveConnection *active, GError **error) { @@ -2635,7 +2562,7 @@ _internal_activate_vpn (NMManager *self, NMActiveConnection *active, GError **er static gboolean _internal_activate_device (NMManager *self, NMActiveConnection *active, GError **error) { - NMDevice *device, *master_device = NULL; + NMDevice *device, *existing, *master_device = NULL; NMConnection *connection; NMConnection *master_connection = NULL; NMActiveConnection *master_ac = NULL; @@ -2783,11 +2710,19 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError * } nm_active_connection_set_master (active, master_ac); - nm_log_dbg (LOGD_CORE, "Activation of '%s' depends on active connection %s", + nm_log_dbg (LOGD_CORE, "Activation of '%s' depends on active connection %p", nm_connection_get_id (connection), - nm_active_connection_get_path (master_ac)); + master_ac); } + /* Check slaves for master connection and possibly activate them */ + autoconnect_slaves (self, connection, device, nm_active_connection_get_subject (active)); + + /* Disconnect the connection if connected or queued on another device */ + existing = nm_manager_get_connection_device (self, connection); + if (existing) + nm_device_steal_connection (existing, connection); + /* Export the new ActiveConnection to clients and start it on the device */ nm_active_connection_export (active); g_object_notify (G_OBJECT (self), NM_MANAGER_ACTIVE_CONNECTIONS); @@ -2825,6 +2760,7 @@ _internal_activate_generic (NMManager *self, NMActiveConnection *active, GError * is exported, make sure the manager's activating-connection property * is up-to-date. */ + active_connection_add (self, active); policy_activating_device_changed (G_OBJECT (priv->policy), NULL, self); } @@ -2894,18 +2830,6 @@ _new_active_connection (NMManager *self, return NULL; } - if (existing_ac) { - NMDevice *existing_device = nm_active_connection_get_device (existing_ac); - - if (existing_device != device) { - g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_ALREADY_ACTIVE, - "Connection '%s' is already active on %s", - nm_connection_get_id (connection), - nm_device_get_iface (existing_device)); - return NULL; - } - } - /* Normalize the specific object */ if (specific_object && g_strcmp0 (specific_object, "/") == 0) specific_object = NULL; @@ -2929,15 +2853,14 @@ _internal_activation_failed (NMManager *self, NMActiveConnection *active, const char *error_desc) { - nm_log_warn (LOGD_CORE, "Failed to activate '%s': %s", - nm_connection_get_id (nm_active_connection_get_connection (active)), - error_desc); + nm_log_dbg (LOGD_CORE, "Failed to activate '%s': %s", + nm_connection_get_id (nm_active_connection_get_connection (active)), + error_desc); if (nm_active_connection_get_state (active) <= NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { nm_active_connection_set_state (active, NM_ACTIVE_CONNECTION_STATE_DEACTIVATING); nm_active_connection_set_state (active, NM_ACTIVE_CONNECTION_STATE_DEACTIVATED); } - active_connection_remove (self, active); } static void @@ -2948,8 +2871,11 @@ _internal_activation_auth_done (NMActiveConnection *active, gpointer user_data2) { NMManager *self = user_data1; + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); GError *error = NULL; + priv->authorizing_connections = g_slist_remove (priv->authorizing_connections, active); + if (success) { if (_internal_activate_generic (self, active, &error)) { g_object_unref (active); @@ -2989,8 +2915,10 @@ nm_manager_activate_connection (NMManager *self, NMAuthSubject *subject, GError **error) { + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); NMActiveConnection *active; char *error_desc = NULL; + GSList *iter; g_return_val_if_fail (self != NULL, NULL); g_return_val_if_fail (connection != NULL, NULL); @@ -3010,6 +2938,22 @@ nm_manager_activate_connection (NMManager *self, return NULL; } + /* Look for a active connection that's equivalent and is already pending authorization + * and eventual activation. This is used to de-duplicate concurrent activations which would + * otherwise race and cause the device to disconnect and reconnect repeatedly. + * In particular, this allows the master and multiple slaves to concurrently auto-activate + * while all the slaves would use the same active-connection. */ + for (iter = priv->authorizing_connections; iter; iter = g_slist_next (iter)) { + active = iter->data; + + if ( connection == nm_active_connection_get_connection (active) + && g_strcmp0 (nm_active_connection_get_specific_object (active), specific_object) == 0 + && nm_active_connection_get_device (active) == device + && nm_auth_subject_is_internal (nm_active_connection_get_subject (active)) + && nm_auth_subject_is_internal (subject)) + return active; + } + active = _new_active_connection (self, connection, specific_object, @@ -3017,8 +2961,8 @@ nm_manager_activate_connection (NMManager *self, subject, error); if (active) { + priv->authorizing_connections = g_slist_prepend (priv->authorizing_connections, active); nm_active_connection_authorize (active, _internal_activation_auth_done, self, NULL); - active_connection_add (self, active); } return active; } @@ -3064,6 +3008,18 @@ validate_activation_request (NMManager *self, goto error; } + /* Not implemented yet, we want to fail early */ + if ( nm_connection_get_setting_connection (connection) + && nm_connection_get_setting_ip6_config (connection) + && !strcmp (nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG), + NM_SETTING_IP6_CONFIG_METHOD_SHARED)) { + g_set_error_literal (error, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_CONNECTION_NOT_AVAILABLE, + "Sharing IPv6 connections is not supported yet."); + return NULL; + } + /* Check whether it's a VPN or not */ if ( nm_connection_get_setting_vpn (connection) || nm_connection_is_type (connection, NM_SETTING_VPN_SETTING_NAME)) @@ -3083,7 +3039,10 @@ validate_activation_request (NMManager *self, "Device not found"); goto error; } - } else { + } else + device = nm_manager_get_best_device_for_connection (self, connection); + + if (!device) { gboolean is_software = nm_connection_is_virtual (connection); /* VPN and software-device connections don't need a device yet */ @@ -3091,22 +3050,17 @@ validate_activation_request (NMManager *self, g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "This connection requires an existing device."); + "No suitable device found for this connection."); goto error; } if (is_software) { - /* Look for an existing device with the connection's interface name */ char *iface; - iface = get_virtual_iface_name (self, connection, NULL); - if (!iface) { - g_set_error_literal (error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "Failed to determine connection's virtual interface name"); + /* Look for an existing device with the connection's interface name */ + iface = get_virtual_iface_name (self, connection, NULL, error); + if (!iface) goto error; - } device = find_device_by_ip_iface (self, iface); g_free (iface); @@ -3252,7 +3206,6 @@ impl_manager_activate_connection (NMManager *self, goto error; nm_active_connection_authorize (active, _activation_auth_done, self, context); - active_connection_add (self, active); g_clear_object (&subject); return; @@ -3330,8 +3283,6 @@ _add_and_activate_auth_done (NMActiveConnection *active, activation_add_done, info); } else { - active_connection_remove (self, active); - g_assert (error_desc); error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, @@ -3438,7 +3389,6 @@ impl_manager_add_and_activate_connection (NMManager *self, goto error; nm_active_connection_authorize (active, _add_and_activate_auth_done, self, context); - active_connection_add (self, active); g_object_unref (connection); g_object_unref (subject); return; @@ -3616,7 +3566,7 @@ done: static gboolean device_is_wake_on_lan (NMDevice *device) { - return nm_platform_link_get_wake_on_lan (nm_device_get_ip_ifindex (device)); + return nm_platform_link_get_wake_on_lan (NM_PLATFORM_GET, nm_device_get_ip_ifindex (device)); } static void @@ -4172,11 +4122,16 @@ impl_manager_check_connectivity (NMManager *manager, nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE); } +static void +start_factory (NMDeviceFactory *factory, gpointer user_data) +{ + nm_device_factory_start (factory); +} + void nm_manager_start (NMManager *self) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - GSList *iter; guint i; /* Set initial radio enabled/disabled state */ @@ -4208,10 +4163,9 @@ nm_manager_start (NMManager *self) system_hostname_changed_cb (priv->settings, NULL, self); /* Start device factories */ - for (iter = priv->factories; iter; iter = iter->next) - nm_device_factory_start (iter->data); + nm_device_factory_manager_for_each_factory (start_factory, NULL); - nm_platform_query_devices (); + platform_query_devices (self); /* * Connections added before the manager is started do not emit @@ -4270,12 +4224,9 @@ connectivity_changed (NMConnectivity *connectivity, gpointer user_data) { NMManager *self = NM_MANAGER (user_data); - NMConnectivityState state; - static const char *connectivity_states[] = { "UNKNOWN", "NONE", "PORTAL", "LIMITED", "FULL" }; - state = nm_connectivity_get_state (connectivity); nm_log_dbg (LOGD_CORE, "connectivity checking indicates %s", - connectivity_states[state]); + nm_connectivity_state_to_string (nm_connectivity_get_state (connectivity))); nm_manager_update_state (self); g_object_notify (G_OBJECT (self), NM_MANAGER_CONNECTIVITY); @@ -4718,6 +4669,7 @@ nm_manager_new (NMSettings *settings, NMManagerPrivate *priv; DBusGConnection *bus; DBusConnection *dbus_connection; + NMConfigData *config_data; g_assert (settings); @@ -4749,7 +4701,16 @@ nm_manager_new (NMSettings *settings, g_signal_connect (priv->policy, "notify::" NM_POLICY_ACTIVATING_IP6_DEVICE, G_CALLBACK (policy_activating_device_changed), singleton); - priv->connectivity = nm_connectivity_new (); + priv->config = g_object_ref (nm_config_get ()); + g_signal_connect (G_OBJECT (priv->config), + NM_CONFIG_SIGNAL_CONFIG_CHANGED, + G_CALLBACK (_config_changed_cb), + singleton); + + config_data = nm_config_get_data (priv->config); + priv->connectivity = nm_connectivity_new (nm_config_data_get_connectivity_uri (config_data), + nm_config_data_get_connectivity_interval (config_data), + nm_config_data_get_connectivity_response (config_data)); g_signal_connect (priv->connectivity, "notify::" NM_CONNECTIVITY_STATE, G_CALLBACK (connectivity_changed), singleton); @@ -4807,7 +4768,7 @@ nm_manager_new (NMSettings *settings, rfkill_change (priv->radio_states[RFKILL_TYPE_WLAN].desc, RFKILL_TYPE_WLAN, initial_wifi_enabled); rfkill_change (priv->radio_states[RFKILL_TYPE_WWAN].desc, RFKILL_TYPE_WWAN, initial_wwan_enabled); - load_device_factories (singleton); + nm_device_factory_manager_load_factories (_register_device_factory, singleton); return singleton; } @@ -5042,13 +5003,18 @@ set_property (GObject *object, guint prop_id, } static void +_deinit_device_factory (NMDeviceFactory *factory, gpointer user_data) +{ + g_signal_handlers_disconnect_matched (factory, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NM_MANAGER (user_data)); +} + +static void dispose (GObject *object) { NMManager *manager = NM_MANAGER (object); NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); DBusGConnection *bus; DBusConnection *dbus_connection; - GSList *iter; g_slist_free_full (priv->auth_chains, (GDestroyNotify) nm_auth_chain_unref); priv->auth_chains = NULL; @@ -5070,7 +5036,14 @@ dispose (GObject *object) g_clear_object (&priv->primary_connection); g_clear_object (&priv->activating_connection); - g_clear_object (&priv->connectivity); + if (priv->config) { + g_signal_handlers_disconnect_by_func (priv->config, _config_changed_cb, manager); + g_clear_object (&priv->config); + } + if (priv->connectivity) { + g_signal_handlers_disconnect_by_func (priv->connectivity, connectivity_changed, manager); + g_clear_object (&priv->connectivity); + } g_free (priv->hostname); @@ -5113,14 +5086,8 @@ dispose (GObject *object) g_clear_object (&priv->fw_monitor); } - for (iter = priv->factories; iter; iter = iter->next) { - NMDeviceFactory *factory = iter->data; - - g_signal_handlers_disconnect_matched (factory, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, manager); - g_object_unref (factory); - } - g_clear_pointer (&priv->factories, g_slist_free); - + nm_device_factory_manager_for_each_factory (_deinit_device_factory, manager); + if (priv->timestamp_update_id) { g_source_remove (priv->timestamp_update_id); priv->timestamp_update_id = 0; diff --git a/src/nm-multi-index.c b/src/nm-multi-index.c new file mode 100644 index 00000000..d45f4834 --- /dev/null +++ b/src/nm-multi-index.c @@ -0,0 +1,441 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2015 Red Hat, Inc. + */ + +#include "config.h" + +#include "nm-multi-index.h" + +#include <string.h> + +#include "nm-glib-compat.h" +#include "nm-macros-internal.h" + + +struct NMMultiIndex { + NMMultiIndexFuncEqual equal_fcn; + NMMultiIndexFuncClone clone_fcn; + GHashTable *hash; +}; + +typedef struct { + GHashTable *index; + gpointer *values; +} ValuesData; + +/******************************************************************************************/ + +static ValuesData * +_values_data_create (void) +{ + ValuesData *values_data; + + values_data = g_slice_new (ValuesData); + values_data->index = g_hash_table_new (NULL, NULL); + values_data->values = NULL; + return values_data; +} + +static void +_values_data_destroy (ValuesData *values_data) +{ + if (values_data) { + g_free (values_data->values); + g_hash_table_unref (values_data->index); + g_slice_free (ValuesData, values_data); + } +} + +static void +_values_data_populate_array (ValuesData *values_data) +{ + guint i, len; + gpointer *values; + GHashTableIter iter; + + nm_assert (values_data); + nm_assert (values_data->index && g_hash_table_size (values_data->index) > 0); + + if (values_data->values) + return; + + len = g_hash_table_size (values_data->index); + values = g_new (gpointer, len + 1); + + g_hash_table_iter_init (&iter, values_data->index); + for (i = 0; g_hash_table_iter_next (&iter, &values[i], NULL); i++) + nm_assert (i < len); + nm_assert (i == len); + values[i] = NULL; + + values_data->values = values; +} + +/******************************************************************************************/ + +/** + * nm_multi_index_lookup(): + * @index: + * @id: + * @out_len: (allow-none): output the number of values + * that are returned. + * + * Returns: (transfer-none): %NULL if there are no values + * or a %NULL terminated array of pointers. + */ +void *const* +nm_multi_index_lookup (const NMMultiIndex *index, + const NMMultiIndexId *id, + guint *out_len) +{ + ValuesData *values_data; + + g_return_val_if_fail (index, NULL); + g_return_val_if_fail (id, NULL); + + values_data = g_hash_table_lookup (index->hash, id); + if (!values_data) { + if (out_len) + *out_len = 0; + return NULL; + } + _values_data_populate_array (values_data); + if (out_len) + *out_len = g_hash_table_size (values_data->index); + return values_data->values; +} + +gboolean +nm_multi_index_contains (const NMMultiIndex *index, + const NMMultiIndexId *id, + gconstpointer value) +{ + ValuesData *values_data; + + g_return_val_if_fail (index, FALSE); + g_return_val_if_fail (id, FALSE); + g_return_val_if_fail (value, FALSE); + + values_data = g_hash_table_lookup (index->hash, id); + return values_data + && g_hash_table_contains (values_data->index, value); +} + +const NMMultiIndexId * +nm_multi_index_lookup_first_by_value (const NMMultiIndex *index, + gconstpointer value) +{ + GHashTableIter iter; + const NMMultiIndexId *id; + ValuesData *values_data; + + g_return_val_if_fail (index, NULL); + g_return_val_if_fail (value, NULL); + + /* reverse-lookup needs to iterate over all hash tables. It should + * still be fairly quick, if the number of hash tables is small. + * There is no O(1) reverse lookup implemented, because this access + * pattern is not what NMMultiIndex is here for. + * You are supposed to use NMMultiIndex by always knowing which @id + * a @value has. + */ + + g_hash_table_iter_init (&iter, index->hash); + while (g_hash_table_iter_next (&iter, (gpointer *) &id, (gpointer *) &values_data)) { + if (g_hash_table_contains (values_data->index, value)) + return id; + } + return NULL; +} + +void +nm_multi_index_foreach (const NMMultiIndex *index, + gconstpointer value, + NMMultiIndexFuncForeach foreach_func, + gpointer user_data) +{ + GHashTableIter iter; + const NMMultiIndexId *id; + ValuesData *values_data; + + g_return_if_fail (index); + g_return_if_fail (foreach_func); + + g_hash_table_iter_init (&iter, index->hash); + while (g_hash_table_iter_next (&iter, (gpointer *) &id, (gpointer *) &values_data)) { + if ( value + && !g_hash_table_contains (values_data->index, value)) + continue; + + _values_data_populate_array (values_data); + if (!foreach_func (id, values_data->values, g_hash_table_size (values_data->index), user_data)) + return; + } +} + +void +nm_multi_index_iter_init (NMMultiIndexIter *iter, + const NMMultiIndex *index, + gconstpointer value) +{ + g_return_if_fail (index); + g_return_if_fail (iter); + + g_hash_table_iter_init (&iter->_iter, index->hash); + iter->_index = index; + iter->_value = value; +} + +gboolean +nm_multi_index_iter_next (NMMultiIndexIter *iter, + const NMMultiIndexId **out_id, + void *const**out_values, + guint *out_len) +{ + const NMMultiIndexId *id; + ValuesData *values_data; + + g_return_val_if_fail (iter, FALSE); + + while (g_hash_table_iter_next (&iter->_iter, (gpointer *) &id, (gpointer *) &values_data)) { + if ( !iter->_value + || g_hash_table_contains (values_data->index, iter->_value)) { + _values_data_populate_array (values_data); + if (out_id) + *out_id = id; + if (out_values) + *out_values = values_data->values; + if (out_len) + *out_len = g_hash_table_size (values_data->index); + return TRUE; + } + } + return FALSE; +} + +/******************************************************************************************/ + +void +nm_multi_index_id_iter_init (NMMultiIndexIdIter *iter, + const NMMultiIndex *index, + const NMMultiIndexId *id) +{ + ValuesData *values_data; + + g_return_if_fail (index); + g_return_if_fail (iter); + g_return_if_fail (id); + + values_data = g_hash_table_lookup (index->hash, id); + if (!values_data) + iter->_state = 1; + else { + iter->_state = 0; + g_hash_table_iter_init (&iter->_iter, values_data->index); + } +} + +gboolean +nm_multi_index_id_iter_next (NMMultiIndexIdIter *iter, + void **out_value) +{ + g_return_val_if_fail (iter, FALSE); + g_return_val_if_fail (iter->_state <= 1, FALSE); + + if (iter->_state == 0) + return g_hash_table_iter_next (&iter->_iter, out_value, NULL); + else { + iter->_state = 2; + return FALSE; + } +} + +/******************************************************************************************/ + +static gboolean +_do_add (NMMultiIndex *index, + const NMMultiIndexId *id, + gconstpointer value) +{ + ValuesData *values_data; + + values_data = g_hash_table_lookup (index->hash, id); + if (!values_data) { + NMMultiIndexId *id_new; + + /* Contrary to GHashTable, we don't take ownership of the @id that was + * provided to nm_multi_index_add(). Instead we clone it via @clone_fcn + * when needed. + * + * The reason is, that we expect in most cases that there exists + * already a @id so that we don't need ownership of it (or clone it). + * By doing this, the caller can pass a stack allocated @id or + * reuse the @id for other insertions. + */ + id_new = index->clone_fcn (id); + if (!id_new) + g_return_val_if_reached (FALSE); + + values_data = _values_data_create (); + g_hash_table_replace (values_data->index, (gpointer) value, (gpointer) value); + + g_hash_table_insert (index->hash, id_new, values_data); + } else { + if (!nm_g_hash_table_replace (values_data->index, (gpointer) value, (gpointer) value)) + return FALSE; + g_clear_pointer (&values_data->values, g_free); + } + return TRUE; +} + +static gboolean +_do_remove (NMMultiIndex *index, + const NMMultiIndexId *id, + gconstpointer value) +{ + ValuesData *values_data; + + values_data = g_hash_table_lookup (index->hash, id); + if (!values_data) + return FALSE; + + if (!g_hash_table_remove (values_data->index, value)) + return FALSE; + + if (g_hash_table_size (values_data->index) == 0) + g_hash_table_remove (index->hash, id); + else + g_clear_pointer (&values_data->values, g_free); + return TRUE; +} + +gboolean +nm_multi_index_add (NMMultiIndex *index, + const NMMultiIndexId *id, + gconstpointer value) +{ + g_return_val_if_fail (index, FALSE); + g_return_val_if_fail (id, FALSE); + g_return_val_if_fail (value, FALSE); + + return _do_add (index, id, value); +} + +gboolean +nm_multi_index_remove (NMMultiIndex *index, + const NMMultiIndexId *id, + gconstpointer value) +{ + g_return_val_if_fail (index, FALSE); + g_return_val_if_fail (value, FALSE); + + if (!id) + g_return_val_if_reached (FALSE); + return _do_remove (index, id, value); +} + +/** + * nm_multi_index_move: + * @index: + * @id_old: (allow-none): remove @value at @id_old + * @id_new: (allow-none): add @value under @id_new + * @value: the value to add + * + * Similar to a remove(), followed by an add(). The difference + * is, that we allow %NULL for both @id_old and @id_new. + * And the return value indicates whether @value was successfully + * removed *and* added. + * + * Returns: %TRUE, if the value was removed from @id_old and added + * as %id_new. %FALSE could mean, that @value was not added to @id_old + * before, or that that @value was already part of @id_new. */ +gboolean +nm_multi_index_move (NMMultiIndex *index, + const NMMultiIndexId *id_old, + const NMMultiIndexId *id_new, + gconstpointer value) +{ + g_return_val_if_fail (index, FALSE); + g_return_val_if_fail (value, FALSE); + + if (!id_old && !id_new) { + /* nothing to do, @value was and is not in @index. */ + return TRUE; + } if (!id_old) { + /* add @value to @index with @id_new */ + return _do_add (index, id_new, value); + } else if (!id_new) { + /* remove @value from @index with @id_old */ + return _do_remove (index, id_old, value); + } else if (index->equal_fcn (id_old, id_new)) { + if (_do_add (index, id_new, value)) { + /* we would expect, that @value is already in @index, + * Return %FALSE, if it wasn't. */ + return FALSE; + } + return TRUE; + } else { + gboolean did_remove; + + did_remove = _do_remove (index, id_old, value); + return _do_add (index, id_new, value) && did_remove; + } +} + +/******************************************************************************************/ + +guint +nm_multi_index_get_num_groups (const NMMultiIndex *index) +{ + g_return_val_if_fail (index, 0); + return g_hash_table_size (index->hash); +} + +NMMultiIndex * +nm_multi_index_new (NMMultiIndexFuncHash hash_fcn, + NMMultiIndexFuncEqual equal_fcn, + NMMultiIndexFuncClone clone_fcn, + NMMultiIndexFuncDestroy destroy_fcn) +{ + NMMultiIndex *index; + + g_return_val_if_fail (hash_fcn, NULL); + g_return_val_if_fail (equal_fcn, NULL); + g_return_val_if_fail (clone_fcn, NULL); + g_return_val_if_fail (destroy_fcn, NULL); + + index = g_new (NMMultiIndex, 1); + index->equal_fcn = equal_fcn; + index->clone_fcn = clone_fcn; + + index->hash = g_hash_table_new_full ((GHashFunc) hash_fcn, + (GEqualFunc) equal_fcn, + (GDestroyNotify) destroy_fcn, + (GDestroyNotify) _values_data_destroy); + return index; +} + +void +nm_multi_index_free (NMMultiIndex *index) +{ + g_return_if_fail (index); + g_hash_table_unref (index->hash); + g_free (index); +} + diff --git a/src/nm-multi-index.h b/src/nm-multi-index.h new file mode 100644 index 00000000..e41ef54e --- /dev/null +++ b/src/nm-multi-index.h @@ -0,0 +1,109 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2015 Red Hat, Inc. + */ + +#ifndef __NM_MULTI_INDEX__ +#define __NM_MULTI_INDEX__ + +#include <glib.h> + +G_BEGIN_DECLS + + +typedef struct { + char _dummy; +} NMMultiIndexId; + +typedef struct NMMultiIndex NMMultiIndex; + +typedef struct { + GHashTableIter _iter; + const NMMultiIndex *_index; + gconstpointer _value; +} NMMultiIndexIter; + +typedef struct { + GHashTableIter _iter; + guint _state; +} NMMultiIndexIdIter; + +typedef gboolean (*NMMultiIndexFuncEqual) (const NMMultiIndexId *id_a, const NMMultiIndexId *id_b); +typedef guint (*NMMultiIndexFuncHash) (const NMMultiIndexId *id); +typedef NMMultiIndexId *(*NMMultiIndexFuncClone) (const NMMultiIndexId *id); +typedef void (*NMMultiIndexFuncDestroy) (NMMultiIndexId *id); + +typedef gboolean (*NMMultiIndexFuncForeach) (const NMMultiIndexId *id, void *const* values, guint len, gpointer user_data); + + +NMMultiIndex *nm_multi_index_new (NMMultiIndexFuncHash hash_fcn, + NMMultiIndexFuncEqual equal_fcn, + NMMultiIndexFuncClone clone_fcn, + NMMultiIndexFuncDestroy destroy_fcn); + +void nm_multi_index_free (NMMultiIndex *index); + +gboolean nm_multi_index_add (NMMultiIndex *index, + const NMMultiIndexId *id, + gconstpointer value); + +gboolean nm_multi_index_remove (NMMultiIndex *index, + const NMMultiIndexId *id, + gconstpointer value); + +gboolean nm_multi_index_move (NMMultiIndex *index, + const NMMultiIndexId *id_old, + const NMMultiIndexId *id_new, + gconstpointer value); + +guint nm_multi_index_get_num_groups (const NMMultiIndex *index); + +void *const*nm_multi_index_lookup (const NMMultiIndex *index, + const NMMultiIndexId *id, + guint *out_len); + +gboolean nm_multi_index_contains (const NMMultiIndex *index, + const NMMultiIndexId *id, + gconstpointer value); + +const NMMultiIndexId *nm_multi_index_lookup_first_by_value (const NMMultiIndex *index, + gconstpointer value); + +void nm_multi_index_foreach (const NMMultiIndex *index, + gconstpointer value, + NMMultiIndexFuncForeach foreach_func, + gpointer user_data); + +void nm_multi_index_iter_init (NMMultiIndexIter *iter, + const NMMultiIndex *index, + gconstpointer value); +gboolean nm_multi_index_iter_next (NMMultiIndexIter *iter, + const NMMultiIndexId **out_id, + void *const**out_values, + guint *out_len); + +void nm_multi_index_id_iter_init (NMMultiIndexIdIter *iter, + const NMMultiIndex *index, + const NMMultiIndexId *id); +gboolean nm_multi_index_id_iter_next (NMMultiIndexIdIter *iter, + void **out_value); + +G_END_DECLS + +#endif /* __NM_MULTI_INDEX__ */ + diff --git a/src/nm-policy.c b/src/nm-policy.c index 87fcdcc9..8a573c8b 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -170,7 +170,7 @@ _set_hostname (NMPolicy *policy, * there was no valid hostname to start with. */ - /* Clear lookup adresses if we have a hostname, so that we don't + /* Clear lookup addresses if we have a hostname, so that we don't * restart the reverse lookup thread later. */ if (new_hostname) diff --git a/src/nm-posix-signals.c b/src/nm-posix-signals.c deleted file mode 100644 index 22fed831..00000000 --- a/src/nm-posix-signals.c +++ /dev/null @@ -1,62 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 2012 Red Hat, Inc. - */ - -#include "config.h" - -#include <signal.h> - -#include "nm-posix-signals.h" - - -/* Stores the original signal mask of NetworkManager process */ -static sigset_t nm_original_signal_mask; - -void -nm_save_original_signal_mask (sigset_t sig_mask) -{ - nm_original_signal_mask = sig_mask; -} - -const sigset_t * -nm_get_original_signal_mask (void) -{ - return &nm_original_signal_mask; -} - -/* - * Unblock signals. - * If a signal set is passed, those signals are unblocked. If user_data is NULL - * the process' signal mask is set to the saved original mask. - * Note: This function can be used in g_spawn_* as GSpawnChildSetupFunc() - * callback. - */ -void -nm_unblock_posix_signals (gpointer user_data) -{ - sigset_t *user_sigset = (sigset_t *) user_data; - - if (user_sigset != NULL) { - pthread_sigmask (SIG_UNBLOCK, user_sigset, NULL); - } else { - const sigset_t *orig_sig_mask = nm_get_original_signal_mask (); - pthread_sigmask (SIG_SETMASK, orig_sig_mask, NULL); - } -} - diff --git a/src/nm-posix-signals.h b/src/nm-posix-signals.h deleted file mode 100644 index e2af559b..00000000 --- a/src/nm-posix-signals.h +++ /dev/null @@ -1,36 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 2012 Red Hat, Inc. - */ - -#ifndef __NETWORKMANAGER_POSIX_SIGNALS_H__ -#define __NETWORKMANAGER_POSIX_SIGNALS_H__ - -#include <glib.h> -#include <signal.h> - -/* - * This function can be used in g_spawn_* as GSpawnChildSetupFunc() - * callback. - */ -void nm_unblock_posix_signals (gpointer user_data); - -void nm_save_original_signal_mask (sigset_t sig_mask); -const sigset_t *nm_get_original_signal_mask (void); - -#endif /* __NETWORKMANAGER_POSIX_SIGNALS_H__ */ diff --git a/src/nm-route-manager.c b/src/nm-route-manager.c new file mode 100644 index 00000000..5ee6c865 --- /dev/null +++ b/src/nm-route-manager.c @@ -0,0 +1,1214 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2015 Red Hat, Inc. + */ + +#include <string.h> + +#include "config.h" + +#include "nm-route-manager.h" +#include "nm-platform.h" +#include "nmp-object.h" +#include "nm-core-internal.h" +#include "nm-logging.h" +#include "gsystem-local-alloc.h" +#include "NetworkManagerUtils.h" + +/* if within half a second after adding an IP address a matching device-route shows + * up, we delete it. */ +#define IP4_DEVICE_ROUTES_WAIT_TIME_NS (NM_UTILS_NS_PER_SECOND / 2) + +#define IP4_DEVICE_ROUTES_GC_INTERVAL_SEC (IP4_DEVICE_ROUTES_WAIT_TIME_NS * 2) + +typedef struct { + guint len; + NMPlatformIPXRoute *entries[1]; +} RouteIndex; + +typedef struct { + GArray *entries; + RouteIndex *index; + + /* list of effective metrics. The indexes of the array correspond to @index, not @entries. */ + GArray *effective_metrics; + + /* this array contains the effective metrics but using the reversed index that corresponds + * to @entries, instead of @index. */ + GArray *effective_metrics_reverse; +} RouteEntries; + +typedef struct { + NMRouteManager *self; + gint64 scheduled_at_ns; + guint idle_id; + NMPObject *obj; +} IP4DeviceRoutePurgeEntry; + +typedef struct { + NMPlatform *platform; + + RouteEntries ip4_routes; + RouteEntries ip6_routes; + struct { + GHashTable *entries; + guint gc_id; + } ip4_device_routes; +} NMRouteManagerPrivate; + +#define NM_ROUTE_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_ROUTE_MANAGER, NMRouteManagerPrivate)) + +G_DEFINE_TYPE (NMRouteManager, nm_route_manager, G_TYPE_OBJECT); + +NM_DEFINE_SINGLETON_GETTER (NMRouteManager, nm_route_manager_get, NM_TYPE_ROUTE_MANAGER); + +/*********************************************************************************************/ + +typedef struct { + const NMPlatformVTableRoute *vt; + + /* a compare function for two routes that considers only the destination fields network/plen. + * It is a looser comparisong then @route_id_cmp(), that means that if @route_dest_cmp() + * returns non-zero, also @route_id_cmp() returns the same value. It also means, that + * sorting by @route_id_cmp() implicitly sorts by @route_dest_cmp() as well. */ + int (*route_dest_cmp) (const NMPlatformIPXRoute *r1, const NMPlatformIPXRoute *r2); + + /* a compare function for two routes that considers only the fields network/plen,metric. */ + int (*route_id_cmp) (const NMPlatformIPXRoute *r1, const NMPlatformIPXRoute *r2); +} VTableIP; + +static const VTableIP vtable_v4, vtable_v6; + +#define VTABLE_ROUTE_INDEX(vtable, garray, idx) ((NMPlatformIPXRoute *) &((garray)->data[(idx) * (vtable)->vt->sizeof_route])) + +#define VTABLE_IS_DEVICE_ROUTE(vtable, route) ((vtable)->vt->is_ip4 \ + ? ((route)->r4.gateway == 0) \ + : IN6_IS_ADDR_UNSPECIFIED (&(route)->r6.gateway) ) + +#define CMP_AND_RETURN_INT(a, b) \ + G_STMT_START { \ + typeof(a) _a = (a), _b = (b); \ + \ + if (_a < _b) \ + return -1; \ + if (_a > _b) \ + return 1; \ + } G_STMT_END + +/*********************************************************************************************/ + +#define _LOG_PREFIX_NAME "route-mgr" + +#define _LOG(level, addr_family, ...) \ + G_STMT_START { \ + const int __addr_family = (addr_family); \ + const NMLogLevel __level = (level); \ + const NMLogDomain __domain = __addr_family == AF_INET ? LOGD_IP4 : (__addr_family == AF_INET6 ? LOGD_IP6 : LOGD_IP); \ + \ + if (nm_logging_enabled (__level, __domain)) { \ + char __ch = __addr_family == AF_INET ? '4' : (__addr_family == AF_INET6 ? '6' : '-'); \ + char __prefix[30] = _LOG_PREFIX_NAME; \ + \ + if ((self) != singleton_instance) \ + g_snprintf (__prefix, sizeof (__prefix), "%s%c[%p]", _LOG_PREFIX_NAME, __ch, (self)); \ + else \ + __prefix[STRLEN (_LOG_PREFIX_NAME)] = __ch; \ + _nm_log ((level), (__domain), 0, \ + "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + __prefix _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } \ + } G_STMT_END +#define _LOG_LEVEL_ENABLED(level, addr_family) \ + ({ \ + const int __addr_family = (addr_family); \ + const NMLogLevel __level = (level); \ + const NMLogDomain __domain = __addr_family == AF_INET ? LOGD_IP4 : (__addr_family == AF_INET6 ? LOGD_IP6 : LOGD_IP); \ + \ + nm_logging_enabled (__level, __domain); \ + }) + +#ifdef NM_MORE_LOGGING +#define _LOGT_ENABLED(addr_family) _LOG_LEVEL_ENABLED (LOGL_TRACE, addr_family) +#define _LOGT(addr_family, ...) _LOG (LOGL_TRACE, addr_family, __VA_ARGS__) +#else +#define _LOGT_ENABLED(addr_family) (FALSE && _LOG_LEVEL_ENABLED (LOGL_TRACE, addr_family)) +#define _LOGT(addr_family, ...) G_STMT_START { if (FALSE) { _LOG (LOGL_TRACE, addr_family, __VA_ARGS__); } } G_STMT_END +#endif + +#define _LOGD(addr_family, ...) _LOG (LOGL_DEBUG, addr_family, __VA_ARGS__) +#define _LOGI(addr_family, ...) _LOG (LOGL_INFO , addr_family, __VA_ARGS__) +#define _LOGW(addr_family, ...) _LOG (LOGL_WARN , addr_family, __VA_ARGS__) +#define _LOGE(addr_family, ...) _LOG (LOGL_ERR , addr_family, __VA_ARGS__) + +/*********************************************************************************************/ + +static gboolean _ip4_device_routes_cancel (NMRouteManager *self); + +/*********************************************************************************************/ + +#if defined (NM_MORE_ASSERTS) && !defined (G_DISABLE_ASSERT) +inline static void +ASSERT_route_index_valid (const VTableIP *vtable, const GArray *entries, const RouteIndex *index, gboolean unique_ifindexes) +{ + guint i, j; + int c; + const NMPlatformIPXRoute *r1, *r2; + gs_unref_hashtable GHashTable *ptrs = g_hash_table_new (NULL, NULL); + const NMPlatformIPXRoute *r_first = NULL, *r_last = NULL; + + g_assert (index); + + if (entries) + g_assert_cmpint (entries->len, ==, index->len); + else + g_assert (index->len == 0); + + if (index->len > 0) { + r_first = VTABLE_ROUTE_INDEX (vtable, entries, 0); + r_last = VTABLE_ROUTE_INDEX (vtable, entries, index->len - 1); + } + + /* assert that the @index is valid for the @entries. */ + + g_assert (!index->entries[index->len]); + for (i = 0; i < index->len; i++) { + r1 = index->entries[i]; + + g_assert (r1); + g_assert (r1 >= r_first); + g_assert (r1 <= r_last); + g_assert_cmpint ((((char *) r1) - ((char *) entries->data)) % vtable->vt->sizeof_route, ==, 0); + + g_assert (!g_hash_table_contains (ptrs, (gpointer) r1)); + g_hash_table_add (ptrs, (gpointer) r1); + + for (j = i; j > 0; ) { + r2 = index->entries[--j]; + + c = vtable->route_id_cmp (r1, r2); + g_assert (c >= 0); + if (c != 0) + break; + if (unique_ifindexes) + g_assert_cmpint (r1->rx.ifindex, !=, r2->rx.ifindex); + } + } +} +#else +#define ASSERT_route_index_valid(vtable, entries, index, unique_ifindexes) G_STMT_START { (void) 0; } G_STMT_END +#endif + +/*********************************************************************************************/ + +static int +_v4_route_dest_cmp (const NMPlatformIP4Route *r1, const NMPlatformIP4Route *r2) +{ + CMP_AND_RETURN_INT (r1->plen, r2->plen); + CMP_AND_RETURN_INT (nm_utils_ip4_address_clear_host_address (r1->network, r1->plen), + nm_utils_ip4_address_clear_host_address (r2->network, r2->plen)); + return 0; +} + +static int +_v6_route_dest_cmp (const NMPlatformIP6Route *r1, const NMPlatformIP6Route *r2) +{ + struct in6_addr n1, n2; + + CMP_AND_RETURN_INT (r1->plen, r2->plen); + + nm_utils_ip6_address_clear_host_address (&n1, &r1->network, r1->plen); + nm_utils_ip6_address_clear_host_address (&n2, &r2->network, r2->plen); + return memcmp (&n1, &n2, sizeof (n1)); +} + +static int +_v4_route_id_cmp (const NMPlatformIP4Route *r1, const NMPlatformIP4Route *r2) +{ + CMP_AND_RETURN_INT (r1->plen, r2->plen); + CMP_AND_RETURN_INT (nm_utils_ip4_address_clear_host_address (r1->network, r1->plen), + nm_utils_ip4_address_clear_host_address (r2->network, r2->plen)); + CMP_AND_RETURN_INT (r1->metric, r2->metric); + return 0; +} + +static int +_v6_route_id_cmp (const NMPlatformIP6Route *r1, const NMPlatformIP6Route *r2) +{ + struct in6_addr n1, n2; + int c; + + CMP_AND_RETURN_INT (r1->plen, r2->plen); + + nm_utils_ip6_address_clear_host_address (&n1, &r1->network, r1->plen); + nm_utils_ip6_address_clear_host_address (&n2, &r2->network, r2->plen); + c = memcmp (&n1, &n2, sizeof (n1)); + if (c != 0) + return c; + + CMP_AND_RETURN_INT (nm_utils_ip6_route_metric_normalize (r1->metric), + nm_utils_ip6_route_metric_normalize (r2->metric)); + return 0; +} + +/*********************************************************************************************/ + +static int +_route_index_create_sort (const NMPlatformIPXRoute **p1, const NMPlatformIPXRoute ** p2, const VTableIP *vtable) +{ + return vtable->route_id_cmp (*p1, *p2); +} + +static RouteIndex * +_route_index_create (const VTableIP *vtable, const GArray *routes) +{ + RouteIndex *index; + guint i; + guint len = routes ? routes->len : 0; + + index = g_malloc (sizeof (RouteIndex) + len * sizeof (NMPlatformIPXRoute *)); + + index->len = len; + for (i = 0; i < len; i++) + index->entries[i] = VTABLE_ROUTE_INDEX (vtable, routes, i); + index->entries[i] = NULL; + + /* this is a stable sort, which is very important at this point. */ + g_qsort_with_data (index->entries, + len, + sizeof (NMPlatformIPXRoute *), + (GCompareDataFunc) _route_index_create_sort, + (gpointer) vtable); + return index; +} + +static int +_vx_route_id_cmp_full (const NMPlatformIPXRoute *r1, const NMPlatformIPXRoute *r2, const VTableIP *vtable) +{ + return vtable->route_id_cmp (r1, r2); +} + +static gssize +_route_index_find (const VTableIP *vtable, const RouteIndex *index, const NMPlatformIPXRoute *needle) +{ + gssize idx, idx2; + + idx = _nm_utils_ptrarray_find_binary_search ((gpointer *) index->entries, index->len, (gpointer) needle, (GCompareDataFunc) _vx_route_id_cmp_full, (gpointer) vtable); + if (idx < 0) + return idx; + + /* we only know that the route at index @idx has matching destination. Also find the one with the right + * ifindex by searching the neighbours */ + + idx2 = idx; + do { + if (index->entries[idx2]->rx.ifindex == needle->rx.ifindex) + return idx2; + } while ( idx2 > 0 + && vtable->route_id_cmp (index->entries[--idx2], needle) != 0); + + for (idx++; idx < index->len; idx++ ){ + if (vtable->route_id_cmp (index->entries[idx], needle) != 0) + break; + if (index->entries[idx]->rx.ifindex == needle->rx.ifindex) + return idx; + } + + return ~idx; +} + +static guint +_route_index_reverse_idx (const VTableIP *vtable, const RouteIndex *index, guint idx_idx, const GArray *routes) +{ + const NMPlatformIPXRoute *r, *r0; + gssize offset; + + /* reverse the @idx_idx that points into @index, to the corresponding index into the unsorted @routes array. */ + + r = index->entries[idx_idx]; + r0 = VTABLE_ROUTE_INDEX (vtable, routes, 0); + + if (vtable->vt->is_ip4) + offset = &r->r4 - &r0->r4; + else + offset = &r->r6 - &r0->r6; + g_assert (offset >= 0 && offset < index->len); + g_assert (VTABLE_ROUTE_INDEX (vtable, routes, offset) == r); + return offset; +} + +/*********************************************************************************************/ + +static gboolean +_route_equals_ignoring_ifindex (const VTableIP *vtable, const NMPlatformIPXRoute *r1, const NMPlatformIPXRoute *r2, gint64 r2_metric) +{ + NMPlatformIPXRoute r2_backup; + + if ( r1->rx.ifindex != r2->rx.ifindex + || (r2_metric >= 0 && ((guint32) r2_metric) != r2->rx.metric)) { + memcpy (&r2_backup, r2, vtable->vt->sizeof_route); + r2_backup.rx.ifindex = r1->rx.ifindex; + if (r2_metric >= 0) + r2_backup.rx.metric = (guint32) r2_metric; + r2 = &r2_backup; + } + return vtable->vt->route_cmp (r1, r2) == 0; +} + +static NMPlatformIPXRoute * +_get_next_ipx_route (const RouteIndex *index, gboolean start_at_zero, guint *cur_idx, int ifindex) +{ + guint i; + + if (start_at_zero) + i = 0; + else + i = *cur_idx + 1; + /* Find the next route with matching @ifindex. */ + for (; i < index->len; i++) { + if (index->entries[i]->rx.ifindex == ifindex) { + *cur_idx = i; + return index->entries[i]; + } + } + *cur_idx = index->len; + return NULL; +} + +static const NMPlatformIPXRoute * +_get_next_known_route (const VTableIP *vtable, const RouteIndex *index, gboolean start_at_zero, guint *cur_idx) +{ + guint i = 0; + const NMPlatformIPXRoute *cur = NULL; + + if (!start_at_zero) { + i = *cur_idx; + cur = index->entries[i]; + i++; + } + /* For @known_routes we expect that all routes have the same @ifindex. This is not enforced however, + * the ifindex value of these routes is ignored. */ + for (; i < index->len; i++) { + const NMPlatformIPXRoute *r = index->entries[i]; + + /* skip over default routes. */ + if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r)) + continue; + + /* @known_routes should not, but could contain duplicate routes. Skip over them. */ + if (cur && vtable->route_id_cmp (cur, r) == 0) + continue; + + *cur_idx = i; + return r; + } + *cur_idx = index->len; + return NULL; +} + +static const NMPlatformIPXRoute * +_get_next_plat_route (const RouteIndex *index, gboolean start_at_zero, guint *cur_idx) +{ + if (start_at_zero) + *cur_idx = 0; + else + ++*cur_idx; + + /* get next route from the platform index. */ + if (*cur_idx < index->len) + return index->entries[*cur_idx]; + *cur_idx = index->len; + return NULL; +} + +static int +_sort_indexes_cmp (guint *a, guint *b) +{ + CMP_AND_RETURN_INT (*a, *b); + g_return_val_if_reached (0); +} + +/*********************************************************************************************/ + +static gboolean +_vx_route_sync (const VTableIP *vtable, NMRouteManager *self, int ifindex, const GArray *known_routes, gboolean ignore_kernel_routes, gboolean full_sync) +{ + NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); + GArray *plat_routes; + RouteEntries *ipx_routes; + RouteIndex *plat_routes_idx, *known_routes_idx; + gboolean success = TRUE; + guint i, i_type; + GArray *to_delete_indexes = NULL; + GPtrArray *to_add_routes = NULL; + guint i_known_routes, i_plat_routes, i_ipx_routes; + const NMPlatformIPXRoute *cur_known_route, *cur_plat_route; + NMPlatformIPXRoute *cur_ipx_route; + gint64 *p_effective_metric = NULL; + gboolean ipx_routes_changed = FALSE; + gint64 *effective_metrics = NULL; + + nm_platform_process_events (priv->platform); + + ipx_routes = vtable->vt->is_ip4 ? &priv->ip4_routes : &priv->ip6_routes; + plat_routes = vtable->vt->route_get_all (priv->platform, ifindex, + ignore_kernel_routes + ? NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT + : NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_RTPROT_KERNEL); + plat_routes_idx = _route_index_create (vtable, plat_routes); + known_routes_idx = _route_index_create (vtable, known_routes); + + effective_metrics = &g_array_index (ipx_routes->effective_metrics, gint64, 0); + + ASSERT_route_index_valid (vtable, plat_routes, plat_routes_idx, TRUE); + ASSERT_route_index_valid (vtable, known_routes, known_routes_idx, FALSE); + + _LOGD (vtable->vt->addr_family, "%3d: sync %u IPv%c routes", ifindex, known_routes_idx->len, vtable->vt->is_ip4 ? '4' : '6'); + if (_LOGT_ENABLED (vtable->vt->addr_family)) { + for (i = 0; i < known_routes_idx->len; i++) { + _LOGT (vtable->vt->addr_family, "%3d: sync new route #%u: %s", + ifindex, i, vtable->vt->route_to_string (VTABLE_ROUTE_INDEX (vtable, known_routes, i))); + } + for (i = 0; i < ipx_routes->index->len; i++) + _LOGT (vtable->vt->addr_family, "%3d: STATE: has #%u - %s (%lld)", + ifindex, i, + vtable->vt->route_to_string (ipx_routes->index->entries[i]), + (long long) g_array_index (ipx_routes->effective_metrics, gint64, i)); + } + + /*************************************************************************** + * Check which routes are in @known_routes, and update @ipx_routes. + * + * This first part only updates @ipx_routes to find out what routes must + * be added/deleted. + **************************************************************************/ + + /* iterate over @ipx_routes and @known_routes */ + cur_ipx_route = _get_next_ipx_route (ipx_routes->index, TRUE, &i_ipx_routes, ifindex); + cur_known_route = _get_next_known_route (vtable, known_routes_idx, TRUE, &i_known_routes); + while (cur_ipx_route || cur_known_route) { + int route_id_cmp_result = -1; + + while ( cur_ipx_route + && ( !cur_known_route + || ((route_id_cmp_result = vtable->route_id_cmp (cur_ipx_route, cur_known_route)) < 0))) { + /* we have @cur_ipx_route, which is less then @cur_known_route. Hence, + * the route does no longer exist in @known_routes */ + if (!to_delete_indexes) + to_delete_indexes = g_array_new (FALSE, FALSE, sizeof (guint)); + g_array_append_val (to_delete_indexes, i_ipx_routes); + + /* find the next @cur_ipx_route with matching ifindex. */ + cur_ipx_route = _get_next_ipx_route (ipx_routes->index, FALSE, &i_ipx_routes, ifindex); + } + if ( cur_ipx_route + && cur_known_route + && route_id_cmp_result == 0) { + if (!_route_equals_ignoring_ifindex (vtable, cur_ipx_route, cur_known_route, -1)) { + /* The routes match. Update the entry in place. As this is an exact match of primary + * fields, this only updates possibly modified fields such as @gateway or @mss. + * Modifiying @cur_ipx_route this way does not invalidate @ipx_routes->index. */ + memcpy (cur_ipx_route, cur_known_route, vtable->vt->sizeof_route); + cur_ipx_route->rx.ifindex = ifindex; + cur_ipx_route->rx.metric = vtable->vt->metric_normalize (cur_ipx_route->rx.metric); + ipx_routes_changed = TRUE; + _LOGT (vtable->vt->addr_family, "%3d: STATE: update #%u - %s", ifindex, i_ipx_routes, vtable->vt->route_to_string (cur_ipx_route)); + } + } else if (cur_known_route) { + g_assert (!cur_ipx_route || route_id_cmp_result > 0); + /* @cur_known_route is new. We cannot immediately add @cur_known_route to @ipx_routes, because + * it would invalidate @ipx_routes->index. Instead remember to add it later. */ + if (!to_add_routes) + to_add_routes = g_ptr_array_new (); + g_ptr_array_add (to_add_routes, (gpointer) cur_known_route); + } + + if (cur_ipx_route && (!cur_known_route || route_id_cmp_result == 0)) + cur_ipx_route = _get_next_ipx_route (ipx_routes->index, FALSE, &i_ipx_routes, ifindex); + if (cur_known_route) + cur_known_route = _get_next_known_route (vtable, known_routes_idx, FALSE, &i_known_routes); + } + + if (!full_sync && to_delete_indexes) { + /*************************************************************************** + * Delete routes in platform, that we are about to remove from @ipx_routes + * + * When doing a non-full_sync, we delete routes from platform that were previously + * known by route-manager, and are now deleted. + ***************************************************************************/ + + /* iterate over @to_delete_indexes and @plat_routes. + * @to_delete_indexes contains the indexes (relative to ipx_routes->index) of items + * we are about to delete. */ + cur_plat_route = _get_next_plat_route (plat_routes_idx, TRUE, &i_plat_routes); + for (i = 0; i < to_delete_indexes->len; i++) { + int route_dest_cmp_result = 0; + i_ipx_routes = g_array_index (to_delete_indexes, guint, i); + cur_ipx_route = ipx_routes->index->entries[i_ipx_routes]; + p_effective_metric = &effective_metrics[i_ipx_routes]; + + nm_assert (cur_ipx_route->rx.ifindex == ifindex); + + if (*p_effective_metric == -1) + continue; + + /* skip over @plat_routes that are ordered before our @cur_ipx_route. */ + while ( cur_plat_route + && (route_dest_cmp_result = vtable->route_dest_cmp (cur_plat_route, cur_ipx_route)) <= 0) { + if ( route_dest_cmp_result == 0 + && cur_plat_route->rx.metric >= *p_effective_metric) + break; + cur_plat_route = _get_next_plat_route (plat_routes_idx, FALSE, &i_plat_routes); + } + + if (!cur_plat_route) { + /* no more platform routes. Break the loop. */ + break; + } + + if ( route_dest_cmp_result == 0 + && cur_plat_route->rx.metric == *p_effective_metric) { + /* we are about to delete cur_ipx_route and we have a matching route + * in platform. Delete it. */ + _LOGT (vtable->vt->addr_family, "%3d: platform rt-rm #%u - %s", ifindex, i_plat_routes, vtable->vt->route_to_string (cur_plat_route)); + vtable->vt->route_delete (priv->platform, ifindex, cur_plat_route); + } + } + } + + /* Update @ipx_routes with the just learned changes. */ + if (to_delete_indexes || to_add_routes) { + if (to_delete_indexes) { + for (i = 0; i < to_delete_indexes->len; i++) { + guint idx = g_array_index (to_delete_indexes, guint, i); + + _LOGT (vtable->vt->addr_family, "%3d: STATE: delete #%u - %s", ifindex, idx, vtable->vt->route_to_string (ipx_routes->index->entries[idx])); + g_array_index (to_delete_indexes, guint, i) = _route_index_reverse_idx (vtable, ipx_routes->index, idx, ipx_routes->entries); + } + g_array_sort (to_delete_indexes, (GCompareFunc) _sort_indexes_cmp); + nm_utils_array_remove_at_indexes (ipx_routes->entries, &g_array_index (to_delete_indexes, guint, 0), to_delete_indexes->len); + nm_utils_array_remove_at_indexes (ipx_routes->effective_metrics_reverse, &g_array_index (to_delete_indexes, guint, 0), to_delete_indexes->len); + g_array_unref (to_delete_indexes); + } + if (to_add_routes) { + guint j = ipx_routes->effective_metrics_reverse->len; + + g_array_set_size (ipx_routes->effective_metrics_reverse, j + to_add_routes->len); + + for (i = 0; i < to_add_routes->len; i++) { + NMPlatformIPXRoute *ipx_route; + + g_array_append_vals (ipx_routes->entries, g_ptr_array_index (to_add_routes, i), 1); + + ipx_route = VTABLE_ROUTE_INDEX (vtable, ipx_routes->entries, ipx_routes->entries->len - 1); + ipx_route->rx.ifindex = ifindex; + ipx_route->rx.metric = vtable->vt->metric_normalize (ipx_route->rx.metric); + + g_array_index (ipx_routes->effective_metrics_reverse, gint64, j++) = -1; + + _LOGT (vtable->vt->addr_family, "%3d: STATE: added #%u - %s", ifindex, ipx_routes->entries->len - 1, vtable->vt->route_to_string (ipx_route)); + } + g_ptr_array_unref (to_add_routes); + } + g_free (ipx_routes->index); + ipx_routes->index = _route_index_create (vtable, ipx_routes->entries); + ipx_routes_changed = TRUE; + ASSERT_route_index_valid (vtable, ipx_routes->entries, ipx_routes->index, TRUE); + } + + if (ipx_routes_changed) { + /*************************************************************************** + * Rebuild the list of effective metrics. In case of conflicting routes, + * we configure device routes with a bumped metric. We do this, because non-direct + * routes might require this direct route to reach the gateway (e.g. the default + * route). + * + * We determine the effective metrics only based on our internal list @ipx_routes + * and don't consider @plat_routes. That means, we might bump the metric of a route + * and thereby cause a conflict with an existing route on an unmanaged device (which + * causes the route on the unmanaged device to be replaced). + * Still, that is not much different then from messing with unmanaged routes when + * the effective and the intended metrics equal. The rules is: NM will leave routes + * on unmanged devices alone, unless they conflict with what NM wants to configure. + ***************************************************************************/ + + g_array_set_size (ipx_routes->effective_metrics, ipx_routes->entries->len); + effective_metrics = &g_array_index (ipx_routes->effective_metrics, gint64, 0); + + /* Completely regenerate the list of effective metrics by walking through + * ipx_routes->index and determining the effective metric. */ + + for (i_ipx_routes = 0; i_ipx_routes < ipx_routes->index->len; i_ipx_routes++) { + gint64 *p_effective_metric_before; + gboolean is_shadowed; + guint i_ipx_routes_before; + + cur_ipx_route = ipx_routes->index->entries[i_ipx_routes]; + p_effective_metric = &effective_metrics[i_ipx_routes]; + + is_shadowed = i_ipx_routes > 0 + && vtable->route_dest_cmp (cur_ipx_route, ipx_routes->index->entries[i_ipx_routes - 1]) == 0; + + if (!is_shadowed) { + /* the route is not shadowed, the effective metric is just as specified. */ + *p_effective_metric = cur_ipx_route->rx.metric; + goto next; + } + if (!VTABLE_IS_DEVICE_ROUTE (vtable, cur_ipx_route)) { + /* The route is not a device route. We want to add redundant device routes, because + * we might need the direct routes to the gateway. For non-direct routes, there is not much + * reason to do the metric increment. */ + *p_effective_metric = -1; + goto next; + } + + /* The current route might be shadowed by several other routes. Find the one with the highest metric, + * i.e. the one with an effecive metric set and in the index before the current index. */ + i_ipx_routes_before = i_ipx_routes; + while (TRUE) { + nm_assert (i_ipx_routes_before > 0); + + i_ipx_routes_before--; + + p_effective_metric_before = &effective_metrics[i_ipx_routes_before]; + + if (*p_effective_metric_before == -1) { + /* this route is also shadowed, continue search. */ + continue; + } + + if (*p_effective_metric_before < cur_ipx_route->rx.metric) { + /* the previous route has a lower metric. There is no conflict, + * just use the original metric. */ + *p_effective_metric = cur_ipx_route->rx.metric; + } else if (*p_effective_metric_before == G_MAXUINT32) { + /* we cannot bump the metric. Don't configure this route. */ + *p_effective_metric = -1; + } else { + /* bump the metric by one. */ + *p_effective_metric = *p_effective_metric_before + 1; + } + break; + } +next: + _LOGT (vtable->vt->addr_family, "%3d: new metric #%u - %s (%lld)", + ifindex, i_ipx_routes, + vtable->vt->route_to_string (cur_ipx_route), + (long long) *p_effective_metric); + } + } + + if (full_sync) { + /*************************************************************************** + * Delete all routes in platform, that no longer exist in @ipx_routes + * + * Different from the delete action above, we delete every unknown route on + * the interface. + ***************************************************************************/ + + /* iterate over @plat_routes and @ipx_routes */ + cur_plat_route = _get_next_plat_route (plat_routes_idx, TRUE, &i_plat_routes); + cur_ipx_route = _get_next_ipx_route (ipx_routes->index, TRUE, &i_ipx_routes, ifindex); + if (cur_ipx_route) + p_effective_metric = &effective_metrics[i_ipx_routes]; + while (cur_plat_route) { + int route_dest_cmp_result = 0; + + g_assert (cur_plat_route->rx.ifindex == ifindex); + + _LOGT (vtable->vt->addr_family, "%3d: platform rt #%u - %s", ifindex, i_plat_routes, vtable->vt->route_to_string (cur_plat_route)); + + /* skip over @cur_ipx_route that are ordered before @cur_plat_route */ + while ( cur_ipx_route + && ((route_dest_cmp_result = vtable->route_dest_cmp (cur_ipx_route, cur_plat_route)) <= 0)) { + if ( route_dest_cmp_result == 0 + && *p_effective_metric != -1 + && *p_effective_metric >= cur_plat_route->rx.metric) { + break; + } + cur_ipx_route = _get_next_ipx_route (ipx_routes->index, FALSE, &i_ipx_routes, ifindex); + if (cur_ipx_route) + p_effective_metric = &effective_metrics[i_ipx_routes]; + } + + /* if @cur_ipx_route is not equal to @plat_route, the route must be deleted. */ + if ( !cur_ipx_route + || route_dest_cmp_result != 0 + || *p_effective_metric != cur_plat_route->rx.metric) + vtable->vt->route_delete (priv->platform, ifindex, cur_plat_route); + + cur_plat_route = _get_next_plat_route (plat_routes_idx, FALSE, &i_plat_routes); + } + } + + /*************************************************************************** + * Restore shadowed routes. These routes are on an other @ifindex then what + * we are syncing now. But the current changes make it necessary to add those + * routes. + * + * Only add some routes that might be necessary. We don't delete any routes + * on other ifindexes here. I.e. we don't do a full sync, but only ~add~ routes + * that were shadowed previously, but should be now present with a different + * metric. + **************************************************************************/ + + if (ipx_routes_changed) { + GArray *gateway_routes = NULL; + + /* @effective_metrics_reverse contains the list of assigned metrics from the last + * sync. Walk through it and see what changes there are (and possibly restore a + * shadowed route). + * Thereby also update @effective_metrics_reverse to be up-to-date again. */ + for (i_ipx_routes = 0; i_ipx_routes < ipx_routes->entries->len; i_ipx_routes++) { + guint i_ipx_routes_reverse; + gint64 *p_effective_metric_reversed; + + p_effective_metric = &effective_metrics[i_ipx_routes]; + + i_ipx_routes_reverse = _route_index_reverse_idx (vtable, ipx_routes->index, i_ipx_routes, ipx_routes->entries); + p_effective_metric_reversed = &g_array_index (ipx_routes->effective_metrics_reverse, gint64, i_ipx_routes_reverse); + + if (*p_effective_metric_reversed == *p_effective_metric) { + /* The entry is up to date. No change, continue with the next one. */ + continue; + } + *p_effective_metric_reversed = *p_effective_metric; + + if (*p_effective_metric == -1) { + /* the entry is shadowed. Nothing to do. */ + continue; + } + + cur_ipx_route = ipx_routes->index->entries[i_ipx_routes]; + if (cur_ipx_route->rx.ifindex == ifindex) { + /* @cur_ipx_route is on the current @ifindex. No need to special handling them + * because we are about to do a full sync of the ifindex. */ + continue; + } + + /* the effective metric from previous sync changed. While @cur_ipx_route is not on the + * ifindex we are about to sync, we still must add this route. Possibly it was shadowed + * before, and now we want to restore it. + * + * Note that we don't do a full sync on the other ifindex. Especially, we don't delete + * or add any further routes then this. That means there might be some stale routes + * (with a higher metric!). They will only be removed on the next sync of that other + * ifindex. */ + + if (!VTABLE_IS_DEVICE_ROUTE (vtable, cur_ipx_route)) { + /* the route to restore has a gateway. We can only restore the route + * when we also have a direct route to the gateway. There can be cases + * where the direct route is shadowed too, and we cannot restore the gateway + * route. + * + * Restore first the direct-routes, and gateway-routes afterwards. + * This can avoid some cases where we would fail to add the + * gateway route. */ + if (!gateway_routes) + gateway_routes = g_array_new (FALSE, FALSE, sizeof (guint)); + g_array_append_val (gateway_routes, i_ipx_routes); + } else + vtable->vt->route_add (priv->platform, 0, cur_ipx_route, *p_effective_metric); + } + + if (gateway_routes) { + for (i = 0; i < gateway_routes->len; i++) { + i_ipx_routes = g_array_index (gateway_routes, guint, i); + vtable->vt->route_add (priv->platform, 0, + ipx_routes->index->entries[i_ipx_routes], + effective_metrics[i_ipx_routes]); + } + g_array_unref (gateway_routes); + } + } + + /*************************************************************************** + * Sync @ipx_routes for @ifindex to platform + **************************************************************************/ + + for (i_type = 0; i_type < 2; i_type++) { + /* iterate (twice) over @ipx_routes and @plat_routes */ + cur_plat_route = _get_next_plat_route (plat_routes_idx, TRUE, &i_plat_routes); + cur_ipx_route = _get_next_ipx_route (ipx_routes->index, TRUE, &i_ipx_routes, ifindex); + /* Iterate here over @ipx_routes instead of @known_routes. That is done because + * we need to know whether a route is shadowed by another route, and that + * requires to look at @ipx_routes. */ + for (; cur_ipx_route; cur_ipx_route = _get_next_ipx_route (ipx_routes->index, FALSE, &i_ipx_routes, ifindex)) { + int route_dest_cmp_result = -1; + + if ( (i_type == 0 && !VTABLE_IS_DEVICE_ROUTE (vtable, cur_ipx_route)) + || (i_type == 1 && VTABLE_IS_DEVICE_ROUTE (vtable, cur_ipx_route))) { + /* Make two runs over the list of @ipx_routes. On the first, only add + * device routes, on the second the others (gateway routes). */ + continue; + } + + p_effective_metric = &effective_metrics[i_ipx_routes]; + + if (*p_effective_metric == -1) { + /* @cur_ipx_route is shadewed by another route. */ + continue; + } + + /* skip over @plat_routes that are ordered before our @cur_ipx_route. */ + while ( cur_plat_route + && (route_dest_cmp_result = vtable->route_dest_cmp (cur_plat_route, cur_ipx_route)) <= 0) { + if ( route_dest_cmp_result == 0 + && cur_plat_route->rx.metric >= *p_effective_metric) + break; + cur_plat_route = _get_next_plat_route (plat_routes_idx, FALSE, &i_plat_routes); + } + + /* only add the route if we don't have an identical route in @plat_routes, + * i.e. if @cur_plat_route is different from @cur_ipx_route. */ + if ( !cur_plat_route + || route_dest_cmp_result != 0 + || !_route_equals_ignoring_ifindex (vtable, cur_plat_route, cur_ipx_route, *p_effective_metric)) { + + if (!vtable->vt->route_add (priv->platform, ifindex, cur_ipx_route, *p_effective_metric)) { + if (cur_ipx_route->rx.source < NM_IP_CONFIG_SOURCE_USER) { + _LOGD (vtable->vt->addr_family, + "ignore error adding IPv%c route to kernel: %s", + vtable->vt->is_ip4 ? '4' : '6', + vtable->vt->route_to_string (cur_ipx_route)); + } else { + /* Remember that there was a failure, but for now continue trying + * to sync the remaining routes. */ + success = FALSE; + } + } + } + } + } + + g_free (known_routes_idx); + g_free (plat_routes_idx); + g_array_unref (plat_routes); + + return success; +} + +/** + * nm_route_manager_ip4_route_sync: + * @ifindex: Interface index + * @known_routes: List of routes + * @ignore_kernel_routes: if %TRUE, ignore kernel routes. + * @full_sync: whether to do a full sync and delete routes + * that are configured on the interface but not currently + * tracked by route-manager. + * + * A convenience function to synchronize routes for a specific interface + * with the least possible disturbance. It simply removes routes that are + * not listed and adds routes that are. + * Default routes are ignored (both in @known_routes and those already + * configured on the device). + * + * Returns: %TRUE on success. + */ +gboolean +nm_route_manager_ip4_route_sync (NMRouteManager *self, int ifindex, const GArray *known_routes, gboolean ignore_kernel_routes, gboolean full_sync) +{ + return _vx_route_sync (&vtable_v4, self, ifindex, known_routes, ignore_kernel_routes, full_sync); +} + +/** + * nm_route_manager_ip6_route_sync: + * @ifindex: Interface index + * @known_routes: List of routes + * @ignore_kernel_routes: if %TRUE, ignore kernel routes. + * @full_sync: whether to do a full sync and delete routes + * that are configured on the interface but not currently + * tracked by route-manager. + * + * A convenience function to synchronize routes for a specific interface + * with the least possible disturbance. It simply removes routes that are + * not listed and adds routes that are. + * Default routes are ignored (both in @known_routes and those already + * configured on the device). + * + * Returns: %TRUE on success. + */ +gboolean +nm_route_manager_ip6_route_sync (NMRouteManager *self, int ifindex, const GArray *known_routes, gboolean ignore_kernel_routes, gboolean full_sync) +{ + return _vx_route_sync (&vtable_v6, self, ifindex, known_routes, ignore_kernel_routes, full_sync); +} + +gboolean +nm_route_manager_route_flush (NMRouteManager *self, int ifindex) +{ + return nm_route_manager_ip4_route_sync (self, ifindex, NULL, FALSE, TRUE) + && nm_route_manager_ip6_route_sync (self, ifindex, NULL, FALSE, TRUE); +} + +/*********************************************************************************************/ + +static gboolean +_ip4_device_routes_entry_expired (const IP4DeviceRoutePurgeEntry *entry, gint64 now) +{ + return entry->scheduled_at_ns + IP4_DEVICE_ROUTES_WAIT_TIME_NS < now; +} + +static IP4DeviceRoutePurgeEntry * +_ip4_device_routes_purge_entry_create (NMRouteManager *self, const NMPlatformIP4Route *route, gint64 now_ns) +{ + IP4DeviceRoutePurgeEntry *entry; + + entry = g_slice_new (IP4DeviceRoutePurgeEntry); + + entry->self = self; + entry->scheduled_at_ns = now_ns; + entry->idle_id = 0; + entry->obj = nmp_object_new (NMP_OBJECT_TYPE_IP4_ROUTE, (NMPlatformObject *) route); + return entry; +} + +static void +_ip4_device_routes_purge_entry_free (IP4DeviceRoutePurgeEntry *entry) +{ + nmp_object_unref (entry->obj); + nm_clear_g_source (&entry->idle_id); + g_slice_free (IP4DeviceRoutePurgeEntry, entry); +} + +static gboolean +_ip4_device_routes_idle_cb (IP4DeviceRoutePurgeEntry *entry) +{ + NMRouteManager *self; + NMRouteManagerPrivate *priv; + + nm_clear_g_source (&entry->idle_id); + + self = entry->self; + priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); + if (_route_index_find (&vtable_v4, priv->ip4_routes.index, &entry->obj->ipx_route) >= 0) { + /* we have an identical route in our list. Don't delete it. */ + return G_SOURCE_REMOVE; + } + + _LOGT (vtable_v4.vt->addr_family, "device-route: delete %s", nmp_object_to_string (entry->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + + nm_platform_ip4_route_delete (priv->platform, + entry->obj->ip4_route.ifindex, + entry->obj->ip4_route.network, + entry->obj->ip4_route.plen, + entry->obj->ip4_route.metric); + + g_hash_table_remove (priv->ip4_device_routes.entries, entry->obj); + _ip4_device_routes_cancel (self); + return G_SOURCE_REMOVE; +} + +static void +_ip4_device_routes_ip4_route_changed (NMPlatform *platform, + NMPObjectType obj_type, + int ifindex, + const NMPlatformIP4Route *route, + NMPlatformSignalChangeType change_type, + NMPlatformReason reason, + NMRouteManager *self) +{ + NMRouteManagerPrivate *priv; + NMPObject obj_needle; + IP4DeviceRoutePurgeEntry *entry; + + if (change_type == NM_PLATFORM_SIGNAL_REMOVED) + return; + + if ( route->source != NM_IP_CONFIG_SOURCE_RTPROT_KERNEL + || route->metric != 0) { + /* we don't have an automatically created device route at hand. Bail out early. */ + return; + } + + priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); + + entry = g_hash_table_lookup (priv->ip4_device_routes.entries, + nmp_object_stackinit (&obj_needle, NMP_OBJECT_TYPE_IP4_ROUTE, (NMPlatformObject *) route)); + if (!entry) + return; + + if (_ip4_device_routes_entry_expired (entry, nm_utils_get_monotonic_timestamp_ns ())) { + _LOGT (vtable_v4.vt->addr_family, "device-route: cleanup-ch %s", nmp_object_to_string (entry->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + g_hash_table_remove (priv->ip4_device_routes.entries, entry->obj); + _ip4_device_routes_cancel (self); + return; + } + + if (entry->idle_id == 0) { + _LOGT (vtable_v4.vt->addr_family, "device-route: schedule %s", nmp_object_to_string (entry->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + entry->idle_id = g_idle_add ((GSourceFunc) _ip4_device_routes_idle_cb, entry); + } +} + +static gboolean +_ip4_device_routes_cancel (NMRouteManager *self) +{ + NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); + + if (priv->ip4_device_routes.gc_id) { + if (g_hash_table_size (priv->ip4_device_routes.entries) > 0) + return G_SOURCE_CONTINUE; + _LOGT (vtable_v4.vt->addr_family, "device-route: cancel"); + if (priv->platform) + g_signal_handlers_disconnect_by_func (priv->platform, G_CALLBACK (_ip4_device_routes_ip4_route_changed), self); + nm_clear_g_source (&priv->ip4_device_routes.gc_id); + } + return G_SOURCE_REMOVE; +} + +static gboolean +_ip4_device_routes_gc (NMRouteManager *self) +{ + NMRouteManagerPrivate *priv; + GHashTableIter iter; + IP4DeviceRoutePurgeEntry *entry; + gint64 now = nm_utils_get_monotonic_timestamp_ns (); + + priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); + + g_hash_table_iter_init (&iter, priv->ip4_device_routes.entries); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &entry)) { + if (_ip4_device_routes_entry_expired (entry, now)) { + _LOGT (vtable_v4.vt->addr_family, "device-route: cleanup-gc %s", nmp_object_to_string (entry->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + g_hash_table_iter_remove (&iter); + } + } + + return _ip4_device_routes_cancel (self); +} + +/** + * nm_route_manager_ip4_route_register_device_route_purge_list: + * + * When adding an IPv4 address, kernel will automatically add a device route with + * metric zero. We don't want that route and want to delete it. However, the route + * by kernel immediately, but some time after. That means during nm_route_manager_ip4_route_sync() + * such a route doesn't exist yet. We must remember that we expect such a route to appear later + * and to remove it. */ +void +nm_route_manager_ip4_route_register_device_route_purge_list (NMRouteManager *self, GArray *device_route_purge_list) +{ + NMRouteManagerPrivate *priv; + guint i; + gint64 now_ns; + + if (!device_route_purge_list || device_route_purge_list->len == 0) + return; + + priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); + + now_ns = nm_utils_get_monotonic_timestamp_ns (); + for (i = 0; i < device_route_purge_list->len; i++) { + IP4DeviceRoutePurgeEntry *entry; + + entry = _ip4_device_routes_purge_entry_create (self, &g_array_index (device_route_purge_list, NMPlatformIP4Route, i), now_ns); + _LOGT (vtable_v4.vt->addr_family, "device-route: watch (%s) %s", + g_hash_table_contains (priv->ip4_device_routes.entries, entry->obj) + ? "update" : "new", + nmp_object_to_string (entry->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + g_hash_table_replace (priv->ip4_device_routes.entries, + nmp_object_ref (entry->obj), + entry); + } + if (priv->ip4_device_routes.gc_id == 0) { + g_signal_connect (priv->platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (_ip4_device_routes_ip4_route_changed), self); + priv->ip4_device_routes.gc_id = g_timeout_add (IP4_DEVICE_ROUTES_GC_INTERVAL_SEC, (GSourceFunc) _ip4_device_routes_gc, self); + } +} + +/*********************************************************************************************/ + +static const VTableIP vtable_v4 = { + .vt = &nm_platform_vtable_route_v4, + .route_dest_cmp = (int (*) (const NMPlatformIPXRoute *, const NMPlatformIPXRoute *)) _v4_route_dest_cmp, + .route_id_cmp = (int (*) (const NMPlatformIPXRoute *, const NMPlatformIPXRoute *)) _v4_route_id_cmp, +}; + +static const VTableIP vtable_v6 = { + .vt = &nm_platform_vtable_route_v6, + .route_dest_cmp = (int (*) (const NMPlatformIPXRoute *, const NMPlatformIPXRoute *)) _v6_route_dest_cmp, + .route_id_cmp = (int (*) (const NMPlatformIPXRoute *, const NMPlatformIPXRoute *)) _v6_route_id_cmp, +}; + +/*********************************************************************************************/ + +static void +nm_route_manager_init (NMRouteManager *self) +{ + NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); + + priv->platform = g_object_ref (NM_PLATFORM_GET); + + priv->ip4_routes.entries = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route)); + priv->ip6_routes.entries = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP6Route)); + priv->ip4_routes.effective_metrics = g_array_new (FALSE, FALSE, sizeof (gint64)); + priv->ip6_routes.effective_metrics = g_array_new (FALSE, FALSE, sizeof (gint64)); + priv->ip4_routes.effective_metrics_reverse = g_array_new (FALSE, FALSE, sizeof (gint64)); + priv->ip6_routes.effective_metrics_reverse = g_array_new (FALSE, FALSE, sizeof (gint64)); + priv->ip4_routes.index = _route_index_create (&vtable_v4, priv->ip4_routes.entries); + priv->ip6_routes.index = _route_index_create (&vtable_v6, priv->ip6_routes.entries); + priv->ip4_device_routes.entries = g_hash_table_new_full ((GHashFunc) nmp_object_id_hash, + (GEqualFunc) nmp_object_id_equal, + (GDestroyNotify) nmp_object_unref, + (GDestroyNotify) _ip4_device_routes_purge_entry_free); +} + +static void +dispose (GObject *object) +{ + NMRouteManager *self = NM_ROUTE_MANAGER (object); + NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE (object); + + g_hash_table_remove_all (priv->ip4_device_routes.entries); + _ip4_device_routes_cancel (self); + + g_clear_object (&priv->platform); + + G_OBJECT_CLASS (nm_route_manager_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE (object); + + g_array_free (priv->ip4_routes.entries, TRUE); + g_array_free (priv->ip6_routes.entries, TRUE); + g_array_free (priv->ip4_routes.effective_metrics, TRUE); + g_array_free (priv->ip6_routes.effective_metrics, TRUE); + g_array_free (priv->ip4_routes.effective_metrics_reverse, TRUE); + g_array_free (priv->ip6_routes.effective_metrics_reverse, TRUE); + g_free (priv->ip4_routes.index); + g_free (priv->ip6_routes.index); + + g_hash_table_unref (priv->ip4_device_routes.entries); + + G_OBJECT_CLASS (nm_route_manager_parent_class)->finalize (object); +} + +static void +nm_route_manager_class_init (NMRouteManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (NMRouteManagerPrivate)); + + /* virtual methods */ + object_class->dispose = dispose; + object_class->finalize = finalize; +} diff --git a/src/nm-route-manager.h b/src/nm-route-manager.h new file mode 100644 index 00000000..1e4ce484 --- /dev/null +++ b/src/nm-route-manager.h @@ -0,0 +1,53 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2015 Red Hat, Inc. + */ + +#include <glib-object.h> + +#include "nm-types.h" + +#ifndef __NETWORKMANAGER_ROUTE_MANAGER_H__ +#define __NETWORKMANAGER_ROUTE_MANAGER_H__ + +#define NM_TYPE_ROUTE_MANAGER (nm_route_manager_get_type ()) +#define NM_ROUTE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_ROUTE_MANAGER, NMRouteManager)) +#define NM_ROUTE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_ROUTE_MANAGER, NMRouteManagerClass)) +#define NM_IS_ROUTE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_ROUTE_MANAGER)) +#define NM_IS_ROUTE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_ROUTE_MANAGER)) +#define NM_ROUTE_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_ROUTE_MANAGER, NMRouteManagerClass)) + +struct _NMRouteManager { + GObject parent; +}; + +typedef struct { + GObjectClass parent; +} NMRouteManagerClass; + +GType nm_route_manager_get_type (void); + +gboolean nm_route_manager_ip4_route_sync (NMRouteManager *self, int ifindex, const GArray *known_routes, gboolean ignore_kernel_routes, gboolean full_sync); +gboolean nm_route_manager_ip6_route_sync (NMRouteManager *self, int ifindex, const GArray *known_routes, gboolean ignore_kernel_routes, gboolean full_sync); +gboolean nm_route_manager_route_flush (NMRouteManager *self, int ifindex); + +void nm_route_manager_ip4_route_register_device_route_purge_list (NMRouteManager *self, GArray *device_route_purge_list); + +NMRouteManager *nm_route_manager_get (void); + +#endif /* NM_ROUTE_MANAGER_H */ diff --git a/src/nm-types.h b/src/nm-types.h index 4a93567e..1a002ac3 100644 --- a/src/nm-types.h +++ b/src/nm-types.h @@ -26,6 +26,8 @@ typedef struct _NMActiveConnection NMActiveConnection; typedef struct _NMVpnConnection NMVpnConnection; typedef struct _NMActRequest NMActRequest; typedef struct _NMAuthSubject NMAuthSubject; +typedef struct _NMConfig NMConfig; +typedef struct _NMConfigData NMConfigData; typedef struct _NMConnectionProvider NMConnectionProvider; typedef struct _NMConnectivity NMConnectivity; typedef struct _NMDBusManager NMDBusManager; @@ -38,12 +40,20 @@ typedef struct _NMIP6Config NMIP6Config; typedef struct _NMManager NMManager; typedef struct _NMPolicy NMPolicy; typedef struct _NMRfkillManager NMRfkillManager; +typedef struct _NMRouteManager NMRouteManager; typedef struct _NMSessionMonitor NMSessionMonitor; typedef struct _NMSleepMonitor NMSleepMonitor; typedef enum { /* In priority order; higher number == higher priority */ NM_IP_CONFIG_SOURCE_UNKNOWN, + + /* platform internal flag used to mark routes with RTM_F_CLONED. */ + _NM_IP_CONFIG_SOURCE_RTM_F_CLONED, + + /* routes from platform with protocol RTPROT_KERNEL. */ + NM_IP_CONFIG_SOURCE_RTPROT_KERNEL, + NM_IP_CONFIG_SOURCE_KERNEL, NM_IP_CONFIG_SOURCE_SHARED, NM_IP_CONFIG_SOURCE_IP4LL, @@ -98,6 +108,7 @@ typedef enum { NM_LINK_TYPE_VETH, NM_LINK_TYPE_VLAN, NM_LINK_TYPE_VXLAN, + NM_LINK_TYPE_BNEP, /* Bluetooth Ethernet emulation */ /* Software types with slaves */ NM_LINK_TYPE_BRIDGE = 0x10000 | 0x20000, @@ -105,10 +116,24 @@ typedef enum { NM_LINK_TYPE_TEAM, } NMLinkType; +typedef enum { + NMP_OBJECT_TYPE_UNKNOWN, + NMP_OBJECT_TYPE_LINK, + NMP_OBJECT_TYPE_IP4_ADDRESS, + NMP_OBJECT_TYPE_IP6_ADDRESS, + NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE, + __NMP_OBJECT_TYPE_LAST, + NMP_OBJECT_TYPE_MAX = __NMP_OBJECT_TYPE_LAST - 1, +} NMPObjectType; + /* settings */ typedef struct _NMAgentManager NMAgentManager; typedef struct _NMSecretAgent NMSecretAgent; typedef struct _NMSettings NMSettings; typedef struct _NMSettingsConnection NMSettingsConnection; +/* utils */ +typedef struct _NMUtilsIPv6IfaceId NMUtilsIPv6IfaceId; + #endif /* NM_TYPES_H */ diff --git a/src/org.freedesktop.NetworkManager.conf b/src/org.freedesktop.NetworkManager.conf index bdfe3e67..0f1019b9 100644 --- a/src/org.freedesktop.NetworkManager.conf +++ b/src/org.freedesktop.NetworkManager.conf @@ -120,5 +120,7 @@ <deny send_interface="org.freedesktop.NetworkManager.VPN.Plugin"/> <deny send_interface="org.freedesktop.NetworkManager.PPP"/> </policy> + + <limit name="max_replies_per_connection">1024</limit> </busconfig> diff --git a/src/platform/Makefile.in b/src/platform/Makefile.in index a41de1ae..0616c88b 100644 --- a/src/platform/Makefile.in +++ b/src/platform/Makefile.in @@ -195,6 +195,7 @@ ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -308,6 +309,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ +LOG_DRIVER = @LOG_DRIVER@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index 7b0d2d64..d5aeb030 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -24,12 +24,16 @@ #include <unistd.h> #include <netinet/icmp6.h> #include <netinet/in.h> +#include <linux/rtnetlink.h> #include "gsystem-local-alloc.h" +#include "nm-utils.h" #include "NetworkManagerUtils.h" #include "nm-fake-platform.h" #include "nm-logging.h" +#include "nm-test-utils.h" + #define debug(format, ...) nm_log_dbg (LOGD_PLATFORM, format, __VA_ARGS__) typedef struct { @@ -45,8 +49,9 @@ typedef struct { NMPlatformLink link; char *udi; - GBytes *address; int vlan_id; + int ib_p_key; + struct in6_addr ip6_lladdr; } NMFakePlatformLink; #define NM_FAKE_PLATFORM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_FAKE_PLATFORM, NMFakePlatformPrivate)) @@ -55,11 +60,12 @@ G_DEFINE_TYPE (NMFakePlatform, nm_fake_platform, NM_TYPE_PLATFORM) /******************************************************************/ -void -nm_fake_platform_setup (void) -{ - nm_platform_setup (NM_TYPE_FAKE_PLATFORM); -} +static void link_changed (NMPlatform *platform, NMFakePlatformLink *device, gboolean raise_signal); + +static gboolean ip6_address_add (NMPlatform *platform, int ifindex, + struct in6_addr addr, struct in6_addr peer_addr, + int plen, guint32 lifetime, guint32 preferred, guint flags); +static gboolean ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, int plen); /******************************************************************/ @@ -110,25 +116,31 @@ type_to_type_name (NMLinkType type) static void link_init (NMFakePlatformLink *device, int ifindex, int type, const char *name) { + gs_free char *ip6_lladdr = NULL; + g_assert (!name || strlen (name) < sizeof(device->link.name)); memset (device, 0, sizeof (*device)); + ip6_lladdr = ifindex > 0 ? g_strdup_printf ("fe80::fa1e:%0x:%0x", ifindex / 256, ifindex % 256) : NULL; + device->link.ifindex = name ? ifindex : 0; device->link.type = type; - device->link.type_name = type_to_type_name (type); + device->link.kind = type_to_type_name (type); device->link.driver = type_to_type_name (type); - device->link.udi = device->udi = g_strdup_printf ("fake:%d", ifindex); + device->udi = g_strdup_printf ("fake:%d", ifindex); + device->link.initialized = TRUE; + device->ip6_lladdr = *nmtst_inet6_from_string (ip6_lladdr); if (name) strcpy (device->link.name, name); switch (device->link.type) { case NM_LINK_TYPE_DUMMY: - device->link.arp = FALSE; + device->link.flags = NM_FLAGS_SET (device->link.flags, IFF_NOARP); break; default: - device->link.arp = TRUE; + device->link.flags = NM_FLAGS_UNSET (device->link.flags, IFF_NOARP); + break; } - device->address = NULL; } static NMFakePlatformLink * @@ -146,7 +158,6 @@ link_get (NMPlatform *platform, int ifindex) return device; not_found: debug ("link not found: %d", ifindex); - platform->error = NM_PLATFORM_ERROR_NOT_FOUND; return NULL; } @@ -164,18 +175,60 @@ link_get_all (NMPlatform *platform) return links; } -static gboolean -_nm_platform_link_get (NMPlatform *platform, int ifindex, NMPlatformLink *l) +static const NMPlatformLink * +_nm_platform_link_get (NMPlatform *platform, int ifindex) { NMFakePlatformLink *device = link_get (platform, ifindex); - if (device) - *l = device->link; - return !!device; + return device ? &device->link : NULL; +} + +static const NMPlatformLink * +_nm_platform_link_get_by_ifname (NMPlatform *platform, const char *ifname) +{ + NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); + guint i; + + for (i = 0; i < priv->links->len; i++) { + NMFakePlatformLink *device = &g_array_index (priv->links, NMFakePlatformLink, i); + + if (!strcmp (device->link.name, ifname)) + return &device->link; + } + return NULL; +} + +static const NMPlatformLink * +_nm_platform_link_get_by_address (NMPlatform *platform, + gconstpointer address, + size_t length) +{ + NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); + guint i; + + if ( length == 0 + || length > NM_UTILS_HWADDR_LEN_MAX + || !address) + g_return_val_if_reached (NULL); + + for (i = 0; i < priv->links->len; i++) { + NMFakePlatformLink *device = &g_array_index (priv->links, NMFakePlatformLink, i); + + if ( device->link.addr.len == length + && memcmp (device->link.addr.data, address, length) == 0) { + return &device->link; + } + } + return NULL; } static gboolean -link_add (NMPlatform *platform, const char *name, NMLinkType type, const void *address, size_t address_len) +link_add (NMPlatform *platform, + const char *name, + NMLinkType type, + const void *address, + size_t address_len, + NMPlatformLink *out_link) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); NMFakePlatformLink device; @@ -184,9 +237,14 @@ link_add (NMPlatform *platform, const char *name, NMLinkType type, const void *a g_array_append_val (priv->links, device); - if (device.link.ifindex) - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, device.link.ifindex, &device, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_INTERNAL); + if (device.link.ifindex) { + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, NMP_OBJECT_TYPE_LINK, device.link.ifindex, &device, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_INTERNAL); + + link_changed (platform, &g_array_index (priv->links, NMFakePlatformLink, priv->links->len - 1), FALSE); + } + if (out_link) + *out_link = device.link; return TRUE; } @@ -230,99 +288,88 @@ link_delete (NMPlatform *platform, int ifindex) memset (route, 0, sizeof (*route)); } - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, ifindex, &deleted_device, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_INTERNAL); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, NMP_OBJECT_TYPE_LINK, ifindex, &deleted_device, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_INTERNAL); return TRUE; } -static int -link_get_ifindex (NMPlatform *platform, const char *name) -{ - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); - int i; - - for (i = 0; i < priv->links->len; i++) { - NMFakePlatformLink *device = &g_array_index (priv->links, NMFakePlatformLink, i); - - if (device && !g_strcmp0 (device->link.name, name)) - return device->link.ifindex; - } - - return 0; -} - -static const char * -link_get_name (NMPlatform *platform, int ifindex) -{ - NMFakePlatformLink *device = link_get (platform, ifindex); - - return device ? device->link.name : NULL; -} - -static NMLinkType -link_get_type (NMPlatform *platform, int ifindex) -{ - NMFakePlatformLink *device = link_get (platform, ifindex); - - return device ? device->link.type : NM_LINK_TYPE_NONE; -} - static const char * link_get_type_name (NMPlatform *platform, int ifindex) { - return type_to_type_name (link_get_type (platform, ifindex)); + return type_to_type_name (nm_platform_link_get_type (platform, ifindex)); } static void -link_changed (NMPlatform *platform, NMFakePlatformLink *device) +link_changed (NMPlatform *platform, NMFakePlatformLink *device, gboolean raise_signal) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); int i; - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, device->link.ifindex, &device->link, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL); + if (raise_signal) + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, NMP_OBJECT_TYPE_LINK, device->link.ifindex, &device->link, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL); + + if (device->link.ifindex && !IN6_IS_ADDR_UNSPECIFIED (&device->ip6_lladdr)) { + if (device->link.connected) + ip6_address_add (platform, device->link.ifindex, device->ip6_lladdr, in6addr_any, 64, NM_PLATFORM_LIFETIME_PERMANENT, NM_PLATFORM_LIFETIME_PERMANENT, 0); + else + ip6_address_delete (platform, device->link.ifindex, device->ip6_lladdr, 64); + } if (device->link.master) { + gboolean connected = FALSE; + NMFakePlatformLink *master = link_get (platform, device->link.master); - g_return_if_fail (master != device); + g_return_if_fail (master && master != device); - master->link.connected = FALSE; for (i = 0; i < priv->links->len; i++) { NMFakePlatformLink *slave = &g_array_index (priv->links, NMFakePlatformLink, i); if (slave && slave->link.master == master->link.ifindex && slave->link.connected) - master->link.connected = TRUE; + connected = TRUE; } - link_changed (platform, master); + if (master->link.connected != connected) { + master->link.connected = connected; + link_changed (platform, master, TRUE); + } } } static gboolean -link_set_up (NMPlatform *platform, int ifindex) +link_set_up (NMPlatform *platform, int ifindex, gboolean *out_no_firmware) { NMFakePlatformLink *device = link_get (platform, ifindex); + gboolean up, connected; + + if (out_no_firmware) + *out_no_firmware = FALSE; if (!device) return FALSE; - device->link.up = TRUE; + up = TRUE; + connected = TRUE; switch (device->link.type) { case NM_LINK_TYPE_DUMMY: case NM_LINK_TYPE_VLAN: - device->link.connected = TRUE; break; case NM_LINK_TYPE_BRIDGE: case NM_LINK_TYPE_BOND: case NM_LINK_TYPE_TEAM: - device->link.connected = FALSE; + connected = FALSE; break; default: - device->link.connected = FALSE; + connected = FALSE; g_error ("Unexpected device type: %d", device->link.type); } - link_changed (platform, device); + if ( NM_FLAGS_HAS (device->link.flags, IFF_UP) != !!up + || device->link.connected != connected) { + device->link.flags = NM_FLAGS_ASSIGN (device->link.flags, IFF_UP, up); + device->link.connected = connected; + link_changed (platform, device, TRUE); + } return TRUE; } @@ -335,10 +382,12 @@ link_set_down (NMPlatform *platform, int ifindex) if (!device) return FALSE; - device->link.up = FALSE; - device->link.connected = FALSE; + if (NM_FLAGS_HAS (device->link.flags, IFF_UP) || device->link.connected) { + device->link.flags = NM_FLAGS_UNSET (device->link.flags, IFF_UP); + device->link.connected = FALSE; - link_changed (platform, device); + link_changed (platform, device, TRUE); + } return TRUE; } @@ -351,9 +400,9 @@ link_set_arp (NMPlatform *platform, int ifindex) if (!device) return FALSE; - device->link.arp = TRUE; + device->link.flags = NM_FLAGS_UNSET (device->link.flags, IFF_NOARP); - link_changed (platform, device); + link_changed (platform, device, TRUE); return TRUE; } @@ -366,64 +415,32 @@ link_set_noarp (NMPlatform *platform, int ifindex) if (!device) return FALSE; - device->link.arp = FALSE; + device->link.flags = NM_FLAGS_SET (device->link.flags, IFF_NOARP); - link_changed (platform, device); + link_changed (platform, device, TRUE); return TRUE; } static gboolean -link_is_up (NMPlatform *platform, int ifindex) -{ - NMFakePlatformLink *device = link_get (platform, ifindex); - - return device ? device->link.up : FALSE; -} - -static gboolean -link_is_connected (NMPlatform *platform, int ifindex) -{ - NMFakePlatformLink *device = link_get (platform, ifindex); - - return device ? device->link.connected : FALSE; -} - -static gboolean -link_uses_arp (NMPlatform *platform, int ifindex) -{ - NMFakePlatformLink *device = link_get (platform, ifindex); - - return device ? device->link.arp : FALSE; -} - -static gboolean link_set_address (NMPlatform *platform, int ifindex, gconstpointer addr, size_t len) { NMFakePlatformLink *device = link_get (platform, ifindex); - if (device->address) - g_bytes_unref (device->address); - - device->address = g_bytes_new (addr, len); - - link_changed (platform, link_get (platform, ifindex)); - - return TRUE; -} - -static gconstpointer -link_get_address (NMPlatform *platform, int ifindex, size_t *length) -{ - NMFakePlatformLink *device = link_get (platform, ifindex); - - if (!device || !device->address) { - if (length) - *length = 0; - return NULL; + if ( len == 0 + || len > NM_UTILS_HWADDR_LEN_MAX + || !addr) + g_return_val_if_reached (FALSE); + + if ( device->link.addr.len != len + || ( len > 0 + && memcmp (device->link.addr.data, addr, len) != 0)) { + memcpy (device->link.addr.data, addr, len); + device->link.addr.len = len; + link_changed (platform, link_get (platform, ifindex), TRUE); } - return g_bytes_get_data (device->address, length); + return TRUE; } static gboolean @@ -433,45 +450,37 @@ link_set_mtu (NMPlatform *platform, int ifindex, guint32 mtu) if (device) { device->link.mtu = mtu; - link_changed (platform, device); + link_changed (platform, device, TRUE); } return !!device; } -static guint32 -link_get_mtu (NMPlatform *platform, int ifindex) +static const char * +link_get_udi (NMPlatform *platform, int ifindex) { NMFakePlatformLink *device = link_get (platform, ifindex); - return device ? device->link.mtu : 0; -} - -static char * -link_get_physical_port_id (NMPlatform *platform, int ifindex) -{ - /* We call link_get just to cause an error to be set if @ifindex is bad. */ - link_get (platform, ifindex); - - return NULL; -} - -static guint -link_get_dev_id (NMPlatform *platform, int ifindex) -{ - /* We call link_get just to cause an error to be set if @ifindex is bad. */ - link_get (platform, ifindex); - - return 0; + if (!device) + return NULL; + return device->udi; } static gboolean -link_get_wake_on_lan (NMPlatform *platform, int ifindex) -{ - /* We call link_get just to cause an error to be set if @ifindex is bad. */ - link_get (platform, ifindex); +link_get_driver_info (NMPlatform *platform, + int ifindex, + char **out_driver_name, + char **out_driver_version, + char **out_fw_version) +{ + if (out_driver_name) + *out_driver_name = NULL; + if (out_driver_version) + *out_driver_version = NULL; + if (out_fw_version) + *out_fw_version = NULL; - return FALSE; + return TRUE; } static gboolean @@ -510,12 +519,21 @@ static gboolean link_enslave (NMPlatform *platform, int master, int slave) { NMFakePlatformLink *device = link_get (platform, slave); + NMFakePlatformLink *master_device = link_get (platform, master); g_return_val_if_fail (device, FALSE); + g_return_val_if_fail (master_device, FALSE); - device->link.master = master; + if (device->link.master != master) { + device->link.master = master; - link_changed (platform, device); + if (NM_IN_SET (master_device->link.type, NM_LINK_TYPE_BOND, NM_LINK_TYPE_TEAM)) { + device->link.flags = NM_FLAGS_SET (device->link.flags, IFF_UP); + device->link.connected = TRUE; + } + + link_changed (platform, device, TRUE); + } return TRUE; } @@ -529,29 +547,17 @@ link_release (NMPlatform *platform, int master_idx, int slave_idx) g_return_val_if_fail (master, FALSE); g_return_val_if_fail (slave, FALSE); - if (slave->link.master != master->link.ifindex) { - platform->error = NM_PLATFORM_ERROR_NOT_SLAVE; + if (slave->link.master != master->link.ifindex) return FALSE; - } slave->link.master = 0; - link_changed (platform, slave); - link_changed (platform, master); + link_changed (platform, slave, TRUE); + link_changed (platform, master, TRUE); return TRUE; } -static int -link_get_master (NMPlatform *platform, int slave) -{ - NMFakePlatformLink *device = link_get (platform, slave); - - g_return_val_if_fail (device, FALSE); - - return device->link.master; -} - static gboolean master_set_option (NMPlatform *platform, int master, const char *option, const char *value) { @@ -585,20 +591,22 @@ slave_get_option (NMPlatform *platform, int slave, const char *option) } static gboolean -vlan_add (NMPlatform *platform, const char *name, int parent, int vlan_id, guint32 vlan_flags) +vlan_add (NMPlatform *platform, const char *name, int parent, int vlan_id, guint32 vlan_flags, NMPlatformLink *out_link) { NMFakePlatformLink *device; - if (!link_add (platform, name, NM_LINK_TYPE_VLAN, NULL, 0)) + if (!link_add (platform, name, NM_LINK_TYPE_VLAN, NULL, 0, NULL)) return FALSE; - device = link_get (platform, link_get_ifindex (platform, name)); + device = link_get (platform, nm_platform_link_get_ifindex (platform, name)); g_return_val_if_fail (device, FALSE); device->vlan_id = vlan_id; device->link.parent = parent; + if (out_link) + *out_link = device->link; return TRUE; } @@ -630,20 +638,44 @@ vlan_set_egress_map (NMPlatform *platform, int ifindex, int from, int to) } static gboolean -infiniband_partition_add (NMPlatform *platform, int parent, int p_key) +infiniband_partition_add (NMPlatform *platform, int parent, int p_key, NMPlatformLink *out_link) { - NMFakePlatformLink *parent_device; - char *name; - gboolean success; + NMFakePlatformLink *device, *parent_device; + gs_free char *name = NULL; parent_device = link_get (platform, parent); g_return_val_if_fail (parent_device != NULL, FALSE); name = g_strdup_printf ("%s.%04x", parent_device->link.name, p_key); - success = link_add (platform, name, NM_LINK_TYPE_INFINIBAND, NULL, 0); - g_free (name); + if (!link_add (platform, name, NM_LINK_TYPE_INFINIBAND, NULL, 0, out_link)) + return FALSE; + + device = link_get (platform, nm_platform_link_get_ifindex (platform, name)); + g_return_val_if_fail (device, FALSE); + + device->ib_p_key = p_key; + device->link.parent = parent; - return success; + return TRUE; +} + +static gboolean +infiniband_get_info (NMPlatform *platform, int ifindex, int *parent, int *p_key, const char **mode) +{ + NMFakePlatformLink *device; + + device = link_get (platform, ifindex); + g_return_val_if_fail (device, FALSE); + g_return_val_if_fail (device->link.type == NM_LINK_TYPE_INFINIBAND, FALSE); + + if (parent) + *parent = device->link.parent; + if (p_key) + *p_key = device->ib_p_key; + if (mode) + *mode = "datagram"; + + return TRUE; } static gboolean @@ -861,12 +893,12 @@ ip4_address_add (NMPlatform *platform, int ifindex, continue; memcpy (item, &address, sizeof (address)); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, ifindex, &address, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex, &address, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL); return TRUE; } g_array_append_val (priv->ip4_addresses, address); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, ifindex, &address, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_INTERNAL); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex, &address, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_INTERNAL); return TRUE; } @@ -901,13 +933,15 @@ ip6_address_add (NMPlatform *platform, int ifindex, if (item->plen != address.plen) continue; - memcpy (item, &address, sizeof (address)); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, ifindex, &address, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL); + if (nm_platform_ip6_address_cmp (item, &address) != 0) { + memcpy (item, &address, sizeof (address)); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex, &address, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL); + } return TRUE; } g_array_append_val (priv->ip6_addresses, address); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, ifindex, &address, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_INTERNAL); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex, &address, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_INTERNAL); return TRUE; } @@ -927,7 +961,7 @@ ip4_address_delete (NMPlatform *platform, int ifindex, in_addr_t addr, int plen, memcpy (&deleted_address, address, sizeof (deleted_address)); memset (address, 0, sizeof (*address)); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, ifindex, &deleted_address, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_INTERNAL); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex, &deleted_address, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_INTERNAL); return TRUE; } } @@ -950,7 +984,7 @@ ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, int memcpy (&deleted_address, address, sizeof (deleted_address)); memset (address, 0, sizeof (*address)); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, ifindex, &deleted_address, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_INTERNAL); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex, &deleted_address, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_INTERNAL); return TRUE; } } @@ -958,8 +992,8 @@ ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, int return TRUE; } -static gboolean -ip4_address_exists (NMPlatform *platform, int ifindex, in_addr_t addr, int plen) +static const NMPlatformIP4Address * +ip4_address_get (NMPlatform *platform, int ifindex, in_addr_t addr, int plen) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); int i; @@ -968,14 +1002,14 @@ ip4_address_exists (NMPlatform *platform, int ifindex, in_addr_t addr, int plen) NMPlatformIP4Address *address = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i); if (address->ifindex == ifindex && address->plen == plen && address->address == addr) - return TRUE; + return address; } - return FALSE; + return NULL; } -static gboolean -ip6_address_exists (NMPlatform *platform, int ifindex, struct in6_addr addr, int plen) +static const NMPlatformIP6Address * +ip6_address_get (NMPlatform *platform, int ifindex, struct in6_addr addr, int plen) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); int i; @@ -985,41 +1019,36 @@ ip6_address_exists (NMPlatform *platform, int ifindex, struct in6_addr addr, int if (address->ifindex == ifindex && address->plen == plen && IN6_ARE_ADDR_EQUAL (&address->address, &addr)) - return TRUE; + return address; } - return FALSE; -} - -static gboolean -ip4_check_reinstall_device_route (NMPlatform *platform, int ifindex, const NMPlatformIP4Address *address, guint32 device_route_metric) -{ - return FALSE; + return NULL; } /******************************************************************/ static GArray * -ip4_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteMode mode) +ip4_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteFlags flags) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); GArray *routes; NMPlatformIP4Route *route; guint i; - g_return_val_if_fail (NM_IN_SET (mode, NM_PLATFORM_GET_ROUTE_MODE_ALL, NM_PLATFORM_GET_ROUTE_MODE_NO_DEFAULT, NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT), NULL); - routes = g_array_new (TRUE, TRUE, sizeof (NMPlatformIP4Route)); + if (!NM_FLAGS_ANY (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT)) + flags |= NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT; + /* Fill routes */ for (i = 0; i < priv->ip4_routes->len; i++) { route = &g_array_index (priv->ip4_routes, NMPlatformIP4Route, i); if (route && (!ifindex || route->ifindex == ifindex)) { if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) { - if (mode != NM_PLATFORM_GET_ROUTE_MODE_NO_DEFAULT) + if (NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT)) g_array_append_val (routes, *route); } else { - if (mode != NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT) + if (NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT)) g_array_append_val (routes, *route); } } @@ -1029,26 +1058,27 @@ ip4_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteMode mod } static GArray * -ip6_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteMode mode) +ip6_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteFlags flags) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); GArray *routes; NMPlatformIP6Route *route; guint i; - g_return_val_if_fail (NM_IN_SET (mode, NM_PLATFORM_GET_ROUTE_MODE_ALL, NM_PLATFORM_GET_ROUTE_MODE_NO_DEFAULT, NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT), NULL); - routes = g_array_new (TRUE, TRUE, sizeof (NMPlatformIP6Route)); + if (!NM_FLAGS_ANY (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT)) + flags |= NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT; + /* Fill routes */ for (i = 0; i < priv->ip6_routes->len; i++) { route = &g_array_index (priv->ip6_routes, NMPlatformIP6Route, i); if (route && (!ifindex || route->ifindex == ifindex)) { if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) { - if (mode != NM_PLATFORM_GET_ROUTE_MODE_NO_DEFAULT) + if (NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT)) g_array_append_val (routes, *route); } else { - if (mode != NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT) + if (NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT)) g_array_append_val (routes, *route); } } @@ -1058,6 +1088,56 @@ ip6_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteMode mod } static gboolean +ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, int plen, guint32 metric) +{ + NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); + int i; + + for (i = 0; i < priv->ip4_routes->len; i++) { + NMPlatformIP4Route *route = &g_array_index (priv->ip4_routes, NMPlatformIP4Route, i); + NMPlatformIP4Route deleted_route; + + if ( route->ifindex != ifindex + || route->network != network + || route->plen != plen + || route->metric != metric) + continue; + + memcpy (&deleted_route, route, sizeof (deleted_route)); + g_array_remove_index (priv->ip4_routes, i); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NMP_OBJECT_TYPE_IP4_ROUTE, ifindex, &deleted_route, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_INTERNAL); + } + + return TRUE; +} + +static gboolean +ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, int plen, guint32 metric) +{ + NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); + int i; + + metric = nm_utils_ip6_route_metric_normalize (metric); + + for (i = 0; i < priv->ip6_routes->len; i++) { + NMPlatformIP6Route *route = &g_array_index (priv->ip6_routes, NMPlatformIP6Route, i); + NMPlatformIP6Route deleted_route; + + if ( route->ifindex != ifindex + || !IN6_ARE_ADDR_EQUAL (&route->network, &network) + || route->plen != plen + || route->metric != metric) + continue; + + memcpy (&deleted_route, route, sizeof (deleted_route)); + g_array_remove_index (priv->ip6_routes, i); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, NMP_OBJECT_TYPE_IP6_ROUTE, ifindex, &deleted_route, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_INTERNAL); + } + + return TRUE; +} + +static gboolean ip4_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, in_addr_t network, int plen, in_addr_t gateway, guint32 pref_src, guint32 metric, guint32 mss) @@ -1065,22 +1145,41 @@ ip4_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); NMPlatformIP4Route route; guint i; + guint8 scope; + + scope = gateway == 0 ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE; memset (&route, 0, sizeof (route)); route.source = NM_IP_CONFIG_SOURCE_KERNEL; route.ifindex = ifindex; route.source = source; - route.network = network; + route.network = nm_utils_ip4_address_clear_host_address (network, plen); route.plen = plen; route.gateway = gateway; route.metric = metric; route.mss = mss; + route.scope_inv = nm_platform_route_scope_inv (scope); + + if (gateway) { + for (i = 0; i < priv->ip4_routes->len; i++) { + NMPlatformIP4Route *item = &g_array_index (priv->ip4_routes, + NMPlatformIP4Route, i); + guint32 gate = ntohl (item->network) >> (32 - item->plen); + guint32 host = ntohl (gateway) >> (32 - item->plen); + + if (ifindex == item->ifindex && gate == host) + break; + } + if (i == priv->ip4_routes->len) { + nm_log_warn (LOGD_PLATFORM, "Fake platform: failure adding ip4-route '%d: %s/%d %d': Network Unreachable", + route.ifindex, nm_utils_inet4_ntop (route.network, NULL), route.plen, route.metric); + return FALSE; + } + } for (i = 0; i < priv->ip4_routes->len; i++) { NMPlatformIP4Route *item = &g_array_index (priv->ip4_routes, NMPlatformIP4Route, i); - if (item->ifindex != route.ifindex) - continue; if (item->network != route.network) continue; if (item->plen != route.plen) @@ -1088,13 +1187,19 @@ ip4_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, if (item->metric != metric) continue; + if (item->ifindex != route.ifindex) { + ip4_route_delete (platform, item->ifindex, item->network, item->plen, item->metric); + i--; + continue; + } + memcpy (item, &route, sizeof (route)); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, ifindex, &route, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NMP_OBJECT_TYPE_IP4_ROUTE, ifindex, &route, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL); return TRUE; } g_array_append_val (priv->ip4_routes, route); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, ifindex, &route, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_INTERNAL); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NMP_OBJECT_TYPE_IP4_ROUTE, ifindex, &route, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_INTERNAL); return TRUE; } @@ -1114,17 +1219,34 @@ ip6_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, route.source = NM_IP_CONFIG_SOURCE_KERNEL; route.ifindex = ifindex; route.source = source; - route.network = network; + nm_utils_ip6_address_clear_host_address (&route.network, &network, plen); route.plen = plen; route.gateway = gateway; route.metric = metric; route.mss = mss; + if (!IN6_IS_ADDR_UNSPECIFIED(&gateway)) { + for (i = 0; i < priv->ip6_routes->len; i++) { + NMPlatformIP6Route *item = &g_array_index (priv->ip6_routes, + NMPlatformIP6Route, i); + guint8 gate_bits = gateway.s6_addr[item->plen / 8] >> (8 - item->plen % 8); + guint8 host_bits = item->network.s6_addr[item->plen / 8] >> (8 - item->plen % 8); + + if ( ifindex == item->ifindex + && memcmp (&gateway, &item->network, item->plen / 8) == 0 + && gate_bits == host_bits) + break; + } + if (i == priv->ip6_routes->len) { + nm_log_warn (LOGD_PLATFORM, "Fake platform: failure adding ip6-route '%d: %s/%d %d': Network Unreachable", + route.ifindex, nm_utils_inet6_ntop (&route.network, NULL), route.plen, route.metric); + return FALSE; + } + } + for (i = 0; i < priv->ip6_routes->len; i++) { NMPlatformIP6Route *item = &g_array_index (priv->ip6_routes, NMPlatformIP6Route, i); - if (item->ifindex != route.ifindex) - continue; if (!IN6_ARE_ADDR_EQUAL (&item->network, &route.network)) continue; if (item->plen != route.plen) @@ -1132,18 +1254,24 @@ ip6_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, if (item->metric != metric) continue; + if (item->ifindex != route.ifindex) { + ip6_route_delete (platform, item->ifindex, item->network, item->plen, item->metric); + i--; + continue; + } + memcpy (item, &route, sizeof (route)); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, ifindex, &route, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, NMP_OBJECT_TYPE_IP6_ROUTE, ifindex, &route, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL); return TRUE; } g_array_append_val (priv->ip6_routes, route); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, ifindex, &route, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_INTERNAL); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, NMP_OBJECT_TYPE_IP6_ROUTE, ifindex, &route, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_INTERNAL); return TRUE; } -static NMPlatformIP4Route * +static const NMPlatformIP4Route * ip4_route_get (NMPlatform *platform, int ifindex, in_addr_t network, int plen, guint32 metric) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); @@ -1162,7 +1290,7 @@ ip4_route_get (NMPlatform *platform, int ifindex, in_addr_t network, int plen, g return NULL; } -static NMPlatformIP6Route * +static const NMPlatformIP6Route * ip6_route_get (NMPlatform *platform, int ifindex, struct in6_addr network, int plen, guint32 metric) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); @@ -1183,48 +1311,6 @@ ip6_route_get (NMPlatform *platform, int ifindex, struct in6_addr network, int p return NULL; } -static gboolean -ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, int plen, guint32 metric) -{ - NMPlatformIP4Route *route = ip4_route_get (platform, ifindex, network, plen, metric); - NMPlatformIP4Route deleted_route; - - if (route) { - memcpy (&deleted_route, route, sizeof (deleted_route)); - memset (route, 0, sizeof (*route)); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, ifindex, &deleted_route, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_INTERNAL); - } - - return TRUE; -} - -static gboolean -ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, int plen, guint32 metric) -{ - NMPlatformIP6Route *route = ip6_route_get (platform, ifindex, network, plen, metric); - NMPlatformIP6Route deleted_route; - - if (route) { - memcpy (&deleted_route, route, sizeof (deleted_route)); - memset (route, 0, sizeof (*route)); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, ifindex, &deleted_route, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_INTERNAL); - } - - return TRUE; -} - -static gboolean -ip4_route_exists (NMPlatform *platform, int ifindex, in_addr_t network, int plen, guint32 metric) -{ - return !!ip4_route_get (platform, ifindex, network, plen, metric); -} - -static gboolean -ip6_route_exists (NMPlatform *platform, int ifindex, struct in6_addr network, int plen, guint32 metric) -{ - return !!ip6_route_get (platform, ifindex, network, plen, metric); -} - /******************************************************************/ static void @@ -1240,21 +1326,25 @@ nm_fake_platform_init (NMFakePlatform *fake_platform) priv->ip6_routes = g_array_new (TRUE, TRUE, sizeof (NMPlatformIP6Route)); } -static gboolean -setup (NMPlatform *platform) +void +nm_fake_platform_setup (void) { + NMPlatform *platform; + + platform = g_object_new (NM_TYPE_FAKE_PLATFORM, NULL); + + nm_platform_setup (platform); + /* skip zero element */ - link_add (platform, NULL, NM_LINK_TYPE_NONE, NULL, 0); + link_add (platform, NULL, NM_LINK_TYPE_NONE, NULL, 0, NULL); /* add loopback interface */ - link_add (platform, "lo", NM_LINK_TYPE_LOOPBACK, NULL, 0); + link_add (platform, "lo", NM_LINK_TYPE_LOOPBACK, NULL, 0, NULL); /* add some ethernets */ - link_add (platform, "eth0", NM_LINK_TYPE_ETHERNET, NULL, 0); - link_add (platform, "eth1", NM_LINK_TYPE_ETHERNET, NULL, 0); - link_add (platform, "eth2", NM_LINK_TYPE_ETHERNET, NULL, 0); - - return TRUE; + link_add (platform, "eth0", NM_LINK_TYPE_ETHERNET, NULL, 0, NULL); + link_add (platform, "eth1", NM_LINK_TYPE_ETHERNET, NULL, 0, NULL); + link_add (platform, "eth2", NM_LINK_TYPE_ETHERNET, NULL, 0, NULL); } static void @@ -1267,7 +1357,6 @@ nm_fake_platform_finalize (GObject *object) for (i = 0; i < priv->links->len; i++) { NMFakePlatformLink *device = &g_array_index (priv->links, NMFakePlatformLink, i); - g_bytes_unref (device->address); g_free (device->udi); } g_array_unref (priv->links); @@ -1290,43 +1379,34 @@ nm_fake_platform_class_init (NMFakePlatformClass *klass) /* virtual methods */ object_class->finalize = nm_fake_platform_finalize; - platform_class->setup = setup; - platform_class->sysctl_set = sysctl_set; platform_class->sysctl_get = sysctl_get; platform_class->link_get = _nm_platform_link_get; + platform_class->link_get_by_ifname = _nm_platform_link_get_by_ifname; + platform_class->link_get_by_address = _nm_platform_link_get_by_address; platform_class->link_get_all = link_get_all; platform_class->link_add = link_add; platform_class->link_delete = link_delete; - platform_class->link_get_ifindex = link_get_ifindex; - platform_class->link_get_name = link_get_name; - platform_class->link_get_type = link_get_type; platform_class->link_get_type_name = link_get_type_name; + platform_class->link_get_udi = link_get_udi; + platform_class->link_set_up = link_set_up; platform_class->link_set_down = link_set_down; platform_class->link_set_arp = link_set_arp; platform_class->link_set_noarp = link_set_noarp; - platform_class->link_is_up = link_is_up; - platform_class->link_is_connected = link_is_connected; - platform_class->link_uses_arp = link_uses_arp; platform_class->link_set_address = link_set_address; - platform_class->link_get_address = link_get_address; - platform_class->link_get_mtu = link_get_mtu; platform_class->link_set_mtu = link_set_mtu; - platform_class->link_get_physical_port_id = link_get_physical_port_id; - platform_class->link_get_dev_id = link_get_dev_id; - platform_class->link_get_wake_on_lan = link_get_wake_on_lan; + platform_class->link_get_driver_info = link_get_driver_info; platform_class->link_supports_carrier_detect = link_supports_carrier_detect; platform_class->link_supports_vlans = link_supports_vlans; platform_class->link_enslave = link_enslave; platform_class->link_release = link_release; - platform_class->link_get_master = link_get_master; platform_class->master_set_option = master_set_option; platform_class->master_get_option = master_get_option; platform_class->slave_set_option = slave_set_option; @@ -1338,6 +1418,7 @@ nm_fake_platform_class_init (NMFakePlatformClass *klass) platform_class->vlan_set_egress_map = vlan_set_egress_map; platform_class->infiniband_partition_add = infiniband_partition_add; + platform_class->infiniband_get_info = infiniband_get_info; platform_class->veth_get_properties = veth_get_properties; platform_class->tun_get_properties = tun_get_properties; @@ -1360,23 +1441,21 @@ nm_fake_platform_class_init (NMFakePlatformClass *klass) platform_class->mesh_set_channel = mesh_set_channel; platform_class->mesh_set_ssid = mesh_set_ssid; + platform_class->ip4_address_get = ip4_address_get; + platform_class->ip6_address_get = ip6_address_get; platform_class->ip4_address_get_all = ip4_address_get_all; platform_class->ip6_address_get_all = ip6_address_get_all; platform_class->ip4_address_add = ip4_address_add; platform_class->ip6_address_add = ip6_address_add; platform_class->ip4_address_delete = ip4_address_delete; platform_class->ip6_address_delete = ip6_address_delete; - platform_class->ip4_address_exists = ip4_address_exists; - platform_class->ip6_address_exists = ip6_address_exists; - - platform_class->ip4_check_reinstall_device_route = ip4_check_reinstall_device_route; + platform_class->ip4_route_get = ip4_route_get; + platform_class->ip6_route_get = ip6_route_get; platform_class->ip4_route_get_all = ip4_route_get_all; platform_class->ip6_route_get_all = ip6_route_get_all; platform_class->ip4_route_add = ip4_route_add; platform_class->ip6_route_add = ip6_route_add; platform_class->ip4_route_delete = ip4_route_delete; platform_class->ip6_route_delete = ip6_route_delete; - platform_class->ip4_route_exists = ip4_route_exists; - platform_class->ip6_route_exists = ip6_route_exists; } diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index c337ee9c..80757f4e 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2012-2013 Red Hat, Inc. + * Copyright (C) 2012-2015 Red Hat, Inc. */ #include "config.h" @@ -31,10 +31,6 @@ #include <linux/if_link.h> #include <linux/if_tun.h> #include <linux/if_tunnel.h> -#include <sys/ioctl.h> -#include <linux/sockios.h> -#include <linux/ethtool.h> -#include <linux/mii.h> #include <netlink/netlink.h> #include <netlink/object.h> #include <netlink/cache.h> @@ -44,9 +40,9 @@ #include <netlink/route/route.h> #include <gudev/gudev.h> -#if HAVE_LIBNL_INET6_ADDR_GEN_MODE +#if HAVE_LIBNL_INET6_ADDR_GEN_MODE || HAVE_LIBNL_INET6_TOKEN #include <netlink/route/link/inet6.h> -#if HAVE_KERNEL_INET6_ADDR_GEN_MODE +#if HAVE_LIBNL_INET6_ADDR_GEN_MODE && HAVE_KERNEL_INET6_ADDR_GEN_MODE #include <linux/if_link.h> #else #define IN6_ADDR_GEN_MODE_EUI64 0 @@ -55,21 +51,101 @@ #endif #include "gsystem-local-alloc.h" +#include "nm-core-internal.h" #include "NetworkManagerUtils.h" #include "nm-linux-platform.h" +#include "nm-platform-utils.h" #include "NetworkManagerUtils.h" #include "nm-utils.h" #include "nm-logging.h" #include "wifi/wifi-utils.h" #include "wifi/wifi-utils-wext.h" +#include "nmp-object.h" /* This is only included for the translation of VLAN flags */ #include "nm-setting-vlan.h" -#define debug(...) nm_log_dbg (LOGD_PLATFORM, __VA_ARGS__) -#define warning(...) nm_log_warn (LOGD_PLATFORM, __VA_ARGS__) -#define error(...) nm_log_err (LOGD_PLATFORM, __VA_ARGS__) +/*********************************************************************************************/ + +#define _LOG_DOMAIN LOGD_PLATFORM +#define _LOG_PREFIX_NAME "platform-linux" + +#define _LOG(level, domain, self, ...) \ + G_STMT_START { \ + const NMLogLevel __level = (level); \ + const NMLogDomain __domain = (domain); \ + \ + if (nm_logging_enabled (__level, __domain)) { \ + char __prefix[32]; \ + const char *__p_prefix = _LOG_PREFIX_NAME; \ + const void *const __self = (self); \ + \ + if (__self && __self != nm_platform_try_get ()) { \ + g_snprintf (__prefix, sizeof (__prefix), "%s[%p]", _LOG_PREFIX_NAME, __self); \ + __p_prefix = __prefix; \ + } \ + _nm_log (__level, __domain, 0, \ + "%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ + __p_prefix _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ + } \ + } G_STMT_END +#define _LOG_LEVEL_ENABLED(level, domain) \ + ( nm_logging_enabled ((level), (domain)) ) + +#ifdef NM_MORE_LOGGING +#define _LOGT_ENABLED() _LOG_LEVEL_ENABLED (LOGL_TRACE, _LOG_DOMAIN) +#define _LOGT(...) _LOG (LOGL_TRACE, _LOG_DOMAIN, platform, __VA_ARGS__) +#else +#define _LOGT_ENABLED() FALSE +#define _LOGT(...) G_STMT_START { if (FALSE) { _LOG (LOGL_TRACE, _LOG_DOMAIN, platform, __VA_ARGS__); } } G_STMT_END +#endif + +#define _LOGD(...) _LOG (LOGL_DEBUG, _LOG_DOMAIN, platform, __VA_ARGS__) +#define _LOGI(...) _LOG (LOGL_INFO , _LOG_DOMAIN, platform, __VA_ARGS__) +#define _LOGW(...) _LOG (LOGL_WARN , _LOG_DOMAIN, platform, __VA_ARGS__) +#define _LOGE(...) _LOG (LOGL_ERR , _LOG_DOMAIN, platform, __VA_ARGS__) + +#define debug(...) _LOG (LOGL_DEBUG, _LOG_DOMAIN, NULL, __VA_ARGS__) +#define warning(...) _LOG (LOGL_WARN , _LOG_DOMAIN, NULL, __VA_ARGS__) +#define error(...) _LOG (LOGL_ERR , _LOG_DOMAIN, NULL, __VA_ARGS__) + +/****************************************************************** + * Forward declarations and enums + ******************************************************************/ +typedef enum { + DELAYED_ACTION_TYPE_NONE = 0, + DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS = (1LL << 0), + DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES = (1LL << 1), + DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES = (1LL << 2), + DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES = (1LL << 3), + DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES = (1LL << 4), + DELAYED_ACTION_TYPE_REFRESH_LINK = (1LL << 5), + DELAYED_ACTION_TYPE_MASTER_CONNECTED = (1LL << 6), + DELAYED_ACTION_TYPE_READ_NETLINK = (1LL << 7), + __DELAYED_ACTION_TYPE_MAX, + + DELAYED_ACTION_TYPE_REFRESH_ALL = DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, + + DELAYED_ACTION_TYPE_MAX = __DELAYED_ACTION_TYPE_MAX -1, +} DelayedActionType; + +static gboolean tun_get_properties_ifname (NMPlatform *platform, const char *ifname, NMPlatformTunProperties *props); +static void delayed_action_schedule (NMPlatform *platform, DelayedActionType action_type, gpointer user_data); +static gboolean delayed_action_handle_all (NMPlatform *platform, gboolean read_netlink); +static void do_request_link (NMPlatform *platform, int ifindex, const char *name, gboolean handle_delayed_action); +static void do_request_all (NMPlatform *platform, DelayedActionType action_type, gboolean handle_delayed_action); +static void cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data); +static gboolean event_handler_read_netlink_all (NMPlatform *platform, gboolean wait_for_acks); +static NMPCacheOpsType cache_remove_netlink (NMPlatform *platform, const NMPObject *obj_needle, NMPObject **out_obj_cache, gboolean *out_was_visible, NMPlatformReason reason); + +/****************************************************************** + * libnl unility functions and wrappers + ******************************************************************/ struct libnl_vtable { @@ -78,41 +154,6 @@ struct libnl_vtable int (*f_nl_has_capability) (int capability); }; - -typedef struct { - struct nl_sock *nlh; - struct nl_sock *nlh_event; - struct nl_cache *link_cache; - struct nl_cache *address_cache; - struct nl_cache *route_cache; - GIOChannel *event_channel; - guint event_id; - - GUdevClient *udev_client; - GHashTable *udev_devices; - - GHashTable *wifi_data; - - int support_kernel_extended_ifa_flags; - int support_user_ipv6ll; -} NMLinuxPlatformPrivate; - -#define NM_LINUX_PLATFORM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_LINUX_PLATFORM, NMLinuxPlatformPrivate)) - -G_DEFINE_TYPE (NMLinuxPlatform, nm_linux_platform, NM_TYPE_PLATFORM) - -static const char *to_string_object (NMPlatform *platform, struct nl_object *obj); -static gboolean _address_match (struct rtnl_addr *addr, int family, int ifindex); -static gboolean _route_match (struct rtnl_route *rtnlroute, int family, int ifindex, gboolean include_proto_kernel); - -void -nm_linux_platform_setup (void) -{ - nm_platform_setup (NM_TYPE_LINUX_PLATFORM); -} - -/******************************************************************/ - static int _nl_f_nl_has_capability (int capability) { @@ -148,73 +189,10 @@ _nl_has_capability (int capability) return (_nl_get_vtable ()->f_nl_has_capability) (capability); } -/******************************************************************/ - -static guint32 -_get_expiry (guint32 now_s, guint32 lifetime_s) -{ - gint64 t = ((gint64) now_s) + ((gint64) lifetime_s); - - return MIN (t, NM_PLATFORM_LIFETIME_PERMANENT - 1); -} - -/* The rtnl_addr object contains relative lifetimes @valid and @preferred - * that count in seconds, starting from the moment when the kernel constructed - * the netlink message. - * - * There is also a field rtnl_addr_last_update_time(), which is the absolute - * time in 1/100th of a second of clock_gettime (CLOCK_MONOTONIC) when the address - * was modified (wrapping every 497 days). - * Immediately at the time when the address was last modified, #NOW and @last_update_time - * are the same, so (only) in that case @valid and @preferred are anchored at @last_update_time. - * However, this is not true in general. As time goes by, whenever kernel sends a new address - * via netlink, the lifetimes keep counting down. - * - * As we cache the rtnl_addr object we must know the absolute expiries. - * As a hack, modify the relative timestamps valid and preferred into absolute - * timestamps of scale nm_utils_get_monotonic_timestamp_s(). - **/ -static void -_rtnl_addr_hack_lifetimes_rel_to_abs (struct rtnl_addr *rtnladdr) -{ - guint32 a_valid = rtnl_addr_get_valid_lifetime (rtnladdr); - guint32 a_preferred = rtnl_addr_get_preferred_lifetime (rtnladdr); - guint32 now; - - if (a_valid == NM_PLATFORM_LIFETIME_PERMANENT && - a_preferred == NM_PLATFORM_LIFETIME_PERMANENT) - return; - - now = (guint32) nm_utils_get_monotonic_timestamp_s (); - - if (a_preferred > a_valid) - a_preferred = a_valid; - - if (a_valid != NM_PLATFORM_LIFETIME_PERMANENT) - rtnl_addr_set_valid_lifetime (rtnladdr, _get_expiry (now, a_valid)); - rtnl_addr_set_preferred_lifetime (rtnladdr, _get_expiry (now, a_preferred)); -} - -/******************************************************************/ - -/* libnl library workarounds and additions */ - /* Automatic deallocation of local variables */ -#define auto_nl_cache __attribute__((cleanup(put_nl_cache))) +#define auto_nl_object __attribute__((cleanup(_nl_auto_nl_object))) static void -put_nl_cache (void *ptr) -{ - struct nl_cache **cache = ptr; - - if (cache && *cache) { - nl_cache_free (*cache); - *cache = NULL; - } -} - -#define auto_nl_object __attribute__((cleanup(put_nl_object))) -static void -put_nl_object (void *ptr) +_nl_auto_nl_object (void *ptr) { struct nl_object **object = ptr; @@ -224,9 +202,9 @@ put_nl_object (void *ptr) } } -#define auto_nl_addr __attribute__((cleanup(put_nl_addr))) +#define auto_nl_addr __attribute__((cleanup(_nl_auto_nl_addr))) static void -put_nl_addr (void *ptr) +_nl_auto_nl_addr (void *ptr) { struct nl_addr **object = ptr; @@ -236,12 +214,10 @@ put_nl_addr (void *ptr) } } -/*******************************************************************/ - /* wrap the libnl alloc functions and abort on out-of-memory*/ static struct nl_addr * -_nm_nl_addr_build (int family, const void *buf, size_t size) +_nl_addr_build (int family, const void *buf, size_t size) { struct nl_addr *addr; @@ -253,7 +229,7 @@ _nm_nl_addr_build (int family, const void *buf, size_t size) } static struct rtnl_link * -_nm_rtnl_link_alloc (int ifindex, const char*name) +_nl_rtnl_link_alloc (int ifindex, const char*name) { struct rtnl_link *rtnllink; @@ -269,7 +245,7 @@ _nm_rtnl_link_alloc (int ifindex, const char*name) } static struct rtnl_addr * -_nm_rtnl_addr_alloc (int ifindex) +_nl_rtnl_addr_alloc (int ifindex) { struct rtnl_addr *rtnladdr; @@ -282,7 +258,7 @@ _nm_rtnl_addr_alloc (int ifindex) } static struct rtnl_route * -_nm_rtnl_route_alloc (void) +_nl_rtnl_route_alloc (void) { struct rtnl_route *rtnlroute = rtnl_route_alloc (); @@ -292,7 +268,7 @@ _nm_rtnl_route_alloc (void) } static struct rtnl_nexthop * -_nm_rtnl_route_nh_alloc (void) +_nl_rtnl_route_nh_alloc (void) { struct rtnl_nexthop *nexthop; @@ -302,11 +278,9 @@ _nm_rtnl_route_nh_alloc (void) return nexthop; } -/*******************************************************************/ - /* rtnl_addr_set_prefixlen fails to update the nl_addr prefixlen */ static void -nm_rtnl_addr_set_prefixlen (struct rtnl_addr *rtnladdr, int plen) +_nl_rtnl_addr_set_prefixlen (struct rtnl_addr *rtnladdr, int plen) { struct nl_addr *nladdr; @@ -316,188 +290,30 @@ nm_rtnl_addr_set_prefixlen (struct rtnl_addr *rtnladdr, int plen) if (nladdr) nl_addr_set_prefixlen (nladdr, plen); } -#define rtnl_addr_set_prefixlen nm_rtnl_addr_set_prefixlen - -typedef enum { - OBJECT_TYPE_UNKNOWN, - OBJECT_TYPE_LINK, - OBJECT_TYPE_IP4_ADDRESS, - OBJECT_TYPE_IP6_ADDRESS, - OBJECT_TYPE_IP4_ROUTE, - OBJECT_TYPE_IP6_ROUTE, - __OBJECT_TYPE_LAST, -} ObjectType; - -static ObjectType -object_type_from_nl_object (const struct nl_object *object) -{ - const char *type_str; - if (!object || !(type_str = nl_object_get_type (object))) - return OBJECT_TYPE_UNKNOWN; - - if (!strcmp (type_str, "route/link")) - return OBJECT_TYPE_LINK; - else if (!strcmp (type_str, "route/addr")) { - switch (rtnl_addr_get_family ((struct rtnl_addr *) object)) { - case AF_INET: - return OBJECT_TYPE_IP4_ADDRESS; - case AF_INET6: - return OBJECT_TYPE_IP6_ADDRESS; - default: - return OBJECT_TYPE_UNKNOWN; - } - } else if (!strcmp (type_str, "route/route")) { - switch (rtnl_route_get_family ((struct rtnl_route *) object)) { - case AF_INET: - return OBJECT_TYPE_IP4_ROUTE; - case AF_INET6: - return OBJECT_TYPE_IP6_ROUTE; - default: - return OBJECT_TYPE_UNKNOWN; - } - } else - return OBJECT_TYPE_UNKNOWN; -} - -static void -_nl_link_family_unset (struct nl_object *obj, int *family) -{ - if (!obj || object_type_from_nl_object (obj) != OBJECT_TYPE_LINK) - *family = AF_UNSPEC; - else { - *family = rtnl_link_get_family ((struct rtnl_link *) obj); - - /* Always explicitly set the family to AF_UNSPEC, even if rtnl_link_get_family() might - * already return %AF_UNSPEC. The reason is, that %AF_UNSPEC is the default family - * and libnl nl_object_identical() function will only succeed, if the family is - * explicitly set (which we cannot be sure, unless setting it). */ - rtnl_link_set_family ((struct rtnl_link *) obj, AF_UNSPEC); - } -} - -/* In our link cache, we coerce the family of all link objects to AF_UNSPEC. - * Thus, before searching for an object, we fixup @needle to have the right - * id (by resetting the family). */ -static struct nl_object * -nm_nl_cache_search (struct nl_cache *cache, struct nl_object *needle) -{ - int family; - struct nl_object *obj; - - _nl_link_family_unset (needle, &family); - obj = nl_cache_search (cache, needle); - if (family != AF_UNSPEC) { - /* restore the family of the @needle instance. If the family was - * unset before, we cannot make it unset again. Thus, in that case - * we cannot undo _nl_link_family_unset() entirely. */ - rtnl_link_set_family ((struct rtnl_link *) needle, family); - } - - return obj; -} - -/* Ask the kernel for an object identical (as in nl_cache_identical) to the - * needle argument. This is a kernel counterpart for nl_cache_search. - * - * The returned object must be freed by the caller with nl_object_put(). - */ -static struct nl_object * -get_kernel_object (struct nl_sock *sock, struct nl_object *needle) +static const char * +_nl_nlmsg_type_to_str (guint16 type, char *buf, gsize len) { - struct nl_object *object = NULL; - ObjectType type = object_type_from_nl_object (needle); + const char *str_type = NULL; switch (type) { - case OBJECT_TYPE_LINK: - { - int ifindex = rtnl_link_get_ifindex ((struct rtnl_link *) needle); - const char *name = rtnl_link_get_name ((struct rtnl_link *) needle); - int nle; - - nle = rtnl_link_get_kernel (sock, ifindex, name, (struct rtnl_link **) &object); - switch (nle) { - case -NLE_SUCCESS: - if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) { - name = rtnl_link_get_name ((struct rtnl_link *) object); - debug ("get_kernel_object for link: %s (%d, family %d)", - name ? name : "(unknown)", - rtnl_link_get_ifindex ((struct rtnl_link *) object), - rtnl_link_get_family ((struct rtnl_link *) object)); - } - - _nl_link_family_unset (object, &nle); - return object; - case -NLE_NODEV: - debug ("get_kernel_object for link %s (%d) had no result", - name ? name : "(unknown)", ifindex); - return NULL; - default: - error ("get_kernel_object for link %s (%d) failed: %s (%d)", - name ? name : "(unknown)", ifindex, nl_geterror (nle), nle); - return NULL; - } - } - case OBJECT_TYPE_IP4_ADDRESS: - case OBJECT_TYPE_IP6_ADDRESS: - case OBJECT_TYPE_IP4_ROUTE: - case OBJECT_TYPE_IP6_ROUTE: - /* Fallback to a one-time cache allocation. */ - { - struct nl_cache *cache; - int nle; - - /* FIXME: every time we refresh *one* object, we request an - * entire dump. E.g. check_cache_items() gets O(n2) complexitly. */ - - nle = nl_cache_alloc_and_fill ( - nl_cache_ops_lookup (nl_object_get_type (needle)), - sock, &cache); - if (nle) { - error ("get_kernel_object for type %d failed: %s (%d)", - type, nl_geterror (nle), nle); - return NULL; - } - - object = nl_cache_search (cache, needle); - - nl_cache_free (cache); - - if (object && (type == OBJECT_TYPE_IP4_ADDRESS || type == OBJECT_TYPE_IP6_ADDRESS)) - _rtnl_addr_hack_lifetimes_rel_to_abs ((struct rtnl_addr *) object); - - if (object) - debug ("get_kernel_object for type %d returned %p", type, object); - else - debug ("get_kernel_object for type %d had no result", type); - return object; - } - default: - g_return_val_if_reached (NULL); - return NULL; + case RTM_NEWLINK: str_type = "NEWLINK"; break; + case RTM_DELLINK: str_type = "DELLINK"; break; + case RTM_NEWADDR: str_type = "NEWADDR"; break; + case RTM_DELADDR: str_type = "DELADDR"; break; + case RTM_NEWROUTE: str_type = "NEWROUTE"; break; + case RTM_DELROUTE: str_type = "DELROUTE"; break; } + if (str_type) + g_strlcpy (buf, str_type, len); + else + g_snprintf (buf, len, "(%d)", type); + return buf; } -/* libnl 3.2 doesn't seem to provide such a generic way to add libnl-route objects. */ -static int -add_kernel_object (struct nl_sock *sock, struct nl_object *object) -{ - switch (object_type_from_nl_object (object)) { - case OBJECT_TYPE_LINK: - return rtnl_link_add (sock, (struct rtnl_link *) object, NLM_F_CREATE); - case OBJECT_TYPE_IP4_ADDRESS: - case OBJECT_TYPE_IP6_ADDRESS: - return rtnl_addr_add (sock, (struct rtnl_addr *) object, NLM_F_CREATE | NLM_F_REPLACE); - case OBJECT_TYPE_IP4_ROUTE: - case OBJECT_TYPE_IP6_ROUTE: - return rtnl_route_add (sock, (struct rtnl_route *) object, NLM_F_CREATE | NLM_F_REPLACE); - default: - g_return_val_if_reached (-NLE_INVAL); - return -NLE_INVAL; - } -} +/******************************************************************/ -/* nm_rtnl_link_parse_info_data(): Re-fetches a link from the kernel +/* _nl_link_parse_info_data(): Re-fetches a link from the kernel * and parses its IFLA_INFO_DATA using a caller-provided parser. * * Code is stolen from rtnl_link_get_kernel(), nl_pickup(), and link_msg_parser(). @@ -519,7 +335,7 @@ static struct nla_policy info_data_link_info_policy[IFLA_INFO_MAX + 1] = { }; static int -info_data_parser (struct nl_msg *msg, void *arg) +_nl_link_parse_info_data_cb (struct nl_msg *msg, void *arg) { NMNLInfoDataClosure *closure = arg; struct nlmsghdr *n = nlmsg_hdr (msg); @@ -548,8 +364,8 @@ info_data_parser (struct nl_msg *msg, void *arg) } static int -nm_rtnl_link_parse_info_data (struct nl_sock *sk, int ifindex, - NMNLInfoDataParser parser, gpointer parser_data) +_nl_link_parse_info_data (struct nl_sock *sk, int ifindex, + NMNLInfoDataParser parser, gpointer parser_data) { NMNLInfoDataClosure data = { .parser = parser, .parser_data = parser_data }; struct nl_msg *msg = NULL; @@ -568,7 +384,7 @@ nm_rtnl_link_parse_info_data (struct nl_sock *sk, int ifindex, cb = nl_cb_clone (nl_socket_get_cb (sk)); if (cb == NULL) return -NLE_NOMEM; - nl_cb_set (cb, NL_CB_VALID, NL_CB_CUSTOM, info_data_parser, &data); + nl_cb_set (cb, NL_CB_VALID, NL_CB_CUSTOM, _nl_link_parse_info_data_cb, &data); err = nl_recvmsgs (sk, cb); nl_cb_put (cb); @@ -581,75 +397,171 @@ nm_rtnl_link_parse_info_data (struct nl_sock *sk, int ifindex, /******************************************************************/ -static gboolean -ethtool_get (const char *name, gpointer edata) +static int +_nl_sock_flush_data (struct nl_sock *sk) +{ + int nle; + struct nl_cb *cb; + + cb = nl_cb_clone (nl_socket_get_cb (sk)); + if (cb == NULL) + return -NLE_NOMEM; + + nl_cb_set (cb, NL_CB_VALID, NL_CB_DEFAULT, NULL, NULL); + nl_cb_set (cb, NL_CB_SEQ_CHECK, NL_CB_DEFAULT, NULL, NULL); + nl_cb_err (cb, NL_CB_DEFAULT, NULL, NULL); + do { + errno = 0; + + nle = nl_recvmsgs (sk, cb); + + /* Work around a libnl bug fixed in 3.2.22 (375a6294) */ + if (nle == 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) + nle = -NLE_AGAIN; + } while (nle != -NLE_AGAIN); + + nl_cb_put (cb); + return nle; +} + +static void +_nl_msg_set_seq (struct nl_sock *sk, struct nl_msg *msg, guint32 *out_seq) { - struct ifreq ifr; - int fd; + guint32 seq; - memset (&ifr, 0, sizeof (ifr)); - strncpy (ifr.ifr_name, name, IFNAMSIZ); - ifr.ifr_data = edata; + /* choose our own sequence number, because libnl does not ensure that + * it isn't zero -- which would confuse our checking for outstanding + * messages. */ + seq = nl_socket_use_seq (sk); + if (seq == 0) + seq = nl_socket_use_seq (sk); - fd = socket (PF_INET, SOCK_DGRAM, 0); - if (fd < 0) { - error ("ethtool: Could not open socket."); - return FALSE; - } + nlmsg_hdr (msg)->nlmsg_seq = seq; + if (out_seq) + *out_seq = seq; +} - if (ioctl (fd, SIOCETHTOOL, &ifr) < 0) { - debug ("ethtool: Request failed: %s", strerror (errno)); - close (fd); - return FALSE; - } +static int +_nl_sock_request_link (NMPlatform *platform, struct nl_sock *sk, int ifindex, const char *name, guint32 *out_seq) +{ + struct nl_msg *msg = NULL; + int err; - close (fd); - return TRUE; + if (name && !name[0]) + name = NULL; + + g_return_val_if_fail (ifindex > 0 || name, -NLE_INVAL); + + _LOGT ("sock: request-link %d%s%s%s", ifindex, name ? ", \"" : "", name ? name : "", name ? "\"" : ""); + + if ((err = rtnl_link_build_get_request (ifindex, name, &msg)) < 0) + return err; + + _nl_msg_set_seq (sk, msg, out_seq); + + err = nl_send_auto (sk, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return 0; } static int -ethtool_get_stringset_index (const char *ifname, int stringset_id, const char *string) +_nl_sock_request_all (NMPlatform *platform, struct nl_sock *sk, NMPObjectType obj_type, guint32 *out_seq) { - gs_free struct ethtool_sset_info *info = NULL; - gs_free struct ethtool_gstrings *strings = NULL; - guint32 len, i; + const NMPClass *klass; + struct rtgenmsg gmsg = { 0 }; + struct nl_msg *msg; + int err; - info = g_malloc0 (sizeof (*info) + sizeof (guint32)); - info->cmd = ETHTOOL_GSSET_INFO; - info->reserved = 0; - info->sset_mask = 1ULL << stringset_id; + klass = nmp_class_from_type (obj_type); - if (!ethtool_get (ifname, info)) - return -1; - if (!info->sset_mask) - return -1; + _LOGT ("sock: request-all-%s", klass->obj_type_name); - len = info->data[0]; + /* reimplement + * nl_rtgen_request (sk, klass->rtm_gettype, klass->addr_family, NLM_F_DUMP); + * because we need the sequence number. + */ + msg = nlmsg_alloc_simple (klass->rtm_gettype, NLM_F_DUMP); + if (!msg) + return -NLE_NOMEM; + + gmsg.rtgen_family = klass->addr_family; + err = nlmsg_append (msg, &gmsg, sizeof (gmsg), NLMSG_ALIGNTO); + if (err < 0) + goto errout; - strings = g_malloc0 (sizeof (*strings) + len * ETH_GSTRING_LEN); - strings->cmd = ETHTOOL_GSTRINGS; - strings->string_set = stringset_id; - strings->len = len; - if (!ethtool_get (ifname, strings)) - return -1; + _nl_msg_set_seq (sk, msg, out_seq); - for (i = 0; i < len; i++) { - if (!strcmp ((char *) &strings->data[i * ETH_GSTRING_LEN], string)) - return i; - } + err = nl_send_auto (sk, msg); +errout: + nlmsg_free(msg); + + return err >= 0 ? 0 : err; +} + +/******************************************************************/ - return -1; +#if HAVE_LIBNL_INET6_ADDR_GEN_MODE +static int _support_user_ipv6ll = 0; +#define _support_user_ipv6ll_still_undecided() (G_UNLIKELY (_support_user_ipv6ll == 0)) +#else +#define _support_user_ipv6ll_still_undecided() (FALSE) +#endif + +static gboolean +_support_user_ipv6ll_get (void) +{ +#if HAVE_LIBNL_INET6_ADDR_GEN_MODE + if (_support_user_ipv6ll_still_undecided ()) { + _support_user_ipv6ll = -1; + nm_log_warn (LOGD_PLATFORM, "kernel support for IFLA_INET6_ADDR_GEN_MODE %s", "failed to detect; assume no support"); + } else + return _support_user_ipv6ll > 0; +#endif + + return FALSE; +} + +static void +_support_user_ipv6ll_detect (const struct rtnl_link *rtnl_link) +{ +#if HAVE_LIBNL_INET6_ADDR_GEN_MODE + /* If we ever see a link with valid IPv6 link-local address + * generation modes, the kernel supports it. + */ + if (_support_user_ipv6ll_still_undecided ()) { + uint8_t mode; + + if (rtnl_link_inet6_get_addr_gen_mode ((struct rtnl_link *) rtnl_link, &mode) == 0) { + _support_user_ipv6ll = 1; + nm_log_dbg (LOGD_PLATFORM, "kernel support for IFLA_INET6_ADDR_GEN_MODE %s", "detected"); + } else { + _support_user_ipv6ll = -1; + nm_log_dbg (LOGD_PLATFORM, "kernel support for IFLA_INET6_ADDR_GEN_MODE %s", "not detected"); + } + } +#endif } /******************************************************************/ +static int _support_kernel_extended_ifa_flags = 0; + +#define _support_kernel_extended_ifa_flags_still_undecided() (G_UNLIKELY (_support_kernel_extended_ifa_flags == 0)) + static void -_check_support_kernel_extended_ifa_flags_init (NMLinuxPlatformPrivate *priv, struct nl_msg *msg) +_support_kernel_extended_ifa_flags_detect (struct nl_msg *msg) { - struct nlmsghdr *msg_hdr = nlmsg_hdr (msg); + struct nlmsghdr *msg_hdr; + + if (!_support_kernel_extended_ifa_flags_still_undecided ()) + return; - g_return_if_fail (priv->support_kernel_extended_ifa_flags == 0); - g_return_if_fail (msg_hdr->nlmsg_type == RTM_NEWADDR); + msg_hdr = nlmsg_hdr (msg); + if (msg_hdr->nlmsg_type != RTM_NEWADDR) + return; /* the extended address flags are only set for AF_INET6 */ if (((struct ifaddrmsg *) nlmsg_data (msg_hdr))->ifa_family != AF_INET6) @@ -659,424 +571,507 @@ _check_support_kernel_extended_ifa_flags_init (NMLinuxPlatformPrivate *priv, str * we assume, that the kernel supports extended flags, IFA_F_MANAGETEMPADDR * and IFA_F_NOPREFIXROUTE (they were added together). **/ - priv->support_kernel_extended_ifa_flags = + _support_kernel_extended_ifa_flags = nlmsg_find_attr (msg_hdr, sizeof (struct ifaddrmsg), 8 /* IFA_FLAGS */) ? 1 : -1; } static gboolean -check_support_kernel_extended_ifa_flags (NMPlatform *platform) +_support_kernel_extended_ifa_flags_get (void) { - NMLinuxPlatformPrivate *priv; + if (_support_kernel_extended_ifa_flags_still_undecided ()) { + nm_log_warn (LOGD_PLATFORM, "Unable to detect kernel support for extended IFA_FLAGS. Assume no kernel support."); + _support_kernel_extended_ifa_flags = -1; + } + return _support_kernel_extended_ifa_flags > 0; +} - g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), FALSE); +/****************************************************************** + * Object type specific utilities + ******************************************************************/ - priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); +static guint +_nm_ip_config_source_to_rtprot (NMIPConfigSource source) +{ + switch (source) { + case NM_IP_CONFIG_SOURCE_UNKNOWN: + return RTPROT_UNSPEC; + case NM_IP_CONFIG_SOURCE_KERNEL: + case NM_IP_CONFIG_SOURCE_RTPROT_KERNEL: + return RTPROT_KERNEL; + case NM_IP_CONFIG_SOURCE_DHCP: + return RTPROT_DHCP; + case NM_IP_CONFIG_SOURCE_RDISC: + return RTPROT_RA; - if (priv->support_kernel_extended_ifa_flags == 0) { - nm_log_warn (LOGD_PLATFORM, "Unable to detect kernel support for extended IFA_FLAGS. Assume no kernel support."); - priv->support_kernel_extended_ifa_flags = -1; + default: + return RTPROT_STATIC; } - - return priv->support_kernel_extended_ifa_flags > 0; } -static gboolean -check_support_user_ipv6ll (NMPlatform *platform) +static NMIPConfigSource +_nm_ip_config_source_from_rtprot (guint rtprot) { - NMLinuxPlatformPrivate *priv; + switch (rtprot) { + case RTPROT_UNSPEC: + return NM_IP_CONFIG_SOURCE_UNKNOWN; + case RTPROT_KERNEL: + return NM_IP_CONFIG_SOURCE_RTPROT_KERNEL; + case RTPROT_REDIRECT: + return NM_IP_CONFIG_SOURCE_KERNEL; + case RTPROT_RA: + return NM_IP_CONFIG_SOURCE_RDISC; + case RTPROT_DHCP: + return NM_IP_CONFIG_SOURCE_DHCP; - g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), FALSE); + default: + return NM_IP_CONFIG_SOURCE_USER; + } +} - priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); +/******************************************************************/ + +typedef struct { + const NMLinkType nm_type; + const char *type_string; + + /* IFLA_INFO_KIND / rtnl_link_get_type() where applicable; the rtnl type + * should only be specified if the device type can be created without + * additional parameters, and if the device type can be determined from + * the rtnl_type. eg, tun/tap should not be specified since both + * tun and tap devices use "tun", and InfiniBand should not be + * specified because a PKey is required at creation. Drivers set this + * value from their 'struct rtnl_link_ops' structure. + */ + const char *rtnl_type; + + /* uevent DEVTYPE where applicable, from /sys/class/net/<ifname>/uevent; + * drivers set this value from their SET_NETDEV_DEV() call and the + * 'struct device_type' name member. + */ + const char *devtype; +} LinkDesc; + +static const LinkDesc linktypes[] = { + { NM_LINK_TYPE_NONE, "none", NULL, NULL }, + { NM_LINK_TYPE_UNKNOWN, "unknown", NULL, NULL }, + + { NM_LINK_TYPE_ETHERNET, "ethernet", NULL, NULL }, + { NM_LINK_TYPE_INFINIBAND, "infiniband", NULL, NULL }, + { NM_LINK_TYPE_OLPC_MESH, "olpc-mesh", NULL, NULL }, + { NM_LINK_TYPE_WIFI, "wifi", NULL, "wlan" }, + { NM_LINK_TYPE_WWAN_ETHERNET, "wwan", NULL, "wwan" }, + { NM_LINK_TYPE_WIMAX, "wimax", "wimax", "wimax" }, + + { NM_LINK_TYPE_DUMMY, "dummy", "dummy", NULL }, + { NM_LINK_TYPE_GRE, "gre", "gre", NULL }, + { NM_LINK_TYPE_GRETAP, "gretap", "gretap", NULL }, + { NM_LINK_TYPE_IFB, "ifb", "ifb", NULL }, + { NM_LINK_TYPE_LOOPBACK, "loopback", NULL, NULL }, + { NM_LINK_TYPE_MACVLAN, "macvlan", "macvlan", NULL }, + { NM_LINK_TYPE_MACVTAP, "macvtap", "macvtap", NULL }, + { NM_LINK_TYPE_OPENVSWITCH, "openvswitch", "openvswitch", NULL }, + { NM_LINK_TYPE_TAP, "tap", NULL, NULL }, + { NM_LINK_TYPE_TUN, "tun", NULL, NULL }, + { NM_LINK_TYPE_VETH, "veth", "veth", NULL }, + { NM_LINK_TYPE_VLAN, "vlan", "vlan", "vlan" }, + { NM_LINK_TYPE_VXLAN, "vxlan", "vxlan", "vxlan" }, + { NM_LINK_TYPE_BNEP, "bluetooth", NULL, "bluetooth" }, + + { NM_LINK_TYPE_BRIDGE, "bridge", "bridge", "bridge" }, + { NM_LINK_TYPE_BOND, "bond", "bond", "bond" }, + { NM_LINK_TYPE_TEAM, "team", "team", NULL }, +}; + +static const char * +nm_link_type_to_rtnl_type_string (NMLinkType type) +{ + int i; - if (priv->support_user_ipv6ll == 0) { - nm_log_warn (LOGD_PLATFORM, "Unable to detect kernel support for IFLA_INET6_ADDR_GEN_MODE. Assume no kernel support."); - priv->support_user_ipv6ll = -1; + for (i = 0; i < G_N_ELEMENTS (linktypes); i++) { + if (type == linktypes[i].nm_type) + return linktypes[i].rtnl_type; } + g_return_val_if_reached (NULL); +} + +const char * +nm_link_type_to_string (NMLinkType type) +{ + int i; - return priv->support_user_ipv6ll > 0; + for (i = 0; i < G_N_ELEMENTS (linktypes); i++) { + if (type == linktypes[i].nm_type) + return linktypes[i].type_string; + } + g_return_val_if_reached (NULL); } +/****************************************************************** + * NMPlatform types and functions + ******************************************************************/ -/* Object type specific utilities */ +typedef struct _NMLinuxPlatformPrivate NMLinuxPlatformPrivate; -static const char * -type_to_string (NMLinkType type) +struct _NMLinuxPlatformPrivate { + struct nl_sock *nlh; + struct nl_sock *nlh_event; + guint32 nlh_seq_expect; + guint32 nlh_seq_last; + NMPCache *cache; + GIOChannel *event_channel; + guint event_id; + + GUdevClient *udev_client; + + struct { + DelayedActionType flags; + GPtrArray *list_master_connected; + GPtrArray *list_refresh_link; + gint is_handling; + guint idle_id; + } delayed_action; + + GHashTable *prune_candidates; + GHashTable *delayed_deletion; + + GHashTable *wifi_data; +}; + +static inline NMLinuxPlatformPrivate * +NM_LINUX_PLATFORM_GET_PRIVATE (const void *self) { - /* Note that this only has to support virtual types */ - switch (type) { - case NM_LINK_TYPE_DUMMY: - return "dummy"; - case NM_LINK_TYPE_GRE: - return "gre"; - case NM_LINK_TYPE_GRETAP: - return "gretap"; - case NM_LINK_TYPE_IFB: - return "ifb"; - case NM_LINK_TYPE_MACVLAN: - return "macvlan"; - case NM_LINK_TYPE_MACVTAP: - return "macvtap"; - case NM_LINK_TYPE_TAP: - return "tap"; - case NM_LINK_TYPE_TUN: - return "tun"; - case NM_LINK_TYPE_VETH: - return "veth"; - case NM_LINK_TYPE_VLAN: - return "vlan"; - case NM_LINK_TYPE_VXLAN: - return "vxlan"; - case NM_LINK_TYPE_BRIDGE: - return "bridge"; - case NM_LINK_TYPE_BOND: - return "bond"; - case NM_LINK_TYPE_TEAM: - return "team"; - default: - g_warning ("Wrong type: %d", type); - return NULL; - } + nm_assert (NM_IS_LINUX_PLATFORM (self)); + + return ((NMLinuxPlatform *) self)->priv; } -#define return_type(t, name) \ - G_STMT_START { \ - if (out_name) \ - *out_name = name; \ - return t; \ - } G_STMT_END +G_DEFINE_TYPE (NMLinuxPlatform, nm_linux_platform, NM_TYPE_PLATFORM) -static NMLinkType -link_type_from_udev (NMPlatform *platform, int ifindex, const char *ifname, int arptype, const char **out_name) +void +nm_linux_platform_setup (void) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - GUdevDevice *udev_device; - const char *prop, *sysfs_path; + g_object_new (NM_TYPE_LINUX_PLATFORM, + NM_PLATFORM_REGISTER_SINGLETON, TRUE, + NULL); +} - g_assert (ifname); +/******************************************************************/ - udev_device = g_hash_table_lookup (priv->udev_devices, GINT_TO_POINTER (ifindex)); - if (!udev_device) - return_type (NM_LINK_TYPE_UNKNOWN, "unknown"); +NMPObjectType +_nlo_get_object_type (const struct nl_object *object) +{ + const char *type_str; + + if (!object || !(type_str = nl_object_get_type (object))) + return NMP_OBJECT_TYPE_UNKNOWN; - if ( g_udev_device_get_property (udev_device, "ID_NM_OLPC_MESH") - || g_udev_device_get_sysfs_attr (udev_device, "anycast_mask")) - return_type (NM_LINK_TYPE_OLPC_MESH, "olpc-mesh"); + if (!strcmp (type_str, "route/link")) + return NMP_OBJECT_TYPE_LINK; + else if (!strcmp (type_str, "route/addr")) { + switch (rtnl_addr_get_family ((struct rtnl_addr *) object)) { + case AF_INET: + return NMP_OBJECT_TYPE_IP4_ADDRESS; + case AF_INET6: + return NMP_OBJECT_TYPE_IP6_ADDRESS; + default: + return NMP_OBJECT_TYPE_UNKNOWN; + } + } else if (!strcmp (type_str, "route/route")) { + switch (rtnl_route_get_family ((struct rtnl_route *) object)) { + case AF_INET: + return NMP_OBJECT_TYPE_IP4_ROUTE; + case AF_INET6: + return NMP_OBJECT_TYPE_IP6_ROUTE; + default: + return NMP_OBJECT_TYPE_UNKNOWN; + } + } else + return NMP_OBJECT_TYPE_UNKNOWN; +} - prop = g_udev_device_get_property (udev_device, "DEVTYPE"); - sysfs_path = g_udev_device_get_sysfs_path (udev_device); - if (wifi_utils_is_wifi (ifname, sysfs_path, prop)) - return_type (NM_LINK_TYPE_WIFI, "wifi"); - else if (g_strcmp0 (prop, "wwan") == 0) - return_type (NM_LINK_TYPE_WWAN_ETHERNET, "wwan"); - else if (g_strcmp0 (prop, "wimax") == 0) - return_type (NM_LINK_TYPE_WIMAX, "wimax"); +/******************************************************************/ - if (arptype == ARPHRD_ETHER) - return_type (NM_LINK_TYPE_ETHERNET, "ethernet"); +static gboolean +check_support_kernel_extended_ifa_flags (NMPlatform *platform) +{ + g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), FALSE); - return_type (NM_LINK_TYPE_UNKNOWN, "unknown"); + return _support_kernel_extended_ifa_flags_get (); } static gboolean -link_is_software (struct rtnl_link *rtnllink) +check_support_user_ipv6ll (NMPlatform *platform) { - const char *type; + g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), FALSE); - /* FIXME: replace somehow with NMLinkType or nm_platform_is_software(), but - * solve the infinite callstack problems that getting the type of a TUN/TAP - * device causes. - */ + return _support_user_ipv6ll_get (); +} - if ( rtnl_link_get_arptype (rtnllink) == ARPHRD_INFINIBAND - && strchr (rtnl_link_get_name (rtnllink), '.')) - return TRUE; +static void +process_events (NMPlatform *platform) +{ + delayed_action_handle_all (platform, TRUE); +} - type = rtnl_link_get_type (rtnllink); - if (type == NULL) - return FALSE; +/******************************************************************/ - if (!strcmp (type, "dummy") || - !strcmp (type, "gre") || - !strcmp (type, "gretap") || - !strcmp (type, "macvlan") || - !strcmp (type, "macvtap") || - !strcmp (type, "tun") || - !strcmp (type, "veth") || - !strcmp (type, "vlan") || - !strcmp (type, "vxlan") || - !strcmp (type, "bridge") || - !strcmp (type, "bond") || - !strcmp (type, "team")) - return TRUE; +#define cache_lookup_all_objects(type, platform, obj_type, visible_only) \ + ((const type *const*) nmp_cache_lookup_multi (NM_LINUX_PLATFORM_GET_PRIVATE ((platform))->cache, \ + nmp_cache_id_init_object_type (NMP_CACHE_ID_STATIC, (obj_type), (visible_only)), \ + NULL)) - return FALSE; -} -static const char * -ethtool_get_driver (const char *ifname) -{ - struct ethtool_drvinfo drvinfo = { 0 }; +/******************************************************************/ - g_return_val_if_fail (ifname != NULL, NULL); +#define DEVTYPE_PREFIX "DEVTYPE=" - drvinfo.cmd = ETHTOOL_GDRVINFO; - if (!ethtool_get (ifname, &drvinfo)) - return NULL; +static char * +read_devtype (const char *sysfs_path) +{ + gs_free char *uevent = g_strdup_printf ("%s/uevent", sysfs_path); + char *contents = NULL; + char *cont, *end; - if (!*drvinfo.driver) + if (!g_file_get_contents (uevent, &contents, NULL, NULL)) return NULL; - - return g_intern_string (drvinfo.driver); + for (cont = contents; cont; cont = end) { + end = strpbrk (cont, "\r\n"); + if (end) + *end++ = '\0'; + if (strncmp (cont, DEVTYPE_PREFIX, STRLEN (DEVTYPE_PREFIX)) == 0) { + cont += STRLEN (DEVTYPE_PREFIX); + memmove (contents, cont, strlen (cont) + 1); + return contents; + } + } + g_free (contents); + return NULL; } -static gboolean -link_is_announceable (NMPlatform *platform, struct rtnl_link *rtnllink) +static const NMPObject * +_lookup_link_cached (NMPlatform *platform, int ifindex, gboolean *completed_from_cache, const NMPObject **link_cached) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + const NMPObject *obj; - /* Software devices are always visible outside the platform */ - if (link_is_software (rtnllink)) - return TRUE; + nm_assert (completed_from_cache && link_cached); - /* Hardware devices must be found by udev so rules get run and tags set */ - if (g_hash_table_lookup (priv->udev_devices, - GINT_TO_POINTER (rtnl_link_get_ifindex (rtnllink)))) - return TRUE; + if (!*completed_from_cache) { + obj = ifindex > 0 ? nmp_cache_lookup_link (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, ifindex) : NULL; - return FALSE; + if (obj && !obj->_link.netlink.is_in_netlink) + *link_cached = obj; + else + *link_cached = NULL; + *completed_from_cache = TRUE; + } + return *link_cached; } static NMLinkType -link_extract_type (NMPlatform *platform, struct rtnl_link *rtnllink, const char **out_name) +link_extract_type (NMPlatform *platform, struct rtnl_link *rtnllink, gboolean *completed_from_cache, const NMPObject **link_cached, const char **out_kind) { - const char *type; + const char *rtnl_type, *ifname; + int i, arptype; - if (!rtnllink) - return_type (NM_LINK_TYPE_NONE, NULL); + if (!rtnllink) { + if (out_kind) + *out_kind = NULL; + return NM_LINK_TYPE_NONE; + } + + rtnl_type = rtnl_link_get_type (rtnllink); + if (!rtnl_type && completed_from_cache) { + const NMPObject *obj; + + obj = _lookup_link_cached (platform, rtnl_link_get_ifindex (rtnllink), completed_from_cache, link_cached); + if (obj && obj->link.kind) { + rtnl_type = obj->link.kind; + _LOGT ("link_extract_type(): complete kind from cache: ifindex=%d, kind=%s", rtnl_link_get_ifindex (rtnllink), rtnl_type); + } + } + if (out_kind) + *out_kind = rtnl_type; + if (rtnl_type) { + for (i = 0; i < G_N_ELEMENTS (linktypes); i++) { + if (g_strcmp0 (rtnl_type, linktypes[i].rtnl_type) == 0) + return linktypes[i].nm_type; + } - type = rtnl_link_get_type (rtnllink); + if (!strcmp (rtnl_type, "tun")) { + NMPlatformTunProperties props; + guint flags; - if (!type) { - int arptype = rtnl_link_get_arptype (rtnllink); - const char *driver; - const char *ifname; + if (tun_get_properties_ifname (platform, rtnl_link_get_name (rtnllink), &props)) { + if (!g_strcmp0 (props.mode, "tap")) + return NM_LINK_TYPE_TAP; + if (!g_strcmp0 (props.mode, "tun")) + return NM_LINK_TYPE_TUN; + } + flags = rtnl_link_get_flags (rtnllink); + + nm_log_dbg (LOGD_PLATFORM, "Failed to read tun properties for interface %d (link flags: %X)", + rtnl_link_get_ifindex (rtnllink), flags); + + /* try guessing the type using the link flags instead... */ + if (flags & IFF_POINTOPOINT) + return NM_LINK_TYPE_TUN; + return NM_LINK_TYPE_TAP; + } + } - if (arptype == ARPHRD_LOOPBACK) - return_type (NM_LINK_TYPE_LOOPBACK, "loopback"); - else if (arptype == ARPHRD_INFINIBAND) - return_type (NM_LINK_TYPE_INFINIBAND, "infiniband"); + arptype = rtnl_link_get_arptype (rtnllink); + if (arptype == ARPHRD_LOOPBACK) + return NM_LINK_TYPE_LOOPBACK; + else if (arptype == ARPHRD_INFINIBAND) + return NM_LINK_TYPE_INFINIBAND; - ifname = rtnl_link_get_name (rtnllink); - if (!ifname) - return_type (NM_LINK_TYPE_UNKNOWN, type); + ifname = rtnl_link_get_name (rtnllink); + if (ifname) { + gs_free char *driver = NULL; + gs_free char *sysfs_path = NULL; + gs_free char *anycast_mask = NULL; + gs_free char *devtype = NULL; - driver = ethtool_get_driver (ifname); if (arptype == 256) { /* Some s390 CTC-type devices report 256 for the encapsulation type * for some reason, but we need to call them Ethernet. */ if (!g_strcmp0 (driver, "ctcm")) - return_type (NM_LINK_TYPE_ETHERNET, "ethernet"); + return NM_LINK_TYPE_ETHERNET; } - if (!g_strcmp0 (driver, "openvswitch")) - return_type (NM_LINK_TYPE_OPENVSWITCH, "openvswitch"); - - return link_type_from_udev (platform, - rtnl_link_get_ifindex (rtnllink), - ifname, - arptype, - out_name); - } else if (!strcmp (type, "dummy")) - return_type (NM_LINK_TYPE_DUMMY, "dummy"); - else if (!strcmp (type, "gre")) - return_type (NM_LINK_TYPE_GRE, "gre"); - else if (!strcmp (type, "gretap")) - return_type (NM_LINK_TYPE_GRETAP, "gretap"); - else if (!strcmp (type, "ifb")) - return_type (NM_LINK_TYPE_IFB, "ifb"); - else if (!strcmp (type, "macvlan")) - return_type (NM_LINK_TYPE_MACVLAN, "macvlan"); - else if (!strcmp (type, "macvtap")) - return_type (NM_LINK_TYPE_MACVTAP, "macvtap"); - else if (!strcmp (type, "tun")) { - NMPlatformTunProperties props; - guint flags; - - if (nm_platform_tun_get_properties (rtnl_link_get_ifindex (rtnllink), &props)) { - if (!g_strcmp0 (props.mode, "tap")) - return_type (NM_LINK_TYPE_TAP, "tap"); - if (!g_strcmp0 (props.mode, "tun")) - return_type (NM_LINK_TYPE_TUN, "tun"); + /* Fallback OVS detection for kernel <= 3.16 */ + if (nmp_utils_ethtool_get_driver_info (ifname, &driver, NULL, NULL)) { + if (!g_strcmp0 (driver, "openvswitch")) + return NM_LINK_TYPE_OPENVSWITCH; } - flags = rtnl_link_get_flags (rtnllink); - - nm_log_dbg (LOGD_PLATFORM, "Failed to read tun properties for interface %d (link flags: %X)", - rtnl_link_get_ifindex (rtnllink), flags); - - /* try guessing the type using the link flags instead... */ - if (flags & IFF_POINTOPOINT) - return_type (NM_LINK_TYPE_TUN, "tun"); - return_type (NM_LINK_TYPE_TAP, "tap"); - } else if (!strcmp (type, "veth")) - return_type (NM_LINK_TYPE_VETH, "veth"); - else if (!strcmp (type, "vlan")) - return_type (NM_LINK_TYPE_VLAN, "vlan"); - else if (!strcmp (type, "vxlan")) - return_type (NM_LINK_TYPE_VXLAN, "vxlan"); - else if (!strcmp (type, "bridge")) - return_type (NM_LINK_TYPE_BRIDGE, "bridge"); - else if (!strcmp (type, "bond")) - return_type (NM_LINK_TYPE_BOND, "bond"); - else if (!strcmp (type, "team")) - return_type (NM_LINK_TYPE_TEAM, "team"); - - return_type (NM_LINK_TYPE_UNKNOWN, type); -} -static const char * -udev_get_driver (NMPlatform *platform, GUdevDevice *device, int ifindex) -{ - GUdevDevice *parent = NULL, *grandparent = NULL; - const char *driver, *subsys; - - driver = g_udev_device_get_driver (device); - if (driver) - return driver; - - /* Try the parent */ - parent = g_udev_device_get_parent (device); - if (parent) { - driver = g_udev_device_get_driver (parent); - if (!driver) { - /* Try the grandparent if it's an ibmebus device or if the - * subsys is NULL which usually indicates some sort of - * platform device like a 'gadget' net interface. - */ - subsys = g_udev_device_get_subsystem (parent); - if ( (g_strcmp0 (subsys, "ibmebus") == 0) - || (subsys == NULL)) { - grandparent = g_udev_device_get_parent (parent); - if (grandparent) { - driver = g_udev_device_get_driver (grandparent); + sysfs_path = g_strdup_printf ("/sys/class/net/%s", ifname); + anycast_mask = g_strdup_printf ("%s/anycast_mask", sysfs_path); + if (g_file_test (anycast_mask, G_FILE_TEST_EXISTS)) + return NM_LINK_TYPE_OLPC_MESH; + + devtype = read_devtype (sysfs_path); + for (i = 0; devtype && i < G_N_ELEMENTS (linktypes); i++) { + if (g_strcmp0 (devtype, linktypes[i].devtype) == 0) { + if (linktypes[i].nm_type == NM_LINK_TYPE_BNEP) { + /* Both BNEP and 6lowpan use DEVTYPE=bluetooth, so we must + * use arptype to distinguish between them. + */ + if (arptype != ARPHRD_ETHER) + continue; } + return linktypes[i].nm_type; } } - } - /* Intern the string so we don't have to worry about memory - * management in NMPlatformLink. - */ - if (driver) - driver = g_intern_string (driver); + /* Fallback for drivers that don't call SET_NETDEV_DEVTYPE() */ + if (wifi_utils_is_wifi (ifname, sysfs_path)) + return NM_LINK_TYPE_WIFI; - g_clear_object (&parent); - g_clear_object (&grandparent); + /* Standard wired ethernet interfaces don't report an rtnl_link_type, so + * only allow fallback to Ethernet if no type is given. This should + * prevent future virtual network drivers from being treated as Ethernet + * when they should be Generic instead. + */ + if (arptype == ARPHRD_ETHER && !rtnl_type && !devtype) + return NM_LINK_TYPE_ETHERNET; + } - return driver; + return NM_LINK_TYPE_UNKNOWN; } -static gboolean -init_link (NMPlatform *platform, NMPlatformLink *info, struct rtnl_link *rtnllink) +gboolean +_nmp_vt_cmd_plobj_init_from_nl_link (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - GUdevDevice *udev_device; + NMPlatformLink *obj = (NMPlatformLink *) _obj; + NMPObjectLink *obj_priv = (NMPObjectLink *) _obj; + struct rtnl_link *nlo = (struct rtnl_link *) _nlo; const char *name; + struct nl_addr *nladdr; + const char *kind; + gboolean completed_from_cache_val = FALSE; + gboolean *completed_from_cache = complete_from_cache ? &completed_from_cache_val : NULL; + const NMPObject *link_cached = NULL; - g_return_val_if_fail (rtnllink, FALSE); + nm_assert (memcmp (obj, ((char [sizeof (NMPObjectLink)]) { 0 }), sizeof (NMPObjectLink)) == 0); - name = rtnl_link_get_name (rtnllink); - memset (info, 0, sizeof (*info)); + if (_LOGT_ENABLED () && !NM_IN_SET (rtnl_link_get_family (nlo), AF_UNSPEC, AF_BRIDGE)) + _LOGT ("netlink object for ifindex %d has unusual family %d", rtnl_link_get_ifindex (nlo), rtnl_link_get_family (nlo)); - info->ifindex = rtnl_link_get_ifindex (rtnllink); - if (name) - g_strlcpy (info->name, name, sizeof (info->name)); - else - info->name[0] = '\0'; - info->type = link_extract_type (platform, rtnllink, &info->type_name); - info->up = !!(rtnl_link_get_flags (rtnllink) & IFF_UP); - info->connected = !!(rtnl_link_get_flags (rtnllink) & IFF_LOWER_UP); - info->arp = !(rtnl_link_get_flags (rtnllink) & IFF_NOARP); - info->master = rtnl_link_get_master (rtnllink); - info->parent = rtnl_link_get_link (rtnllink); - info->mtu = rtnl_link_get_mtu (rtnllink); - - udev_device = g_hash_table_lookup (priv->udev_devices, GINT_TO_POINTER (info->ifindex)); - if (udev_device) { - info->driver = udev_get_driver (platform, udev_device, info->ifindex); - if (!info->driver) - info->driver = g_intern_string (rtnl_link_get_type (rtnllink)); - if (!info->driver) - info->driver = ethtool_get_driver (info->name); - if (!info->driver) - info->driver = "unknown"; - info->udi = g_udev_device_get_sysfs_path (udev_device); - } + obj->ifindex = rtnl_link_get_ifindex (nlo); - return TRUE; -} + if (id_only) + return TRUE; -/* Hack: Empty bridges and bonds have IFF_LOWER_UP flag and therefore they break - * the carrier detection. This hack makes nm-platform think they don't have the - * IFF_LOWER_UP flag. This seems to also apply to bonds (specifically) with all - * slaves down. - * - * Note: This is still a bit racy but when NetworkManager asks for enslaving a slave, - * nm-platform will do that synchronously and will immediately ask for both master - * and slave information after the enslaving request. After the synchronous call, the - * master carrier is already updated with the slave carrier in mind. - * - * https://bugzilla.redhat.com/show_bug.cgi?id=910348 - */ -static void -hack_empty_master_iff_lower_up (NMPlatform *platform, struct nl_object *object) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - struct rtnl_link *rtnllink; - int ifindex; - struct nl_object *slave; - const char *type; + name = rtnl_link_get_name (nlo); + if (name) + g_strlcpy (obj->name, name, sizeof (obj->name)); + obj->type = link_extract_type (platform, nlo, completed_from_cache, &link_cached, &kind); + obj->kind = g_intern_string (kind); + obj->flags = rtnl_link_get_flags (nlo); + obj->connected = NM_FLAGS_HAS (obj->flags, IFF_LOWER_UP); + obj->master = rtnl_link_get_master (nlo); + obj->parent = rtnl_link_get_link (nlo); + obj->mtu = rtnl_link_get_mtu (nlo); + obj->arptype = rtnl_link_get_arptype (nlo); + + if (obj->type == NM_LINK_TYPE_VLAN) { + if (!g_strcmp0 (rtnl_link_get_type (nlo), "vlan")) + obj->vlan_id = rtnl_link_vlan_get_id (nlo); + else if (completed_from_cache) { + _lookup_link_cached (platform, obj->ifindex, completed_from_cache, &link_cached); + if (link_cached) + obj->vlan_id = link_cached->link.vlan_id; + } + } - if (!object) - return; - if (strcmp (nl_object_get_type (object), "route/link")) - return; + if ((nladdr = rtnl_link_get_addr (nlo))) { + unsigned int l = 0; - rtnllink = (struct rtnl_link *) object; + l = nl_addr_get_len (nladdr); + if (l > 0 && l <= NM_UTILS_HWADDR_LEN_MAX) { + G_STATIC_ASSERT (NM_UTILS_HWADDR_LEN_MAX == sizeof (obj->addr.data)); + memcpy (obj->addr.data, nl_addr_get_binary_addr (nladdr), l); + obj->addr.len = l; + } + } - ifindex = rtnl_link_get_ifindex (rtnllink); +#if HAVE_LIBNL_INET6_ADDR_GEN_MODE + if (_support_user_ipv6ll_get ()) { + guint8 mode = 0; - type = rtnl_link_get_type (rtnllink); - if (!type || (strcmp (type, "bridge") != 0 && strcmp (type, "bond") != 0)) - return; + if (rtnl_link_inet6_get_addr_gen_mode (nlo, &mode) == 0) + obj->inet6_addr_gen_mode_inv = _nm_platform_uint8_inv (mode); + } +#endif - for (slave = nl_cache_get_first (priv->link_cache); slave; slave = nl_cache_get_next (slave)) { - struct rtnl_link *rtnlslave = (struct rtnl_link *) slave; - if (rtnl_link_get_master (rtnlslave) == ifindex - && rtnl_link_get_flags (rtnlslave) & IFF_LOWER_UP) - return; +#if HAVE_LIBNL_INET6_TOKEN + if ((rtnl_link_inet6_get_token (nlo, &nladdr)) == 0) { + if ( nl_addr_get_family (nladdr) == AF_INET6 + && nl_addr_get_len (nladdr) == sizeof (struct in6_addr)) { + struct in6_addr *addr; + NMUtilsIPv6IfaceId *iid = &obj->inet6_token.iid; + + addr = nl_addr_get_binary_addr (nladdr); + iid->id_u8[7] = addr->s6_addr[15]; + iid->id_u8[6] = addr->s6_addr[14]; + iid->id_u8[5] = addr->s6_addr[13]; + iid->id_u8[4] = addr->s6_addr[12]; + iid->id_u8[3] = addr->s6_addr[11]; + iid->id_u8[2] = addr->s6_addr[10]; + iid->id_u8[1] = addr->s6_addr[9]; + iid->id_u8[0] = addr->s6_addr[8]; + obj->inet6_token.is_valid = TRUE; + } + nl_addr_put (nladdr); } +#endif - rtnl_link_unset_flags (rtnllink, IFF_LOWER_UP); -} + obj_priv->netlink.is_in_netlink = TRUE; -static guint32 -_get_remaining_time (guint32 start_timestamp, guint32 end_timestamp) -{ - /* Return the remaining time between @start_timestamp until @end_timestamp. - * - * If @end_timestamp is NM_PLATFORM_LIFETIME_PERMANENT, it returns - * NM_PLATFORM_LIFETIME_PERMANENT. If @start_timestamp already passed - * @end_timestamp it returns 0. Beware, NMPlatformIPAddress treats a @lifetime - * of 0 as permanent. - */ - if (end_timestamp == NM_PLATFORM_LIFETIME_PERMANENT) - return NM_PLATFORM_LIFETIME_PERMANENT; - if (start_timestamp >= end_timestamp) - return 0; - return end_timestamp - start_timestamp; + return TRUE; } /* _timestamp_nl_to_ms: @@ -1112,15 +1107,18 @@ _timestamp_nl_to_ms (guint32 timestamp_nl, gint64 monotonic_ms) } static guint32 -_rtnl_addr_last_update_time_to_nm (const struct rtnl_addr *rtnladdr) +_rtnl_addr_last_update_time_to_nm (const struct rtnl_addr *rtnladdr, gint32 *out_now_nm) { guint32 last_update_time = rtnl_addr_get_last_update_time ((struct rtnl_addr *) rtnladdr); struct timespec tp; gint64 now_nl, now_nm, result; /* timestamp is unset. Default to 1. */ - if (!last_update_time) + if (!last_update_time) { + if (out_now_nm) + *out_now_nm = 0; return 1; + } /* do all the calculations in milliseconds scale */ @@ -1131,6 +1129,9 @@ _rtnl_addr_last_update_time_to_nm (const struct rtnl_addr *rtnladdr) result = now_nm - (now_nl - _timestamp_nl_to_ms (last_update_time, now_nl)); + if (out_now_nm) + *out_now_nm = now_nm / 1000; + /* converting the last_update_time into nm_utils_get_monotonic_timestamp_ms() scale is * a good guess but fails in the following situations: * @@ -1148,686 +1149,952 @@ _rtnl_addr_last_update_time_to_nm (const struct rtnl_addr *rtnladdr) return result / 1000; } -static void -_init_ip_address_lifetime (NMPlatformIPAddress *address, const struct rtnl_addr *rtnladdr) +static guint32 +_extend_lifetime (guint32 lifetime, guint32 seconds) { - guint32 a_valid = rtnl_addr_get_valid_lifetime ((struct rtnl_addr *) rtnladdr); - guint32 a_preferred = rtnl_addr_get_preferred_lifetime ((struct rtnl_addr *) rtnladdr); - - /* the meaning of the valid and preferred lifetimes is different from the - * original meaning. See _rtnl_addr_hack_lifetimes_rel_to_abs(). - * Beware: this function expects hacked rtnl_addr objects. - */ + guint64 v; - if (a_valid == NM_PLATFORM_LIFETIME_PERMANENT && - a_preferred == NM_PLATFORM_LIFETIME_PERMANENT) { - address->timestamp = 0; - address->lifetime = NM_PLATFORM_LIFETIME_PERMANENT; - address->preferred = NM_PLATFORM_LIFETIME_PERMANENT; - return; - } + if ( lifetime == NM_PLATFORM_LIFETIME_PERMANENT + || seconds == 0) + return lifetime; - /* The valies are hacked and absolute expiry times. They must - * be positive and preferred<=valid. */ - g_assert (a_preferred <= a_valid && - a_valid > 0 && - a_preferred > 0); - - if (a_valid <= 1) { - /* Since we want to have positive @timestamp and @valid != 0, - * we must handle this case special. */ - address->timestamp = 1; - address->lifetime = 1; /* Extend the lifetime by one second */ - address->preferred = 0; /* no longer preferred. */ - return; - } + v = (guint64) lifetime + (guint64) seconds; + return MIN (v, NM_PLATFORM_LIFETIME_PERMANENT - 1); +} - /* _rtnl_addr_last_update_time_to_nm() might be wrong, so don't rely on - * timestamp to have any meaning beyond anchoring the relative durations - * @lifetime and @preferred. - */ - address->timestamp = _rtnl_addr_last_update_time_to_nm (rtnladdr); +/* The rtnl_addr object contains relative lifetimes @valid and @preferred + * that count in seconds, starting from the moment when the kernel constructed + * the netlink message. + * + * There is also a field rtnl_addr_last_update_time(), which is the absolute + * time in 1/100th of a second of clock_gettime (CLOCK_MONOTONIC) when the address + * was modified (wrapping every 497 days). + * Immediately at the time when the address was last modified, #NOW and @last_update_time + * are the same, so (only) in that case @valid and @preferred are anchored at @last_update_time. + * However, this is not true in general. As time goes by, whenever kernel sends a new address + * via netlink, the lifetimes keep counting down. + **/ +static void +_nlo_rtnl_addr_get_lifetimes (const struct rtnl_addr *rtnladdr, + guint32 *out_timestamp, + guint32 *out_lifetime, + guint32 *out_preferred) +{ + guint32 timestamp = 0; + gint32 now; + guint32 lifetime = rtnl_addr_get_valid_lifetime ((struct rtnl_addr *) rtnladdr); + guint32 preferred = rtnl_addr_get_preferred_lifetime ((struct rtnl_addr *) rtnladdr); + + if ( lifetime != NM_PLATFORM_LIFETIME_PERMANENT + || preferred != NM_PLATFORM_LIFETIME_PERMANENT) { + if (preferred > lifetime) + preferred = lifetime; + timestamp = _rtnl_addr_last_update_time_to_nm (rtnladdr, &now); + + if (now == 0) { + /* strange. failed to detect the last-update time and assumed that timestamp is 1. */ + nm_assert (timestamp == 1); + now = nm_utils_get_monotonic_timestamp_s (); + } + if (timestamp < now) { + guint32 diff = now - timestamp; - /* We would expect @timestamp to be less then @a_valid. Just to be sure, - * fix it up. */ - address->timestamp = MIN (address->timestamp, a_valid - 1); - address->lifetime = _get_remaining_time (address->timestamp, a_valid); - address->preferred = _get_remaining_time (address->timestamp, a_preferred); + lifetime = _extend_lifetime (lifetime, diff); + preferred = _extend_lifetime (preferred, diff); + } else + nm_assert (timestamp == now); + } + *out_timestamp = timestamp; + *out_lifetime = lifetime; + *out_preferred = preferred; } -static gboolean -init_ip4_address (NMPlatformIP4Address *address, struct rtnl_addr *rtnladdr) +gboolean +_nmp_vt_cmd_plobj_init_from_nl_ip4_address (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache) { - struct nl_addr *nladdr = rtnl_addr_get_local (rtnladdr); - struct nl_addr *nlpeer = rtnl_addr_get_peer (rtnladdr); + NMPlatformIP4Address *obj = (NMPlatformIP4Address *) _obj; + struct rtnl_addr *nlo = (struct rtnl_addr *) _nlo; + struct nl_addr *nladdr = rtnl_addr_get_local (nlo); + struct nl_addr *nlpeer = rtnl_addr_get_peer (nlo); const char *label; - g_return_val_if_fail (nladdr, FALSE); + if (!nladdr || nl_addr_get_len (nladdr) != sizeof (obj->address)) + g_return_val_if_reached (FALSE); - memset (address, 0, sizeof (*address)); + obj->ifindex = rtnl_addr_get_ifindex (nlo); + obj->plen = rtnl_addr_get_prefixlen (nlo); + memcpy (&obj->address, nl_addr_get_binary_addr (nladdr), sizeof (obj->address)); - address->source = NM_IP_CONFIG_SOURCE_KERNEL; - address->ifindex = rtnl_addr_get_ifindex (rtnladdr); - address->plen = rtnl_addr_get_prefixlen (rtnladdr); - _init_ip_address_lifetime ((NMPlatformIPAddress *) address, rtnladdr); - if (!nladdr || nl_addr_get_len (nladdr) != sizeof (address->address)) { - g_return_val_if_reached (FALSE); - return FALSE; - } - memcpy (&address->address, nl_addr_get_binary_addr (nladdr), sizeof (address->address)); + if (id_only) + return TRUE; + + obj->source = NM_IP_CONFIG_SOURCE_KERNEL; + _nlo_rtnl_addr_get_lifetimes (nlo, + &obj->timestamp, + &obj->lifetime, + &obj->preferred); if (nlpeer) { - if (nl_addr_get_len (nlpeer) != sizeof (address->peer_address)) { - g_return_val_if_reached (FALSE); - return FALSE; - } - memcpy (&address->peer_address, nl_addr_get_binary_addr (nlpeer), sizeof (address->peer_address)); + if (nl_addr_get_len (nlpeer) != sizeof (obj->peer_address)) + g_warn_if_reached (); + else + memcpy (&obj->peer_address, nl_addr_get_binary_addr (nlpeer), sizeof (obj->peer_address)); } - label = rtnl_addr_get_label (rtnladdr); + label = rtnl_addr_get_label (nlo); /* Check for ':'; we're only interested in labels used as interface aliases */ if (label && strchr (label, ':')) - g_strlcpy (address->label, label, sizeof (address->label)); + g_strlcpy (obj->label, label, sizeof (obj->label)); return TRUE; } -static gboolean -init_ip6_address (NMPlatformIP6Address *address, struct rtnl_addr *rtnladdr) +gboolean +_nmp_vt_cmd_plobj_init_from_nl_ip6_address (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache) { - struct nl_addr *nladdr = rtnl_addr_get_local (rtnladdr); - struct nl_addr *nlpeer = rtnl_addr_get_peer (rtnladdr); + NMPlatformIP6Address *obj = (NMPlatformIP6Address *) _obj; + struct rtnl_addr *nlo = (struct rtnl_addr *) _nlo; + struct nl_addr *nladdr = rtnl_addr_get_local (nlo); + struct nl_addr *nlpeer = rtnl_addr_get_peer (nlo); - memset (address, 0, sizeof (*address)); - - address->source = NM_IP_CONFIG_SOURCE_KERNEL; - address->ifindex = rtnl_addr_get_ifindex (rtnladdr); - address->plen = rtnl_addr_get_prefixlen (rtnladdr); - _init_ip_address_lifetime ((NMPlatformIPAddress *) address, rtnladdr); - address->flags = rtnl_addr_get_flags (rtnladdr); - if (!nladdr || nl_addr_get_len (nladdr) != sizeof (address->address)) { + if (!nladdr || nl_addr_get_len (nladdr) != sizeof (obj->address)) g_return_val_if_reached (FALSE); - return FALSE; - } - memcpy (&address->address, nl_addr_get_binary_addr (nladdr), sizeof (address->address)); - if (nlpeer) { - if (nl_addr_get_len (nlpeer) != sizeof (address->peer_address)) { - g_return_val_if_reached (FALSE); - return FALSE; - } - memcpy (&address->peer_address, nl_addr_get_binary_addr (nlpeer), sizeof (address->peer_address)); - } - return TRUE; -} + obj->ifindex = rtnl_addr_get_ifindex (nlo); + obj->plen = rtnl_addr_get_prefixlen (nlo); + memcpy (&obj->address, nl_addr_get_binary_addr (nladdr), sizeof (obj->address)); -static guint -source_to_rtprot (NMIPConfigSource source) -{ - switch (source) { - case NM_IP_CONFIG_SOURCE_UNKNOWN: - return RTPROT_UNSPEC; - case NM_IP_CONFIG_SOURCE_KERNEL: - return RTPROT_KERNEL; - case NM_IP_CONFIG_SOURCE_DHCP: - return RTPROT_DHCP; - case NM_IP_CONFIG_SOURCE_RDISC: - return RTPROT_RA; + if (id_only) + return TRUE; - default: - return RTPROT_STATIC; - } -} + obj->source = NM_IP_CONFIG_SOURCE_KERNEL; + _nlo_rtnl_addr_get_lifetimes (nlo, + &obj->timestamp, + &obj->lifetime, + &obj->preferred); + obj->flags = rtnl_addr_get_flags (nlo); -static NMIPConfigSource -rtprot_to_source (guint rtprot) -{ - switch (rtprot) { - case RTPROT_UNSPEC: - return NM_IP_CONFIG_SOURCE_UNKNOWN; - case RTPROT_REDIRECT: - case RTPROT_KERNEL: - return NM_IP_CONFIG_SOURCE_KERNEL; - case RTPROT_RA: - return NM_IP_CONFIG_SOURCE_RDISC; - case RTPROT_DHCP: - return NM_IP_CONFIG_SOURCE_DHCP; - - default: - return NM_IP_CONFIG_SOURCE_USER; + if (nlpeer) { + if (nl_addr_get_len (nlpeer) != sizeof (obj->peer_address)) + g_warn_if_reached (); + else + memcpy (&obj->peer_address, nl_addr_get_binary_addr (nlpeer), sizeof (obj->peer_address)); } -} -static gboolean -_rtnl_route_is_default (const struct rtnl_route *rtnlroute) -{ - struct nl_addr *dst; - - return rtnlroute - && (dst = rtnl_route_get_dst ((struct rtnl_route *) rtnlroute)) - && nl_addr_get_prefixlen (dst) == 0; + return TRUE; } -static gboolean -init_ip4_route (NMPlatformIP4Route *route, struct rtnl_route *rtnlroute) +gboolean +_nmp_vt_cmd_plobj_init_from_nl_ip4_route (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache) { + NMPlatformIP4Route *obj = (NMPlatformIP4Route *) _obj; + struct rtnl_route *nlo = (struct rtnl_route *) _nlo; struct nl_addr *dst, *gw; struct rtnl_nexthop *nexthop; + struct nl_addr *pref_src; - memset (route, 0, sizeof (*route)); - - /* Multi-hop routes not supported. */ - if (rtnl_route_get_nnexthops (rtnlroute) != 1) + if (rtnl_route_get_type (nlo) != RTN_UNICAST || + rtnl_route_get_table (nlo) != RT_TABLE_MAIN || + rtnl_route_get_tos (nlo) != 0 || + rtnl_route_get_nnexthops (nlo) != 1) return FALSE; - nexthop = rtnl_route_nexthop_n (rtnlroute, 0); - dst = rtnl_route_get_dst (rtnlroute); - gw = rtnl_route_nh_get_gateway (nexthop); + nexthop = rtnl_route_nexthop_n (nlo, 0); + if (!nexthop) + g_return_val_if_reached (FALSE); + + dst = rtnl_route_get_dst (nlo); + if (!dst) + g_return_val_if_reached (FALSE); - route->ifindex = rtnl_route_nh_get_ifindex (nexthop); - route->plen = nl_addr_get_prefixlen (dst); - /* Workaround on previous workaround for libnl default route prefixlen bug. */ if (nl_addr_get_len (dst)) { - if (nl_addr_get_len (dst) != sizeof (route->network)) { + if (nl_addr_get_len (dst) != sizeof (obj->network)) g_return_val_if_reached (FALSE); - return FALSE; - } - memcpy (&route->network, nl_addr_get_binary_addr (dst), sizeof (route->network)); + memcpy (&obj->network, nl_addr_get_binary_addr (dst), sizeof (obj->network)); } + obj->ifindex = rtnl_route_nh_get_ifindex (nexthop); + obj->plen = nl_addr_get_prefixlen (dst); + obj->metric = rtnl_route_get_priority (nlo); + obj->scope_inv = nm_platform_route_scope_inv (rtnl_route_get_scope (nlo)); + + gw = rtnl_route_nh_get_gateway (nexthop); if (gw) { - if (nl_addr_get_len (gw) != sizeof (route->network)) { - g_return_val_if_reached (FALSE); - return FALSE; - } - memcpy (&route->gateway, nl_addr_get_binary_addr (gw), sizeof (route->gateway)); + if (nl_addr_get_len (gw) != sizeof (obj->gateway)) + g_warn_if_reached (); + else + memcpy (&obj->gateway, nl_addr_get_binary_addr (gw), sizeof (obj->gateway)); + } + rtnl_route_get_metric (nlo, RTAX_ADVMSS, &obj->mss); + if (rtnl_route_get_flags (nlo) & RTM_F_CLONED) { + /* we must not straight way reject cloned routes, because we might have cached + * a non-cloned route. If we now receive an update of the route with the route + * being cloned, we must still return the object, so that we can remove the old + * one from the cache. + * + * This happens, because this route is not nmp_object_is_alive(). + * */ + obj->source = _NM_IP_CONFIG_SOURCE_RTM_F_CLONED; + } else + obj->source = _nm_ip_config_source_from_rtprot (rtnl_route_get_protocol (nlo)); + + pref_src = rtnl_route_get_pref_src (nlo); + if (pref_src) { + if (nl_addr_get_len (pref_src) != sizeof (obj->pref_src)) + g_warn_if_reached (); + else + memcpy (&obj->pref_src, nl_addr_get_binary_addr (pref_src), sizeof (obj->pref_src)); } - route->metric = rtnl_route_get_priority (rtnlroute); - rtnl_route_get_metric (rtnlroute, RTAX_ADVMSS, &route->mss); - route->source = rtprot_to_source (rtnl_route_get_protocol (rtnlroute)); return TRUE; } -static gboolean -init_ip6_route (NMPlatformIP6Route *route, struct rtnl_route *rtnlroute) +gboolean +_nmp_vt_cmd_plobj_init_from_nl_ip6_route (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache) { + NMPlatformIP6Route *obj = (NMPlatformIP6Route *) _obj; + struct rtnl_route *nlo = (struct rtnl_route *) _nlo; struct nl_addr *dst, *gw; struct rtnl_nexthop *nexthop; - memset (route, 0, sizeof (*route)); - - /* Multi-hop routes not supported. */ - if (rtnl_route_get_nnexthops (rtnlroute) != 1) + if (rtnl_route_get_type (nlo) != RTN_UNICAST || + rtnl_route_get_table (nlo) != RT_TABLE_MAIN || + rtnl_route_get_tos (nlo) != 0 || + rtnl_route_get_nnexthops (nlo) != 1) return FALSE; - nexthop = rtnl_route_nexthop_n (rtnlroute, 0); - dst = rtnl_route_get_dst (rtnlroute); - gw = rtnl_route_nh_get_gateway (nexthop); + nexthop = rtnl_route_nexthop_n (nlo, 0); + if (!nexthop) + g_return_val_if_reached (FALSE); + + dst = rtnl_route_get_dst (nlo); + if (!dst) + g_return_val_if_reached (FALSE); - route->ifindex = rtnl_route_nh_get_ifindex (nexthop); - route->plen = nl_addr_get_prefixlen (dst); - /* Workaround on previous workaround for libnl default route prefixlen bug. */ if (nl_addr_get_len (dst)) { - if (nl_addr_get_len (dst) != sizeof (route->network)) { + if (nl_addr_get_len (dst) != sizeof (obj->network)) g_return_val_if_reached (FALSE); - return FALSE; - } - memcpy (&route->network, nl_addr_get_binary_addr (dst), sizeof (route->network)); + memcpy (&obj->network, nl_addr_get_binary_addr (dst), sizeof (obj->network)); } + obj->ifindex = rtnl_route_nh_get_ifindex (nexthop); + obj->plen = nl_addr_get_prefixlen (dst); + obj->metric = rtnl_route_get_priority (nlo); + + if (id_only) + return TRUE; + + gw = rtnl_route_nh_get_gateway (nexthop); if (gw) { - if (nl_addr_get_len (gw) != sizeof (route->network)) { - g_return_val_if_reached (FALSE); - return FALSE; - } - memcpy (&route->gateway, nl_addr_get_binary_addr (gw), sizeof (route->gateway)); + if (nl_addr_get_len (gw) != sizeof (obj->gateway)) + g_warn_if_reached (); + else + memcpy (&obj->gateway, nl_addr_get_binary_addr (gw), sizeof (obj->gateway)); } - route->metric = rtnl_route_get_priority (rtnlroute); - rtnl_route_get_metric (rtnlroute, RTAX_ADVMSS, &route->mss); - route->source = rtprot_to_source (rtnl_route_get_protocol (rtnlroute)); + rtnl_route_get_metric (nlo, RTAX_ADVMSS, &obj->mss); + if (rtnl_route_get_flags (nlo) & RTM_F_CLONED) + obj->source = _NM_IP_CONFIG_SOURCE_RTM_F_CLONED; + else + obj->source = _nm_ip_config_source_from_rtprot (rtnl_route_get_protocol (nlo)); return TRUE; } -static char to_string_buffer[255]; - -#define SET_AND_RETURN_STRING_BUFFER(...) \ - G_STMT_START { \ - g_snprintf (to_string_buffer, sizeof (to_string_buffer), ## __VA_ARGS__); \ - return to_string_buffer; \ - } G_STMT_END +/******************************************************************/ -static const char * -to_string_link (NMPlatform *platform, struct rtnl_link *obj) +static void +do_emit_signal (NMPlatform *platform, const NMPObject *obj, NMPCacheOpsType cache_op, gboolean was_visible, NMPlatformReason reason) { - NMPlatformLink pl_obj; + gboolean is_visible; + NMPObject obj_clone; + const NMPClass *klass; - if (init_link (platform, &pl_obj, obj)) - return nm_platform_link_to_string (&pl_obj); - SET_AND_RETURN_STRING_BUFFER ("(invalid link %p)", obj); -} + nm_assert (NM_IN_SET ((NMPlatformSignalChangeType) cache_op, (NMPlatformSignalChangeType) NMP_CACHE_OPS_UNCHANGED, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_SIGNAL_REMOVED)); -static const char * -to_string_ip4_address (struct rtnl_addr *obj) -{ - NMPlatformIP4Address pl_obj; + nm_assert (obj || cache_op == NMP_CACHE_OPS_UNCHANGED); + nm_assert (!obj || cache_op == NMP_CACHE_OPS_REMOVED || obj == nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, obj)); + nm_assert (!obj || cache_op != NMP_CACHE_OPS_REMOVED || obj != nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, obj)); - if (init_ip4_address (&pl_obj, obj)) - return nm_platform_ip4_address_to_string (&pl_obj); - SET_AND_RETURN_STRING_BUFFER ("(invalid ip4 address %p)", obj); + switch (cache_op) { + case NMP_CACHE_OPS_ADDED: + if (!nmp_object_is_visible (obj)) + return; + break; + case NMP_CACHE_OPS_UPDATED: + is_visible = nmp_object_is_visible (obj); + if (!was_visible && is_visible) + cache_op = NMP_CACHE_OPS_ADDED; + else if (was_visible && !is_visible) { + /* This is a bit ugly. The object was visible and changed in a way that it became invisible. + * We raise a removed signal, but contrary to a real 'remove', @obj is already changed to be + * different from what it was when the user saw it the last time. + * + * The more correct solution would be to have cache_pre_hook() create a clone of the original + * value before it was changed to become invisible. + * + * But, don't bother. Probably nobody depends on the original values and only cares about the + * id properties (which are still correct). + */ + cache_op = NMP_CACHE_OPS_REMOVED; + } else if (!is_visible) + return; + break; + case NMP_CACHE_OPS_REMOVED: + if (!was_visible) + return; + break; + default: + g_assert (cache_op == NMP_CACHE_OPS_UNCHANGED); + return; + } + + klass = NMP_OBJECT_GET_CLASS (obj); + + _LOGT ("emit signal %s %s: %s (%ld)", + klass->signal_type, + nm_platform_signal_change_type_to_string ((NMPlatformSignalChangeType) cache_op), + nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0), + (long) reason); + + /* don't expose @obj directly, but clone the public fields. A signal handler might + * call back into NMPlatform which could invalidate (or modify) @obj. */ + memcpy (&obj_clone.object, &obj->object, klass->sizeof_public); + g_signal_emit_by_name (platform, klass->signal_type, klass->obj_type, obj_clone.object.ifindex, &obj_clone.object, (NMPlatformSignalChangeType) cache_op, reason); } -static const char * -to_string_ip6_address (struct rtnl_addr *obj) +/******************************************************************/ + +static DelayedActionType +delayed_action_refresh_from_object_type (NMPObjectType obj_type) { - NMPlatformIP6Address pl_obj; + switch (obj_type) { + case NMP_OBJECT_TYPE_LINK: return DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS; + case NMP_OBJECT_TYPE_IP4_ADDRESS: return DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES; + case NMP_OBJECT_TYPE_IP6_ADDRESS: return DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES; + case NMP_OBJECT_TYPE_IP4_ROUTE: return DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES; + case NMP_OBJECT_TYPE_IP6_ROUTE: return DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES; + default: g_return_val_if_reached (DELAYED_ACTION_TYPE_NONE); + } +} - if (init_ip6_address (&pl_obj, obj)) - return nm_platform_ip6_address_to_string (&pl_obj); - SET_AND_RETURN_STRING_BUFFER ("(invalid ip6 address %p)", obj); +static NMPObjectType +delayed_action_refresh_to_object_type (DelayedActionType action_type) +{ + switch (action_type) { + case DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS: return NMP_OBJECT_TYPE_LINK; + case DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES: return NMP_OBJECT_TYPE_IP4_ADDRESS; + case DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES: return NMP_OBJECT_TYPE_IP6_ADDRESS; + case DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES: return NMP_OBJECT_TYPE_IP4_ROUTE; + case DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES: return NMP_OBJECT_TYPE_IP6_ROUTE; + default: g_return_val_if_reached (NMP_OBJECT_TYPE_UNKNOWN); + } } static const char * -to_string_ip4_route (struct rtnl_route *obj) +delayed_action_to_string (DelayedActionType action_type) +{ + switch (action_type) { + case DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS : return "refresh-all-links"; + case DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES : return "refresh-all-ip4-addresses"; + case DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES : return "refresh-all-ip6-addresses"; + case DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES : return "refresh-all-ip4-routes"; + case DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES : return "refresh-all-ip6-routes"; + case DELAYED_ACTION_TYPE_REFRESH_LINK : return "refresh-link"; + case DELAYED_ACTION_TYPE_MASTER_CONNECTED : return "master-connected"; + case DELAYED_ACTION_TYPE_READ_NETLINK : return "read-netlink"; + default: + return "unknown"; + } +} + +#define _LOGT_delayed_action(action_type, arg, operation) \ + _LOGT ("delayed-action: %s %s (%d) [%p / %d]", ""operation, delayed_action_to_string (action_type), (int) action_type, arg, GPOINTER_TO_INT (arg)) + +static void +delayed_action_handle_MASTER_CONNECTED (NMPlatform *platform, int master_ifindex) { - NMPlatformIP4Route pl_obj; + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + auto_nmp_obj NMPObject *obj_cache = NULL; + gboolean was_visible; + NMPCacheOpsType cache_op; - if (init_ip4_route (&pl_obj, obj)) - return nm_platform_ip4_route_to_string (&pl_obj); - SET_AND_RETURN_STRING_BUFFER ("(invalid ip4 route %p)", obj); + cache_op = nmp_cache_update_link_master_connected (priv->cache, master_ifindex, &obj_cache, &was_visible, cache_pre_hook, platform); + do_emit_signal (platform, obj_cache, cache_op, was_visible, NM_PLATFORM_REASON_INTERNAL); } -static const char * -to_string_ip6_route (struct rtnl_route *obj) +static void +delayed_action_handle_REFRESH_LINK (NMPlatform *platform, int ifindex) { - NMPlatformIP6Route pl_obj; - - if (init_ip6_route (&pl_obj, obj)) - return nm_platform_ip6_route_to_string (&pl_obj); - SET_AND_RETURN_STRING_BUFFER ("(invalid ip6 route %p)", obj); + do_request_link (platform, ifindex, NULL, FALSE); } -static const char * -to_string_object_with_type (NMPlatform *platform, struct nl_object *obj, ObjectType type) +static void +delayed_action_handle_REFRESH_ALL (NMPlatform *platform, DelayedActionType flags) { - switch (type) { - case OBJECT_TYPE_LINK: - return to_string_link (platform, (struct rtnl_link *) obj); - case OBJECT_TYPE_IP4_ADDRESS: - return to_string_ip4_address ((struct rtnl_addr *) obj); - case OBJECT_TYPE_IP6_ADDRESS: - return to_string_ip6_address ((struct rtnl_addr *) obj); - case OBJECT_TYPE_IP4_ROUTE: - return to_string_ip4_route ((struct rtnl_route *) obj); - case OBJECT_TYPE_IP6_ROUTE: - return to_string_ip6_route ((struct rtnl_route *) obj); - default: - SET_AND_RETURN_STRING_BUFFER ("(unknown netlink object %p)", obj); - } + do_request_all (platform, flags, FALSE); } -static const char * -to_string_object (NMPlatform *platform, struct nl_object *obj) +static void +delayed_action_handle_READ_NETLINK (NMPlatform *platform) { - return to_string_object_with_type (platform, obj, object_type_from_nl_object (obj)); + event_handler_read_netlink_all (platform, TRUE); } -#undef SET_AND_RETURN_STRING_BUFFER +static gboolean +delayed_action_handle_one (NMPlatform *platform) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + gpointer user_data; -/******************************************************************/ + if (priv->delayed_action.flags == DELAYED_ACTION_TYPE_NONE) { + nm_clear_g_source (&priv->delayed_action.idle_id); + return FALSE; + } -/* Object and cache manipulation */ + /* First process DELAYED_ACTION_TYPE_MASTER_CONNECTED actions. + * This type of action is entirely cache-internal and is here to resolve a + * cache inconsistency. It should be fixed right away. */ + if (NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_MASTER_CONNECTED)) { + nm_assert (priv->delayed_action.list_master_connected->len > 0); -static const char *signal_by_type_and_status[__OBJECT_TYPE_LAST] = { - [OBJECT_TYPE_LINK] = NM_PLATFORM_SIGNAL_LINK_CHANGED, - [OBJECT_TYPE_IP4_ADDRESS] = NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, - [OBJECT_TYPE_IP6_ADDRESS] = NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, - [OBJECT_TYPE_IP4_ROUTE] = NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, - [OBJECT_TYPE_IP6_ROUTE] = NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, -}; + user_data = priv->delayed_action.list_master_connected->pdata[0]; + g_ptr_array_remove_index_fast (priv->delayed_action.list_master_connected, 0); + if (priv->delayed_action.list_master_connected->len == 0) + priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_MASTER_CONNECTED; + nm_assert (_nm_utils_ptrarray_find_first (priv->delayed_action.list_master_connected->pdata, priv->delayed_action.list_master_connected->len, user_data) < 0); -static struct nl_cache * -choose_cache_by_type (NMPlatform *platform, ObjectType object_type) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + _LOGT_delayed_action (DELAYED_ACTION_TYPE_MASTER_CONNECTED, user_data, "handle"); + delayed_action_handle_MASTER_CONNECTED (platform, GPOINTER_TO_INT (user_data)); + return TRUE; + } + nm_assert (priv->delayed_action.list_master_connected->len == 0); + + /* Next we prefer read-netlink, because the buffer size is limited and we want to process events + * from netlink early. */ + if (NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_READ_NETLINK)) { + _LOGT_delayed_action (DELAYED_ACTION_TYPE_READ_NETLINK, NULL, "handle"); + priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_READ_NETLINK; + delayed_action_handle_READ_NETLINK (platform); + return TRUE; + } - switch (object_type) { - case OBJECT_TYPE_LINK: - return priv->link_cache; - case OBJECT_TYPE_IP4_ADDRESS: - case OBJECT_TYPE_IP6_ADDRESS: - return priv->address_cache; - case OBJECT_TYPE_IP4_ROUTE: - case OBJECT_TYPE_IP6_ROUTE: - return priv->route_cache; - default: - g_return_val_if_reached (NULL); - return NULL; + if (NM_FLAGS_ANY (priv->delayed_action.flags, DELAYED_ACTION_TYPE_REFRESH_ALL)) { + DelayedActionType flags, iflags; + + flags = priv->delayed_action.flags & DELAYED_ACTION_TYPE_REFRESH_ALL; + + priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_REFRESH_ALL; + + if (_LOGT_ENABLED ()) { + for (iflags = (DelayedActionType) 0x1LL; iflags <= DELAYED_ACTION_TYPE_MAX; iflags <<= 1) { + if (NM_FLAGS_HAS (flags, iflags)) + _LOGT_delayed_action (iflags, NULL, "handle"); + } + } + + delayed_action_handle_REFRESH_ALL (platform, flags); + return TRUE; } + + nm_assert (priv->delayed_action.flags == DELAYED_ACTION_TYPE_REFRESH_LINK); + nm_assert (priv->delayed_action.list_refresh_link->len > 0); + + user_data = priv->delayed_action.list_refresh_link->pdata[0]; + g_ptr_array_remove_index_fast (priv->delayed_action.list_refresh_link, 0); + if (priv->delayed_action.list_master_connected->len == 0) + priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_REFRESH_LINK; + nm_assert (_nm_utils_ptrarray_find_first (priv->delayed_action.list_refresh_link->pdata, priv->delayed_action.list_refresh_link->len, user_data) < 0); + + _LOGT_delayed_action (DELAYED_ACTION_TYPE_REFRESH_LINK, user_data, "handle"); + + delayed_action_handle_REFRESH_LINK (platform, GPOINTER_TO_INT (user_data)); + + return TRUE; } -static struct nl_cache * -choose_cache (NMPlatform *platform, struct nl_object *object) +static gboolean +delayed_action_handle_all (NMPlatform *platform, gboolean read_netlink) { - return choose_cache_by_type (platform, object_type_from_nl_object (object)); + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + gboolean any = FALSE; + + nm_clear_g_source (&priv->delayed_action.idle_id); + priv->delayed_action.is_handling++; + if (read_netlink) + delayed_action_schedule (platform, DELAYED_ACTION_TYPE_READ_NETLINK, NULL); + while (delayed_action_handle_one (platform)) + any = TRUE; + priv->delayed_action.is_handling--; + return any; } static gboolean -object_has_ifindex (struct nl_object *object, int ifindex) -{ - switch (object_type_from_nl_object (object)) { - case OBJECT_TYPE_IP4_ADDRESS: - case OBJECT_TYPE_IP6_ADDRESS: - return ifindex == rtnl_addr_get_ifindex ((struct rtnl_addr *) object); - case OBJECT_TYPE_IP4_ROUTE: - case OBJECT_TYPE_IP6_ROUTE: - { - struct rtnl_route *rtnlroute = (struct rtnl_route *) object; - struct rtnl_nexthop *nexthop; +delayed_action_handle_idle (gpointer user_data) +{ + NM_LINUX_PLATFORM_GET_PRIVATE (user_data)->delayed_action.idle_id = 0; + delayed_action_handle_all (user_data, FALSE); + return G_SOURCE_REMOVE; +} + +static void +delayed_action_schedule (NMPlatform *platform, DelayedActionType action_type, gpointer user_data) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + DelayedActionType iflags; + + nm_assert (action_type != DELAYED_ACTION_TYPE_NONE); + + if (NM_FLAGS_HAS (action_type, DELAYED_ACTION_TYPE_REFRESH_LINK)) { + nm_assert (nm_utils_is_power_of_two (action_type)); + if (_nm_utils_ptrarray_find_first (priv->delayed_action.list_refresh_link->pdata, priv->delayed_action.list_refresh_link->len, user_data) < 0) + g_ptr_array_add (priv->delayed_action.list_refresh_link, user_data); + } else if (NM_FLAGS_HAS (action_type, DELAYED_ACTION_TYPE_MASTER_CONNECTED)) { + nm_assert (nm_utils_is_power_of_two (action_type)); + if (_nm_utils_ptrarray_find_first (priv->delayed_action.list_master_connected->pdata, priv->delayed_action.list_master_connected->len, user_data) < 0) + g_ptr_array_add (priv->delayed_action.list_master_connected, user_data); + } else + nm_assert (!user_data); - if (rtnl_route_get_nnexthops (rtnlroute) != 1) - return FALSE; - nexthop = rtnl_route_nexthop_n (rtnlroute, 0); + priv->delayed_action.flags |= action_type; - return ifindex == rtnl_route_nh_get_ifindex (nexthop); + if (_LOGT_ENABLED ()) { + for (iflags = (DelayedActionType) 0x1LL; iflags <= DELAYED_ACTION_TYPE_MAX; iflags <<= 1) { + if (NM_FLAGS_HAS (action_type, iflags)) + _LOGT_delayed_action (iflags, user_data, "schedule"); } - default: - g_assert_not_reached (); } + + if (priv->delayed_action.is_handling == 0 && priv->delayed_action.idle_id == 0) + priv->delayed_action.idle_id = g_idle_add (delayed_action_handle_idle, platform); } -static gboolean refresh_object (NMPlatform *platform, struct nl_object *object, gboolean removed, NMPlatformReason reason); +/******************************************************************/ static void -check_cache_items (NMPlatform *platform, struct nl_cache *cache, int ifindex) +cache_prune_candidates_record_all (NMPlatform *platform, NMPObjectType obj_type) { - auto_nl_cache struct nl_cache *cloned_cache = nl_cache_clone (cache); - struct nl_object *object; - GPtrArray *objects_to_refresh = g_ptr_array_new_with_free_func ((GDestroyNotify) nl_object_put); - guint i; + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - for (object = nl_cache_get_first (cloned_cache); object; object = nl_cache_get_next (object)) { - if (object_has_ifindex (object, ifindex)) { - nl_object_get (object); - g_ptr_array_add (objects_to_refresh, object); - } - } + priv->prune_candidates = nmp_cache_lookup_all_to_hash (priv->cache, + nmp_cache_id_init_object_type (NMP_CACHE_ID_STATIC, obj_type, FALSE), + priv->prune_candidates); + _LOGT ("cache-prune: record %s (now %u candidates)", nmp_class_from_type (obj_type)->obj_type_name, + priv->prune_candidates ? g_hash_table_size (priv->prune_candidates) : 0); +} + +static void +cache_prune_candidates_record_one (NMPlatform *platform, NMPObject *obj) +{ + NMLinuxPlatformPrivate *priv; + + if (!obj) + return; + + priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - for (i = 0; i < objects_to_refresh->len; i++) - refresh_object (platform, objects_to_refresh->pdata[i], TRUE, NM_PLATFORM_REASON_CACHE_CHECK); + if (!priv->prune_candidates) + priv->prune_candidates = g_hash_table_new_full (NULL, NULL, (GDestroyNotify) nmp_object_unref, NULL); - g_ptr_array_free (objects_to_refresh, TRUE); + if (_LOGT_ENABLED () && !g_hash_table_contains (priv->prune_candidates, obj)) + _LOGT ("cache-prune: record-one: %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); + g_hash_table_add (priv->prune_candidates, nmp_object_ref (obj)); } static void -announce_object (NMPlatform *platform, const struct nl_object *object, NMPlatformSignalChangeType change_type, NMPlatformReason reason) +cache_prune_candidates_drop (NMPlatform *platform, const NMPObject *obj) +{ + NMLinuxPlatformPrivate *priv; + + if (!obj) + return; + + priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + if (priv->prune_candidates) { + if (_LOGT_ENABLED () && g_hash_table_contains (priv->prune_candidates, obj)) + _LOGT ("cache-prune: drop-one: %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); + g_hash_table_remove (priv->prune_candidates, obj); + } +} + +static void +cache_prune_candidates_prune (NMPlatform *platform) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - ObjectType object_type = object_type_from_nl_object (object); - const char *sig = signal_by_type_and_status[object_type]; + GHashTable *prune_candidates; + GHashTableIter iter; + const NMPObject *obj; + gboolean was_visible; + NMPCacheOpsType cache_op; - switch (object_type) { - case OBJECT_TYPE_LINK: - { - struct rtnl_link *rtnl_link = (struct rtnl_link *) object; - NMPlatformLink device; + if (!priv->prune_candidates) + return; -#if HAVE_LIBNL_INET6_ADDR_GEN_MODE - /* If we ever see a link with valid IPv6 link-local address - * generation modes, the kernel supports it. - */ - if (priv->support_user_ipv6ll == 0) { - uint8_t mode; + prune_candidates = priv->prune_candidates; + priv->prune_candidates = NULL; - if (rtnl_link_inet6_get_addr_gen_mode (rtnl_link, &mode) == 0) - priv->support_user_ipv6ll = 1; - } -#endif + g_hash_table_iter_init (&iter, prune_candidates); + while (g_hash_table_iter_next (&iter, (gpointer *)&obj, NULL)) { + auto_nmp_obj NMPObject *obj_cache = NULL; - if (!init_link (platform, &device, rtnl_link)) - return; + _LOGT ("cache-prune: prune %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); + cache_op = nmp_cache_remove (priv->cache, obj, TRUE, &obj_cache, &was_visible, cache_pre_hook, platform); + do_emit_signal (platform, obj_cache, cache_op, was_visible, NM_PLATFORM_REASON_INTERNAL); + } - /* Skip hardware devices not yet discovered by udev. They will be - * announced by udev_device_added(). This doesn't apply to removed - * devices, as those come either from udev_device_removed(), - * event_notification() or link_delete() which block the announcment - * themselves when appropriate. - */ - switch (change_type) { - case NM_PLATFORM_SIGNAL_ADDED: - case NM_PLATFORM_SIGNAL_CHANGED: - if (!link_is_software (rtnl_link) && !device.driver) - return; - break; - default: - break; - } + g_hash_table_unref (prune_candidates); +} - /* Link deletion or setting down is sometimes accompanied by address - * and/or route deletion. - * - * More precisely, kernel removes routes when interface goes !IFF_UP and - * removes both addresses and routes when interface is removed. - */ - switch (change_type) { - case NM_PLATFORM_SIGNAL_CHANGED: - if (!device.connected) - check_cache_items (platform, priv->route_cache, device.ifindex); - break; - case NM_PLATFORM_SIGNAL_REMOVED: - check_cache_items (platform, priv->address_cache, device.ifindex); - check_cache_items (platform, priv->route_cache, device.ifindex); - g_hash_table_remove (priv->wifi_data, GINT_TO_POINTER (device.ifindex)); - break; - default: - break; - } +static void +cache_delayed_deletion_prune (NMPlatform *platform) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + GPtrArray *prune_list = NULL; + GHashTableIter iter; + guint i; + NMPObject *obj; - g_signal_emit_by_name (platform, sig, device.ifindex, &device, change_type, reason); - } + if (g_hash_table_size (priv->delayed_deletion) == 0) return; - case OBJECT_TYPE_IP4_ADDRESS: - { - NMPlatformIP4Address address; - /* Address deletion is sometimes accompanied by route deletion. We need to - * check all routes belonging to the same interface. - */ - switch (change_type) { - case NM_PLATFORM_SIGNAL_REMOVED: - check_cache_items (platform, - priv->route_cache, - rtnl_addr_get_ifindex ((struct rtnl_addr *) object)); - break; - default: - break; - } + g_hash_table_iter_init (&iter, priv->delayed_deletion); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &obj)) { + if (obj) { + if (!prune_list) + prune_list = g_ptr_array_new_full (g_hash_table_size (priv->delayed_deletion), (GDestroyNotify) nmp_object_unref); + g_ptr_array_add (prune_list, nmp_object_ref (obj)); + } + } - if (!_address_match ((struct rtnl_addr *) object, AF_INET, 0)) { - nm_log_dbg (LOGD_PLATFORM, "skip announce unmatching IP4 address %s", to_string_ip4_address ((struct rtnl_addr *) object)); - return; - } - if (!init_ip4_address (&address, (struct rtnl_addr *) object)) - return; - g_signal_emit_by_name (platform, sig, address.ifindex, &address, change_type, reason); + g_hash_table_remove_all (priv->delayed_deletion); + + if (prune_list) { + for (i = 0; i < prune_list->len; i++) { + obj = prune_list->pdata[i]; + _LOGT ("delayed-deletion: delete %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ID, NULL, 0)); + cache_remove_netlink (platform, obj, NULL, NULL, NM_PLATFORM_REASON_EXTERNAL); } - return; - case OBJECT_TYPE_IP6_ADDRESS: - { - NMPlatformIP6Address address; + g_ptr_array_unref (prune_list); + } +} - if (!_address_match ((struct rtnl_addr *) object, AF_INET6, 0)) { - nm_log_dbg (LOGD_PLATFORM, "skip announce unmatching IP6 address %s", to_string_ip6_address ((struct rtnl_addr *) object)); - return; +static void +cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data) +{ + NMPlatform *platform = NM_PLATFORM (user_data); + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + const NMPClass *klass; + char str_buf[sizeof (_nm_platform_to_string_buffer)]; + char str_buf2[sizeof (_nm_platform_to_string_buffer)]; + + nm_assert (old || new); + nm_assert (NM_IN_SET (ops_type, NMP_CACHE_OPS_ADDED, NMP_CACHE_OPS_REMOVED, NMP_CACHE_OPS_UPDATED)); + nm_assert (ops_type != NMP_CACHE_OPS_ADDED || (old == NULL && NMP_OBJECT_IS_VALID (new) && nmp_object_is_alive (new))); + nm_assert (ops_type != NMP_CACHE_OPS_REMOVED || (new == NULL && NMP_OBJECT_IS_VALID (old) && nmp_object_is_alive (old))); + nm_assert (ops_type != NMP_CACHE_OPS_UPDATED || (NMP_OBJECT_IS_VALID (old) && nmp_object_is_alive (old) && NMP_OBJECT_IS_VALID (new) && nmp_object_is_alive (new))); + nm_assert (new == NULL || old == NULL || nmp_object_id_equal (new, old)); + + klass = old ? NMP_OBJECT_GET_CLASS (old) : NMP_OBJECT_GET_CLASS (new); + + nm_assert (klass == (new ? NMP_OBJECT_GET_CLASS (new) : NMP_OBJECT_GET_CLASS (old))); + + _LOGT ("update-cache-%s: %s: %s%s%s", + klass->obj_type_name, + (ops_type == NMP_CACHE_OPS_UPDATED + ? "UPDATE" + : (ops_type == NMP_CACHE_OPS_REMOVED + ? "REMOVE" + : (ops_type == NMP_CACHE_OPS_ADDED) ? "ADD" : "???")), + (ops_type != NMP_CACHE_OPS_ADDED + ? nmp_object_to_string (old, NMP_OBJECT_TO_STRING_ALL, str_buf2, sizeof (str_buf2)) + : nmp_object_to_string (new, NMP_OBJECT_TO_STRING_ALL, str_buf2, sizeof (str_buf2))), + (ops_type == NMP_CACHE_OPS_UPDATED) ? " -> " : "", + (ops_type == NMP_CACHE_OPS_UPDATED + ? nmp_object_to_string (new, NMP_OBJECT_TO_STRING_ALL, str_buf, sizeof (str_buf)) + : "")); + + switch (klass->obj_type) { + case NMP_OBJECT_TYPE_LINK: + { + /* check whether changing a slave link can cause a master link (bridge or bond) to go up/down */ + if ( old + && nmp_cache_link_connected_needs_toggle_by_ifindex (priv->cache, old->link.master, new, old)) + delayed_action_schedule (platform, DELAYED_ACTION_TYPE_MASTER_CONNECTED, GINT_TO_POINTER (old->link.master)); + if ( new + && (!old || old->link.master != new->link.master) + && nmp_cache_link_connected_needs_toggle_by_ifindex (priv->cache, new->link.master, new, old)) + delayed_action_schedule (platform, DELAYED_ACTION_TYPE_MASTER_CONNECTED, GINT_TO_POINTER (new->link.master)); + } + { + /* check whether we are about to change a master link that needs toggling connected state. */ + if ( new /* <-- nonsensical, make coverity happy */ + && nmp_cache_link_connected_needs_toggle (cache, new, new, old)) + delayed_action_schedule (platform, DELAYED_ACTION_TYPE_MASTER_CONNECTED, GINT_TO_POINTER (new->link.ifindex)); + } + { + int ifindex = 0; + + /* if we remove a link (from netlink), we must refresh the addresses and routes */ + if ( ops_type == NMP_CACHE_OPS_REMOVED + && old /* <-- nonsensical, make coverity happy */) + ifindex = old->link.ifindex; + else if ( ops_type == NMP_CACHE_OPS_UPDATED + && old && new /* <-- nonsensical, make coverity happy */ + && !new->_link.netlink.is_in_netlink + && new->_link.netlink.is_in_netlink != old->_link.netlink.is_in_netlink) + ifindex = new->link.ifindex; + + if (ifindex > 0) { + delayed_action_schedule (platform, + DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, + NULL); } - if (!init_ip6_address (&address, (struct rtnl_addr *) object)) - return; - g_signal_emit_by_name (platform, sig, address.ifindex, &address, change_type, reason); } - return; - case OBJECT_TYPE_IP4_ROUTE: { - NMPlatformIP4Route route; - - if (reason == _NM_PLATFORM_REASON_CACHE_CHECK_INTERNAL) - return; - - if (!_route_match ((struct rtnl_route *) object, AF_INET, 0, FALSE)) { - nm_log_dbg (LOGD_PLATFORM, "skip announce unmatching IP4 route %s", to_string_ip4_route ((struct rtnl_route *) object)); - return; + /* if a link goes down, we must refresh routes */ + if ( ops_type == NMP_CACHE_OPS_UPDATED + && old && new /* <-- nonsensical, make coverity happy */ + && old->_link.netlink.is_in_netlink + && NM_FLAGS_HAS (old->link.flags, IFF_LOWER_UP) + && new->_link.netlink.is_in_netlink + && !NM_FLAGS_HAS (new->link.flags, IFF_LOWER_UP)) { + delayed_action_schedule (platform, + DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, + NULL); } - if (init_ip4_route (&route, (struct rtnl_route *) object)) - g_signal_emit_by_name (platform, sig, route.ifindex, &route, change_type, reason); } - return; - case OBJECT_TYPE_IP6_ROUTE: { - NMPlatformIP6Route route; - - if (reason == _NM_PLATFORM_REASON_CACHE_CHECK_INTERNAL) - return; + /* on enslave/release, we also refresh the master. */ + int ifindex1 = 0, ifindex2 = 0; + gboolean changed_master, changed_connected; + + changed_master = (new && new->_link.netlink.is_in_netlink && new->link.master > 0 ? new->link.master : 0) + != (old && old->_link.netlink.is_in_netlink && old->link.master > 0 ? old->link.master : 0); + changed_connected = (new && new->_link.netlink.is_in_netlink ? NM_FLAGS_HAS (new->link.flags, IFF_LOWER_UP) : 2) + != (old && old->_link.netlink.is_in_netlink ? NM_FLAGS_HAS (old->link.flags, IFF_LOWER_UP) : 2); + + if (changed_master || changed_connected) { + ifindex1 = (old && old->_link.netlink.is_in_netlink && old->link.master > 0) ? old->link.master : 0; + ifindex2 = (new && new->_link.netlink.is_in_netlink && new->link.master > 0) ? new->link.master : 0; + + if (ifindex1 > 0) + delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (ifindex1)); + if (ifindex2 > 0 && ifindex1 != ifindex2) + delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (ifindex2)); + } - if (!_route_match ((struct rtnl_route *) object, AF_INET6, 0, FALSE)) { - nm_log_dbg (LOGD_PLATFORM, "skip announce unmatching IP6 route %s", to_string_ip6_route ((struct rtnl_route *) object)); - return; + } + break; + case NMP_OBJECT_TYPE_IP4_ADDRESS: + case NMP_OBJECT_TYPE_IP6_ADDRESS: + { + /* Address deletion is sometimes accompanied by route deletion. We need to + * check all routes belonging to the same interface. */ + if (ops_type == NMP_CACHE_OPS_REMOVED) { + delayed_action_schedule (platform, + (klass->obj_type == NMP_OBJECT_TYPE_IP4_ADDRESS) + ? DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES + : DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, + NULL); } - if (init_ip6_route (&route, (struct rtnl_route *) object)) - g_signal_emit_by_name (platform, sig, route.ifindex, &route, change_type, reason); } - return; default: - g_return_if_reached (); + break; } } -static struct nl_object * build_rtnl_link (int ifindex, const char *name, NMLinkType type); +static NMPCacheOpsType +cache_remove_netlink (NMPlatform *platform, const NMPObject *obj_needle, NMPObject **out_obj_cache, gboolean *out_was_visible, NMPlatformReason reason) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + NMPObject *obj_cache; + gboolean was_visible; + NMPCacheOpsType cache_op; -static gboolean -refresh_object (NMPlatform *platform, struct nl_object *object, gboolean removed, NMPlatformReason reason) + cache_op = nmp_cache_remove_netlink (priv->cache, obj_needle, &obj_cache, &was_visible, cache_pre_hook, platform); + do_emit_signal (platform, obj_cache, cache_op, was_visible, NM_PLATFORM_REASON_INTERNAL); + + if (out_obj_cache) + *out_obj_cache = obj_cache; + else + nmp_object_unref (obj_cache); + if (out_was_visible) + *out_was_visible = was_visible; + + return cache_op; +} + +static NMPCacheOpsType +cache_update_netlink (NMPlatform *platform, NMPObject *obj, NMPObject **out_obj_cache, gboolean *out_was_visible, NMPlatformReason reason) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - auto_nl_object struct nl_object *cached_object = NULL; - auto_nl_object struct nl_object *kernel_object = NULL; - struct nl_cache *cache; - int nle; + NMPObject *obj_cache; + gboolean was_visible; + NMPCacheOpsType cache_op; - cache = choose_cache (platform, object); - cached_object = nm_nl_cache_search (cache, object); - kernel_object = get_kernel_object (priv->nlh, object); + /* This is basically a convenience method to call nmp_cache_update() and do_emit_signal() + * at once. */ - if (removed) { - if (kernel_object) - return TRUE; + cache_op = nmp_cache_update_netlink (priv->cache, obj, &obj_cache, &was_visible, cache_pre_hook, platform); + do_emit_signal (platform, obj_cache, cache_op, was_visible, reason); - /* Only announce object if it was still in the cache. */ - if (cached_object) { - nl_cache_remove (cached_object); + if (out_obj_cache) + *out_obj_cache = obj_cache; + else + nmp_object_unref (obj_cache); + if (out_was_visible) + *out_was_visible = was_visible; - announce_object (platform, cached_object, NM_PLATFORM_SIGNAL_REMOVED, reason); - } - } else { - ObjectType type; + return cache_op; +} - if (!kernel_object) - return FALSE; +/******************************************************************/ - /* Unsupported object types should never have reached the caches */ - type = object_type_from_nl_object (kernel_object); - g_assert (type != OBJECT_TYPE_UNKNOWN); +static void +_new_sequence_number (NMPlatform *platform, guint32 seq) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - hack_empty_master_iff_lower_up (platform, kernel_object); + _LOGT ("_new_sequence_number(): new sequence number %u", seq); - if (cached_object) - nl_cache_remove (cached_object); - nle = nl_cache_add (cache, kernel_object); - if (nle) { - nm_log_dbg (LOGD_PLATFORM, "refresh_object(reason %d) failed during nl_cache_add with %d", reason, nle); - return FALSE; - } + priv->nlh_seq_expect = seq; +} + +static void +do_request_link (NMPlatform *platform, int ifindex, const char *name, gboolean handle_delayed_action) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + guint32 seq; - announce_object (platform, kernel_object, cached_object ? NM_PLATFORM_SIGNAL_CHANGED : NM_PLATFORM_SIGNAL_ADDED, reason); + _LOGT ("do_request_link (%d,%s)", ifindex, name ? name : ""); - /* Refresh the master device (even on enslave/release) */ - if (type == OBJECT_TYPE_LINK) { - int kernel_master = rtnl_link_get_master ((struct rtnl_link *) kernel_object); - int cached_master = cached_object ? rtnl_link_get_master ((struct rtnl_link *) cached_object) : 0; - struct nl_object *master_object; + if (ifindex > 0) { + NMPObject *obj; - if (kernel_master) { - master_object = build_rtnl_link (kernel_master, NULL, NM_LINK_TYPE_NONE); - refresh_object (platform, master_object, FALSE, NM_PLATFORM_REASON_INTERNAL); - nl_object_put (master_object); - } - if (cached_master && cached_master != kernel_master) { - master_object = build_rtnl_link (cached_master, NULL, NM_LINK_TYPE_NONE); - refresh_object (platform, master_object, FALSE, NM_PLATFORM_REASON_INTERNAL); - nl_object_put (master_object); + cache_prune_candidates_record_one (platform, + (NMPObject *) nmp_cache_lookup_link (priv->cache, ifindex)); + obj = nmp_object_new_link (ifindex); + _LOGT ("delayed-deletion: protect object %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ID, NULL, 0)); + g_hash_table_insert (priv->delayed_deletion, obj, NULL); + } + + event_handler_read_netlink_all (platform, FALSE); + + if (_nl_sock_request_link (platform, priv->nlh_event, ifindex, name, &seq) == 0) + _new_sequence_number (platform, seq); + + event_handler_read_netlink_all (platform, TRUE); + + cache_delayed_deletion_prune (platform); + cache_prune_candidates_prune (platform); + + if (handle_delayed_action) + delayed_action_handle_all (platform, FALSE); +} + +static void +do_request_one_type (NMPlatform *platform, NMPObjectType obj_type, gboolean handle_delayed_action) +{ + do_request_all (platform, delayed_action_refresh_from_object_type (obj_type), handle_delayed_action); +} + +static void +do_request_all (NMPlatform *platform, DelayedActionType action_type, gboolean handle_delayed_action) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + guint32 seq; + DelayedActionType iflags; + + nm_assert (!NM_FLAGS_ANY (action_type, ~DELAYED_ACTION_TYPE_REFRESH_ALL)); + action_type &= DELAYED_ACTION_TYPE_REFRESH_ALL; + + for (iflags = (DelayedActionType) 0x1LL; iflags <= DELAYED_ACTION_TYPE_MAX; iflags <<= 1) { + if (NM_FLAGS_HAS (action_type, iflags)) + cache_prune_candidates_record_all (platform, delayed_action_refresh_to_object_type (iflags)); + } + + for (iflags = (DelayedActionType) 0x1LL; iflags <= DELAYED_ACTION_TYPE_MAX; iflags <<= 1) { + if (NM_FLAGS_HAS (action_type, iflags)) { + NMPObjectType obj_type = delayed_action_refresh_to_object_type (iflags); + + /* clear any delayed action that request a refresh of this object type. */ + priv->delayed_action.flags &= ~iflags; + if (obj_type == NMP_OBJECT_TYPE_LINK) { + priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_REFRESH_LINK; + g_ptr_array_set_size (priv->delayed_action.list_refresh_link, 0); } + + event_handler_read_netlink_all (platform, FALSE); + + if (_nl_sock_request_all (platform, priv->nlh_event, obj_type, &seq) == 0) + _new_sequence_number (platform, seq); } } + event_handler_read_netlink_all (platform, TRUE); - return TRUE; + cache_prune_candidates_prune (platform); + + if (handle_delayed_action) + delayed_action_handle_all (platform, FALSE); } -/* Decreases the reference count if @obj for convenience */ static gboolean -add_object (NMPlatform *platform, struct nl_object *obj) +kernel_add_object (NMPlatform *platform, NMPObjectType obj_type, const struct nl_object *nlo) { - auto_nl_object struct nl_object *object = obj; NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); int nle; - g_return_val_if_fail (object, FALSE); + g_return_val_if_fail (nlo, FALSE); - nle = add_kernel_object (priv->nlh, object); + switch (obj_type) { + case NMP_OBJECT_TYPE_LINK: + nle = rtnl_link_add (priv->nlh, (struct rtnl_link *) nlo, NLM_F_CREATE); + break; + case NMP_OBJECT_TYPE_IP4_ADDRESS: + case NMP_OBJECT_TYPE_IP6_ADDRESS: + nle = rtnl_addr_add (priv->nlh, (struct rtnl_addr *) nlo, NLM_F_CREATE | NLM_F_REPLACE); + break; + case NMP_OBJECT_TYPE_IP4_ROUTE: + case NMP_OBJECT_TYPE_IP6_ROUTE: + nle = rtnl_route_add (priv->nlh, (struct rtnl_route *) nlo, NLM_F_CREATE | NLM_F_REPLACE); + break; + default: + g_return_val_if_reached (-NLE_INVAL); + } + + _LOGT ("kernel-add-%s: returned %s (%d)", + nmp_class_from_type (obj_type)->obj_type_name, nl_geterror (nle), -nle); - /* NLE_EXIST is considered equivalent to success to avoid race conditions. You - * never know when something sends an identical object just before - * NetworkManager. - */ switch (nle) { case -NLE_SUCCESS: + return -NLE_SUCCESS; case -NLE_EXIST: - break; + /* NLE_EXIST is considered equivalent to success to avoid race conditions. You + * never know when something sends an identical object just before + * NetworkManager. */ + if (obj_type != NMP_OBJECT_TYPE_LINK) + return -NLE_SUCCESS; + /* fall-through */ default: - error ("Netlink error adding %s: %s", to_string_object (platform, object), nl_geterror (nle)); - if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) { - char buf[256]; - struct nl_dump_params dp = { - .dp_type = NL_DUMP_DETAILS, - .dp_buf = buf, - .dp_buflen = sizeof (buf), - }; - - nl_object_dump (object, &dp); - buf[sizeof (buf) - 1] = '\0'; - debug ("netlink object:\n%s", buf); - } - return FALSE; + return nle; } - - return refresh_object (platform, object, FALSE, NM_PLATFORM_REASON_INTERNAL); } -/* Decreases the reference count if @obj for convenience */ -static gboolean -delete_object (NMPlatform *platform, struct nl_object *object, gboolean do_refresh_object) +static int +kernel_delete_object (NMPlatform *platform, NMPObjectType object_type, const struct nl_object *object) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - int object_type; int nle; - gboolean result = FALSE; - - object_type = object_type_from_nl_object (object); - g_return_val_if_fail (object_type != OBJECT_TYPE_UNKNOWN, FALSE); switch (object_type) { - case OBJECT_TYPE_LINK: + case NMP_OBJECT_TYPE_LINK: nle = rtnl_link_delete (priv->nlh, (struct rtnl_link *) object); break; - case OBJECT_TYPE_IP4_ADDRESS: - case OBJECT_TYPE_IP6_ADDRESS: + case NMP_OBJECT_TYPE_IP4_ADDRESS: + case NMP_OBJECT_TYPE_IP6_ADDRESS: nle = rtnl_addr_delete (priv->nlh, (struct rtnl_addr *) object, 0); break; - case OBJECT_TYPE_IP4_ROUTE: - case OBJECT_TYPE_IP6_ROUTE: + case NMP_OBJECT_TYPE_IP4_ROUTE: + case NMP_OBJECT_TYPE_IP6_ROUTE: nle = rtnl_route_delete (priv->nlh, (struct rtnl_route *) object, 0); break; default: @@ -1836,40 +2103,134 @@ delete_object (NMPlatform *platform, struct nl_object *object, gboolean do_refre switch (nle) { case -NLE_SUCCESS: - break; + return NLE_SUCCESS; case -NLE_OBJ_NOTFOUND: - debug("delete_object failed with \"%s\" (%d), meaning the object was already removed", - nl_geterror (nle), nle); - break; + _LOGT ("kernel-delete-%s: failed with \"%s\" (%d), meaning the object was already removed", + nmp_class_from_type (object_type)->obj_type_name, nl_geterror (nle), -nle); + return -NLE_SUCCESS; case -NLE_FAILURE: - if (object_type == OBJECT_TYPE_IP6_ADDRESS) { + if (object_type == NMP_OBJECT_TYPE_IP6_ADDRESS) { /* On RHEL7 kernel, deleting a non existing address fails with ENXIO (which libnl maps to NLE_FAILURE) */ - debug("delete_object for address failed with \"%s\" (%d), meaning the address was already removed", - nl_geterror (nle), nle); - break; + _LOGT ("kernel-delete-%s: deleting address failed with \"%s\" (%d), meaning the address was already removed", + nmp_class_from_type (object_type)->obj_type_name, nl_geterror (nle), -nle); + return NLE_SUCCESS; } - goto DEFAULT; + break; case -NLE_NOADDR: - if (object_type == OBJECT_TYPE_IP4_ADDRESS || object_type == OBJECT_TYPE_IP6_ADDRESS) { - debug("delete_object for address failed with \"%s\" (%d), meaning the address was already removed", - nl_geterror (nle), nle); - break; + if (object_type == NMP_OBJECT_TYPE_IP4_ADDRESS || object_type == NMP_OBJECT_TYPE_IP6_ADDRESS) { + _LOGT ("kernel-delete-%s: deleting address failed with \"%s\" (%d), meaning the address was already removed", + nmp_class_from_type (object_type)->obj_type_name, nl_geterror (nle), -nle); + return -NLE_SUCCESS; } - goto DEFAULT; - DEFAULT: + break; default: - error ("Netlink error deleting %s: %s (%d)", to_string_object (platform, object), nl_geterror (nle), nle); - goto out; + break; } + _LOGT ("kernel-delete-%s: failed with %s (%d)", + nmp_class_from_type (object_type)->obj_type_name, nl_geterror (nle), -nle); + return nle; +} - if (do_refresh_object) - refresh_object (platform, object, TRUE, NM_PLATFORM_REASON_INTERNAL); +static int +kernel_change_link (NMPlatform *platform, struct rtnl_link *nlo, gboolean *complete_from_cache) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + struct nl_msg *msg; + int nle; + const int nlflags = 0; + int ifindex; - result = TRUE; + ifindex = rtnl_link_get_ifindex (nlo); -out: - nl_object_put (object); - return result; + g_return_val_if_fail (ifindex > 0, FALSE); + + /* Previously, we were using rtnl_link_change(), which builds a request based + * on the diff with an original link instance. + * + * The diff only reused ifi_family, ifi_index, ifi_flags, and name from + * the original link (see rtnl_link_build_change_request()). + * + * We don't do that anymore as we don't have an "orig" netlink instance that + * we can use. Instead the caller must ensure to properly initialize @nlo, + * especially it must set family, ifindex (or ifname) and flags. + * ifname should be set *only* if the caller wishes to change the name. + * + * @complete_from_cache is a convenience to copy the link flags over the link inside + * the platform cache. */ + + if (*complete_from_cache) { + const NMPObject *obj_cache; + + obj_cache = nmp_cache_lookup_link (priv->cache, ifindex); + if (!obj_cache || !obj_cache->_link.netlink.is_in_netlink) { + _LOGT ("kernel-change-link: failure changing link %d: cannot complete link", ifindex); + *complete_from_cache = FALSE; + return -NLE_INVAL; + } + + rtnl_link_set_flags (nlo, obj_cache->link.flags); + + /* If the caller wants to rename the link, he should explicitly set + * rtnl_link_set_name(). In all other cases, it should leave the name + * unset. Unfortunately, there is not public API in libnl to modify the + * attribute mask and clear (link->ce_mask = ~LINK_ATTR_IFNAME), so we + * require the caller to do the right thing -- i.e. don't set the name. + */ + } + + /* We don't use rtnl_link_change() because we have no original rtnl_link object + * at hand. We also don't use rtnl_link_add() because that doesn't have the + * hack to retry with RTM_SETLINK. Reimplement a mix of both. */ + + nle = rtnl_link_build_add_request (nlo, nlflags, &msg); + if (nle < 0) { + _LOGT ("kernel-change-link: failure changing link %d: cannot construct message (%s, %d)", + ifindex, nl_geterror (nle), -nle); + return nle; + } + +retry: + nle = nl_send_auto_complete (priv->nlh, msg); + if (nle < 0) + goto errout; + + nle = nl_wait_for_ack(priv->nlh); + if (nle == -NLE_OPNOTSUPP && nlmsg_hdr (msg)->nlmsg_type == RTM_NEWLINK) { + nlmsg_hdr (msg)->nlmsg_type = RTM_SETLINK; + goto retry; + } + +errout: + nlmsg_free(msg); + + /* NLE_EXIST is considered equivalent to success to avoid race conditions. You + * never know when something sends an identical object just before + * NetworkManager. + * + * When netlink returns NLE_OBJ_NOTFOUND, it usually means it failed to find + * firmware for the device, especially on nm_platform_link_set_up (). + * This is basically the same check as in the original code and could + * potentially be improved. + */ + switch (nle) { + case -NLE_SUCCESS: + _LOGT ("kernel-change-link: success changing link %d", ifindex); + break; + case -NLE_EXIST: + _LOGT ("kernel-change-link: success changing link %d: %s (%d)", + ifindex, nl_geterror (nle), -nle); + break; + case -NLE_OBJ_NOTFOUND: + _LOGT ("kernel-change-link: failure changing link %d: firmware not found (%s, %d)", + ifindex, nl_geterror (nle), -nle); + break; + default: + _LOGT ("kernel-change-link: failure changing link %d: netlink error (%s, %d)", + ifindex, nl_geterror (nle), -nle); + break; + } + + return nle; } static void @@ -1881,47 +2242,40 @@ ref_object (struct nl_object *obj, void *data) *out = obj; } -static gboolean -_rtnl_addr_timestamps_equal_fuzzy (guint32 ts1, guint32 ts2) +static int +event_seq_check (struct nl_msg *msg, gpointer user_data) { - guint32 diff; + NMPlatform *platform = NM_PLATFORM (user_data); + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + struct nlmsghdr *hdr; - if (ts1 == ts2) - return TRUE; - if (ts1 == NM_PLATFORM_LIFETIME_PERMANENT || - ts2 == NM_PLATFORM_LIFETIME_PERMANENT) - return FALSE; + hdr = nlmsg_hdr (msg); - /** accept the timestamps as equal if they are within two seconds. */ - diff = ts1 > ts2 ? ts1 - ts2 : ts2 - ts1; - return diff <= 2; -} + if (hdr->nlmsg_seq == 0) + return NL_OK; -static gboolean -nm_nl_object_diff (ObjectType type, struct nl_object *_a, struct nl_object *_b) -{ - if (nl_object_diff (_a, _b)) { - /* libnl thinks objects are different*/ - return TRUE; - } + priv->nlh_seq_last = hdr->nlmsg_seq; - if (type == OBJECT_TYPE_IP4_ADDRESS || type == OBJECT_TYPE_IP6_ADDRESS) { - struct rtnl_addr *a = (struct rtnl_addr *) _a; - struct rtnl_addr *b = (struct rtnl_addr *) _b; + if (priv->nlh_seq_expect == 0) + _LOGT ("event_seq_check(): seq %u received (not waited)", hdr->nlmsg_seq); + else if (hdr->nlmsg_seq == priv->nlh_seq_expect) { + _LOGT ("event_seq_check(): seq %u received", hdr->nlmsg_seq); - /* libnl nl_object_diff() ignores differences in timestamp. Let's care about - * them (if they are large enough). - * - * Note that these valid and preferred timestamps are absolute, after - * _rtnl_addr_hack_lifetimes_rel_to_abs(). */ - if ( !_rtnl_addr_timestamps_equal_fuzzy (rtnl_addr_get_preferred_lifetime (a), - rtnl_addr_get_preferred_lifetime (b)) - || !_rtnl_addr_timestamps_equal_fuzzy (rtnl_addr_get_valid_lifetime (a), - rtnl_addr_get_valid_lifetime (b))) - return TRUE; - } + priv->nlh_seq_expect = 0; + } else + _LOGT ("event_seq_check(): seq %u received (wait for %u)", hdr->nlmsg_seq, priv->nlh_seq_last); - return FALSE; + return NL_OK; +} + +static int +event_err (struct sockaddr_nl *nla, struct nlmsgerr *nlerr, gpointer platform) +{ + _LOGT ("event_err(): error from kernel: %s (%d) for request %d", + strerror (nlerr ? -nlerr->error : 0), + nlerr ? -nlerr->error : 0, + NM_LINUX_PLATFORM_GET_PRIVATE (platform)->nlh_seq_last); + return NL_OK; } /* This function does all the magic to avoid race conditions caused @@ -1934,123 +2288,71 @@ static int event_notification (struct nl_msg *msg, gpointer user_data) { NMPlatform *platform = NM_PLATFORM (user_data); - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - struct nl_cache *cache; - auto_nl_object struct nl_object *object = NULL; - auto_nl_object struct nl_object *cached_object = NULL; - auto_nl_object struct nl_object *kernel_object = NULL; - int event; - int nle; - ObjectType type; + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (user_data); + auto_nl_object struct nl_object *nlo = NULL; + auto_nmp_obj NMPObject *obj = NULL; + struct nlmsghdr *msghdr; + char buf_nlmsg_type[16]; - event = nlmsg_hdr (msg)->nlmsg_type; + msghdr = nlmsg_hdr (msg); - if (priv->support_kernel_extended_ifa_flags == 0 && event == RTM_NEWADDR) { - /* if kernel support for extended ifa flags is still undecided, use the opportunity - * now and use @msg to decide it. This saves a blocking net link request. - **/ - _check_support_kernel_extended_ifa_flags_init (priv, msg); - } + if (_support_kernel_extended_ifa_flags_still_undecided () && msghdr->nlmsg_type == RTM_NEWADDR) + _support_kernel_extended_ifa_flags_detect (msg); - nl_msg_parse (msg, ref_object, &object); - g_return_val_if_fail (object, NL_OK); - - type = object_type_from_nl_object (object); + nl_msg_parse (msg, ref_object, &nlo); + if (!nlo) + return NL_OK; - if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) { - if (type == OBJECT_TYPE_LINK) { - const char *name = rtnl_link_get_name ((struct rtnl_link *) object); + if (_support_user_ipv6ll_still_undecided() && msghdr->nlmsg_type == RTM_NEWLINK) + _support_user_ipv6ll_detect ((struct rtnl_link *) nlo); - debug ("netlink event (type %d) for link: %s (%d, family %d)", - event, name ? name : "(unknown)", - rtnl_link_get_ifindex ((struct rtnl_link *) object), - rtnl_link_get_family ((struct rtnl_link *) object)); - } else - debug ("netlink event (type %d)", event); - } + obj = nmp_object_from_nl (platform, nlo, FALSE, TRUE); - cache = choose_cache_by_type (platform, type); - cached_object = nm_nl_cache_search (cache, object); - kernel_object = get_kernel_object (priv->nlh, object); + _LOGD ("event-notification: %s, seq %u: %s", + _nl_nlmsg_type_to_str (msghdr->nlmsg_type, buf_nlmsg_type, sizeof (buf_nlmsg_type)), + msghdr->nlmsg_seq, nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + if (obj) { + auto_nmp_obj NMPObject *obj_cache = NULL; - hack_empty_master_iff_lower_up (platform, kernel_object); + switch (msghdr->nlmsg_type) { - /* Removed object */ - switch (event) { - case RTM_DELLINK: - case RTM_DELADDR: - case RTM_DELROUTE: - /* Ignore inconsistent deletion - * - * Quick external deletion and addition can be occasionally - * seen as just a change. - */ - if (kernel_object) - return NL_OK; - /* Ignore internal deletion */ - if (!cached_object) - return NL_OK; - - nl_cache_remove (cached_object); - /* Don't announce removed interfaces that are not recognized by - * udev. They were either not yet discovered or they have been - * already removed and announced. - */ - if (event == RTM_DELLINK) { - if (!link_is_announceable (platform, (struct rtnl_link *) cached_object)) - return NL_OK; - } - announce_object (platform, cached_object, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_EXTERNAL); + case RTM_NEWLINK: + if ( NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_LINK + && g_hash_table_lookup (priv->delayed_deletion, obj) != NULL) { + /* the object is scheduled for delayed deletion. Replace that object + * by clearing the value from priv->delayed_deletion. */ + _LOGT ("delayed-deletion: clear delayed deletion of protected object %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ID, NULL, 0)); + g_hash_table_insert (priv->delayed_deletion, nmp_object_ref (obj), NULL); + } + /* fall-through */ + case RTM_NEWADDR: + case RTM_NEWROUTE: + cache_update_netlink (platform, obj, &obj_cache, NULL, NM_PLATFORM_REASON_EXTERNAL); + break; - return NL_OK; - case RTM_NEWLINK: - case RTM_NEWADDR: - case RTM_NEWROUTE: - /* Ignore inconsistent addition or change (kernel will send a good one) - * - * Quick sequence of RTM_NEWLINK notifications can be occasionally - * collapsed to just one addition or deletion, depending of whether we - * already have the object in cache. - */ - if (!kernel_object) - return NL_OK; - - /* Ignore unsupported object types (e.g. AF_PHONET family addresses) */ - if (type == OBJECT_TYPE_UNKNOWN) - return NL_OK; - - /* Handle external addition */ - if (!cached_object) { - nle = nl_cache_add (cache, kernel_object); - if (nle) { - error ("netlink cache error: %s", nl_geterror (nle)); - return NL_OK; + case RTM_DELLINK: + if ( NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_LINK + && g_hash_table_contains (priv->delayed_deletion, obj)) { + /* We sometimes receive spurious RTM_DELLINK events. In this case, we want to delay + * the deletion of the object until later. */ + _LOGT ("delayed-deletion: delay deletion of protected object %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ID, NULL, 0)); + g_hash_table_insert (priv->delayed_deletion, nmp_object_ref (obj), nmp_object_ref (obj)); + break; } - announce_object (platform, kernel_object, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_EXTERNAL); - return NL_OK; - } - /* Ignore non-change - * - * This also catches notifications for internal addition or change, unless - * another action occured very soon after it. - */ - if (!nm_nl_object_diff (type, kernel_object, cached_object)) - return NL_OK; - - /* Handle external change */ - nl_cache_remove (cached_object); - nle = nl_cache_add (cache, kernel_object); - if (nle) { - error ("netlink cache error: %s", nl_geterror (nle)); - return NL_OK; + /* fall-through */ + case RTM_DELADDR: + case RTM_DELROUTE: + cache_remove_netlink (platform, obj, &obj_cache, NULL, NM_PLATFORM_REASON_EXTERNAL); + break; + + default: + break; } - announce_object (platform, kernel_object, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_EXTERNAL); - return NL_OK; - default: - error ("Unknown netlink event: %d", event); - return NL_OK; + cache_prune_candidates_drop (platform, obj_cache); } + + return NL_OK; } /******************************************************************/ @@ -2219,40 +2521,81 @@ sysctl_get (NMPlatform *platform, const char *path) /******************************************************************/ +static const NMPObject * +cache_lookup_link (NMPlatform *platform, int ifindex) +{ + const NMPObject *obj_cache; + + obj_cache = nmp_cache_lookup_link (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, ifindex); + if (!nmp_object_is_visible (obj_cache)) + return NULL; + + return obj_cache; +} + static GArray * link_get_all (NMPlatform *platform) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - GArray *links = g_array_sized_new (FALSE, FALSE, sizeof (NMPlatformLink), nl_cache_nitems (priv->link_cache)); - NMPlatformLink device; - struct nl_object *object; - for (object = nl_cache_get_first (priv->link_cache); object; object = nl_cache_get_next (object)) { - struct rtnl_link *rtnl_link = (struct rtnl_link *) object; + return nmp_cache_lookup_multi_to_array (priv->cache, + NMP_OBJECT_TYPE_LINK, + nmp_cache_id_init_object_type (NMP_CACHE_ID_STATIC, NMP_OBJECT_TYPE_LINK, TRUE)); +} - if (link_is_announceable (platform, rtnl_link)) { - if (init_link (platform, &device, rtnl_link)) - g_array_append_val (links, device); - } - } +static const NMPlatformLink * +_nm_platform_link_get (NMPlatform *platform, int ifindex) +{ + const NMPObject *obj; - return links; + obj = cache_lookup_link (platform, ifindex); + return obj ? &obj->link : NULL; } -static gboolean -_nm_platform_link_get (NMPlatform *platform, int ifindex, NMPlatformLink *l) +static const NMPlatformLink * +_nm_platform_link_get_by_ifname (NMPlatform *platform, + const char *ifname) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - auto_nl_object struct rtnl_link *rtnllink = NULL; + const NMPObject *obj = NULL; - rtnllink = rtnl_link_get (priv->link_cache, ifindex); - if (rtnllink) { - if (link_is_announceable (platform, rtnllink)) { - if (init_link (platform, l, rtnllink)) - return TRUE; - } + if (ifname && *ifname) { + obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, + 0, ifname, TRUE, NM_LINK_TYPE_NONE, NULL, NULL); } - return FALSE; + return obj ? &obj->link : NULL; +} + +struct _nm_platform_link_get_by_address_data { + gconstpointer address; + guint8 length; +}; + +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); +} + +static const NMPlatformLink * +_nm_platform_link_get_by_address (NMPlatform *platform, + gconstpointer address, + size_t length) +{ + const NMPObject *obj; + struct _nm_platform_link_get_by_address_data d = { + .address = address, + .length = length, + }; + + if (length <= 0 || length > NM_UTILS_HWADDR_LEN_MAX) + return NULL; + if (!address) + return NULL; + + obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, + 0, NULL, TRUE, NM_LINK_TYPE_NONE, + (NMPObjectMatchFn) _nm_platform_link_get_by_address_match_link, &d); + return obj ? &obj->link : NULL; } static struct nl_object * @@ -2261,331 +2604,392 @@ build_rtnl_link (int ifindex, const char *name, NMLinkType type) struct rtnl_link *rtnllink; int nle; - rtnllink = _nm_rtnl_link_alloc (ifindex, name); + rtnllink = _nl_rtnl_link_alloc (ifindex, name); if (type) { - nle = rtnl_link_set_type (rtnllink, type_to_string (type)); + nle = rtnl_link_set_type (rtnllink, nm_link_type_to_rtnl_type_string (type)); g_assert (!nle); } return (struct nl_object *) rtnllink; } +struct nl_object * +_nmp_vt_cmd_plobj_to_nl_link (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only) +{ + const NMPlatformLink *obj = (const NMPlatformLink *) _obj; + + return build_rtnl_link (obj->ifindex, + obj->name[0] ? obj->name : NULL, + obj->type); +} + static gboolean -link_add (NMPlatform *platform, const char *name, NMLinkType type, const void *address, size_t address_len) +do_add_link (NMPlatform *platform, const char *name, const struct rtnl_link *nlo) { - int r; - struct nl_object *l; + NMPObject obj_needle; + int nle; - if (type == NM_LINK_TYPE_BOND) { - /* When the kernel loads the bond module, either via explicit modprobe - * or automatically in response to creating a bond master, it will also - * create a 'bond0' interface. Since the bond we're about to create may - * or may not be named 'bond0' prevent potential confusion about a bond - * that the user didn't want by telling the bonding module not to create - * bond0 automatically. - */ - if (!g_file_test ("/sys/class/net/bonding_masters", G_FILE_TEST_EXISTS)) - /* Ignore return value to shut up the compiler */ - r = system ("modprobe bonding max_bonds=0"); - } + event_handler_read_netlink_all (platform, FALSE); - debug ("link: add link '%s' of type '%s' (%d)", - name, type_to_string (type), (int) type); + nle = kernel_add_object (platform, NMP_OBJECT_TYPE_LINK, (const struct nl_object *) nlo); + if (nle < 0) { + _LOGE ("do-add-link: failure adding link '%s': %s", name, nl_geterror (nle)); + return FALSE; + } + _LOGD ("do-add-link: success adding link '%s'", name); - l = build_rtnl_link (0, name, type); + nmp_object_stackinit_id_link (&obj_needle, 0); + g_strlcpy (obj_needle.link.name, name, sizeof (obj_needle.link.name)); - g_assert ( (address != NULL) ^ (address_len == 0) ); - if (address) { - auto_nl_addr struct nl_addr *nladdr = _nm_nl_addr_build (AF_LLC, address, address_len); + delayed_action_handle_all (platform, TRUE); - rtnl_link_set_addr ((struct rtnl_link *) l, nladdr); + /* FIXME: we add the link object via the second netlink socket. Sometimes, + * the notification is not yet ready via nlh_event, so we have to re-request the + * link so that it is in the cache. A better solution would be to do everything + * via one netlink socket. */ + if (!nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, 0, obj_needle.link.name, FALSE, NM_LINK_TYPE_NONE, NULL, NULL)) { + _LOGT ("do-add-link: reload: the added link is not yet ready. Request %s", obj_needle.link.name); + do_request_link (platform, 0, obj_needle.link.name, TRUE); } - return add_object (platform, l); + + /* Return true, because kernel_add_object() succeeded. This doesn't indicate that the + * object is now actuall in the cache, because there could be a race. + * + * For that, you'd have to look at @out_obj. */ + return TRUE; } -static struct rtnl_link * -link_get (NMPlatform *platform, int ifindex) +static gboolean +do_add_link_with_lookup (NMPlatform *platform, const char *name, const struct rtnl_link *nlo, NMLinkType expected_link_type, NMPlatformLink *out_link) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - struct rtnl_link *rtnllink = rtnl_link_get (priv->link_cache, ifindex); + const NMPObject *obj; - if (!rtnllink) { - platform->error = NM_PLATFORM_ERROR_NOT_FOUND; - return NULL; + do_add_link (platform, name, nlo); + + obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, + 0, name, FALSE, expected_link_type, NULL, NULL); + if (out_link && obj) + *out_link = obj->link; + return !!obj; +} + +static gboolean +do_add_addrroute (NMPlatform *platform, const NMPObject *obj_id, const struct nl_object *nlo) +{ + int nle; + + nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_id), + NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS, + NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); + + event_handler_read_netlink_all (platform, FALSE); + + nle = kernel_add_object (platform, NMP_OBJECT_GET_CLASS (obj_id)->obj_type, (const struct nl_object *) nlo); + if (nle < 0) { + _LOGW ("do-add-%s: failure adding %s '%s': %s (%d)", + NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, + NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, + nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), + nl_geterror (nle), -nle); + return FALSE; } + _LOGD ("do-add-%s: success adding object %s", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0)); - /* physical interfaces must be found by udev before they can be used */ - if (!link_is_announceable (platform, rtnllink)) { - platform->error = NM_PLATFORM_ERROR_NOT_FOUND; - rtnl_link_put (rtnllink); - return NULL; + delayed_action_handle_all (platform, TRUE); + + /* FIXME: instead of re-requesting the added object, add it via nlh_event + * so that the events are in sync. */ + if (!nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, obj_id)) { + _LOGT ("do-add-%s: reload: the added object is not yet ready. Request %s", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0)); + do_request_one_type (platform, NMP_OBJECT_GET_TYPE (obj_id), TRUE); } - return rtnllink; + /* The return value doesn't say, whether the object is in the platform cache after adding + * it. + * Instead the return value says, whether kernel_add_object() succeeded. */ + return TRUE; } + static gboolean -link_change (NMPlatform *platform, int ifindex, struct rtnl_link *change) +do_delete_object (NMPlatform *platform, const NMPObject *obj_id, const struct nl_object *nlo) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex); + auto_nl_object struct nl_object *nlo_free = NULL; int nle; - if (!rtnllink) - return FALSE; - g_return_val_if_fail (rtnl_link_get_ifindex (change) > 0, FALSE); + event_handler_read_netlink_all (platform, FALSE); - nle = rtnl_link_change (priv->nlh, rtnllink, change, 0); + if (!nlo) + nlo = nlo_free = nmp_object_to_nl (platform, obj_id, FALSE); + + nle = kernel_delete_object (platform, NMP_OBJECT_GET_TYPE (obj_id), nlo); + if (nle < 0) + _LOGE ("do-delete-%s: failure deleting '%s': %s (%d)", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), nl_geterror (nle), -nle); + else + _LOGD ("do-delete-%s: success deleting '%s'", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0)); + + delayed_action_handle_all (platform, TRUE); + + /* FIXME: instead of re-requesting the deleted object, add it via nlh_event + * so that the events are in sync. */ + if (NMP_OBJECT_GET_TYPE (obj_id) == NMP_OBJECT_TYPE_LINK) { + const NMPObject *obj; + + obj = nmp_cache_lookup_link_full (priv->cache, obj_id->link.ifindex, obj_id->link.ifindex <= 0 && obj_id->link.name[0] ? obj_id->link.name : NULL, FALSE, NM_LINK_TYPE_NONE, NULL, NULL); + if (obj && obj->_link.netlink.is_in_netlink) { + _LOGT ("do-delete-%s: reload: the deleted object is not yet removed. Request %s", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0)); + do_request_link (platform, obj_id->link.ifindex, obj_id->link.name, TRUE); + } + } else { + if (nmp_cache_lookup_obj (priv->cache, obj_id)) { + _LOGT ("do-delete-%s: reload: the deleted object is not yet removed. Request %s", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0)); + do_request_one_type (platform, NMP_OBJECT_GET_TYPE (obj_id), TRUE); + } + } + + /* The return value doesn't say, whether the object is in the platform cache after adding + * it. + * Instead the return value says, whether kernel_add_object() succeeded. */ + return nle >= 0; +} + +static NMPlatformError +do_change_link (NMPlatform *platform, struct rtnl_link *nlo, gboolean complete_from_cache) +{ + int nle; + int ifindex; + gboolean complete_from_cache2 = complete_from_cache; + + ifindex = rtnl_link_get_ifindex (nlo); + if (ifindex <= 0) + g_return_val_if_reached (NM_PLATFORM_ERROR_BUG); + + nle = kernel_change_link (platform, nlo, &complete_from_cache2); - /* NLE_EXIST is considered equivalent to success to avoid race conditions. You - * never know when something sends an identical object just before - * NetworkManager. - * - * When netlink returns NLE_OBJ_NOTFOUND, it usually means it failed to find - * firmware for the device, especially on nm_platform_link_set_up (). - * This is basically the same check as in the original code and could - * potentially be improved. - */ switch (nle) { case -NLE_SUCCESS: + _LOGD ("do-change-link: success changing link %d", ifindex); + break; case -NLE_EXIST: + _LOGD ("do-change-link: success changing link %d: %s (%d)", ifindex, nl_geterror (nle), -nle); break; case -NLE_OBJ_NOTFOUND: - error ("Firmware not found for changing link %s; Netlink error: %s)", to_string_link (platform, change), nl_geterror (nle)); - platform->error = NM_PLATFORM_ERROR_NO_FIRMWARE; - return FALSE; + /* fall-through */ default: - error ("Netlink error changing link %s: %s", to_string_link (platform, change), nl_geterror (nle)); - return FALSE; + if (complete_from_cache != complete_from_cache2) + _LOGD ("do-change-link: failure changing link %d: link does not exist in cache", ifindex); + else + _LOGE ("do-change-link: failure changing link %d: %s (%d)", ifindex, nl_geterror (nle), -nle); + return nle == -NLE_OBJ_NOTFOUND ? NM_PLATFORM_ERROR_NO_FIRMWARE : NM_PLATFORM_ERROR_UNSPECIFIED; } - return refresh_object (platform, (struct nl_object *) rtnllink, FALSE, NM_PLATFORM_REASON_INTERNAL); + /* FIXME: as we modify the link via a separate socket, the cache is not in + * sync and we have to refetch the link. */ + do_request_link (platform, ifindex, NULL, TRUE); + return NM_PLATFORM_ERROR_SUCCESS; } static gboolean -link_delete (NMPlatform *platform, int ifindex) +link_add (NMPlatform *platform, + const char *name, + NMLinkType type, + const void *address, + size_t address_len, + NMPlatformLink *out_link) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - struct rtnl_link *rtnllink = rtnl_link_get (priv->link_cache, ifindex); + auto_nl_object struct nl_object *l = NULL; - if (!rtnllink) { - platform->error = NM_PLATFORM_ERROR_NOT_FOUND; - return FALSE; + if (type == NM_LINK_TYPE_BOND) { + /* When the kernel loads the bond module, either via explicit modprobe + * or automatically in response to creating a bond master, it will also + * create a 'bond0' interface. Since the bond we're about to create may + * or may not be named 'bond0' prevent potential confusion about a bond + * that the user didn't want by telling the bonding module not to create + * bond0 automatically. + */ + if (!g_file_test ("/sys/class/net/bonding_masters", G_FILE_TEST_EXISTS)) + nm_utils_modprobe (NULL, TRUE, "bonding", "max_bonds=0", NULL); } - return delete_object (platform, build_rtnl_link (ifindex, NULL, NM_LINK_TYPE_NONE), TRUE); -} + debug ("link: add link '%s' of type '%s' (%d)", + name, nm_link_type_to_string (type), (int) type); -static int -link_get_ifindex (NMPlatform *platform, const char *ifname) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + l = build_rtnl_link (0, name, type); - return rtnl_link_name2i (priv->link_cache, ifname); -} + g_assert ( (address != NULL) ^ (address_len == 0) ); + if (address) { + auto_nl_addr struct nl_addr *nladdr = _nl_addr_build (AF_LLC, address, address_len); -static const char * -link_get_name (NMPlatform *platform, int ifindex) -{ - auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex); + rtnl_link_set_addr ((struct rtnl_link *) l, nladdr); + } - return rtnllink ? rtnl_link_get_name (rtnllink) : NULL; + return do_add_link_with_lookup (platform, name, (struct rtnl_link *) l, type, out_link); } -static NMLinkType -link_get_type (NMPlatform *platform, int ifindex) +static gboolean +link_delete (NMPlatform *platform, int ifindex) { - auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex); + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + NMPObject obj_needle; + const NMPObject *obj; + + obj = nmp_cache_lookup_link (priv->cache, ifindex); + if (!obj || !obj->_link.netlink.is_in_netlink) + return FALSE; - return link_extract_type (platform, rtnllink, NULL); + nmp_object_stackinit_id_link (&obj_needle, ifindex); + return do_delete_object (platform, &obj_needle, NULL); } static const char * link_get_type_name (NMPlatform *platform, int ifindex) { - auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex); - const char *type; - - link_extract_type (platform, rtnllink, &type); - return type; -} + const NMPObject *obj = cache_lookup_link (platform, ifindex); -static guint32 -link_get_flags (NMPlatform *platform, int ifindex) -{ - auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex); - - if (!rtnllink) - return IFF_NOARP; + if (!obj) + return NULL; - return rtnl_link_get_flags (rtnllink); + if (obj->link.type != NM_LINK_TYPE_UNKNOWN) { + /* We could detect the @link_type. In this case the function returns + * our internel module names, which differs from rtnl_link_get_type(): + * - NM_LINK_TYPE_INFINIBAND (gives "infiniband", instead of "ipoib") + * - NM_LINK_TYPE_TAP (gives "tap", instead of "tun"). + * Note that this functions is only used by NMDeviceGeneric to + * set type_description. */ + return nm_link_type_to_string (obj->link.type); + } + /* Link type not detected. Fallback to rtnl_link_get_type()/IFLA_INFO_KIND. */ + return str_if_set (obj->link.kind, "unknown"); } static gboolean -link_refresh (NMPlatform *platform, int ifindex) +link_get_unmanaged (NMPlatform *platform, int ifindex, gboolean *unmanaged) { - auto_nl_object struct rtnl_link *rtnllink = _nm_rtnl_link_alloc (ifindex, NULL); + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + const NMPObject *link; + GUdevDevice *udev_device = NULL; - return refresh_object (platform, (struct nl_object *) rtnllink, FALSE, NM_PLATFORM_REASON_EXTERNAL); -} + link = nmp_cache_lookup_link (priv->cache, ifindex); + if (link) + udev_device = link->_link.udev.device; -static gboolean -link_is_up (NMPlatform *platform, int ifindex) -{ - return !!(link_get_flags (platform, ifindex) & IFF_UP); -} + if (udev_device && g_udev_device_get_property (udev_device, "NM_UNMANAGED")) { + *unmanaged = g_udev_device_get_property_as_boolean (udev_device, "NM_UNMANAGED"); + return TRUE; + } -static gboolean -link_is_connected (NMPlatform *platform, int ifindex) -{ - return !!(link_get_flags (platform, ifindex) & IFF_LOWER_UP); + return FALSE; } static gboolean -link_uses_arp (NMPlatform *platform, int ifindex) +link_refresh (NMPlatform *platform, int ifindex) { - return !(link_get_flags (platform, ifindex) & IFF_NOARP); + do_request_link (platform, ifindex, NULL, TRUE); + return !!cache_lookup_link (platform, ifindex); } -static gboolean +static NMPlatformError link_change_flags (NMPlatform *platform, int ifindex, unsigned int flags, gboolean value) { - auto_nl_object struct rtnl_link *change = _nm_rtnl_link_alloc (ifindex, NULL); + auto_nl_object struct rtnl_link *change = _nl_rtnl_link_alloc (ifindex, NULL); + const NMPObject *obj_cache; + char buf[256]; + obj_cache = cache_lookup_link (platform, ifindex); + if (!obj_cache) + return NM_PLATFORM_ERROR_NOT_FOUND; + + rtnl_link_set_flags (change, obj_cache->link.flags); if (value) rtnl_link_set_flags (change, flags); else rtnl_link_unset_flags (change, flags); - if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) { - char buf[512]; - - rtnl_link_flags2str (flags, buf, sizeof (buf)); - debug ("link: change %d: flags %s '%s' (%d)", ifindex, value ? "set" : "unset", buf, flags); - } + _LOGD ("link: change %d: flags %s '%s' (%d)", ifindex, + value ? "set" : "unset", + rtnl_link_flags2str (flags, buf, sizeof (buf)), + flags); - return link_change (platform, ifindex, change); + return do_change_link (platform, change, FALSE); } static gboolean -link_set_up (NMPlatform *platform, int ifindex) +link_set_up (NMPlatform *platform, int ifindex, gboolean *out_no_firmware) { - return link_change_flags (platform, ifindex, IFF_UP, TRUE); + NMPlatformError plerr; + + plerr = link_change_flags (platform, ifindex, IFF_UP, TRUE); + if (out_no_firmware) + *out_no_firmware = plerr == NM_PLATFORM_ERROR_NO_FIRMWARE; + return plerr == NM_PLATFORM_ERROR_SUCCESS; } static gboolean link_set_down (NMPlatform *platform, int ifindex) { - return link_change_flags (platform, ifindex, IFF_UP, FALSE); + return link_change_flags (platform, ifindex, IFF_UP, FALSE) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean link_set_arp (NMPlatform *platform, int ifindex) { - return link_change_flags (platform, ifindex, IFF_NOARP, FALSE); + return link_change_flags (platform, ifindex, IFF_NOARP, FALSE) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean link_set_noarp (NMPlatform *platform, int ifindex) { - return link_change_flags (platform, ifindex, IFF_NOARP, TRUE); + return link_change_flags (platform, ifindex, IFF_NOARP, TRUE) == NM_PLATFORM_ERROR_SUCCESS; } -static gboolean -link_get_user_ipv6ll_enabled (NMPlatform *platform, int ifindex) +static const char * +link_get_udi (NMPlatform *platform, int ifindex) { -#if HAVE_LIBNL_INET6_ADDR_GEN_MODE - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + const NMPObject *obj = cache_lookup_link (platform, ifindex); - if (priv->support_user_ipv6ll > 0) { - auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex); - uint8_t mode = 0; + if ( !obj + || !obj->_link.netlink.is_in_netlink + || !obj->_link.udev.device) + return NULL; + return g_udev_device_get_sysfs_path (obj->_link.udev.device); +} - if (rtnllink) { - if (rtnl_link_inet6_get_addr_gen_mode (rtnllink, &mode) != 0) { - /* Default to "disabled" on error */ - return FALSE; - } - return mode == IN6_ADDR_GEN_MODE_NONE; - } - } -#endif - return FALSE; +static GObject * +link_get_udev_device (NMPlatform *platform, int ifindex) +{ + const NMPObject *obj_cache; + + /* we don't use cache_lookup_link() because this would return NULL + * if the link is not visible in libnl. For link_get_udev_device() + * we want to return whatever we have, even if the link itself + * appears invisible via other platform functions. */ + + obj_cache = nmp_cache_lookup_link (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, ifindex); + return obj_cache ? (GObject *) obj_cache->_link.udev.device : NULL; } static gboolean link_set_user_ipv6ll_enabled (NMPlatform *platform, int ifindex, gboolean enabled) { #if HAVE_LIBNL_INET6_ADDR_GEN_MODE - if (check_support_user_ipv6ll (platform)) { - auto_nl_object struct rtnl_link *change = _nm_rtnl_link_alloc (ifindex, NULL); + if (_support_user_ipv6ll_get ()) { + auto_nl_object struct rtnl_link *nlo = _nl_rtnl_link_alloc (ifindex, NULL); guint8 mode = enabled ? IN6_ADDR_GEN_MODE_NONE : IN6_ADDR_GEN_MODE_EUI64; char buf[32]; - rtnl_link_inet6_set_addr_gen_mode (change, mode); + rtnl_link_inet6_set_addr_gen_mode (nlo, mode); debug ("link: change %d: set IPv6 address generation mode to %s", ifindex, rtnl_link_inet6_addrgenmode2str (mode, buf, sizeof (buf))); - return link_change (platform, ifindex, change); + return do_change_link (platform, nlo, TRUE) == NM_PLATFORM_ERROR_SUCCESS; } #endif return FALSE; } static gboolean -supports_ethtool_carrier_detect (const char *ifname) -{ - struct ethtool_cmd edata = { .cmd = ETHTOOL_GLINK }; - - /* We ignore the result. If the ETHTOOL_GLINK call succeeded, then we - * assume the device supports carrier-detect, otherwise we assume it - * doesn't. - */ - return ethtool_get (ifname, &edata); -} - -static gboolean -supports_mii_carrier_detect (const char *ifname) -{ - int fd; - struct ifreq ifr; - struct mii_ioctl_data *mii; - gboolean supports_mii = FALSE; - - fd = socket (PF_INET, SOCK_DGRAM, 0); - if (fd < 0) { - nm_log_err (LOGD_PLATFORM, "couldn't open control socket."); - return FALSE; - } - - memset (&ifr, 0, sizeof (struct ifreq)); - strncpy (ifr.ifr_name, ifname, IFNAMSIZ); - - errno = 0; - if (ioctl (fd, SIOCGMIIPHY, &ifr) < 0) { - nm_log_dbg (LOGD_PLATFORM, "SIOCGMIIPHY failed: %d", errno); - goto out; - } - - /* 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 (fd, SIOCGMIIREG, &ifr) == 0) { - nm_log_dbg (LOGD_PLATFORM, "SIOCGMIIREG result 0x%X", mii->val_out); - supports_mii = TRUE; - } else { - nm_log_dbg (LOGD_PLATFORM, "SIOCGMIIREG failed: %d", errno); - } - - out: - close (fd); - nm_log_dbg (LOGD_PLATFORM, "MII %s supported", supports_mii ? "is" : "not"); - return supports_mii; -} - -static gboolean link_supports_carrier_detect (NMPlatform *platform, int ifindex) { - const char *name = nm_platform_link_get_name (ifindex); + const char *name = nm_platform_link_get_name (platform, ifindex); if (!name) return FALSE; @@ -2594,103 +2998,56 @@ 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 supports_ethtool_carrier_detect (name) || supports_mii_carrier_detect (name); + return nmp_utils_ethtool_supports_carrier_detect (name) || nmp_utils_mii_supports_carrier_detect (name); } static gboolean link_supports_vlans (NMPlatform *platform, int ifindex) { - auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex); - const char *name = nm_platform_link_get_name (ifindex); - gs_free struct ethtool_gfeatures *features = NULL; - int idx, block, bit, size; + const NMPObject *obj; - /* Only ARPHRD_ETHER links can possibly support VLANs. */ - if (!rtnllink || rtnl_link_get_arptype (rtnllink) != ARPHRD_ETHER) - return FALSE; + obj = cache_lookup_link (platform, ifindex); - if (!name) - return FALSE; - - idx = ethtool_get_stringset_index (name, ETH_SS_FEATURES, "vlan-challenged"); - if (idx == -1) { - debug ("vlan-challenged ethtool feature does not exist?"); - return FALSE; - } - - block = idx / 32; - bit = idx % 32; - size = block + 1; - - features = g_malloc0 (sizeof (*features) + size * sizeof (struct ethtool_get_features_block)); - features->cmd = ETHTOOL_GFEATURES; - features->size = size; - - if (!ethtool_get (name, features)) + /* Only ARPHRD_ETHER links can possibly support VLANs. */ + if (!obj || obj->link.arptype != ARPHRD_ETHER) return FALSE; - return !(features->features[block].active & (1 << bit)); + return nmp_utils_ethtool_supports_vlans (obj->link.name); } static gboolean link_set_address (NMPlatform *platform, int ifindex, gconstpointer address, size_t length) { - auto_nl_object struct rtnl_link *change = _nm_rtnl_link_alloc (ifindex, NULL); - auto_nl_addr struct nl_addr *nladdr = _nm_nl_addr_build (AF_LLC, address, length); + auto_nl_object struct rtnl_link *change = _nl_rtnl_link_alloc (ifindex, NULL); + auto_nl_addr struct nl_addr *nladdr = _nl_addr_build (AF_LLC, address, length); + gs_free char *mac = NULL; rtnl_link_set_addr (change, nladdr); - if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) { - char *mac = nm_utils_hwaddr_ntoa (address, length); - - debug ("link: change %d: address %s (%lu bytes)", ifindex, mac, (unsigned long) length); - g_free (mac); - } - - return link_change (platform, ifindex, change); + _LOGD ("link: change %d: address %s (%lu bytes)", ifindex, + (mac = nm_utils_hwaddr_ntoa (address, length)), + (unsigned long) length); + return do_change_link (platform, change, TRUE) == NM_PLATFORM_ERROR_SUCCESS; } -static gconstpointer -link_get_address (NMPlatform *platform, int ifindex, size_t *length) +static gboolean +link_get_permanent_address (NMPlatform *platform, + int ifindex, + guint8 *buf, + size_t *length) { - auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex); - struct nl_addr *nladdr; - size_t l = 0; - gconstpointer a = NULL; - - if (rtnllink && - (nladdr = rtnl_link_get_addr (rtnllink))) { - l = nl_addr_get_len (nladdr); - if (l > NM_UTILS_HWADDR_LEN_MAX) { - if (length) - *length = 0; - g_return_val_if_reached (NULL); - } else if (l > 0) - a = nl_addr_get_binary_addr (nladdr); - } - - if (length) - *length = l; - return a; + return nmp_utils_ethtool_get_permanent_address (nm_platform_link_get_name (platform, ifindex), buf, length); } static gboolean link_set_mtu (NMPlatform *platform, int ifindex, guint32 mtu) { - auto_nl_object struct rtnl_link *change = _nm_rtnl_link_alloc (ifindex, NULL); + auto_nl_object struct rtnl_link *change = _nl_rtnl_link_alloc (ifindex, NULL); rtnl_link_set_mtu (change, mtu); debug ("link: change %d: mtu %lu", ifindex, (unsigned long)mtu); - return link_change (platform, ifindex, change); -} - -static guint32 -link_get_mtu (NMPlatform *platform, int ifindex) -{ - auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex); - - return rtnllink ? rtnl_link_get_mtu (rtnllink) : 0; + return do_change_link (platform, change, TRUE) == NM_PLATFORM_ERROR_SUCCESS; } static char * @@ -2699,7 +3056,7 @@ link_get_physical_port_id (NMPlatform *platform, int ifindex) const char *ifname; char *path, *id; - ifname = nm_platform_link_get_name (ifindex); + ifname = nm_platform_link_get_name (platform, ifindex); if (!ifname) return NULL; @@ -2719,7 +3076,7 @@ link_get_dev_id (NMPlatform *platform, int ifindex) gs_free char *path = NULL, *id = NULL; gint64 int_val; - ifname = nm_platform_link_get_name (ifindex); + ifname = nm_platform_link_get_name (platform, ifindex); if (!ifname) return 0; @@ -2731,16 +3088,20 @@ link_get_dev_id (NMPlatform *platform, int ifindex) return 0; /* Value is reported as hex */ - int_val = nm_utils_ascii_str_to_int64 (id, 16, 0, G_MAXUINT16, 0); + int_val = _nm_utils_ascii_str_to_int64 (id, 16, 0, G_MAXUINT16, 0); return errno ? 0 : (int) int_val; } static int -vlan_add (NMPlatform *platform, const char *name, int parent, int vlan_id, guint32 vlan_flags) -{ - struct nl_object *object = build_rtnl_link (0, name, NM_LINK_TYPE_VLAN); - struct rtnl_link *rtnllink = (struct rtnl_link *) object; +vlan_add (NMPlatform *platform, + const char *name, + int parent, + int vlan_id, + guint32 vlan_flags, + NMPlatformLink *out_link) +{ + auto_nl_object struct rtnl_link *rtnllink = (struct rtnl_link *) build_rtnl_link (0, name, NM_LINK_TYPE_VLAN); unsigned int kernel_flags; kernel_flags = 0; @@ -2758,63 +3119,61 @@ vlan_add (NMPlatform *platform, const char *name, int parent, int vlan_id, guint debug ("link: add vlan '%s', parent %d, vlan id %d, flags %X (native: %X)", name, parent, vlan_id, (unsigned int) vlan_flags, kernel_flags); - return add_object (platform, object); + return do_add_link_with_lookup (platform, name, rtnllink, NM_LINK_TYPE_VLAN, out_link); } static gboolean vlan_get_info (NMPlatform *platform, int ifindex, int *parent, int *vlan_id) { - auto_nl_object struct rtnl_link *rtnllink = link_get (platform, ifindex); + const NMPObject *obj = cache_lookup_link (platform, ifindex); + int p = 0, v = 0; + if (obj) { + p = obj->link.parent; + v = obj->link.vlan_id; + } if (parent) - *parent = rtnllink ? rtnl_link_get_link (rtnllink) : 0; + *parent = p; if (vlan_id) - *vlan_id = rtnllink ? rtnl_link_vlan_get_id (rtnllink) : 0; - - return !!rtnllink; + *vlan_id = v; + return !!obj; } static gboolean vlan_set_ingress_map (NMPlatform *platform, int ifindex, int from, int to) { - /* We have to use link_get() because a "blank" rtnl_link won't have the - * right data structures to be able to call rtnl_link_vlan_set_ingress_map() - * on it. (Likewise below in vlan_set_egress_map().) - */ - auto_nl_object struct rtnl_link *change = link_get (platform, ifindex); + auto_nl_object struct rtnl_link *change = (struct rtnl_link *) build_rtnl_link (ifindex, NULL, NM_LINK_TYPE_VLAN); - if (!change) - return FALSE; + rtnl_link_set_type (change, "vlan"); rtnl_link_vlan_set_ingress_map (change, from, to); debug ("link: change %d: vlan ingress map %d -> %d", ifindex, from, to); - return link_change (platform, ifindex, change); + return do_change_link (platform, change, TRUE) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean vlan_set_egress_map (NMPlatform *platform, int ifindex, int from, int to) { - auto_nl_object struct rtnl_link *change = link_get (platform, ifindex); + auto_nl_object struct rtnl_link *change = (struct rtnl_link *) build_rtnl_link (ifindex, NULL, NM_LINK_TYPE_VLAN); - if (!change) - return FALSE; + rtnl_link_set_type (change, "vlan"); rtnl_link_vlan_set_egress_map (change, from, to); debug ("link: change %d: vlan egress map %d -> %d", ifindex, from, to); - return link_change (platform, ifindex, change); + return do_change_link (platform, change, TRUE) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean link_enslave (NMPlatform *platform, int master, int slave) { - auto_nl_object struct rtnl_link *change = _nm_rtnl_link_alloc (slave, NULL); + auto_nl_object struct rtnl_link *change = _nl_rtnl_link_alloc (slave, NULL); rtnl_link_set_master (change, master); debug ("link: change %d: enslave to master %d", slave, master); - return link_change (platform, slave, change); + return do_change_link (platform, change, TRUE) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean @@ -2823,19 +3182,11 @@ link_release (NMPlatform *platform, int master, int slave) return link_enslave (platform, 0, slave); } -static int -link_get_master (NMPlatform *platform, int slave) -{ - auto_nl_object struct rtnl_link *rtnllink = link_get (platform, slave); - - return rtnllink ? rtnl_link_get_master (rtnllink) : 0; -} - static char * -link_option_path (int master, const char *category, const char *option) +link_option_path (NMPlatform *platform, int master, const char *category, const char *option) { - const char *name = nm_platform_link_get_name (master); - + const char *name = nm_platform_link_get_name (platform, master); + if (!name || !category || !option) return NULL; @@ -2846,31 +3197,30 @@ link_option_path (int master, const char *category, const char *option) } static gboolean -link_set_option (int master, const char *category, const char *option, const char *value) +link_set_option (NMPlatform *platform, int master, const char *category, const char *option, const char *value) { - gs_free char *path = link_option_path (master, category, option); + gs_free char *path = link_option_path (platform, master, category, option); - return path && nm_platform_sysctl_set (path, value); + return path && nm_platform_sysctl_set (platform, path, value); } static char * -link_get_option (int master, const char *category, const char *option) +link_get_option (NMPlatform *platform, int master, const char *category, const char *option) { - gs_free char *path = link_option_path (master, category, option); + gs_free char *path = link_option_path (platform, master, category, option); - return path ? nm_platform_sysctl_get (path) : NULL; + return path ? nm_platform_sysctl_get (platform, path) : NULL; } static const char * master_category (NMPlatform *platform, int master) { - switch (link_get_type (platform, master)) { + switch (nm_platform_link_get_type (platform, master)) { case NM_LINK_TYPE_BRIDGE: return "bridge"; case NM_LINK_TYPE_BOND: return "bonding"; default: - g_return_val_if_reached (NULL); return NULL; } } @@ -2878,18 +3228,15 @@ master_category (NMPlatform *platform, int master) static const char * slave_category (NMPlatform *platform, int slave) { - int master = link_get_master (platform, slave); + int master = nm_platform_link_get_master (platform, slave); - if (master <= 0) { - platform->error = NM_PLATFORM_ERROR_NOT_SLAVE; + if (master <= 0) return NULL; - } - switch (link_get_type (platform, master)) { + switch (nm_platform_link_get_type (platform, master)) { case NM_LINK_TYPE_BRIDGE: return "brport"; default: - g_return_val_if_reached (NULL); return NULL; } } @@ -2897,84 +3244,187 @@ slave_category (NMPlatform *platform, int slave) static gboolean master_set_option (NMPlatform *platform, int master, const char *option, const char *value) { - return link_set_option (master, master_category (platform, master), option, value); + return link_set_option (platform, master, master_category (platform, master), option, value); } static char * master_get_option (NMPlatform *platform, int master, const char *option) { - return link_get_option (master, master_category (platform, master), option); + return link_get_option (platform, master, master_category (platform, master), option); } static gboolean slave_set_option (NMPlatform *platform, int slave, const char *option, const char *value) { - return link_set_option (slave, slave_category (platform, slave), option, value); + return link_set_option (platform, slave, slave_category (platform, slave), option, value); } static char * slave_get_option (NMPlatform *platform, int slave, const char *option) { - return link_get_option (slave, slave_category (platform, slave), option); + return link_get_option (platform, slave, slave_category (platform, slave), option); } static gboolean -infiniband_partition_add (NMPlatform *platform, int parent, int p_key) +infiniband_partition_add (NMPlatform *platform, int parent, int p_key, NMPlatformLink *out_link) { - const char *parent_name; - char *path, *id; - gboolean success; + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + const NMPObject *obj_parent; + const NMPObject *obj; + gs_free char *path = NULL; + gs_free char *id = NULL; + gs_free char *ifname = NULL; + + obj_parent = nmp_cache_lookup_link (priv->cache, parent); + if (!obj_parent || !obj_parent->link.name[0]) + g_return_val_if_reached (FALSE); - parent_name = nm_platform_link_get_name (parent); - g_return_val_if_fail (parent_name != NULL, FALSE); + ifname = g_strdup_printf ("%s.%04x", obj_parent->link.name, p_key); - path = g_strdup_printf ("/sys/class/net/%s/create_child", ASSERT_VALID_PATH_COMPONENT (parent_name)); + path = g_strdup_printf ("/sys/class/net/%s/create_child", ASSERT_VALID_PATH_COMPONENT (obj_parent->link.name)); id = g_strdup_printf ("0x%04x", p_key); - success = nm_platform_sysctl_set (path, id); - g_free (id); - g_free (path); + if (!nm_platform_sysctl_set (platform, path, id)) + return FALSE; - if (success) { - gs_free char *ifname = g_strdup_printf ("%s.%04x", parent_name, p_key); - auto_nl_object struct rtnl_link *rtnllink = _nm_rtnl_link_alloc (0, ifname); + do_request_link (platform, 0, ifname, TRUE); - success = refresh_object (platform, (struct nl_object *) rtnllink, FALSE, NM_PLATFORM_REASON_INTERNAL); + obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, + 0, ifname, FALSE, NM_LINK_TYPE_INFINIBAND, NULL, NULL); + if (out_link && obj) + *out_link = obj->link; + return !!obj; +} + +typedef struct { + int p_key; + const char *mode; +} IpoibInfo; + +/* IFLA_IPOIB_* were introduced in the 3.7 kernel, but the kernel headers + * we're building against might not have those properties even though the + * running kernel might. + */ +#define IFLA_IPOIB_UNSPEC 0 +#define IFLA_IPOIB_PKEY 1 +#define IFLA_IPOIB_MODE 2 +#define IFLA_IPOIB_UMCAST 3 +#undef IFLA_IPOIB_MAX +#define IFLA_IPOIB_MAX IFLA_IPOIB_UMCAST + +#define IPOIB_MODE_DATAGRAM 0 /* using unreliable datagram QPs */ +#define IPOIB_MODE_CONNECTED 1 /* using connected QPs */ + +static const struct nla_policy infiniband_info_policy[IFLA_IPOIB_MAX + 1] = { + [IFLA_IPOIB_PKEY] = { .type = NLA_U16 }, + [IFLA_IPOIB_MODE] = { .type = NLA_U16 }, + [IFLA_IPOIB_UMCAST] = { .type = NLA_U16 }, +}; + +static int +infiniband_info_data_parser (struct nlattr *info_data, gpointer parser_data) +{ + IpoibInfo *info = parser_data; + struct nlattr *tb[IFLA_MACVLAN_MAX + 1]; + int err; + + err = nla_parse_nested (tb, IFLA_IPOIB_MAX, info_data, + (struct nla_policy *) infiniband_info_policy); + if (err < 0) + return err; + if (!tb[IFLA_IPOIB_PKEY] || !tb[IFLA_IPOIB_MODE]) + return -EINVAL; + + info->p_key = nla_get_u16 (tb[IFLA_IPOIB_PKEY]); + + switch (nla_get_u16 (tb[IFLA_IPOIB_MODE])) { + case IPOIB_MODE_DATAGRAM: + info->mode = "datagram"; + break; + case IPOIB_MODE_CONNECTED: + info->mode = "connected"; + break; + default: + return -NLE_PARSE_ERR; } - return success; + return 0; +} + +static gboolean +infiniband_get_info (NMPlatform *platform, int ifindex, int *parent, int *p_key, const char **mode) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + const NMPObject *obj; + IpoibInfo info = { -1, NULL }; + + obj = cache_lookup_link (platform, ifindex); + if (!obj) + return FALSE; + + if (parent) + *parent = obj->link.parent; + + if (_nl_link_parse_info_data (priv->nlh, + ifindex, + infiniband_info_data_parser, + &info) != 0) { + const char *iface = obj->link.name; + char *path, *contents = NULL; + + /* Fall back to reading sysfs */ + path = g_strdup_printf ("/sys/class/net/%s/mode", ASSERT_VALID_PATH_COMPONENT (iface)); + contents = nm_platform_sysctl_get (platform, path); + g_free (path); + if (!contents) + return FALSE; + + if (strstr (contents, "datagram")) + info.mode = "datagram"; + else if (strstr (contents, "connected")) + info.mode = "connected"; + g_free (contents); + + path = g_strdup_printf ("/sys/class/net/%s/pkey", ASSERT_VALID_PATH_COMPONENT (iface)); + contents = nm_platform_sysctl_get (platform, path); + g_free (path); + if (!contents) + return FALSE; + + info.p_key = (int) _nm_utils_ascii_str_to_int64 (contents, 16, 0, 0xFFFF, -1); + g_free (contents); + + if (info.p_key < 0) + return FALSE; + } + + if (p_key) + *p_key = info.p_key; + if (mode) + *mode = info.mode; + return TRUE; } static gboolean veth_get_properties (NMPlatform *platform, int ifindex, NMPlatformVethProperties *props) { const char *ifname; - gs_free struct ethtool_stats *stats = NULL; - int peer_ifindex_stat; + int peer_ifindex; - ifname = nm_platform_link_get_name (ifindex); + ifname = nm_platform_link_get_name (platform, ifindex); if (!ifname) return FALSE; - peer_ifindex_stat = ethtool_get_stringset_index (ifname, ETH_SS_STATS, "peer_ifindex"); - if (peer_ifindex_stat == -1) { - debug ("%s: peer_ifindex ethtool stat does not exist?", ifname); + peer_ifindex = nmp_utils_ethtool_get_peer_ifindex (ifname); + if (peer_ifindex <= 0) return FALSE; - } - stats = g_malloc0 (sizeof (*stats) + (peer_ifindex_stat + 1) * sizeof (guint64)); - stats->cmd = ETHTOOL_GSTATS; - stats->n_stats = peer_ifindex_stat + 1; - if (!ethtool_get (ifname, stats)) - return FALSE; - - props->peer = stats->data[peer_ifindex_stat]; + props->peer = peer_ifindex; return TRUE; } static gboolean -tun_get_properties (NMPlatform *platform, int ifindex, NMPlatformTunProperties *props) +tun_get_properties_ifname (NMPlatform *platform, const char *ifname, NMPlatformTunProperties *props) { - const char *ifname; char *path, *val; gboolean success = TRUE; @@ -2984,16 +3434,15 @@ tun_get_properties (NMPlatform *platform, int ifindex, NMPlatformTunProperties * props->owner = -1; props->group = -1; - ifname = nm_platform_link_get_name (ifindex); if (!ifname || !nm_utils_iface_valid_name (ifname)) return FALSE; ifname = ASSERT_VALID_PATH_COMPONENT (ifname); path = g_strdup_printf ("/sys/class/net/%s/owner", ifname); - val = nm_platform_sysctl_get (path); + val = nm_platform_sysctl_get (platform, path); g_free (path); if (val) { - props->owner = nm_utils_ascii_str_to_int64 (val, 10, -1, G_MAXINT64, -1); + props->owner = _nm_utils_ascii_str_to_int64 (val, 10, -1, G_MAXINT64, -1); if (errno) success = FALSE; g_free (val); @@ -3001,10 +3450,10 @@ tun_get_properties (NMPlatform *platform, int ifindex, NMPlatformTunProperties * success = FALSE; path = g_strdup_printf ("/sys/class/net/%s/group", ifname); - val = nm_platform_sysctl_get (path); + val = nm_platform_sysctl_get (platform, path); g_free (path); if (val) { - props->group = nm_utils_ascii_str_to_int64 (val, 10, -1, G_MAXINT64, -1); + props->group = _nm_utils_ascii_str_to_int64 (val, 10, -1, G_MAXINT64, -1); if (errno) success = FALSE; g_free (val); @@ -3012,12 +3461,12 @@ tun_get_properties (NMPlatform *platform, int ifindex, NMPlatformTunProperties * success = FALSE; path = g_strdup_printf ("/sys/class/net/%s/tun_flags", ifname); - val = nm_platform_sysctl_get (path); + val = nm_platform_sysctl_get (platform, path); g_free (path); if (val) { gint64 flags; - flags = nm_utils_ascii_str_to_int64 (val, 16, 0, G_MAXINT64, 0); + flags = _nm_utils_ascii_str_to_int64 (val, 16, 0, G_MAXINT64, 0); if (!errno) { #ifndef IFF_MULTI_QUEUE const int IFF_MULTI_QUEUE = 0x0100; @@ -3035,6 +3484,12 @@ tun_get_properties (NMPlatform *platform, int ifindex, NMPlatformTunProperties * return success; } +static gboolean +tun_get_properties (NMPlatform *platform, int ifindex, NMPlatformTunProperties *props) +{ + return tun_get_properties_ifname (platform, nm_platform_link_get_name (platform, ifindex), props); +} + static const struct nla_policy macvlan_info_policy[IFLA_MACVLAN_MAX + 1] = { [IFLA_MACVLAN_MODE] = { .type = NLA_U32 }, #ifdef MACVLAN_FLAG_NOPROMISC @@ -3084,20 +3539,20 @@ static gboolean macvlan_get_properties (NMPlatform *platform, int ifindex, NMPlatformMacvlanProperties *props) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - auto_nl_object struct rtnl_link *rtnllink = NULL; int err; + const NMPObject *obj; - rtnllink = link_get (platform, ifindex); - if (!rtnllink) + obj = cache_lookup_link (platform, ifindex); + if (!obj) return FALSE; - props->parent_ifindex = rtnl_link_get_link (rtnllink); + props->parent_ifindex = obj->link.parent; - err = nm_rtnl_link_parse_info_data (priv->nlh, ifindex, - macvlan_info_data_parser, props); + err = _nl_link_parse_info_data (priv->nlh, ifindex, + macvlan_info_data_parser, props); if (err != 0) { warning ("(%s) could not read properties: %s", - rtnl_link_get_name (rtnllink), nl_geterror (err)); + obj->link.name, nl_geterror (err)); } return (err == 0); } @@ -3224,11 +3679,11 @@ vxlan_get_properties (NMPlatform *platform, int ifindex, NMPlatformVxlanProperti NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); int err; - err = nm_rtnl_link_parse_info_data (priv->nlh, ifindex, - vxlan_info_data_parser, props); + err = _nl_link_parse_info_data (priv->nlh, ifindex, + vxlan_info_data_parser, props); if (err != 0) { - warning ("(%s) could not read properties: %s", - link_get_name (platform, ifindex), nl_geterror (err)); + warning ("(%s) could not read vxlan properties: %s", + nm_platform_link_get_name (platform, ifindex), nl_geterror (err)); } return (err == 0); } @@ -3278,11 +3733,11 @@ gre_get_properties (NMPlatform *platform, int ifindex, NMPlatformGreProperties * NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); int err; - err = nm_rtnl_link_parse_info_data (priv->nlh, ifindex, - gre_info_data_parser, props); + err = _nl_link_parse_info_data (priv->nlh, ifindex, + gre_info_data_parser, props); if (err != 0) { - warning ("(%s) could not read properties: %s", - link_get_name (platform, ifindex), nl_geterror (err)); + warning ("(%s) could not read gre properties: %s", + nm_platform_link_get_name (platform, ifindex), nl_geterror (err)); } return (err == 0); } @@ -3295,26 +3750,25 @@ wifi_get_wifi_data (NMPlatform *platform, int ifindex) wifi_data = g_hash_table_lookup (priv->wifi_data, GINT_TO_POINTER (ifindex)); if (!wifi_data) { - NMLinkType type; - const char *ifname; - - type = link_get_type (platform, ifindex); - ifname = link_get_name (platform, ifindex); - - if (type == NM_LINK_TYPE_WIFI) - wifi_data = wifi_utils_init (ifname, ifindex, TRUE); - else if (type == NM_LINK_TYPE_OLPC_MESH) { - /* The kernel driver now uses nl80211, but we force use of WEXT because - * the cfg80211 interactions are not quite ready to support access to - * mesh control through nl80211 just yet. - */ + const NMPlatformLink *pllink; + + pllink = nm_platform_link_get (platform, ifindex); + if (pllink) { + if (pllink->type == NM_LINK_TYPE_WIFI) + wifi_data = wifi_utils_init (pllink->name, ifindex, TRUE); + else if (pllink->type == NM_LINK_TYPE_OLPC_MESH) { + /* The kernel driver now uses nl80211, but we force use of WEXT because + * the cfg80211 interactions are not quite ready to support access to + * mesh control through nl80211 just yet. + */ #if HAVE_WEXT - wifi_data = wifi_wext_init (ifname, ifindex, FALSE); + wifi_data = wifi_wext_init (pllink->name, ifindex, FALSE); #endif - } + } - if (wifi_data) - g_hash_table_insert (priv->wifi_data, GINT_TO_POINTER (ifindex), wifi_data); + if (wifi_data) + g_hash_table_insert (priv->wifi_data, GINT_TO_POINTER (ifindex), wifi_data); + } } return wifi_data; @@ -3460,18 +3914,11 @@ mesh_set_ssid (NMPlatform *platform, int ifindex, const guint8 *ssid, gsize len) static gboolean link_get_wake_on_lan (NMPlatform *platform, int ifindex) { - NMLinkType type = link_get_type (platform, ifindex); - - if (type == NM_LINK_TYPE_ETHERNET) { - struct ethtool_wolinfo wol; + NMLinkType type = nm_platform_link_get_type (platform, ifindex); - memset (&wol, 0, sizeof (wol)); - wol.cmd = ETHTOOL_GWOL; - if (!ethtool_get (link_get_name (platform, ifindex), &wol)) - return FALSE; - - return wol.wolopts != 0; - } else if (type == NM_LINK_TYPE_WIFI) { + if (type == NM_LINK_TYPE_ETHERNET) + return nmp_utils_ethtool_get_wake_on_lan (nm_platform_link_get_name (platform, ifindex)); + else if (type == NM_LINK_TYPE_WIFI) { WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex); if (!wifi_data) @@ -3482,55 +3929,45 @@ link_get_wake_on_lan (NMPlatform *platform, int ifindex) return FALSE; } -/******************************************************************/ - static gboolean -_address_match (struct rtnl_addr *addr, int family, int ifindex) +link_get_driver_info (NMPlatform *platform, + int ifindex, + char **out_driver_name, + char **out_driver_version, + char **out_fw_version) { - g_return_val_if_fail (addr, FALSE); - - return rtnl_addr_get_family (addr) == family && - (ifindex == 0 || rtnl_addr_get_ifindex (addr) == ifindex); + return nmp_utils_ethtool_get_driver_info (nm_platform_link_get_name (platform, ifindex), + out_driver_name, + out_driver_version, + out_fw_version); } +/******************************************************************/ + static GArray * -ip4_address_get_all (NMPlatform *platform, int ifindex) +ipx_address_get_all (NMPlatform *platform, int ifindex, NMPObjectType obj_type) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - GArray *addresses; - NMPlatformIP4Address address; - struct nl_object *object; - addresses = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Address)); + nm_assert (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS)); - for (object = nl_cache_get_first (priv->address_cache); object; object = nl_cache_get_next (object)) { - if (_address_match ((struct rtnl_addr *) object, AF_INET, ifindex)) { - if (init_ip4_address (&address, (struct rtnl_addr *) object)) - g_array_append_val (addresses, address); - } - } + return nmp_cache_lookup_multi_to_array (priv->cache, + obj_type, + nmp_cache_id_init_addrroute_visible_by_ifindex (NMP_CACHE_ID_STATIC, + obj_type, + ifindex)); +} - return addresses; +static GArray * +ip4_address_get_all (NMPlatform *platform, int ifindex) +{ + return ipx_address_get_all (platform, ifindex, NMP_OBJECT_TYPE_IP4_ADDRESS); } static GArray * ip6_address_get_all (NMPlatform *platform, int ifindex) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - GArray *addresses; - NMPlatformIP6Address address; - struct nl_object *object; - - addresses = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP6Address)); - - for (object = nl_cache_get_first (priv->address_cache); object; object = nl_cache_get_next (object)) { - if (_address_match ((struct rtnl_addr *) object, AF_INET6, ifindex)) { - if (init_ip6_address (&address, (struct rtnl_addr *) object)) - g_array_append_val (addresses, address); - } - } - - return addresses; + return ipx_address_get_all (platform, ifindex, NMP_OBJECT_TYPE_IP6_ADDRESS); } #define IPV4LL_NETWORK (htonl (0xA9FE0000L)) @@ -3543,7 +3980,8 @@ ip4_is_link_local (const struct in_addr *src) } static struct nl_object * -build_rtnl_addr (int family, +build_rtnl_addr (NMPlatform *platform, + int family, int ifindex, gconstpointer addr, gconstpointer peer_addr, @@ -3553,10 +3991,10 @@ build_rtnl_addr (int family, guint flags, const char *label) { - auto_nl_object struct rtnl_addr *rtnladdr = _nm_rtnl_addr_alloc (ifindex); + auto_nl_object struct rtnl_addr *rtnladdr = _nl_rtnl_addr_alloc (ifindex); struct rtnl_addr *rtnladdr_copy; int addrlen = family == AF_INET ? sizeof (in_addr_t) : sizeof (struct in6_addr); - auto_nl_addr struct nl_addr *nladdr = _nm_nl_addr_build (family, addr, addrlen); + auto_nl_addr struct nl_addr *nladdr = _nl_addr_build (family, addr, addrlen); int nle; /* IP address */ @@ -3568,7 +4006,7 @@ build_rtnl_addr (int family, /* Tighten scope (IPv4 only) */ if (family == AF_INET && ip4_is_link_local (addr)) - rtnl_addr_set_scope (rtnladdr, rtnl_str2scope ("link")); + rtnl_addr_set_scope (rtnladdr, RT_SCOPE_LINK); /* IPv4 Broadcast address */ if (family == AF_INET) { @@ -3576,14 +4014,14 @@ build_rtnl_addr (int family, auto_nl_addr struct nl_addr *bcaddr = NULL; bcast = *((in_addr_t *) addr) | ~nm_utils_ip4_prefix_to_netmask (plen); - bcaddr = _nm_nl_addr_build (family, &bcast, addrlen); + bcaddr = _nl_addr_build (family, &bcast, addrlen); g_assert (bcaddr); rtnl_addr_set_broadcast (rtnladdr, bcaddr); } /* Peer/point-to-point address */ if (peer_addr) { - auto_nl_addr struct nl_addr *nlpeer = _nm_nl_addr_build (family, peer_addr, addrlen); + auto_nl_addr struct nl_addr *nlpeer = _nl_addr_build (family, peer_addr, addrlen); nle = rtnl_addr_set_peer (rtnladdr, nlpeer); if (nle && nle != -NLE_AF_NOSUPPORT) { @@ -3593,19 +4031,15 @@ build_rtnl_addr (int family, } } - rtnl_addr_set_prefixlen (rtnladdr, plen); - if (lifetime) { - /* note that here we set the relative timestamps (ticking from *now*). - * Contrary to the rtnl_addr objects from our cache, which have absolute - * timestamps (see _rtnl_addr_hack_lifetimes_rel_to_abs()). - * - * This is correct, because we only use build_rtnl_addr() for - * add_object(), delete_object() and cache search (ip_address_exists). */ + _nl_rtnl_addr_set_prefixlen (rtnladdr, plen); + if ( (lifetime != 0 && lifetime != NM_PLATFORM_LIFETIME_PERMANENT) + || (preferred != 0 && preferred != NM_PLATFORM_LIFETIME_PERMANENT)) { + /* note that here we set the relative timestamps (ticking from *now*). */ rtnl_addr_set_valid_lifetime (rtnladdr, lifetime); rtnl_addr_set_preferred_lifetime (rtnladdr, preferred); } if (flags) { - if ((flags & ~0xFF) && !check_support_kernel_extended_ifa_flags (nm_platform_get ())) { + if ((flags & ~0xFF) && !_support_kernel_extended_ifa_flags_get ()) { /* Older kernels don't accept unknown netlink attributes. * * With commit libnl commit 5206c050504f8676a24854519b9c351470fb7cc6, libnl will only set @@ -3625,6 +4059,48 @@ build_rtnl_addr (int family, return (struct nl_object *) rtnladdr_copy; } +struct nl_object * +_nmp_vt_cmd_plobj_to_nl_ip4_address (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only) +{ + const NMPlatformIP4Address *obj = (const NMPlatformIP4Address *) _obj; + guint32 lifetime, preferred; + + nmp_utils_lifetime_get (obj->timestamp, obj->lifetime, obj->preferred, + 0, 0, &lifetime, &preferred); + + return build_rtnl_addr (platform, + AF_INET, + obj->ifindex, + &obj->address, + obj->peer_address ? &obj->peer_address : NULL, + obj->plen, + lifetime, + preferred, + 0, + obj->label[0] ? obj->label : NULL); +} + +struct nl_object * +_nmp_vt_cmd_plobj_to_nl_ip6_address (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only) +{ + const NMPlatformIP6Address *obj = (const NMPlatformIP6Address *) _obj; + guint32 lifetime, preferred; + + nmp_utils_lifetime_get (obj->timestamp, obj->lifetime, obj->preferred, + 0, 0, &lifetime, &preferred); + + return build_rtnl_addr (platform, + AF_INET6, + obj->ifindex, + &obj->address, + !IN6_IS_ADDR_UNSPECIFIED (&obj->peer_address) ? &obj->peer_address : NULL, + obj->plen, + lifetime, + preferred, + 0, + NULL); +} + static gboolean ip4_address_add (NMPlatform *platform, int ifindex, @@ -3635,10 +4111,16 @@ ip4_address_add (NMPlatform *platform, guint32 preferred, const char *label) { - return add_object (platform, build_rtnl_addr (AF_INET, ifindex, &addr, - peer_addr ? &peer_addr : NULL, - plen, lifetime, preferred, 0, - label)); + NMPObject obj_needle; + auto_nl_object struct nl_object *nlo = NULL; + + nlo = build_rtnl_addr (platform, AF_INET, ifindex, &addr, + peer_addr ? &peer_addr : NULL, + plen, lifetime, preferred, 0, + label); + return do_add_addrroute (platform, + nmp_object_stackinit_id_ip4_address (&obj_needle, ifindex, addr, plen), + nlo); } static gboolean @@ -3651,169 +4133,114 @@ ip6_address_add (NMPlatform *platform, guint32 preferred, guint flags) { - return add_object (platform, build_rtnl_addr (AF_INET6, ifindex, &addr, - IN6_IS_ADDR_UNSPECIFIED (&peer_addr) ? NULL : &peer_addr, - plen, lifetime, preferred, flags, - NULL)); + NMPObject obj_needle; + auto_nl_object struct nl_object *nlo = NULL; + + nlo = build_rtnl_addr (platform, AF_INET6, ifindex, &addr, + IN6_IS_ADDR_UNSPECIFIED (&peer_addr) ? NULL : &peer_addr, + plen, lifetime, preferred, flags, + NULL); + return do_add_addrroute (platform, + nmp_object_stackinit_id_ip6_address (&obj_needle, ifindex, &addr, plen), + nlo); } static gboolean ip4_address_delete (NMPlatform *platform, int ifindex, in_addr_t addr, int plen, in_addr_t peer_address) { - return delete_object (platform, build_rtnl_addr (AF_INET, ifindex, &addr, peer_address ? &peer_address : NULL, plen, 0, 0, 0, NULL), TRUE); + NMPObject obj_needle; + + nmp_object_stackinit_id_ip4_address (&obj_needle, ifindex, addr, plen); + obj_needle.ip4_address.peer_address = peer_address; + return do_delete_object (platform, &obj_needle, NULL); } static gboolean ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, int plen) { - return delete_object (platform, build_rtnl_addr (AF_INET6, ifindex, &addr, NULL, plen, 0, 0, 0, NULL), TRUE); + NMPObject obj_needle; + + nmp_object_stackinit_id_ip6_address (&obj_needle, ifindex, &addr, plen); + return do_delete_object (platform, &obj_needle, NULL); } -static gboolean -ip_address_exists (NMPlatform *platform, int family, int ifindex, gconstpointer addr, int plen) +static const NMPlatformIP4Address * +ip4_address_get (NMPlatform *platform, int ifindex, in_addr_t addr, int plen) { - auto_nl_object struct nl_object *object = build_rtnl_addr (family, ifindex, addr, NULL, plen, 0, 0, 0, NULL); - auto_nl_object struct nl_object *cached_object = nl_cache_search (choose_cache (platform, object), object); + NMPObject obj_needle; + const NMPObject *obj; - return !!cached_object; + nmp_object_stackinit_id_ip4_address (&obj_needle, ifindex, addr, plen); + obj = nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_needle); + if (nmp_object_is_visible (obj)) + return &obj->ip4_address; + return NULL; } -static gboolean -ip4_address_exists (NMPlatform *platform, int ifindex, in_addr_t addr, int plen) +static const NMPlatformIP6Address * +ip6_address_get (NMPlatform *platform, int ifindex, struct in6_addr addr, int plen) { - return ip_address_exists (platform, AF_INET, ifindex, &addr, plen); -} + NMPObject obj_needle; + const NMPObject *obj; -static gboolean -ip6_address_exists (NMPlatform *platform, int ifindex, struct in6_addr addr, int plen) -{ - return ip_address_exists (platform, AF_INET6, ifindex, &addr, plen); + nmp_object_stackinit_id_ip6_address (&obj_needle, ifindex, &addr, plen); + obj = nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_needle); + if (nmp_object_is_visible (obj)) + return &obj->ip6_address; + return NULL; } -static gboolean -ip4_check_reinstall_device_route (NMPlatform *platform, int ifindex, const NMPlatformIP4Address *address, guint32 device_route_metric) +/******************************************************************/ + +static GArray * +ipx_route_get_all (NMPlatform *platform, int ifindex, NMPObjectType obj_type, NMPlatformGetRouteFlags flags) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - NMPlatformIP4Address addr_candidate; - NMPlatformIP4Route route_candidate; - struct nl_object *object; - guint32 device_network; - - for (object = nl_cache_get_first (priv->address_cache); object; object = nl_cache_get_next (object)) { - if (_address_match ((struct rtnl_addr *) object, AF_INET, 0)) { - if (init_ip4_address (&addr_candidate, (struct rtnl_addr *) object)) - if ( addr_candidate.plen == address->plen - && addr_candidate.address == address->address) { - /* If we already have the same address installed on any interface, - * we back off. - * Perform this check first, as we expect to have significantly less - * addresses to search. */ - return FALSE; - } - } - } + NMPCacheId cache_id; + const NMPlatformIPRoute *const* routes; + GArray *array; + const NMPClass *klass; + gboolean with_rtprot_kernel; + guint i, len; - device_network = nm_utils_ip4_address_clear_host_address (address->address, address->plen); - - for (object = nl_cache_get_first (priv->route_cache); object; object = nl_cache_get_next (object)) { - if (_route_match ((struct rtnl_route *) object, AF_INET, 0, TRUE)) { - if (init_ip4_route (&route_candidate, (struct rtnl_route *) object)) { - if ( route_candidate.network == device_network - && route_candidate.plen == address->plen - && ( route_candidate.metric == 0 - || route_candidate.metric == device_route_metric)) { - /* There is already any route with metric 0 or the metric we want to install - * for the same subnet. */ - return FALSE; - } - } - } - } + nm_assert (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); - return TRUE; -} + if (!NM_FLAGS_ANY (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT)) + flags |= NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT; -/******************************************************************/ + klass = nmp_class_from_type (obj_type); -static gboolean -_route_match (struct rtnl_route *rtnlroute, int family, int ifindex, gboolean include_proto_kernel) -{ - struct rtnl_nexthop *nexthop; + nmp_cache_id_init_routes_visible (&cache_id, + obj_type, + NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT), + NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT), + ifindex); - g_return_val_if_fail (rtnlroute, FALSE); + routes = (const NMPlatformIPRoute *const*) nmp_cache_lookup_multi (priv->cache, &cache_id, &len); - if (rtnl_route_get_type (rtnlroute) != RTN_UNICAST || - rtnl_route_get_table (rtnlroute) != RT_TABLE_MAIN || - rtnl_route_get_tos (rtnlroute) != 0 || - (!include_proto_kernel && rtnl_route_get_protocol (rtnlroute) == RTPROT_KERNEL) || - rtnl_route_get_family (rtnlroute) != family || - rtnl_route_get_nnexthops (rtnlroute) != 1 || - rtnl_route_get_flags (rtnlroute) & RTM_F_CLONED) - return FALSE; + array = g_array_sized_new (FALSE, FALSE, klass->sizeof_public, len); - if (ifindex == 0) - return TRUE; + with_rtprot_kernel = NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_RTPROT_KERNEL); + for (i = 0; i < len; i++) { + nm_assert (NMP_OBJECT_GET_CLASS (NMP_OBJECT_UP_CAST (routes[i])) == klass); - nexthop = rtnl_route_nexthop_n (rtnlroute, 0); - return rtnl_route_nh_get_ifindex (nexthop) == ifindex; + if ( with_rtprot_kernel + || routes[i]->source != NM_IP_CONFIG_SOURCE_RTPROT_KERNEL) + g_array_append_vals (array, routes[i], 1); + } + return array; } static GArray * -ip4_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteMode mode) +ip4_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteFlags flags) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - GArray *routes; - NMPlatformIP4Route route; - struct nl_object *object; - - g_return_val_if_fail (NM_IN_SET (mode, NM_PLATFORM_GET_ROUTE_MODE_ALL, NM_PLATFORM_GET_ROUTE_MODE_NO_DEFAULT, NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT), NULL); - - routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route)); - - for (object = nl_cache_get_first (priv->route_cache); object; object = nl_cache_get_next (object)) { - if (_route_match ((struct rtnl_route *) object, AF_INET, ifindex, FALSE)) { - if (_rtnl_route_is_default ((struct rtnl_route *) object)) { - if (mode == NM_PLATFORM_GET_ROUTE_MODE_NO_DEFAULT) - continue; - } else { - if (mode == NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT) - continue; - } - if (init_ip4_route (&route, (struct rtnl_route *) object)) - g_array_append_val (routes, route); - } - } - - return routes; + return ipx_route_get_all (platform, ifindex, NMP_OBJECT_TYPE_IP4_ROUTE, flags); } static GArray * -ip6_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteMode mode) +ip6_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteFlags flags) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - GArray *routes; - NMPlatformIP6Route route; - struct nl_object *object; - - g_return_val_if_fail (NM_IN_SET (mode, NM_PLATFORM_GET_ROUTE_MODE_ALL, NM_PLATFORM_GET_ROUTE_MODE_NO_DEFAULT, NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT), NULL); - - routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP6Route)); - - for (object = nl_cache_get_first (priv->route_cache); object; object = nl_cache_get_next (object)) { - if (_route_match ((struct rtnl_route *) object, AF_INET6, ifindex, FALSE)) { - if (_rtnl_route_is_default ((struct rtnl_route *) object)) { - if (mode == NM_PLATFORM_GET_ROUTE_MODE_NO_DEFAULT) - continue; - } else { - if (mode == NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT) - continue; - } - if (init_ip6_route (&route, (struct rtnl_route *) object)) - g_array_append_val (routes, route); - } - } - - return routes; + return ipx_route_get_all (platform, ifindex, NMP_OBJECT_TYPE_IP6_ROUTE, flags); } static void @@ -3846,26 +4273,26 @@ build_rtnl_route (int family, int ifindex, NMIPConfigSource source, int addrlen = (family == AF_INET) ? sizeof (in_addr_t) : sizeof (struct in6_addr); /* Workaround a libnl bug by using zero destination address length for default routes */ auto_nl_addr struct nl_addr *dst = NULL; - auto_nl_addr struct nl_addr *gw = gateway ? _nm_nl_addr_build (family, gateway, addrlen) : NULL; - auto_nl_addr struct nl_addr *pref_src_nl = pref_src ? _nm_nl_addr_build (family, pref_src, addrlen) : NULL; + auto_nl_addr struct nl_addr *gw = gateway ? _nl_addr_build (family, gateway, addrlen) : NULL; + auto_nl_addr struct nl_addr *pref_src_nl = pref_src ? _nl_addr_build (family, pref_src, addrlen) : NULL; /* There seem to be problems adding a route with non-zero host identifier. * Adding IPv6 routes is simply ignored, without error message. * In the IPv4 case, we got an error. Thus, we have to make sure, that * the address is sane. */ clear_host_address (family, network, plen, network_clean); - dst = _nm_nl_addr_build (family, network_clean, plen ? addrlen : 0); + dst = _nl_addr_build (family, network_clean, plen ? addrlen : 0); nl_addr_set_prefixlen (dst, plen); - rtnlroute = _nm_rtnl_route_alloc (); + rtnlroute = _nl_rtnl_route_alloc (); rtnl_route_set_table (rtnlroute, RT_TABLE_MAIN); rtnl_route_set_tos (rtnlroute, 0); rtnl_route_set_dst (rtnlroute, dst); rtnl_route_set_priority (rtnlroute, metric); rtnl_route_set_family (rtnlroute, family); - rtnl_route_set_protocol (rtnlroute, source_to_rtprot (source)); + rtnl_route_set_protocol (rtnlroute, _nm_ip_config_source_to_rtprot (source)); - nexthop = _nm_rtnl_route_nh_alloc (); + nexthop = _nl_rtnl_route_nh_alloc (); rtnl_route_nh_set_ifindex (nexthop, ifindex); if (gw && !nl_addr_iszero (gw)) rtnl_route_nh_set_gateway (nexthop, gw); @@ -3879,111 +4306,113 @@ build_rtnl_route (int family, int ifindex, NMIPConfigSource source, return (struct nl_object *) rtnlroute; } -static gboolean -ip4_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, - in_addr_t network, int plen, in_addr_t gateway, - guint32 pref_src, guint32 metric, guint32 mss) +struct nl_object * +_nmp_vt_cmd_plobj_to_nl_ip4_route (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only) { - return add_object (platform, build_rtnl_route (AF_INET, ifindex, source, &network, plen, &gateway, pref_src ? &pref_src : NULL, metric, mss)); + const NMPlatformIP4Route *obj = (const NMPlatformIP4Route *) _obj; + + return build_rtnl_route (AF_INET, + obj->ifindex, + obj->source, + &obj->network, + obj->plen, + &obj->gateway, + obj->pref_src ? &obj->pref_src : NULL, + obj->metric, + obj->mss); } -static gboolean -ip6_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, - struct in6_addr network, int plen, struct in6_addr gateway, - guint32 metric, guint32 mss) +struct nl_object * +_nmp_vt_cmd_plobj_to_nl_ip6_route (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only) { - metric = nm_utils_ip6_route_metric_normalize (metric); + const NMPlatformIP6Route *obj = (const NMPlatformIP6Route *) _obj; - return add_object (platform, build_rtnl_route (AF_INET6, ifindex, source, &network, plen, &gateway, NULL, metric, mss)); + return build_rtnl_route (AF_INET6, + obj->ifindex, + obj->source, + &obj->network, + obj->plen, + &obj->gateway, + NULL, + obj->metric, + obj->mss); } -static struct rtnl_route * -route_search_cache (struct nl_cache *cache, int family, int ifindex, const void *network, int plen, guint32 metric) +static gboolean +ip4_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, + in_addr_t network, int plen, in_addr_t gateway, + guint32 pref_src, guint32 metric, guint32 mss) { - guint32 network_clean[4], dst_clean[4]; - struct nl_object *object; - - clear_host_address (family, network, plen, network_clean); + NMPObject obj_needle; + auto_nl_object struct nl_object *nlo = NULL; - for (object = nl_cache_get_first (cache); object; object = nl_cache_get_next (object)) { - struct nl_addr *dst; - struct rtnl_route *rtnlroute = (struct rtnl_route *) object; - - if (!_route_match (rtnlroute, family, ifindex, FALSE)) - continue; - - if (metric != rtnl_route_get_priority (rtnlroute)) - continue; - - dst = rtnl_route_get_dst (rtnlroute); - if ( !dst - || nl_addr_get_family (dst) != family - || nl_addr_get_prefixlen (dst) != plen) - continue; - - /* plen = 0 means all host bits, so all bits should be cleared. - * Likewise if the binary address is not present or all zeros. - */ - if (plen == 0 || nl_addr_iszero (dst)) - memset (dst_clean, 0, sizeof (dst_clean)); - else - clear_host_address (family, nl_addr_get_binary_addr (dst), plen, dst_clean); - - if (memcmp (dst_clean, network_clean, - family == AF_INET ? sizeof (guint32) : sizeof (struct in6_addr)) != 0) - continue; - - rtnl_route_get (rtnlroute); - return rtnlroute; - } - return NULL; + nlo = build_rtnl_route (AF_INET, ifindex, source, &network, plen, &gateway, pref_src ? &pref_src : NULL, metric, mss); + return do_add_addrroute (platform, + nmp_object_stackinit_id_ip4_route (&obj_needle, ifindex, network, plen, metric), + nlo); } static gboolean -refresh_route (NMPlatform *platform, int family, int ifindex, const void *network, int plen, guint32 metric) +ip6_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, + struct in6_addr network, int plen, struct in6_addr gateway, + guint32 metric, guint32 mss) { - struct nl_cache *cache; - auto_nl_object struct rtnl_route *cached_object = NULL; + NMPObject obj_needle; + auto_nl_object struct nl_object *nlo = NULL; - cache = choose_cache_by_type (platform, family == AF_INET ? OBJECT_TYPE_IP4_ROUTE : OBJECT_TYPE_IP6_ROUTE); - cached_object = route_search_cache (cache, family, ifindex, network, plen, metric); + metric = nm_utils_ip6_route_metric_normalize (metric); - if (cached_object) - return refresh_object (platform, (struct nl_object *) cached_object, TRUE, NM_PLATFORM_REASON_INTERNAL); - return TRUE; + nlo = build_rtnl_route (AF_INET6, ifindex, source, &network, plen, &gateway, NULL, metric, mss); + return do_add_addrroute (platform, + nmp_object_stackinit_id_ip6_route (&obj_needle, ifindex, &network, plen, metric), + nlo); } static gboolean ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, int plen, guint32 metric) { + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); in_addr_t gateway = 0; - struct rtnl_route *cached_object; - struct nl_object *route = build_rtnl_route (AF_INET, ifindex, NM_IP_CONFIG_SOURCE_UNKNOWN, &network, plen, &gateway, NULL, metric, 0); + auto_nl_object struct nl_object *nlo = build_rtnl_route (AF_INET, ifindex, NM_IP_CONFIG_SOURCE_UNKNOWN, &network, plen, &gateway, NULL, metric, 0); uint8_t scope = RT_SCOPE_NOWHERE; - struct nl_cache *cache; + const NMPObject *obj; + NMPObject obj_needle; - g_return_val_if_fail (route, FALSE); + g_return_val_if_fail (nlo, FALSE); - cache = choose_cache_by_type (platform, OBJECT_TYPE_IP4_ROUTE); + nmp_object_stackinit_id_ip4_route (&obj_needle, ifindex, network, plen, metric); if (metric == 0) { /* Deleting an IPv4 route with metric 0 does not only delete an exectly matching route. * If no route with metric 0 exists, it might delete another route to the same destination. * For nm_platform_ip4_route_delete() we don't want this semantic. * - * Instead, re-fetch the route from kernel, and if that fails, there is nothing to do. - * On success, there is still a race that we might end up deleting the wrong route. */ - if (!refresh_object (platform, (struct nl_object *) route, FALSE, _NM_PLATFORM_REASON_CACHE_CHECK_INTERNAL)) { - rtnl_route_put ((struct rtnl_route *) route); - return TRUE; - } + * Instead, make sure that we have the most recent state and process all + * delayed actions (including re-reading data from netlink). */ + delayed_action_handle_all (platform, TRUE); } - /* when deleting an IPv4 route, several fields of the provided route must match. - * Lookup in the cache so that we hopefully get the right values. */ - cached_object = (struct rtnl_route *) nl_cache_search (cache, route); - if (!cached_object) - cached_object = route_search_cache (cache, AF_INET, ifindex, &network, plen, metric); + obj = nmp_cache_lookup_obj (priv->cache, &obj_needle); + + if (metric == 0 && !obj) { + /* hmm... we are about to delete an IP4 route with metric 0. We must only + * send the delete request if such a route really exists. Above we refreshed + * the platform cache, still no such route exists. + * + * Be extra careful and reload the routes. We must be sure that such a + * route doesn't exists, because when we add an IPv4 address, we immediately + * afterwards try to delete the kernel-added device route with metric 0. + * It might be, that we didn't yet get the notification about that route. + * + * FIXME: once our ip4_address_add() is sure that upon return we have + * the latest state from in the platform cache, we might save this + * additional expensive cache-resync. */ + do_request_one_type (platform, NMP_OBJECT_TYPE_IP4_ROUTE, TRUE); + + obj = nmp_cache_lookup_obj (priv->cache, &obj_needle); + if (!obj) + return TRUE; + } if (!_nl_has_capability (1 /* NL_CAPABILITY_ROUTE_BUILD_MSG_SET_SCOPE */)) { /* When searching for a matching IPv4 route to delete, the kernel @@ -4000,8 +4429,8 @@ ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, int plen * So, this workaround is only needed unless we have NL_CAPABILITY_ROUTE_BUILD_MSG_SET_SCOPE. **/ - if (cached_object) - scope = rtnl_route_get_scope (cached_object); + if (obj) + scope = nm_platform_route_scope_inv (obj->ip4_route.scope_inv); if (scope == RT_SCOPE_NOWHERE) { /* If we would set the scope to RT_SCOPE_NOWHERE, libnl would guess the scope. @@ -4010,7 +4439,7 @@ ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, int plen scope = RT_SCOPE_UNIVERSE; } } - rtnl_route_set_scope ((struct rtnl_route *) route, scope); + rtnl_route_set_scope ((struct rtnl_route *) nlo, scope); /* we only support routes with TOS zero. As such, delete_route() is also only able to delete * routes with tos==0. build_rtnl_route() already initializes tos properly. */ @@ -4022,179 +4451,51 @@ ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, int plen * pref_src: NULL */ - rtnl_route_put (cached_object); - return delete_object (platform, route, FALSE) && refresh_route (platform, AF_INET, ifindex, &network, plen, metric); + return do_delete_object (platform, &obj_needle, nlo); } static gboolean ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, int plen, guint32 metric) { struct in6_addr gateway = IN6ADDR_ANY_INIT; + auto_nl_object struct nl_object *nlo = NULL; + NMPObject obj_needle; metric = nm_utils_ip6_route_metric_normalize (metric); - return delete_object (platform, build_rtnl_route (AF_INET6, ifindex, NM_IP_CONFIG_SOURCE_UNKNOWN ,&network, plen, &gateway, NULL, metric, 0), FALSE) && - refresh_route (platform, AF_INET6, ifindex, &network, plen, metric); -} - -static gboolean -ip_route_exists (NMPlatform *platform, int family, int ifindex, gpointer network, int plen, guint32 metric) -{ - auto_nl_object struct nl_object *object = build_rtnl_route (family, ifindex, - NM_IP_CONFIG_SOURCE_UNKNOWN, - network, plen, NULL, NULL, metric, 0); - struct nl_cache *cache = choose_cache (platform, object); - auto_nl_object struct nl_object *cached_object = nl_cache_search (cache, object); - - if (!cached_object) - cached_object = (struct nl_object *) route_search_cache (cache, family, ifindex, network, plen, metric); - return !!cached_object; -} - -static gboolean -ip4_route_exists (NMPlatform *platform, int ifindex, in_addr_t network, int plen, guint32 metric) -{ - return ip_route_exists (platform, AF_INET, ifindex, &network, plen, metric); -} - -static gboolean -ip6_route_exists (NMPlatform *platform, int ifindex, struct in6_addr network, int plen, guint32 metric) -{ - metric = nm_utils_ip6_route_metric_normalize (metric); - - return ip_route_exists (platform, AF_INET6, ifindex, &network, plen, metric); -} - -/******************************************************************/ + nlo = build_rtnl_route (AF_INET6, ifindex, NM_IP_CONFIG_SOURCE_UNKNOWN ,&network, plen, &gateway, NULL, metric, 0); -/* Initialize the link cache while ensuring all links are of AF_UNSPEC, - * family (even though the kernel might set AF_BRIDGE for bridges). - * See also: _nl_link_family_unset() */ -static void -init_link_cache (NMPlatform *platform) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - struct nl_object *object = NULL; + nmp_object_stackinit_id_ip6_route (&obj_needle, ifindex, &network, plen, metric); - rtnl_link_alloc_cache (priv->nlh, AF_UNSPEC, &priv->link_cache); - - do { - for (object = nl_cache_get_first (priv->link_cache); object; object = nl_cache_get_next (object)) { - if (rtnl_link_get_family ((struct rtnl_link *)object) != AF_UNSPEC) - break; - } - - if (object) { - /* A non-AF_UNSPEC object encoutnered */ - struct nl_object *existing; - - nl_object_get (object); - nl_cache_remove (object); - rtnl_link_set_family ((struct rtnl_link *)object, AF_UNSPEC); - existing = nl_cache_search (priv->link_cache, object); - if (existing) - nl_object_put (existing); - else - nl_cache_add (priv->link_cache, object); - nl_object_put (object); - } - } while (object); + return do_delete_object (platform, &obj_needle, nlo); } -/* Calls announce_object with appropriate arguments for all objects - * which are not coherent between old and new caches and deallocates - * the old cache. */ -static void -cache_announce_changes (NMPlatform *platform, struct nl_cache *new, struct nl_cache *old) +static const NMPlatformIP4Route * +ip4_route_get (NMPlatform *platform, int ifindex, in_addr_t network, int plen, guint32 metric) { - struct nl_object *object; - - if (!old) - return; + NMPObject obj_needle; + const NMPObject *obj; - for (object = nl_cache_get_first (new); object; object = nl_cache_get_next (object)) { - struct nl_object *cached_object = nm_nl_cache_search (old, object); - - if (cached_object) { - ObjectType type = object_type_from_nl_object (object); - if (nm_nl_object_diff (type, object, cached_object)) - announce_object (platform, object, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_EXTERNAL); - nl_object_put (cached_object); - } else - announce_object (platform, object, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_EXTERNAL); - } - for (object = nl_cache_get_first (old); object; object = nl_cache_get_next (object)) { - struct nl_object *cached_object = nm_nl_cache_search (new, object); - if (cached_object) - nl_object_put (cached_object); - else - announce_object (platform, object, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_EXTERNAL); - } - - nl_cache_free (old); + nmp_object_stackinit_id_ip4_route (&obj_needle, ifindex, network, plen, metric); + obj = nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_needle); + if (nmp_object_is_visible (obj)) + return &obj->ip4_route; + return NULL; } -/* The cache should always avoid containing objects not handled by NM, like - * e.g. addresses of the AF_PHONET family. */ -static void -cache_remove_unknown (struct nl_cache *cache) +static const NMPlatformIP6Route * +ip6_route_get (NMPlatform *platform, int ifindex, struct in6_addr network, int plen, guint32 metric) { - GPtrArray *objects_to_remove = NULL; - struct nl_object *object; - - for (object = nl_cache_get_first (cache); object; object = nl_cache_get_next (object)) { - if (object_type_from_nl_object (object) == OBJECT_TYPE_UNKNOWN) { - if (!objects_to_remove) - objects_to_remove = g_ptr_array_new_with_free_func ((GDestroyNotify) nl_object_put); - nl_object_get (object); - g_ptr_array_add (objects_to_remove, object); - } - } - - if (objects_to_remove) { - guint i; - - for (i = 0; i < objects_to_remove->len; i++) - nl_cache_remove (g_ptr_array_index (objects_to_remove, i)); + NMPObject obj_needle; + const NMPObject *obj; - g_ptr_array_free (objects_to_remove, TRUE); - } -} - -/* Creates and populates the netlink object caches. Called upon platform init and - * when we run out of sync (out of buffer space, netlink congestion control). In case - * the caches already exist, it finds changed, added and removed objects, announces - * them and destroys the old caches. */ -static void -cache_repopulate_all (NMPlatform *platform) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - struct nl_cache *old_link_cache = priv->link_cache; - struct nl_cache *old_address_cache = priv->address_cache; - struct nl_cache *old_route_cache = priv->route_cache; - struct nl_object *object; - - debug ("platform: %spopulate platform cache", old_link_cache ? "re" : ""); - - /* Allocate new netlink caches */ - init_link_cache (platform); - rtnl_addr_alloc_cache (priv->nlh, &priv->address_cache); - rtnl_route_alloc_cache (priv->nlh, AF_UNSPEC, 0, &priv->route_cache); - g_assert (priv->link_cache && priv->address_cache && priv->route_cache); - - /* Remove all unknown objects from the caches */ - cache_remove_unknown (priv->link_cache); - cache_remove_unknown (priv->address_cache); - cache_remove_unknown (priv->route_cache); - - for (object = nl_cache_get_first (priv->address_cache); object; object = nl_cache_get_next (object)) { - _rtnl_addr_hack_lifetimes_rel_to_abs ((struct rtnl_addr *) object); - } + metric = nm_utils_ip6_route_metric_normalize (metric); - /* Make sure all changes we've missed are announced. */ - cache_announce_changes (platform, priv->link_cache, old_link_cache); - cache_announce_changes (platform, priv->address_cache, old_address_cache); - cache_announce_changes (platform, priv->route_cache, old_route_cache); + nmp_object_stackinit_id_ip6_route (&obj_needle, ifindex, &network, plen, metric); + obj = nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_needle); + if (nmp_object_is_visible (obj)) + return &obj->ip6_route; + return NULL; } /******************************************************************/ @@ -4208,10 +4509,9 @@ verify_source (struct nl_msg *msg, gpointer user_data) { struct ucred *creds = nlmsg_get_creds (msg); - if (!creds || creds->pid || creds->uid || creds->gid) { + if (!creds || creds->pid) { if (creds) - warning ("netlink: received non-kernel message (pid %d uid %d gid %d)", - creds->pid, creds->uid, creds->gid); + warning ("netlink: received non-kernel message (pid %d)", creds->pid); else warning ("netlink: received message without credentials"); return NL_STOP; @@ -4225,35 +4525,42 @@ event_handler (GIOChannel *channel, GIOCondition io_condition, gpointer user_data) { - NMPlatform *platform = NM_PLATFORM (user_data); + delayed_action_handle_all (NM_PLATFORM (user_data), TRUE); + return TRUE; +} + +static gboolean +event_handler_read_netlink_one (NMPlatform *platform) +{ NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); int nle; nle = nl_recvmsgs_default (priv->nlh_event); + + /* Work around a libnl bug fixed in 3.2.22 (375a6294) */ + if (nle == 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) + nle = -NLE_AGAIN; + if (nle < 0) switch (nle) { + case -NLE_AGAIN: + return FALSE; case -NLE_DUMP_INTR: - /* this most likely happens due to our request (RTM_GETADDR, AF_INET6, NLM_F_DUMP) - * to detect support for support_kernel_extended_ifa_flags. This is not critical - * and can happen easily. */ debug ("Uncritical failure to retrieve incoming events: %s (%d)", nl_geterror (nle), nle); break; case -NLE_NOMEM: warning ("Too many netlink events. Need to resynchronize platform cache"); /* Drain the event queue, we've lost events and are out of sync anyway and we'd * like to free up some space. We'll read in the status synchronously. */ - nl_socket_modify_cb (priv->nlh_event, NL_CB_VALID, NL_CB_DEFAULT, NULL, NULL); - do { - errno = 0; - - nle = nl_recvmsgs_default (priv->nlh_event); - - /* Work around a libnl bug fixed in 3.2.22 (375a6294) */ - if (nle == 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) - nle = -NLE_AGAIN; - } while (nle != -NLE_AGAIN); - nl_socket_modify_cb (priv->nlh_event, NL_CB_VALID, NL_CB_CUSTOM, event_notification, user_data); - cache_repopulate_all (platform); + _nl_sock_flush_data (priv->nlh_event); + priv->nlh_seq_expect = 0; + delayed_action_schedule (platform, + DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, + NULL); break; default: error ("Failed to retrieve incoming events: %s (%d)", nl_geterror (nle), nle); @@ -4262,6 +4569,71 @@ event_handler (GIOChannel *channel, return TRUE; } +static gboolean +event_handler_read_netlink_all (NMPlatform *platform, gboolean wait_for_acks) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + int r; + struct pollfd pfd; + gboolean any = FALSE; + gint64 timestamp = 0, now; + const int TIMEOUT = 250; + int timeout = 0; + guint32 wait_for_seq = 0; + + while (TRUE) { + while (event_handler_read_netlink_one (platform)) + any = TRUE; + + if (!wait_for_acks || priv->nlh_seq_expect == 0) { + if (wait_for_seq) + _LOGT ("read-netlink-all: ACK for sequence number %u received", priv->nlh_seq_expect); + return any; + } + + now = nm_utils_get_monotonic_timestamp_ms (); + if (wait_for_seq != priv->nlh_seq_expect) { + /* We are waiting for a new sequence number (or we will wait for the first time). + * Reset/start counting the overall wait time. */ + _LOGT ("read-netlink-all: wait for ACK for sequence number %u...", priv->nlh_seq_expect); + wait_for_seq = priv->nlh_seq_expect; + timestamp = now; + timeout = TIMEOUT; + } else { + if ((now - timestamp) >= TIMEOUT) { + /* timeout. Don't wait for this sequence number anymore. */ + break; + } + + /* readjust the wait-time. */ + timeout = TIMEOUT - (now - timestamp); + } + + memset (&pfd, 0, sizeof (pfd)); + pfd.fd = nl_socket_get_fd (priv->nlh_event); + pfd.events = POLLIN; + r = poll (&pfd, 1, timeout); + + if (r == 0) { + /* timeout. */ + break; + } + if (r < 0) { + int errsv = errno; + + if (errsv != EINTR) { + _LOGE ("read-netlink-all: poll failed with %s", strerror (errsv)); + return any; + } + /* Continue to read again, even if there might be nothing to read after EINTR. */ + } + } + + _LOGW ("read-netlink-all: timeout waiting for ACK to sequence number %u...", wait_for_seq); + priv->nlh_seq_expect = 0; + return any; +} + static struct nl_sock * setup_socket (gboolean event, gpointer user_data) { @@ -4278,7 +4650,8 @@ setup_socket (gboolean event, gpointer user_data) /* Dispatch event messages (event socket only) */ if (event) { nl_socket_modify_cb (sock, NL_CB_VALID, NL_CB_CUSTOM, event_notification, user_data); - nl_socket_disable_seq_check (sock); + nl_socket_modify_cb (sock, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, event_seq_check, user_data); + nl_socket_modify_err_cb (sock, NL_CB_CUSTOM, event_err, user_data); } nle = nl_connect (sock, NETLINK_ROUTE); @@ -4298,14 +4671,23 @@ setup_socket (gboolean event, gpointer user_data) /******************************************************************/ static void +cache_update_link_udev (NMPlatform *platform, int ifindex, GUdevDevice *udev_device) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + auto_nmp_obj NMPObject *obj_cache = NULL; + gboolean was_visible; + NMPCacheOpsType cache_op; + + cache_op = nmp_cache_update_link_udev (priv->cache, ifindex, udev_device, &obj_cache, &was_visible, cache_pre_hook, platform); + do_emit_signal (platform, obj_cache, cache_op, was_visible, NM_PLATFORM_REASON_INTERNAL); +} + +static void udev_device_added (NMPlatform *platform, GUdevDevice *udev_device) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - auto_nl_object struct rtnl_link *rtnllink = NULL; const char *ifname; int ifindex; - gboolean was_announceable = FALSE; ifname = g_udev_device_get_name (udev_device); if (!ifname) { @@ -4329,59 +4711,37 @@ udev_device_added (NMPlatform *platform, return; } - rtnllink = rtnl_link_get (priv->link_cache, ifindex); - if (rtnllink) - was_announceable = link_is_announceable (platform, rtnllink); - - g_hash_table_insert (priv->udev_devices, GINT_TO_POINTER (ifindex), - g_object_ref (udev_device)); + cache_update_link_udev (platform, ifindex, udev_device); +} - /* Announce devices only if they also have been discovered via Netlink. */ - if (rtnllink && link_is_announceable (platform, rtnllink)) - announce_object (platform, (struct nl_object *) rtnllink, was_announceable ? NM_PLATFORM_SIGNAL_CHANGED : NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_REASON_EXTERNAL); +static gboolean +_udev_device_removed_match_link (const NMPObject *obj, gpointer udev_device) +{ + return obj->_link.udev.device == udev_device; } static void udev_device_removed (NMPlatform *platform, GUdevDevice *udev_device) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - auto_nl_object struct rtnl_link *rtnllink = NULL; int ifindex = 0; - gboolean was_announceable = FALSE; if (g_udev_device_get_property (udev_device, "IFINDEX")) ifindex = g_udev_device_get_property_as_int (udev_device, "IFINDEX"); else { - GHashTableIter iter; - gpointer key, value; + const NMPObject *obj; - /* This should not happen, but just to be sure. - * If we can't get IFINDEX, go through the devices and - * compare the pointers. - */ - g_hash_table_iter_init (&iter, priv->udev_devices); - while (g_hash_table_iter_next (&iter, &key, &value)) { - if ((GUdevDevice *)value == udev_device) { - ifindex = GPOINTER_TO_INT (key); - break; - } - } + obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, + 0, NULL, FALSE, NM_LINK_TYPE_NONE, _udev_device_removed_match_link, udev_device); + if (obj) + ifindex = obj->link.ifindex; } debug ("udev-remove: IFINDEX=%d", ifindex); if (ifindex <= 0) return; - rtnllink = rtnl_link_get (priv->link_cache, ifindex); - if (rtnllink) - was_announceable = link_is_announceable (platform, rtnllink); - - g_hash_table_remove (priv->udev_devices, GINT_TO_POINTER (ifindex)); - - /* Announce device removal if it is no longer announceable. */ - if (was_announceable && !link_is_announceable (platform, rtnllink)) - announce_object (platform, (struct nl_object *) rtnllink, NM_PLATFORM_SIGNAL_REMOVED, NM_PLATFORM_REASON_EXTERNAL); + cache_update_link_udev (platform, ifindex, NULL); } static void @@ -4416,28 +4776,40 @@ handle_udev_event (GUdevClient *client, /******************************************************************/ static void -nm_linux_platform_init (NMLinuxPlatform *platform) +nm_linux_platform_init (NMLinuxPlatform *self) { + NMLinuxPlatformPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_LINUX_PLATFORM, NMLinuxPlatformPrivate); + + self->priv = priv; + + priv->delayed_deletion = g_hash_table_new_full ((GHashFunc) nmp_object_id_hash, + (GEqualFunc) nmp_object_id_equal, + (GDestroyNotify) nmp_object_unref, + (GDestroyNotify) nmp_object_unref); + priv->cache = nmp_cache_new (); + priv->delayed_action.list_master_connected = g_ptr_array_new (); + priv->delayed_action.list_refresh_link = g_ptr_array_new (); + priv->wifi_data = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) wifi_utils_deinit); } -static gboolean -setup (NMPlatform *platform) +static void +constructed (GObject *_object) { + NMPlatform *platform = NM_PLATFORM (_object); NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); const char *udev_subsys[] = { "net", NULL }; - GUdevEnumerator *enumerator; - GList *devices, *iter; int channel_flags; gboolean status; int nle; -#if HAVE_LIBNL_INET6_ADDR_GEN_MODE - struct nl_object *object; -#endif + GUdevEnumerator *enumerator; + GList *devices, *iter; + + _LOGD ("create"); /* Initialize netlink socket for requests */ priv->nlh = setup_socket (FALSE, platform); g_assert (priv->nlh); - debug ("Netlink socket for requests established: %d", nl_socket_get_local_port (priv->nlh)); + debug ("Netlink socket for requests established: port=%u, fd=%d", nl_socket_get_local_port (priv->nlh), nl_socket_get_fd (priv->nlh)); /* Initialize netlink socket for events */ priv->nlh_event = setup_socket (TRUE, platform); @@ -4454,7 +4826,7 @@ setup (NMPlatform *platform) RTNLGRP_IPV4_ROUTE, RTNLGRP_IPV6_ROUTE, 0); g_assert (!nle); - debug ("Netlink socket for events established: %d", nl_socket_get_local_port (priv->nlh_event)); + debug ("Netlink socket for events established: port=%u, fd=%d", nl_socket_get_local_port (priv->nlh_event), nl_socket_get_fd (priv->nlh_event)); priv->event_channel = g_io_channel_unix_new (nl_socket_get_fd (priv->nlh_event)); g_io_channel_set_encoding (priv->event_channel, NULL, NULL); @@ -4465,37 +4837,31 @@ setup (NMPlatform *platform) channel_flags | G_IO_FLAG_NONBLOCK, NULL); g_assert (status); priv->event_id = g_io_add_watch (priv->event_channel, - (EVENT_CONDITIONS | ERROR_CONDITIONS | DISCONNECT_CONDITIONS), - event_handler, platform); - - cache_repopulate_all (platform); - -#if HAVE_LIBNL_INET6_ADDR_GEN_MODE - /* Initial check for user IPv6LL support once the link cache is allocated - * and filled. If there are no links in the cache yet then we'll check - * when a new link shows up in announce_object(). - */ - object = nl_cache_get_first (priv->link_cache); - if (object) { - uint8_t mode; - - if (rtnl_link_inet6_get_addr_gen_mode ((struct rtnl_link *) object, &mode) == 0) - priv->support_user_ipv6ll = 1; - else - priv->support_user_ipv6ll = -1; - } -#else - priv->support_user_ipv6ll = -1; -#endif + (EVENT_CONDITIONS | ERROR_CONDITIONS | DISCONNECT_CONDITIONS), + event_handler, platform); /* Set up udev monitoring */ priv->udev_client = g_udev_client_new (udev_subsys); g_signal_connect (priv->udev_client, "uevent", G_CALLBACK (handle_udev_event), platform); - priv->udev_devices = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref); + + /* complete construction of the GObject instance before populating the cache. */ + G_OBJECT_CLASS (nm_linux_platform_parent_class)->constructed (_object); + + _LOGD ("populate platform cache"); + delayed_action_schedule (platform, + DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES | + DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, + NULL); + + delayed_action_handle_all (platform, FALSE); /* And read initial device list */ enumerator = g_udev_enumerator_new (priv->udev_client); g_udev_enumerator_add_match_subsystem (enumerator, "net"); + g_udev_enumerator_add_match_is_initialized (enumerator); devices = g_udev_enumerator_execute (enumerator); @@ -4505,16 +4871,26 @@ setup (NMPlatform *platform) } g_list_free (devices); g_object_unref (enumerator); +} - /* request all IPv6 addresses (hopeing that there is at least one), to check for - * the IFA_FLAGS attribute. */ - nle = nl_rtgen_request (priv->nlh_event, RTM_GETADDR, AF_INET6, NLM_F_DUMP); - if (nle < 0) - nm_log_warn (LOGD_PLATFORM, "Netlink error: requesting RTM_GETADDR failed with %s", nl_geterror (nle)); +static void +dispose (GObject *object) +{ + NMPlatform *platform = NM_PLATFORM (object); + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - priv->wifi_data = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) wifi_utils_deinit); + _LOGD ("dispose"); - return TRUE; + priv->delayed_action.flags = DELAYED_ACTION_TYPE_NONE; + g_ptr_array_set_size (priv->delayed_action.list_master_connected, 0); + g_ptr_array_set_size (priv->delayed_action.list_refresh_link, 0); + + nm_clear_g_source (&priv->delayed_action.idle_id); + + g_clear_pointer (&priv->prune_candidates, g_hash_table_unref); + g_clear_pointer (&priv->delayed_deletion, g_hash_table_unref); + + G_OBJECT_CLASS (nm_linux_platform_parent_class)->dispose (object); } static void @@ -4522,17 +4898,18 @@ nm_linux_platform_finalize (GObject *object) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (object); + nmp_cache_free (priv->cache); + + g_ptr_array_unref (priv->delayed_action.list_master_connected); + g_ptr_array_unref (priv->delayed_action.list_refresh_link); + /* Free netlink resources */ g_source_remove (priv->event_id); g_io_channel_unref (priv->event_channel); nl_socket_free (priv->nlh); nl_socket_free (priv->nlh_event); - nl_cache_free (priv->link_cache); - nl_cache_free (priv->address_cache); - nl_cache_free (priv->route_cache); g_object_unref (priv->udev_client); - g_hash_table_unref (priv->udev_devices); g_hash_table_unref (priv->wifi_data); G_OBJECT_CLASS (nm_linux_platform_parent_class)->finalize (object); @@ -4549,21 +4926,21 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) g_type_class_add_private (klass, sizeof (NMLinuxPlatformPrivate)); /* virtual methods */ + object_class->constructed = constructed; + object_class->dispose = dispose; object_class->finalize = nm_linux_platform_finalize; - platform_class->setup = setup; - platform_class->sysctl_set = sysctl_set; platform_class->sysctl_get = sysctl_get; platform_class->link_get = _nm_platform_link_get; + platform_class->link_get_by_ifname = _nm_platform_link_get_by_ifname; + platform_class->link_get_by_address = _nm_platform_link_get_by_address; platform_class->link_get_all = link_get_all; platform_class->link_add = link_add; platform_class->link_delete = link_delete; - platform_class->link_get_ifindex = link_get_ifindex; - platform_class->link_get_name = link_get_name; - platform_class->link_get_type = link_get_type; platform_class->link_get_type_name = link_get_type_name; + platform_class->link_get_unmanaged = link_get_unmanaged; platform_class->link_refresh = link_refresh; @@ -4571,28 +4948,26 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->link_set_down = link_set_down; platform_class->link_set_arp = link_set_arp; platform_class->link_set_noarp = link_set_noarp; - platform_class->link_is_up = link_is_up; - platform_class->link_is_connected = link_is_connected; - platform_class->link_uses_arp = link_uses_arp; - platform_class->link_get_user_ipv6ll_enabled = link_get_user_ipv6ll_enabled; + platform_class->link_get_udi = link_get_udi; + platform_class->link_get_udev_device = link_get_udev_device; + platform_class->link_set_user_ipv6ll_enabled = link_set_user_ipv6ll_enabled; - platform_class->link_get_address = link_get_address; platform_class->link_set_address = link_set_address; - platform_class->link_get_mtu = link_get_mtu; + platform_class->link_get_permanent_address = link_get_permanent_address; platform_class->link_set_mtu = link_set_mtu; platform_class->link_get_physical_port_id = link_get_physical_port_id; platform_class->link_get_dev_id = link_get_dev_id; platform_class->link_get_wake_on_lan = link_get_wake_on_lan; + platform_class->link_get_driver_info = link_get_driver_info; platform_class->link_supports_carrier_detect = link_supports_carrier_detect; platform_class->link_supports_vlans = link_supports_vlans; platform_class->link_enslave = link_enslave; platform_class->link_release = link_release; - platform_class->link_get_master = link_get_master; platform_class->master_set_option = master_set_option; platform_class->master_get_option = master_get_option; platform_class->slave_set_option = slave_set_option; @@ -4604,6 +4979,7 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->vlan_set_egress_map = vlan_set_egress_map; platform_class->infiniband_partition_add = infiniband_partition_add; + platform_class->infiniband_get_info = infiniband_get_info; platform_class->veth_get_properties = veth_get_properties; platform_class->tun_get_properties = tun_get_properties; @@ -4626,26 +5002,27 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->mesh_set_channel = mesh_set_channel; platform_class->mesh_set_ssid = mesh_set_ssid; + platform_class->ip4_address_get = ip4_address_get; + platform_class->ip6_address_get = ip6_address_get; platform_class->ip4_address_get_all = ip4_address_get_all; platform_class->ip6_address_get_all = ip6_address_get_all; platform_class->ip4_address_add = ip4_address_add; platform_class->ip6_address_add = ip6_address_add; platform_class->ip4_address_delete = ip4_address_delete; platform_class->ip6_address_delete = ip6_address_delete; - platform_class->ip4_address_exists = ip4_address_exists; - platform_class->ip6_address_exists = ip6_address_exists; - - platform_class->ip4_check_reinstall_device_route = ip4_check_reinstall_device_route; + platform_class->ip4_route_get = ip4_route_get; + platform_class->ip6_route_get = ip6_route_get; platform_class->ip4_route_get_all = ip4_route_get_all; platform_class->ip6_route_get_all = ip6_route_get_all; platform_class->ip4_route_add = ip4_route_add; platform_class->ip6_route_add = ip6_route_add; platform_class->ip4_route_delete = ip4_route_delete; platform_class->ip6_route_delete = ip6_route_delete; - platform_class->ip4_route_exists = ip4_route_exists; - platform_class->ip6_route_exists = ip6_route_exists; platform_class->check_support_kernel_extended_ifa_flags = check_support_kernel_extended_ifa_flags; platform_class->check_support_user_ipv6ll = check_support_user_ipv6ll; + + platform_class->process_events = process_events; } + diff --git a/src/platform/nm-linux-platform.h b/src/platform/nm-linux-platform.h index 6f2c1993..a9e2cd82 100644 --- a/src/platform/nm-linux-platform.h +++ b/src/platform/nm-linux-platform.h @@ -32,8 +32,12 @@ /******************************************************************/ +struct _NMLinuxPlatformPrivate; + typedef struct { NMPlatform parent; + + struct _NMLinuxPlatformPrivate *priv; } NMLinuxPlatform; typedef struct { diff --git a/src/platform/nm-platform-utils.c b/src/platform/nm-platform-utils.c new file mode 100644 index 00000000..d3c62bdd --- /dev/null +++ b/src/platform/nm-platform-utils.c @@ -0,0 +1,436 @@ +/* -*- 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 + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2015 Red Hat, Inc. + */ + +#include "nm-platform-utils.h" + +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <linux/ethtool.h> +#include <linux/sockios.h> +#include <linux/mii.h> +#include <linux/version.h> + +#include "gsystem-local-alloc.h" +#include "nm-utils.h" +#include "NetworkManagerUtils.h" +#include "nm-logging.h" + + +/****************************************************************** + * ethtool + ******************************************************************/ + +static gboolean +ethtool_get (const char *name, gpointer edata) +{ + struct ifreq ifr; + int fd; + + if (!name || !*name) + return FALSE; + + memset (&ifr, 0, sizeof (ifr)); + strncpy (ifr.ifr_name, name, IFNAMSIZ); + ifr.ifr_data = edata; + + fd = socket (PF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + nm_log_err (LOGD_PLATFORM, "ethtool: Could not open socket."); + return FALSE; + } + + if (ioctl (fd, SIOCETHTOOL, &ifr) < 0) { + nm_log_dbg (LOGD_PLATFORM, "ethtool: Request failed: %s", strerror (errno)); + close (fd); + return FALSE; + } + + close (fd); + return TRUE; +} + +static int +ethtool_get_stringset_index (const char *ifname, int stringset_id, const char *string) +{ + gs_free struct ethtool_sset_info *info = NULL; + gs_free struct ethtool_gstrings *strings = NULL; + guint32 len, i; + + info = g_malloc0 (sizeof (*info) + sizeof (guint32)); + info->cmd = ETHTOOL_GSSET_INFO; + info->reserved = 0; + info->sset_mask = 1ULL << stringset_id; + + if (!ethtool_get (ifname, info)) + return -1; + if (!info->sset_mask) + return -1; + + len = info->data[0]; + + strings = g_malloc0 (sizeof (*strings) + len * ETH_GSTRING_LEN); + strings->cmd = ETHTOOL_GSTRINGS; + strings->string_set = stringset_id; + strings->len = len; + if (!ethtool_get (ifname, strings)) + return -1; + + for (i = 0; i < len; i++) { + if (!strcmp ((char *) &strings->data[i * ETH_GSTRING_LEN], string)) + return i; + } + + return -1; +} + +gboolean +nmp_utils_ethtool_get_driver_info (const char *ifname, + char **out_driver_name, + char **out_driver_version, + char **out_fw_version) +{ + struct ethtool_drvinfo drvinfo = { 0 }; + + if (!ifname) + return FALSE; + + drvinfo.cmd = ETHTOOL_GDRVINFO; + if (!ethtool_get (ifname, &drvinfo)) + return FALSE; + + if (out_driver_name) + *out_driver_name = g_strdup (drvinfo.driver); + if (out_driver_version) + *out_driver_version = g_strdup (drvinfo.version); + if (out_fw_version) + *out_fw_version = g_strdup (drvinfo.fw_version); + + return TRUE; +} + +gboolean +nmp_utils_ethtool_get_permanent_address (const char *ifname, + guint8 *buf, + size_t *length) +{ + gs_free struct ethtool_perm_addr *epaddr = NULL; + + if (!ifname) + return FALSE; + + epaddr = g_malloc0 (sizeof (*epaddr) + NM_UTILS_HWADDR_LEN_MAX); + epaddr->cmd = ETHTOOL_GPERMADDR; + epaddr->size = NM_UTILS_HWADDR_LEN_MAX; + + if (!ethtool_get (ifname, epaddr)) + return FALSE; + + g_assert (epaddr->size <= NM_UTILS_HWADDR_LEN_MAX); + memcpy (buf, epaddr->data, epaddr->size); + *length = epaddr->size; + return TRUE; +} + +gboolean +nmp_utils_ethtool_supports_carrier_detect (const char *ifname) +{ + struct ethtool_cmd edata = { .cmd = ETHTOOL_GLINK }; + + /* We ignore the result. If the ETHTOOL_GLINK call succeeded, then we + * assume the device supports carrier-detect, otherwise we assume it + * doesn't. + */ + return ethtool_get (ifname, &edata); +} + +gboolean +nmp_utils_ethtool_supports_vlans (const char *ifname) +{ + gs_free struct ethtool_gfeatures *features = NULL; + int idx, block, bit, size; + + if (!ifname) + return FALSE; + + idx = ethtool_get_stringset_index (ifname, ETH_SS_FEATURES, "vlan-challenged"); + if (idx == -1) { + nm_log_dbg (LOGD_PLATFORM, "ethtool: vlan-challenged ethtool feature does not exist for %s?", ifname); + return FALSE; + } + + block = idx / 32; + bit = idx % 32; + size = block + 1; + + features = g_malloc0 (sizeof (*features) + size * sizeof (struct ethtool_get_features_block)); + features->cmd = ETHTOOL_GFEATURES; + features->size = size; + + if (!ethtool_get (ifname, features)) + return FALSE; + + return !(features->features[block].active & (1 << bit)); +} + +int +nmp_utils_ethtool_get_peer_ifindex (const char *ifname) +{ + gs_free struct ethtool_stats *stats = NULL; + int peer_ifindex_stat; + + if (!ifname) + return 0; + + peer_ifindex_stat = ethtool_get_stringset_index (ifname, ETH_SS_STATS, "peer_ifindex"); + if (peer_ifindex_stat == -1) { + nm_log_dbg (LOGD_PLATFORM, "ethtool: peer_ifindex stat for %s does not exist?", ifname); + return FALSE; + } + + stats = g_malloc0 (sizeof (*stats) + (peer_ifindex_stat + 1) * sizeof (guint64)); + stats->cmd = ETHTOOL_GSTATS; + stats->n_stats = peer_ifindex_stat + 1; + if (!ethtool_get (ifname, stats)) + return 0; + + return stats->data[peer_ifindex_stat]; +} + +gboolean +nmp_utils_ethtool_get_wake_on_lan (const char *ifname) +{ + struct ethtool_wolinfo wol; + + if (!ifname) + return FALSE; + + memset (&wol, 0, sizeof (wol)); + wol.cmd = ETHTOOL_GWOL; + if (!ethtool_get (ifname, &wol)) + return FALSE; + + return wol.wolopts != 0; +} + +gboolean +nmp_utils_ethtool_get_link_speed (const char *ifname, guint32 *out_speed) +{ + struct ethtool_cmd edata = { + .cmd = ETHTOOL_GSET, + }; + guint32 speed; + + if (!ethtool_get (ifname, &edata)) + return FALSE; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) + speed = edata.speed; +#else + speed = ethtool_cmd_speed (&edata); +#endif + if (speed == G_MAXUINT16 || speed == G_MAXUINT32) + speed = 0; + + if (out_speed) + *out_speed = speed; + return TRUE; +} + +/****************************************************************** + * mii + ******************************************************************/ + +gboolean +nmp_utils_mii_supports_carrier_detect (const char *ifname) +{ + int fd, errsv; + struct ifreq ifr; + struct mii_ioctl_data *mii; + gboolean supports_mii = FALSE; + + if (!ifname) + return FALSE; + + fd = socket (PF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + nm_log_err (LOGD_PLATFORM, "mii: couldn't open control socket (%s)", ifname); + return FALSE; + } + + memset (&ifr, 0, sizeof (struct ifreq)); + strncpy (ifr.ifr_name, ifname, IFNAMSIZ); + + errno = 0; + if (ioctl (fd, SIOCGMIIPHY, &ifr) < 0) { + errsv = errno; + nm_log_dbg (LOGD_PLATFORM, "mii: SIOCGMIIPHY failed: %s (%d) (%s)", strerror (errsv), errsv, ifname); + goto out; + } + + /* 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 (fd, SIOCGMIIREG, &ifr) == 0) { + nm_log_dbg (LOGD_PLATFORM, "mii: SIOCGMIIREG result 0x%X (%s)", mii->val_out, ifname); + supports_mii = TRUE; + } else { + errsv = errno; + nm_log_dbg (LOGD_PLATFORM, "mii: SIOCGMIIREG failed: %s (%d) (%s)", strerror (errsv), errsv, ifname); + } + +out: + close (fd); + nm_log_dbg (LOGD_PLATFORM, "mii: MII %s supported (%s)", supports_mii ? "is" : "not", ifname); + return supports_mii; +} + +/****************************************************************** + * udev + ******************************************************************/ + +const char * +nmp_utils_udev_get_driver (GUdevDevice *device) +{ + GUdevDevice *parent = NULL, *grandparent = NULL; + const char *driver, *subsys; + + driver = g_udev_device_get_driver (device); + if (driver) + goto out; + + /* Try the parent */ + parent = g_udev_device_get_parent (device); + if (parent) { + driver = g_udev_device_get_driver (parent); + if (!driver) { + /* Try the grandparent if it's an ibmebus device or if the + * subsys is NULL which usually indicates some sort of + * platform device like a 'gadget' net interface. + */ + subsys = g_udev_device_get_subsystem (parent); + if ( (g_strcmp0 (subsys, "ibmebus") == 0) + || (subsys == NULL)) { + grandparent = g_udev_device_get_parent (parent); + if (grandparent) + driver = g_udev_device_get_driver (grandparent); + } + } + } + g_clear_object (&parent); + g_clear_object (&grandparent); + +out: + /* Intern the string so we don't have to worry about memory + * management in NMPlatformLink. */ + return g_intern_string (driver); +} + +/****************************************************************** + * utils + ******************************************************************/ + +/** + * Takes a pair @timestamp and @duration, and returns the remaining duration based + * on the new timestamp @now. + */ +guint32 +nmp_utils_lifetime_rebase_relative_time_on_now (guint32 timestamp, + guint32 duration, + guint32 now, + guint32 padding) +{ + gint64 t; + + if (duration == NM_PLATFORM_LIFETIME_PERMANENT) + return NM_PLATFORM_LIFETIME_PERMANENT; + + if (timestamp == 0) { + /* if the @timestamp is zero, assume it was just left unset and that the relative + * @duration starts counting from @now. This is convenient to construct an address + * and print it in nm_platform_ip4_address_to_string(). + * + * In general it does not make sense to set the @duration without anchoring at + * @timestamp because you don't know the absolute expiration time when looking + * at the address at a later moment. */ + timestamp = now; + } + + /* For timestamp > now, just accept it and calculate the expected(?) result. */ + t = (gint64) timestamp + (gint64) duration - (gint64) now; + + /* Optional padding to avoid potential races. */ + t += (gint64) padding; + + if (t <= 0) + return 0; + if (t >= NM_PLATFORM_LIFETIME_PERMANENT) + return NM_PLATFORM_LIFETIME_PERMANENT - 1; + return t; +} + +gboolean +nmp_utils_lifetime_get (guint32 timestamp, + guint32 lifetime, + guint32 preferred, + guint32 now, + guint32 padding, + guint32 *out_lifetime, + guint32 *out_preferred) +{ + guint32 t_lifetime, t_preferred; + + if (lifetime == 0) { + *out_lifetime = NM_PLATFORM_LIFETIME_PERMANENT; + *out_preferred = NM_PLATFORM_LIFETIME_PERMANENT; + + /* We treat lifetime==0 as permanent addresses to allow easy creation of such addresses + * (without requiring to set the lifetime fields to NM_PLATFORM_LIFETIME_PERMANENT). + * In that case we also expect that the other fields (timestamp and preferred) are left unset. */ + g_return_val_if_fail (timestamp == 0 && preferred == 0, TRUE); + } else { + if (!now) + now = nm_utils_get_monotonic_timestamp_s (); + t_lifetime = nmp_utils_lifetime_rebase_relative_time_on_now (timestamp, lifetime, now, padding); + if (!t_lifetime) { + *out_lifetime = 0; + *out_preferred = 0; + return FALSE; + } + t_preferred = nmp_utils_lifetime_rebase_relative_time_on_now (timestamp, preferred, now, padding); + + *out_lifetime = t_lifetime; + *out_preferred = MIN (t_preferred, t_lifetime); + + /* Assert that non-permanent addresses have a (positive) @timestamp. nmp_utils_lifetime_rebase_relative_time_on_now() + * treats addresses with timestamp 0 as *now*. Addresses passed to _address_get_lifetime() always + * should have a valid @timestamp, otherwise on every re-sync, their lifetime will be extended anew. + */ + g_return_val_if_fail ( timestamp != 0 + || ( lifetime == NM_PLATFORM_LIFETIME_PERMANENT + && preferred == NM_PLATFORM_LIFETIME_PERMANENT), TRUE); + g_return_val_if_fail (t_preferred <= t_lifetime, TRUE); + } + return TRUE; +} + + diff --git a/src/platform/nm-platform-utils.h b/src/platform/nm-platform-utils.h new file mode 100644 index 00000000..557de844 --- /dev/null +++ b/src/platform/nm-platform-utils.h @@ -0,0 +1,66 @@ +/* -*- 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 + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2015 Red Hat, Inc. + */ + +#ifndef __NM_PLATFORM_UTILS_H__ +#define __NM_PLATFORM_UTILS_H__ + +#include "config.h" + +#include <gudev/gudev.h> + +#include "nm-platform.h" + + +const char *nmp_utils_ethtool_get_driver (const char *ifname); +gboolean nmp_utils_ethtool_supports_carrier_detect (const char *ifname); +gboolean nmp_utils_ethtool_supports_vlans (const char *ifname); +int nmp_utils_ethtool_get_peer_ifindex (const char *ifname); +gboolean nmp_utils_ethtool_get_wake_on_lan (const char *ifname); +gboolean nmp_utils_ethtool_get_link_speed (const char *ifname, guint32 *out_speed); + +gboolean nmp_utils_ethtool_get_driver_info (const char *ifname, + char **out_driver_name, + char **out_driver_version, + char **out_fw_version); + +gboolean nmp_utils_ethtool_get_permanent_address (const char *ifname, + guint8 *buf, + size_t *length); + + +gboolean nmp_utils_mii_supports_carrier_detect (const char *ifname); + + +const char *nmp_utils_udev_get_driver (GUdevDevice *device); + +guint32 nmp_utils_lifetime_rebase_relative_time_on_now (guint32 timestamp, + guint32 duration, + guint32 now, + guint32 padding); + +gboolean nmp_utils_lifetime_get (guint32 timestamp, + guint32 lifetime, + guint32 preferred, + guint32 now, + guint32 padding, + guint32 *out_lifetime, + guint32 *out_preferred); + +#endif /* __NM_PLATFORM_UTILS_H__ */ diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index ba7708d6..b6d87ff9 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -27,16 +27,59 @@ #include <arpa/inet.h> #include <string.h> #include <netlink/route/addr.h> +#include <netlink/route/rtnl.h> #include "gsystem-local-alloc.h" #include "NetworkManagerUtils.h" #include "nm-utils.h" #include "nm-platform.h" +#include "nm-platform-utils.h" #include "NetworkManagerUtils.h" #include "nm-logging.h" #include "nm-enum-types.h" +#include "nm-core-internal.h" + +#define ADDRESS_LIFETIME_PADDING 5 + +G_STATIC_ASSERT (sizeof ( ((NMPlatformLink *) NULL)->addr.data ) == NM_UTILS_HWADDR_LEN_MAX); + +#define _LOG_DOMAIN LOGD_PLATFORM +#define _LOG_PREFIX_NAME "platform" + +#define _LOG(level, domain, self, ...) \ + G_STMT_START { \ + const NMLogLevel __level = (level); \ + const NMLogDomain __domain = (domain); \ + \ + if (nm_logging_enabled (__level, __domain)) { \ + char __prefix[32]; \ + const char *__p_prefix = _LOG_PREFIX_NAME; \ + const void *const __self = (self); \ + \ + if (__self && __self != nm_platform_try_get ()) { \ + g_snprintf (__prefix, sizeof (__prefix), "%s[%p]", _LOG_PREFIX_NAME, __self); \ + __p_prefix = __prefix; \ + } \ + _nm_log (__level, __domain, 0, \ + "%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ + __p_prefix _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ + } \ + } G_STMT_END +#define _LOG_LEVEL_ENABLED(level, domain) \ + ( nm_logging_enabled ((level), (domain)) ) + +#ifdef NM_MORE_LOGGING +#define _LOGT_ENABLED() _LOG_LEVEL_ENABLED (LOGL_TRACE, _LOG_DOMAIN) +#define _LOGT(...) _LOG (LOGL_TRACE, _LOG_DOMAIN, self, __VA_ARGS__) +#else +#define _LOGT_ENABLED() FALSE +#define _LOGT(...) G_STMT_START { if (FALSE) { _LOG (LOGL_TRACE, _LOG_DOMAIN, self, __VA_ARGS__); } } G_STMT_END +#endif -#define debug(...) nm_log_dbg (LOGD_PLATFORM, __VA_ARGS__) +#define _LOGD(...) _LOG (LOGL_DEBUG, _LOG_DOMAIN, self, __VA_ARGS__) +#define _LOGI(...) _LOG (LOGL_INFO , _LOG_DOMAIN, self, __VA_ARGS__) +#define _LOGW(...) _LOG (LOGL_WARN , _LOG_DOMAIN, self, __VA_ARGS__) +#define _LOGE(...) _LOG (LOGL_ERR , _LOG_DOMAIN, self, __VA_ARGS__) #define NM_PLATFORM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_PLATFORM, NMPlatformPrivate)) @@ -54,19 +97,46 @@ enum { static guint signals[LAST_SIGNAL] = { 0 }; +enum { + PROP_0, + PROP_REGISTER_SINGLETON, + LAST_PROP, +}; + +typedef struct { + gboolean register_singleton; +} NMPlatformPrivate; + /******************************************************************/ /* Singleton NMPlatform subclass instance and cached class object */ -static NMPlatform *platform = NULL; -static NMPlatformClass *klass = NULL; +NM_DEFINE_SINGLETON_INSTANCE (NMPlatform); + +NM_DEFINE_SINGLETON_WEAK_REF (NMPlatform); + +/* Just always initialize a @klass instance. NM_PLATFORM_GET_CLASS() + * is only a plain read on the self instance, which the compiler + * like can optimize out. + */ +#define _CHECK_SELF_VOID(self, klass) \ + NMPlatformClass *klass; \ + do { \ + g_return_if_fail (NM_IS_PLATFORM (self)); \ + klass = NM_PLATFORM_GET_CLASS (self); \ + (void) klass; \ + } while (0) + +#define _CHECK_SELF(self, klass, err_val) \ + NMPlatformClass *klass; \ + do { \ + g_return_val_if_fail (NM_IS_PLATFORM (self), err_val); \ + klass = NM_PLATFORM_GET_CLASS (self); \ + (void) klass; \ + } while (0) /** * nm_platform_setup: - * @type: The #GType for a subclass of #NMPlatform - * - * Do not use this function directly, it is intended to be called by - * NMPlatform subclasses. For the linux platform initialization use - * nm_linux_platform_setup() instead. + * @instance: the #NMPlatform instance * * Failing to set up #NMPlatform singleton results in a fatal error, * as well as trying to initialize it multiple times without freeing @@ -74,122 +144,82 @@ static NMPlatformClass *klass = NULL; * * NetworkManager will typically use only one platform object during * its run. Test programs might want to switch platform implementations, - * though. This is done with a combination of nm_platform_free() and - * nm_*_platform_setup(). + * though. */ void -nm_platform_setup (GType type) +nm_platform_setup (NMPlatform *instance) { - gboolean status; + g_return_if_fail (NM_IS_PLATFORM (instance)); + g_return_if_fail (!singleton_instance); - g_assert (platform == NULL); + singleton_instance = instance; - platform = g_object_new (type, NULL); - g_assert (NM_IS_PLATFORM (platform)); + nm_singleton_instance_weak_ref_register (); - klass = NM_PLATFORM_GET_CLASS (platform); - g_assert (klass->setup); - - status = klass->setup (platform); - g_assert (status); -} - -/** - * nm_platform_free: - * - * Free #NMPlatform singleton created by nm_*_platform_setup(). - */ -void -nm_platform_free (void) -{ - g_assert (platform); - - g_object_unref (platform); - platform = NULL; + nm_log_dbg (LOGD_CORE, "setup NMPlatform singleton (%p, %s)", instance, G_OBJECT_TYPE_NAME (instance)); } /** * nm_platform_get: + * @self: platform instance * * Retrieve #NMPlatform singleton. Use this whenever you want to connect to - * #NMPlatform signals. It is an error to call it before nm_*_platform_setup() - * or after nm_platform_free(). + * #NMPlatform signals. It is an error to call it before nm_platform_setup(). * * Returns: (transfer none): The #NMPlatform singleton reference. */ NMPlatform * -nm_platform_get (void) +nm_platform_get () { - g_assert (platform); + g_assert (singleton_instance); - return platform; + return singleton_instance; } -/******************************************************************/ - -/** - * nm_platform_set_error: - * @error: The error code - * - * Convenience function to falsify platform->error. It can be used for example - * by functions that want to save the error, execute some operations and - * restore it. - */ -void nm_platform_set_error (NMPlatformError error) +NMPlatform * +nm_platform_try_get (void) { - platform->error = error; + return singleton_instance; } -/** - * nm_platform_get_error: - * - * Convenience function to quickly retrieve the error code of the last - * operation. - * - * Returns: Integer error code. - */ -NMPlatformError -nm_platform_get_error (void) -{ - g_assert (platform); - - return platform->error; -} +/******************************************************************/ /** - * nm_platform_get_error_message: + * nm_platform_error_to_string: + * @error_code: the error code to stringify. * - * Returns: Static human-readable string for the error. Don't free. + * Returns: A string representation of the error. + * For negative numbers, this function interprets + * the code as -errno. */ const char * -nm_platform_get_error_msg (void) -{ - g_assert (platform); - - switch (platform->error) { - case NM_PLATFORM_ERROR_NONE: - return "unknown error"; +nm_platform_error_to_string (NMPlatformError error) +{ + switch (error) { + case NM_PLATFORM_ERROR_SUCCESS: + return "success"; + case NM_PLATFORM_ERROR_BUG: + return "bug"; + case NM_PLATFORM_ERROR_UNSPECIFIED: + return "unspecified"; case NM_PLATFORM_ERROR_NOT_FOUND: - return "object not found"; + return "not-found"; case NM_PLATFORM_ERROR_EXISTS: - return "object already exists"; + return "exists"; case NM_PLATFORM_ERROR_WRONG_TYPE: - return "object is wrong type"; + return "wrong-type"; case NM_PLATFORM_ERROR_NOT_SLAVE: - return "link not a slave"; + return "not-slave"; case NM_PLATFORM_ERROR_NO_FIRMWARE: - return "firmware not found"; + return "no-firmware"; default: - return "invalid error number"; + if (error < 0) + return g_strerror (- ((int) error)); + return "unknown"; } } -static void -reset_error (void) -{ - g_assert (platform); - platform->error = NM_PLATFORM_ERROR_NONE; -} +/******************************************************************/ #define IFA_F_MANAGETEMPADDR_STR "mngtmpaddr" #define IFA_F_NOPREFIXROUTE_STR "noprefixroute" @@ -208,35 +238,53 @@ nm_platform_check_support_libnl_extended_ifa_flags () } gboolean -nm_platform_check_support_kernel_extended_ifa_flags () +nm_platform_check_support_kernel_extended_ifa_flags (NMPlatform *self) { - g_return_val_if_fail (NM_IS_PLATFORM (platform), FALSE); + _CHECK_SELF (self, klass, FALSE); if (!klass->check_support_kernel_extended_ifa_flags) return FALSE; - return klass->check_support_kernel_extended_ifa_flags (platform); + return klass->check_support_kernel_extended_ifa_flags (self); } gboolean -nm_platform_check_support_user_ipv6ll (void) +nm_platform_check_support_user_ipv6ll (NMPlatform *self) { static int supported = -1; - g_return_val_if_fail (NM_IS_PLATFORM (platform), FALSE); + _CHECK_SELF (self, klass, FALSE); if (!klass->check_support_user_ipv6ll) return FALSE; if (supported < 0) - supported = klass->check_support_user_ipv6ll (platform) ? 1 : 0; + supported = klass->check_support_user_ipv6ll (self) ? 1 : 0; return !!supported; } +/** + * nm_platform_process_events: + * @self: platform instance + * + * Process pending events or handle pending delayed-actions. + * Effectively, this reads the netlink socket and processes + * new netlink messages. Possibly it will raise change signals. + */ +void +nm_platform_process_events (NMPlatform *self) +{ + _CHECK_SELF_VOID (self, klass); + + if (klass->process_events) + klass->process_events (self); +} + /******************************************************************/ /** * nm_platform_sysctl_set: + * @self: platform instance * @path: Absolute option path * @value: Value to write * @@ -247,23 +295,25 @@ nm_platform_check_support_user_ipv6ll (void) * Returns: %TRUE on success. */ gboolean -nm_platform_sysctl_set (const char *path, const char *value) +nm_platform_sysctl_set (NMPlatform *self, const char *path, const char *value) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (path, FALSE); g_return_val_if_fail (value, FALSE); g_return_val_if_fail (klass->sysctl_set, FALSE); - return klass->sysctl_set (platform, path, value); + return klass->sysctl_set (self, path, value); } gboolean -nm_platform_sysctl_set_ip6_hop_limit_safe (const char *iface, int value) +nm_platform_sysctl_set_ip6_hop_limit_safe (NMPlatform *self, const char *iface, int value) { const char *path; gint64 cur; + _CHECK_SELF (self, klass, FALSE); + /* the hop-limit provided via RA is uint8. */ if (value > 0xFF) return FALSE; @@ -273,7 +323,7 @@ nm_platform_sysctl_set_ip6_hop_limit_safe (const char *iface, int value) return FALSE; path = nm_utils_ip6_property_path (iface, "hop_limit"); - cur = nm_platform_sysctl_get_int_checked (path, 10, 1, G_MAXINT32, -1); + cur = nm_platform_sysctl_get_int_checked (self, path, 10, 1, G_MAXINT32, -1); /* only allow increasing the hop-limit to avoid DOS by an attacker * setting a low hop-limit (CVE-2015-2924, rh#1209902) */ @@ -284,7 +334,7 @@ nm_platform_sysctl_set_ip6_hop_limit_safe (const char *iface, int value) char svalue[20]; sprintf (svalue, "%d", value); - nm_platform_sysctl_set (path, svalue); + nm_platform_sysctl_set (self, path, svalue); } return TRUE; @@ -292,23 +342,25 @@ nm_platform_sysctl_set_ip6_hop_limit_safe (const char *iface, int value) /** * nm_platform_sysctl_get: + * @self: platform instance * @path: Absolute path to sysctl * * Returns: (transfer full): Contents of the virtual sysctl file. */ char * -nm_platform_sysctl_get (const char *path) +nm_platform_sysctl_get (NMPlatform *self, const char *path) { - reset_error (); + _CHECK_SELF (self, klass, NULL); g_return_val_if_fail (path, NULL); g_return_val_if_fail (klass->sysctl_get, NULL); - return klass->sysctl_get (platform, path); + return klass->sysctl_get (self, path); } /** * nm_platform_sysctl_get_int32: + * @self: platform instance * @path: Absolute path to sysctl * @fallback: default value, if the content of path could not be read * as decimal integer. @@ -318,13 +370,14 @@ nm_platform_sysctl_get (const char *path) * value, on success %errno will be set to zero. */ gint32 -nm_platform_sysctl_get_int32 (const char *path, gint32 fallback) +nm_platform_sysctl_get_int32 (NMPlatform *self, const char *path, gint32 fallback) { - return nm_platform_sysctl_get_int_checked (path, 10, G_MININT32, G_MAXINT32, fallback); + return nm_platform_sysctl_get_int_checked (self, path, 10, G_MININT32, G_MAXINT32, fallback); } /** * nm_platform_sysctl_get_int_checked: + * @self: platform instance * @path: Absolute path to sysctl * @base: base of numeric conversion * @min: minimal value that is still valid @@ -339,22 +392,24 @@ nm_platform_sysctl_get_int32 (const char *path, gint32 fallback) * (inclusive) or @fallback. */ gint64 -nm_platform_sysctl_get_int_checked (const char *path, guint base, gint64 min, gint64 max, gint64 fallback) +nm_platform_sysctl_get_int_checked (NMPlatform *self, const char *path, guint base, gint64 min, gint64 max, gint64 fallback) { char *value = NULL; gint32 ret; + _CHECK_SELF (self, klass, fallback); + g_return_val_if_fail (path, fallback); if (path) - value = nm_platform_sysctl_get (path); + value = nm_platform_sysctl_get (self, path); if (!value) { errno = EINVAL; return fallback; } - ret = nm_utils_ascii_str_to_int64 (value, base, min, max, fallback); + ret = _nm_utils_ascii_str_to_int64 (value, base, min, max, fallback); g_free (value); return ret; } @@ -362,47 +417,25 @@ nm_platform_sysctl_get_int_checked (const char *path, guint base, gint64 min, gi /******************************************************************/ /** - * nm_platform_query_devices: - * - * Emit #NMPlatform:link-changed ADDED signals for all currently-known links. - * Should only be called at startup. - */ -void -nm_platform_query_devices (void) -{ - GArray *links_array; - NMPlatformLink *links; - int i; - - links_array = nm_platform_link_get_all (); - links = (NMPlatformLink *) links_array->data; - for (i = 0; i < links_array->len; i++) { - g_signal_emit (platform, signals[SIGNAL_LINK_CHANGED], 0, - links[i].ifindex, &links[i], NM_PLATFORM_SIGNAL_ADDED, - NM_PLATFORM_REASON_INTERNAL); - } - g_array_unref (links_array); -} - -/** * nm_platform_link_get_all: + * self: platform instance * * Retrieve a snapshot of configuration for all links at once. The result is * owned by the caller and should be freed with g_array_unref(). */ GArray * -nm_platform_link_get_all (void) +nm_platform_link_get_all (NMPlatform *self) { GArray *links, *result; guint i, j, nresult; GHashTable *unseen; NMPlatformLink *item; - reset_error (); + _CHECK_SELF (self, klass, NULL); g_return_val_if_fail (klass->link_get_all, NULL); - links = klass->link_get_all (platform); + links = klass->link_get_all (self); if (!links || links->len == 0) return links; @@ -411,11 +444,9 @@ nm_platform_link_get_all (void) for (i = 0; i < links->len; i++) { item = &g_array_index (links, NMPlatformLink, i); - if (item->ifindex <= 0 || g_hash_table_contains (unseen, GINT_TO_POINTER (item->ifindex))) { - g_warn_if_reached (); - item->ifindex = 0; - continue; - } + _LOGT ("link-get: %3d: %s", i, nm_platform_link_to_string (item)); + + nm_assert (item->ifindex > 0 && !g_hash_table_contains (unseen, GINT_TO_POINTER (item->ifindex))); g_hash_table_insert (unseen, GINT_TO_POINTER (item->ifindex), NULL); } @@ -466,6 +497,8 @@ nm_platform_link_get_all (void) if (item->parent > 0 && g_hash_table_contains (unseen, GINT_TO_POINTER (item->parent))) continue; + _LOGT ("link-get: add %3d -> %3d: %s", i, j, nm_platform_link_to_string (item)); + g_hash_table_remove (unseen, GINT_TO_POINTER (item->ifindex)); g_array_index (result, NMPlatformLink, j++) = *item; item->ifindex = 0; @@ -473,10 +506,12 @@ nm_platform_link_get_all (void) } if (!found_something) { - /* there is a circle, pop the first (remaining) element from the list */ - g_warn_if_reached (); + /* There is a loop, pop the first (remaining) element from the list. + * This can happen for veth pairs where each peer is parent of the other end. */ item = &g_array_index (links, NMPlatformLink, first_idx); + _LOGT ("link-get: add (loop) %3d -> %3d: %s", first_idx, j, nm_platform_link_to_string (item)); + g_hash_table_remove (unseen, GINT_TO_POINTER (item->ifindex)); g_array_index (result, NMPlatformLink, j++) = *item; item->ifindex = 0; @@ -491,182 +526,229 @@ nm_platform_link_get_all (void) /** * nm_platform_link_get: + * @self: platform instance * @ifindex: ifindex of the link - * @link: (out): output NMPlatformLink structure. * - * If a link with given @ifindex exists, fill the given NMPlatformLink - * structure. + * Lookup the internal NMPlatformLink object. * - * Returns: %TRUE, if such a link exists, %FALSE otherwise. - * If the link does not exist, the content of @link is undefined. + * Returns: %NULL, if such a link exists or the internal + * platform link object. Do not modify the returned value. + * Also, be aware that any subsequent platform call might + * invalidated/modify the returned instance. **/ -gboolean -nm_platform_link_get (int ifindex, NMPlatformLink *link) +const NMPlatformLink * +nm_platform_link_get (NMPlatform *self, int ifindex) { - g_return_val_if_fail (ifindex > 0, FALSE); - g_return_val_if_fail (link, FALSE); + _CHECK_SELF (self, klass, NULL); + + if (ifindex > 0) + return klass->link_get (self, ifindex); + return NULL; +} + +/** + * nm_platform_link_get_by_ifname: + * @self: platform instance + * @ifname: the ifname + * + * Returns: the first #NMPlatformLink instance with the given name. + **/ +const NMPlatformLink * +nm_platform_link_get_by_ifname (NMPlatform *self, const char *ifname) +{ + _CHECK_SELF (self, klass, NULL); - g_return_val_if_fail (klass->link_get, FALSE); - return !!klass->link_get (platform, ifindex, link); + if (ifname && *ifname) + return klass->link_get_by_ifname (self, ifname); + return NULL; +} + +/** + * nm_platform_link_get_by_address: + * @self: platform instance + * @address: a pointer to the binary hardware address + * @length: the size of @address in bytes + * + * Returns: the first #NMPlatformLink object with a matching + * address. + **/ +const NMPlatformLink * +nm_platform_link_get_by_address (NMPlatform *self, + gconstpointer address, + size_t length) +{ + _CHECK_SELF (self, klass, NULL); + + g_return_val_if_fail (length == 0 || address, NULL); + if (length > 0) { + if (length > NM_UTILS_HWADDR_LEN_MAX) + g_return_val_if_reached (NULL); + return klass->link_get_by_address (self, address, length); + } + return NULL; +} + +static NMPlatformError +_link_add_check_existing (NMPlatform *self, const char *name, NMLinkType type, NMPlatformLink *out_link) +{ + const NMPlatformLink *pllink; + + pllink = nm_platform_link_get_by_ifname (self, name); + if (pllink) { + gboolean wrong_type; + + wrong_type = type != NM_LINK_TYPE_NONE && pllink->type != type; + _LOGD ("link: skip adding link due to existing interface '%s' of type %s%s%s", + name, + nm_link_type_to_string (pllink->type), + wrong_type ? ", expected " : "", + wrong_type ? nm_link_type_to_string (type) : ""); + if (out_link) + *out_link = *pllink; + if (wrong_type) + return NM_PLATFORM_ERROR_WRONG_TYPE; + return NM_PLATFORM_ERROR_EXISTS; + } + return NM_PLATFORM_ERROR_SUCCESS; } /** * nm_platform_link_add: + * @self: platform instance * @name: Interface name * @type: Interface type * @address: (allow-none): set the mac address of the link * @address_len: the length of the @address + * @out_link: on success, the link object * - * Add a software interface. Sets platform->error to NM_PLATFORM_ERROR_EXISTS - * if interface is already already exists. Any link-changed ADDED signal will be - * emitted directly, before this function finishes. + * Add a software interface. If the interface already exists and is of type + * @type, return NM_PLATFORM_ERROR_EXISTS and returns the link + * in @out_link. If the interface already exists and is not of type @type, + * return NM_PLATFORM_ERROR_WRONG_TYPE. + * + * Any link-changed ADDED signal will be emitted directly, before this + * function finishes. + * + * Returns: the error reason or NM_PLATFORM_ERROR_SUCCESS. */ -static gboolean -nm_platform_link_add (const char *name, NMLinkType type, const void *address, size_t address_len) +static NMPlatformError +nm_platform_link_add (NMPlatform *self, + const char *name, + NMLinkType type, + const void *address, + size_t address_len, + NMPlatformLink *out_link) { - reset_error (); + NMPlatformError plerr; - g_return_val_if_fail (name, FALSE); - g_return_val_if_fail (klass->link_add, FALSE); - g_return_val_if_fail ( (address != NULL) ^ (address_len == 0) , FALSE); + _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); - if (nm_platform_link_exists (name)) { - debug ("link: already exists"); - platform->error = NM_PLATFORM_ERROR_EXISTS; - return FALSE; - } + g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (klass->link_add, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail ( (address != NULL) ^ (address_len == 0) , NM_PLATFORM_ERROR_BUG); + + plerr = _link_add_check_existing (self, name, type, out_link); + if (plerr != NM_PLATFORM_ERROR_SUCCESS) + return plerr; - return klass->link_add (platform, name, type, address, address_len); + _LOGD ("link: adding %s '%s'", nm_link_type_to_string (type), name); + if (!klass->link_add (self, name, type, address, address_len, out_link)) + return NM_PLATFORM_ERROR_UNSPECIFIED; + return NM_PLATFORM_ERROR_SUCCESS; } /** * nm_platform_dummy_add: + * @self: platform instance * @name: New interface name + * @out_link: on success, the link object * * Create a software ethernet-like interface */ -gboolean -nm_platform_dummy_add (const char *name) -{ - g_return_val_if_fail (name, FALSE); - - debug ("link: adding dummy '%s'", name); - return nm_platform_link_add (name, NM_LINK_TYPE_DUMMY, NULL, 0); -} - -/** - * nm_platform_link_exists: - * @name: Interface name - * - * Returns: %TRUE if an interface of this name exists, %FALSE otherwise. - */ -gboolean -nm_platform_link_exists (const char *name) +NMPlatformError +nm_platform_dummy_add (NMPlatform *self, const char *name, NMPlatformLink *out_link) { - int ifindex = nm_platform_link_get_ifindex (name); - - reset_error(); - return ifindex > 0; + return nm_platform_link_add (self, name, NM_LINK_TYPE_DUMMY, NULL, 0, out_link); } /** * nm_platform_link_delete: + * @self: platform instance * @ifindex: Interface index - * - * Delete a software interface. Sets platform->error to - * NM_PLATFORM_ERROR_NOT_FOUND if ifindex not available. */ gboolean -nm_platform_link_delete (int ifindex) +nm_platform_link_delete (NMPlatform *self, int ifindex) { - const char *name; + const NMPlatformLink *pllink; - reset_error (); + _CHECK_SELF (self, klass, FALSE); - g_return_val_if_fail (klass->link_delete, FALSE); - - name = nm_platform_link_get_name (ifindex); - - if (!name) + pllink = nm_platform_link_get (self, ifindex); + if (!pllink) return FALSE; - debug ("link: deleting '%s' (%d)", name, ifindex); - return klass->link_delete (platform, ifindex); + _LOGD ("link: deleting '%s' (%d)", pllink->name, ifindex); + return klass->link_delete (self, ifindex); } /** * nm_platform_link_get_index: + * @self: platform instance * @name: Interface name * * Returns: The interface index corresponding to the given interface name * or 0. Inteface name is owned by #NMPlatform, don't free it. */ int -nm_platform_link_get_ifindex (const char *name) +nm_platform_link_get_ifindex (NMPlatform *self, const char *name) { - int ifindex; - - reset_error (); + const NMPlatformLink *pllink; - g_return_val_if_fail (name, 0); - g_return_val_if_fail (klass->link_get_ifindex, 0); - - ifindex = klass->link_get_ifindex (platform, name); - - if (!ifindex) { - debug ("link not found: %s", name); - platform->error = NM_PLATFORM_ERROR_NOT_FOUND; - } - - return ifindex; + pllink = nm_platform_link_get_by_ifname (self, name); + return pllink ? pllink->ifindex : 0; } /** * nm_platform_link_get_name: + * @self: platform instance * @name: Interface name * * Returns: The interface name corresponding to the given interface index * or %NULL. */ const char * -nm_platform_link_get_name (int ifindex) +nm_platform_link_get_name (NMPlatform *self, int ifindex) { - const char *name; - - reset_error (); - - g_return_val_if_fail (klass->link_get_name, NULL); + const NMPlatformLink *pllink; - name = klass->link_get_name (platform, ifindex); - - if (!name) { - debug ("link not found: %d", ifindex); - platform->error = NM_PLATFORM_ERROR_NOT_FOUND; - return FALSE; - } + _CHECK_SELF (self, klass, NULL); - return name; + pllink = nm_platform_link_get (self, ifindex); + return pllink ? pllink->name : NULL; } /** * nm_platform_link_get_type: + * @self: platform instance * @ifindex: Interface index. * * Returns: Link type constant as defined in nm-platform.h. On error, * NM_LINK_TYPE_NONE is returned. */ NMLinkType -nm_platform_link_get_type (int ifindex) +nm_platform_link_get_type (NMPlatform *self, int ifindex) { - reset_error (); + const NMPlatformLink *pllink; - g_return_val_if_fail (klass->link_get_type, NM_LINK_TYPE_NONE); + _CHECK_SELF (self, klass, NM_LINK_TYPE_NONE); - return klass->link_get_type (platform, ifindex); + pllink = nm_platform_link_get (self, ifindex); + return pllink ? pllink->type : NM_LINK_TYPE_NONE; } /** * nm_platform_link_get_type_name: + * @self: platform instance * @ifindex: Interface index. * * Returns: A string describing the type of link. In some cases this @@ -674,113 +756,200 @@ nm_platform_link_get_type (int ifindex) * other cases it may not. On error, %NULL is returned. */ const char * -nm_platform_link_get_type_name (int ifindex) +nm_platform_link_get_type_name (NMPlatform *self, int ifindex) { - reset_error (); + _CHECK_SELF (self, klass, NULL); g_return_val_if_fail (klass->link_get_type_name, NULL); - return klass->link_get_type_name (platform, ifindex); + return klass->link_get_type_name (self, ifindex); +} + +/** + * nm_platform_link_get_unmanaged: + * @self: platform instance + * @ifindex: interface index + * @unmanaged: management status (in case %TRUE is returned) + * + * Returns: %TRUE if platform overrides NM default-unmanaged status, + * %FALSE otherwise (with @unmanaged unmodified). + */ +gboolean +nm_platform_link_get_unmanaged (NMPlatform *self, int ifindex, gboolean *unmanaged) +{ + _CHECK_SELF (self, klass, FALSE); + + if (klass->link_get_unmanaged) + return klass->link_get_unmanaged (self, ifindex, unmanaged); + return FALSE; } /** * nm_platform_link_is_software: + * @self: platform instance * @ifindex: Interface index. * * Returns: %TRUE if ifindex belongs to a software interface, not backed by * a physical device. */ gboolean -nm_platform_link_is_software (int ifindex) +nm_platform_link_is_software (NMPlatform *self, int ifindex) { - return (nm_platform_link_get_type (ifindex) & 0x10000); + return (nm_platform_link_get_type (self, ifindex) & 0x10000); } /** * nm_platform_link_supports_slaves: + * @self: platform instance * @ifindex: Interface index. * * Returns: %TRUE if ifindex belongs to an interface capable of enslaving * other interfaces. */ gboolean -nm_platform_link_supports_slaves (int ifindex) +nm_platform_link_supports_slaves (NMPlatform *self, int ifindex) { - return (nm_platform_link_get_type (ifindex) & 0x20000); + return (nm_platform_link_get_type (self, ifindex) & 0x20000); } /** * nm_platform_link_refresh: + * @self: platform instance * @ifindex: Interface index * * Reload the cache for ifindex synchronously. */ gboolean -nm_platform_link_refresh (int ifindex) +nm_platform_link_refresh (NMPlatform *self, int ifindex) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); if (klass->link_refresh) - return klass->link_refresh (platform, ifindex); + return klass->link_refresh (self, ifindex); return TRUE; } +static guint32 +_link_get_flags (NMPlatform *self, int ifindex) +{ + const NMPlatformLink *pllink; + + pllink = nm_platform_link_get (self, ifindex); + return pllink ? pllink->flags : IFF_NOARP; +} + /** * nm_platform_link_is_up: + * @self: platform instance * @ifindex: Interface index * * Check if the interface is up. */ gboolean -nm_platform_link_is_up (int ifindex) +nm_platform_link_is_up (NMPlatform *self, int ifindex) { - reset_error (); - - g_return_val_if_fail (ifindex >= 0, FALSE); - g_return_val_if_fail (klass->link_is_up, FALSE); + _CHECK_SELF (self, klass, FALSE); - return klass->link_is_up (platform, ifindex); + return NM_FLAGS_HAS (_link_get_flags (self, ifindex), IFF_UP); } /** * nm_platform_link_is_connected: + * @self: platform instance * @ifindex: Interface index * * Check if the interface is connected. */ gboolean -nm_platform_link_is_connected (int ifindex) +nm_platform_link_is_connected (NMPlatform *self, int ifindex) { - reset_error (); + const NMPlatformLink *pllink; - g_return_val_if_fail (ifindex >= 0, FALSE); - g_return_val_if_fail (klass->link_is_connected, FALSE); + _CHECK_SELF (self, klass, FALSE); - return klass->link_is_connected (platform, ifindex); + pllink = nm_platform_link_get (self, ifindex); + return pllink ? pllink->connected : FALSE; } /** * nm_platform_link_uses_arp: + * @self: platform instance * @ifindex: Interface index * * Check if the interface is configured to use ARP. */ gboolean -nm_platform_link_uses_arp (int ifindex) +nm_platform_link_uses_arp (NMPlatform *self, int ifindex) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); + + return !NM_FLAGS_HAS (_link_get_flags (self, ifindex), IFF_NOARP); +} + +/** + * nm_platform_link_get_ipv6_token: + * @self: platform instance + * @ifindex: Interface index + * @iid: Tokenized interface identifier + * + * Returns IPv6 tokenized interface identifier. If the platform or OS doesn't + * support IPv6 tokenized interface identifiers, or the token is not set + * this call will fail and return %FALSE. + * + * Returns: %TRUE a tokenized identifier was available + */ +gboolean +nm_platform_link_get_ipv6_token (NMPlatform *self, int ifindex, NMUtilsIPv6IfaceId *iid) +{ + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex >= 0, FALSE); - g_return_val_if_fail (klass->link_uses_arp, FALSE); + g_return_val_if_fail (iid, FALSE); + +#if HAVE_LIBNL_INET6_TOKEN + { + const NMPlatformLink *pllink; - return klass->link_uses_arp (platform, ifindex); + pllink = nm_platform_link_get (self, ifindex); + if (pllink && pllink->inet6_token.is_valid) { + *iid = pllink->inet6_token.iid; + return TRUE; + } + } +#endif + return FALSE; +} + +const char * +nm_platform_link_get_udi (NMPlatform *self, int ifindex) +{ + _CHECK_SELF (self, klass, FALSE); + + g_return_val_if_fail (ifindex >= 0, NULL); + + if (klass->link_get_udi) + return klass->link_get_udi (self, ifindex); + return NULL; +} + +GObject * +nm_platform_link_get_udev_device (NMPlatform *self, int ifindex) +{ + _CHECK_SELF (self, klass, FALSE); + + g_return_val_if_fail (ifindex >= 0, NULL); + + if (klass->link_get_udev_device) + return klass->link_get_udev_device (self, ifindex); + return NULL; } /** * nm_platform_link_get_user_ip6vll_enabled: + * @self: platform instance * @ifindex: Interface index * * Check whether NM handles IPv6LL address creation for the link. If the @@ -790,20 +959,28 @@ nm_platform_link_uses_arp (int ifindex) * Returns: %TRUE if NM handles the IPv6LL address for @ifindex */ gboolean -nm_platform_link_get_user_ipv6ll_enabled (int ifindex) +nm_platform_link_get_user_ipv6ll_enabled (NMPlatform *self, int ifindex) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex >= 0, FALSE); - g_return_val_if_fail (klass->check_support_user_ipv6ll, FALSE); - if (klass->link_get_user_ipv6ll_enabled) - return klass->link_get_user_ipv6ll_enabled (platform, ifindex); +#if HAVE_LIBNL_INET6_ADDR_GEN_MODE + { + const NMPlatformLink *pllink; + + pllink = nm_platform_link_get (self, ifindex); + if (pllink && pllink->inet6_addr_gen_mode_inv) + return _nm_platform_uint8_inv (pllink->inet6_addr_gen_mode_inv) == IN6_ADDR_GEN_MODE_NONE; + } +#endif return FALSE; } + /** * nm_platform_link_set_user_ip6vll_enabled: + * @self: platform instance * @ifindex: Interface index * * Set whether NM handles IPv6LL address creation for the link. If the @@ -813,189 +990,248 @@ nm_platform_link_get_user_ipv6ll_enabled (int ifindex) * Returns: %TRUE if the operation was successful, %FALSE if it failed. */ gboolean -nm_platform_link_set_user_ipv6ll_enabled (int ifindex, gboolean enabled) +nm_platform_link_set_user_ipv6ll_enabled (NMPlatform *self, int ifindex, gboolean enabled) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex >= 0, FALSE); g_return_val_if_fail (klass->check_support_user_ipv6ll, FALSE); if (klass->link_set_user_ipv6ll_enabled) - return klass->link_set_user_ipv6ll_enabled (platform, ifindex, enabled); + return klass->link_set_user_ipv6ll_enabled (self, ifindex, enabled); return FALSE; } /** * nm_platform_link_set_address: + * @self: platform instance * @ifindex: Interface index * @address: The new MAC address * * Set interface MAC address. */ gboolean -nm_platform_link_set_address (int ifindex, gconstpointer address, size_t length) +nm_platform_link_set_address (NMPlatform *self, int ifindex, gconstpointer address, size_t length) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (address, FALSE); g_return_val_if_fail (length > 0, FALSE); g_return_val_if_fail (klass->link_set_address, FALSE); - debug ("link: setting '%s' (%d) hardware address", nm_platform_link_get_name (ifindex), ifindex); - return klass->link_set_address (platform, ifindex, address, length); + _LOGD ("link: setting '%s' (%d) hardware address", nm_platform_link_get_name (self, ifindex), ifindex); + return klass->link_set_address (self, ifindex, address, length); } /** * nm_platform_link_get_address: + * @self: platform instance * @ifindex: Interface index * @length: Pointer to a variable to store address length * - * Saves interface hardware address to @address. + * Returns: the interface hardware address as an array of bytes of + * length @length. */ gconstpointer -nm_platform_link_get_address (int ifindex, size_t *length) +nm_platform_link_get_address (NMPlatform *self, int ifindex, size_t *length) { - reset_error (); + const NMPlatformLink *pllink; + gconstpointer a = NULL; + guint8 l = 0; + + _CHECK_SELF (self, klass, NULL); if (length) *length = 0; g_return_val_if_fail (ifindex > 0, NULL); - g_return_val_if_fail (klass->link_get_address, NULL); - return klass->link_get_address (platform, ifindex, length); + pllink = nm_platform_link_get (self, ifindex); + if (pllink && pllink->addr.len > 0) { + if (pllink->addr.len > NM_UTILS_HWADDR_LEN_MAX) { + if (length) + *length = 0; + g_return_val_if_reached (NULL); + } + a = pllink->addr.data; + l = pllink->addr.len; + } + + if (length) + *length = l; + return a; +} + +/** + * nm_platform_link_get_permanent_address: + * @self: platform instance + * @ifindex: Interface index + * @buf: buffer of at least %NM_UTILS_HWADDR_LEN_MAX bytes, on success + * the permanent hardware address + * @length: Pointer to a variable to store address length + * + * Returns: %TRUE on success, %FALSE on failure to read the permanent hardware + * address. + */ +gboolean +nm_platform_link_get_permanent_address (NMPlatform *self, int ifindex, guint8 *buf, size_t *length) +{ + _CHECK_SELF (self, klass, FALSE); + + if (length) + *length = 0; + + g_return_val_if_fail (ifindex > 0, FALSE); + g_return_val_if_fail (buf, FALSE); + g_return_val_if_fail (length, FALSE); + + if (klass->link_get_permanent_address) + return klass->link_get_permanent_address (self, ifindex, buf, length); + return FALSE; } gboolean -nm_platform_link_supports_carrier_detect (int ifindex) +nm_platform_link_supports_carrier_detect (NMPlatform *self, int ifindex) { + _CHECK_SELF (self, klass, FALSE); + g_return_val_if_fail (ifindex >= 0, FALSE); g_return_val_if_fail (klass->link_supports_carrier_detect, FALSE); - return klass->link_supports_carrier_detect (platform, ifindex); + return klass->link_supports_carrier_detect (self, ifindex); } gboolean -nm_platform_link_supports_vlans (int ifindex) +nm_platform_link_supports_vlans (NMPlatform *self, int ifindex) { + _CHECK_SELF (self, klass, FALSE); + g_return_val_if_fail (ifindex >= 0, FALSE); g_return_val_if_fail (klass->link_supports_vlans, FALSE); - return klass->link_supports_vlans (platform, ifindex); + return klass->link_supports_vlans (self, ifindex); } /** * nm_platform_link_set_up: + * @self: platform instance * @ifindex: Interface index + * @out_no_firmware: (allow-none): if the failure reason is due to missing firmware. * * Bring the interface up. */ gboolean -nm_platform_link_set_up (int ifindex) +nm_platform_link_set_up (NMPlatform *self, int ifindex, gboolean *out_no_firmware) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (klass->link_set_up, FALSE); - debug ("link: setting up '%s' (%d)", nm_platform_link_get_name (ifindex), ifindex); - return klass->link_set_up (platform, ifindex); + _LOGD ("link: setting up '%s' (%d)", nm_platform_link_get_name (self, ifindex), ifindex); + return klass->link_set_up (self, ifindex, out_no_firmware); } /** * nm_platform_link_set_down: + * @self: platform instance * @ifindex: Interface index * * Take the interface down. */ gboolean -nm_platform_link_set_down (int ifindex) +nm_platform_link_set_down (NMPlatform *self, int ifindex) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (klass->link_set_down, FALSE); - debug ("link: setting down '%s' (%d)", nm_platform_link_get_name (ifindex), ifindex); - return klass->link_set_down (platform, ifindex); + _LOGD ("link: setting down '%s' (%d)", nm_platform_link_get_name (self, ifindex), ifindex); + return klass->link_set_down (self, ifindex); } /** * nm_platform_link_set_arp: + * @self: platform instance * @ifindex: Interface index * * Enable ARP on the interface. */ gboolean -nm_platform_link_set_arp (int ifindex) +nm_platform_link_set_arp (NMPlatform *self, int ifindex) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex >= 0, FALSE); g_return_val_if_fail (klass->link_set_arp, FALSE); - debug ("link: setting arp '%s' (%d)", nm_platform_link_get_name (ifindex), ifindex); - return klass->link_set_arp (platform, ifindex); + _LOGD ("link: setting arp '%s' (%d)", nm_platform_link_get_name (self, ifindex), ifindex); + return klass->link_set_arp (self, ifindex); } /** * nm_platform_link_set_noarp: + * @self: platform instance * @ifindex: Interface index * * Disable ARP on the interface. */ gboolean -nm_platform_link_set_noarp (int ifindex) +nm_platform_link_set_noarp (NMPlatform *self, int ifindex) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex >= 0, FALSE); g_return_val_if_fail (klass->link_set_noarp, FALSE); - debug ("link: setting noarp '%s' (%d)", nm_platform_link_get_name (ifindex), ifindex); - return klass->link_set_noarp (platform, ifindex); + _LOGD ("link: setting noarp '%s' (%d)", nm_platform_link_get_name (self, ifindex), ifindex); + return klass->link_set_noarp (self, ifindex); } /** * nm_platform_link_set_mtu: + * @self: platform instance * @ifindex: Interface index * @mtu: The new MTU value * * Set interface MTU. */ gboolean -nm_platform_link_set_mtu (int ifindex, guint32 mtu) +nm_platform_link_set_mtu (NMPlatform *self, int ifindex, guint32 mtu) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex >= 0, FALSE); g_return_val_if_fail (mtu > 0, FALSE); g_return_val_if_fail (klass->link_set_mtu, FALSE); - debug ("link: setting '%s' (%d) mtu %"G_GUINT32_FORMAT, nm_platform_link_get_name (ifindex), ifindex, mtu); - return klass->link_set_mtu (platform, ifindex, mtu); + _LOGD ("link: setting '%s' (%d) mtu %"G_GUINT32_FORMAT, nm_platform_link_get_name (self, ifindex), ifindex, mtu); + return klass->link_set_mtu (self, ifindex, mtu); } /** * nm_platform_link_get_mtu: + * @self: platform instance * @ifindex: Interface index * * Returns: MTU value for the interface or 0 on error. */ guint32 -nm_platform_link_get_mtu (int ifindex) +nm_platform_link_get_mtu (NMPlatform *self, int ifindex) { - reset_error (); + const NMPlatformLink *pllink; - g_return_val_if_fail (ifindex >= 0, 0); - g_return_val_if_fail (klass->link_get_mtu, 0); + _CHECK_SELF (self, klass, 0); - return klass->link_get_mtu (platform, ifindex); + pllink = nm_platform_link_get (self, ifindex); + return pllink ? pllink->mtu : 0; } /** * nm_platform_link_get_physical_port_id: + * @self: platform instance * @ifindex: Interface index * * The physical port ID, if present, indicates some unique identifier of @@ -1008,18 +1244,20 @@ nm_platform_link_get_mtu (int ifindex) * or if the interface has no physical port ID. */ char * -nm_platform_link_get_physical_port_id (int ifindex) +nm_platform_link_get_physical_port_id (NMPlatform *self, int ifindex) { - reset_error (); + _CHECK_SELF (self, klass, NULL); g_return_val_if_fail (ifindex >= 0, NULL); - g_return_val_if_fail (klass->link_get_physical_port_id, NULL); - return klass->link_get_physical_port_id (platform, ifindex); + if (klass->link_get_physical_port_id) + return klass->link_get_physical_port_id (self, ifindex); + return NULL; } /** * nm_platform_link_get_dev_id: + * @self: platform instance * @ifindex: Interface index * * In contrast to the physical device ID (which indicates which parent a @@ -1030,233 +1268,277 @@ nm_platform_link_get_physical_port_id (int ifindex) * interface has no device ID. */ guint -nm_platform_link_get_dev_id (int ifindex) +nm_platform_link_get_dev_id (NMPlatform *self, int ifindex) { - reset_error (); + _CHECK_SELF (self, klass, 0); g_return_val_if_fail (ifindex >= 0, 0); - g_return_val_if_fail (klass->link_get_dev_id, 0); - return klass->link_get_dev_id (platform, ifindex); + if (klass->link_get_dev_id) + return klass->link_get_dev_id (self, ifindex); + return 0; } /** * nm_platform_link_get_wake_onlan: + * @self: platform instance * @ifindex: Interface index * * Returns: the "Wake-on-LAN" status for @ifindex. */ gboolean -nm_platform_link_get_wake_on_lan (int ifindex) +nm_platform_link_get_wake_on_lan (NMPlatform *self, int ifindex) +{ + _CHECK_SELF (self, klass, FALSE); + + g_return_val_if_fail (ifindex >= 0, FALSE); + + if (klass->link_get_wake_on_lan) + return klass->link_get_wake_on_lan (self, ifindex); + return FALSE; +} + +/** + * nm_platform_link_get_driver_info: + * @self: platform instance + * @ifindex: Interface index + * @out_driver_name: (transfer full): on success, the driver name if available + * @out_driver_version: (transfer full): on success, the driver version if available + * @out_fw_version: (transfer full): on success, the firmware version if available + * + * Returns: %TRUE on success (though @out_driver_name, @out_driver_version and + * @out_fw_version can be %NULL if no information was available), %FALSE on + * failure. + */ +gboolean +nm_platform_link_get_driver_info (NMPlatform *self, + int ifindex, + char **out_driver_name, + char **out_driver_version, + char **out_fw_version) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex >= 0, FALSE); - g_return_val_if_fail (klass->link_get_wake_on_lan, FALSE); + g_return_val_if_fail (klass->link_get_driver_info, FALSE); - return klass->link_get_wake_on_lan (platform, ifindex); + return klass->link_get_driver_info (self, + ifindex, + out_driver_name, + out_driver_version, + out_fw_version); } /** * nm_platform_link_enslave: + * @self: platform instance * @master: Interface index of the master * @slave: Interface index of the slave * * Enslave @slave to @master. */ gboolean -nm_platform_link_enslave (int master, int slave) +nm_platform_link_enslave (NMPlatform *self, int master, int slave) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); - g_assert (platform); g_return_val_if_fail (master > 0, FALSE); g_return_val_if_fail (slave> 0, FALSE); g_return_val_if_fail (klass->link_enslave, FALSE); - debug ("link: enslaving '%s' (%d) to master '%s' (%d)", - nm_platform_link_get_name (slave), slave, - nm_platform_link_get_name (master), master); - return klass->link_enslave (platform, master, slave); + _LOGD ("link: enslaving '%s' (%d) to master '%s' (%d)", + nm_platform_link_get_name (self, slave), slave, + nm_platform_link_get_name (self, master), master); + return klass->link_enslave (self, master, slave); } /** * nm_platform_link_release: + * @self: platform instance * @master: Interface index of the master * @slave: Interface index of the slave * * Release @slave from @master. */ gboolean -nm_platform_link_release (int master, int slave) +nm_platform_link_release (NMPlatform *self, int master, int slave) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); - g_assert (platform); g_return_val_if_fail (master > 0, FALSE); g_return_val_if_fail (slave > 0, FALSE); g_return_val_if_fail (klass->link_release, FALSE); - if (nm_platform_link_get_master (slave) != master) { - platform->error = NM_PLATFORM_ERROR_NOT_SLAVE; + if (nm_platform_link_get_master (self, slave) != master) return FALSE; - } - debug ("link: releasing '%s' (%d) from master '%s' (%d)", - nm_platform_link_get_name (slave), slave, - nm_platform_link_get_name (master), master); - return klass->link_release (platform, master, slave); + _LOGD ("link: releasing '%s' (%d) from master '%s' (%d)", + nm_platform_link_get_name (self, slave), slave, + nm_platform_link_get_name (self, master), master); + return klass->link_release (self, master, slave); } /** * nm_platform_link_get_master: + * @self: platform instance * @slave: Interface index of the slave. * * Returns: Interfase index of the slave's master. */ int -nm_platform_link_get_master (int slave) +nm_platform_link_get_master (NMPlatform *self, int slave) { - reset_error (); + const NMPlatformLink *pllink; + + _CHECK_SELF (self, klass, 0); - g_assert (platform); g_return_val_if_fail (slave >= 0, FALSE); - g_return_val_if_fail (klass->link_get_master, FALSE); - if (!nm_platform_link_get_name (slave)) { - platform->error = NM_PLATFORM_ERROR_NOT_FOUND; - return 0; - } - return klass->link_get_master (platform, slave); + pllink = nm_platform_link_get (self, slave); + return pllink ? pllink->master : 0; } /** * nm_platform_bridge_add: + * @self: platform instance * @name: New interface name * @address: (allow-none): set the mac address of the new bridge * @address_len: the length of the @address + * @out_link: on success, the link object * * Create a software bridge. */ -gboolean -nm_platform_bridge_add (const char *name, const void *address, size_t address_len) +NMPlatformError +nm_platform_bridge_add (NMPlatform *self, + const char *name, + const void *address, + size_t address_len, + NMPlatformLink *out_link) { - debug ("link: adding bridge '%s'", name); - return nm_platform_link_add (name, NM_LINK_TYPE_BRIDGE, address, address_len); + return nm_platform_link_add (self, name, NM_LINK_TYPE_BRIDGE, address, address_len, out_link); } /** * nm_platform_bond_add: + * @self: platform instance * @name: New interface name + * @out_link: on success, the link object * * Create a software bonding device. */ -gboolean -nm_platform_bond_add (const char *name) +NMPlatformError +nm_platform_bond_add (NMPlatform *self, const char *name, NMPlatformLink *out_link) { - debug ("link: adding bond '%s'", name); - return nm_platform_link_add (name, NM_LINK_TYPE_BOND, NULL, 0); + return nm_platform_link_add (self, name, NM_LINK_TYPE_BOND, NULL, 0, out_link); } /** * nm_platform_team_add: + * @self: platform instance * @name: New interface name + * @out_link: on success, the link object * * Create a software teaming device. */ -gboolean -nm_platform_team_add (const char *name) +NMPlatformError +nm_platform_team_add (NMPlatform *self, const char *name, NMPlatformLink *out_link) { - debug ("link: adding team '%s'", name); - return nm_platform_link_add (name, NM_LINK_TYPE_TEAM, NULL, 0); + return nm_platform_link_add (self, name, NM_LINK_TYPE_TEAM, NULL, 0, out_link); } /** * nm_platform_vlan_add: + * @self: platform instance * @name: New interface name * @vlanid: VLAN identifier * @vlanflags: VLAN flags from libnm + * @out_link: on success, the link object * * Create a software VLAN device. */ -gboolean -nm_platform_vlan_add (const char *name, int parent, int vlanid, guint32 vlanflags) +NMPlatformError +nm_platform_vlan_add (NMPlatform *self, + const char *name, + int parent, + int vlanid, + guint32 vlanflags, + NMPlatformLink *out_link) { - reset_error (); + NMPlatformError plerr; - g_assert (platform); - g_return_val_if_fail (parent >= 0, FALSE); - g_return_val_if_fail (vlanid >= 0, FALSE); - g_return_val_if_fail (name, FALSE); - g_return_val_if_fail (klass->vlan_add, FALSE); + _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); - if (nm_platform_link_exists (name)) { - debug ("link already exists: %s", name); - platform->error = NM_PLATFORM_ERROR_EXISTS; - return FALSE; - } + g_return_val_if_fail (parent >= 0, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (vlanid >= 0, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (klass->vlan_add, NM_PLATFORM_ERROR_BUG); + + plerr = _link_add_check_existing (self, name, NM_LINK_TYPE_VLAN, out_link); + if (plerr != NM_PLATFORM_ERROR_SUCCESS) + return plerr; - debug ("link: adding vlan '%s' parent %d vlanid %d vlanflags %x", - name, parent, vlanid, vlanflags); - return klass->vlan_add (platform, name, parent, vlanid, vlanflags); + _LOGD ("link: adding vlan '%s' parent %d vlanid %d vlanflags %x", + name, parent, vlanid, vlanflags); + if (!klass->vlan_add (self, name, parent, vlanid, vlanflags, out_link)) + return NM_PLATFORM_ERROR_UNSPECIFIED; + return NM_PLATFORM_ERROR_SUCCESS; } gboolean -nm_platform_master_set_option (int ifindex, const char *option, const char *value) +nm_platform_master_set_option (NMPlatform *self, int ifindex, const char *option, const char *value) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (option, FALSE); g_return_val_if_fail (value, FALSE); g_return_val_if_fail (klass->master_set_option, FALSE); - return klass->master_set_option (platform, ifindex, option, value); + return klass->master_set_option (self, ifindex, option, value); } char * -nm_platform_master_get_option (int ifindex, const char *option) +nm_platform_master_get_option (NMPlatform *self, int ifindex, const char *option) { - reset_error (); + _CHECK_SELF (self, klass, NULL); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (option, FALSE); g_return_val_if_fail (klass->master_set_option, FALSE); - return klass->master_get_option (platform, ifindex, option); + return klass->master_get_option (self, ifindex, option); } gboolean -nm_platform_slave_set_option (int ifindex, const char *option, const char *value) +nm_platform_slave_set_option (NMPlatform *self, int ifindex, const char *option, const char *value) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (option, FALSE); g_return_val_if_fail (value, FALSE); g_return_val_if_fail (klass->slave_set_option, FALSE); - return klass->slave_set_option (platform, ifindex, option, value); + return klass->slave_set_option (self, ifindex, option, value); } char * -nm_platform_slave_get_option (int ifindex, const char *option) +nm_platform_slave_get_option (NMPlatform *self, int ifindex, const char *option) { - reset_error (); + _CHECK_SELF (self, klass, NULL); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (option, FALSE); g_return_val_if_fail (klass->slave_set_option, FALSE); - return klass->slave_get_option (platform, ifindex, option); + return klass->slave_get_option (self, ifindex, option); } gboolean -nm_platform_vlan_get_info (int ifindex, int *parent, int *vlanid) +nm_platform_vlan_get_info (NMPlatform *self, int ifindex, int *parent, int *vlanid) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); - g_assert (platform); g_return_val_if_fail (klass->vlan_get_info, FALSE); if (parent) @@ -1264,261 +1546,274 @@ nm_platform_vlan_get_info (int ifindex, int *parent, int *vlanid) if (vlanid) *vlanid = 0; - if (nm_platform_link_get_type (ifindex) != NM_LINK_TYPE_VLAN) + if (nm_platform_link_get_type (self, ifindex) != NM_LINK_TYPE_VLAN) return FALSE; - return klass->vlan_get_info (platform, ifindex, parent, vlanid); + return klass->vlan_get_info (self, ifindex, parent, vlanid); } gboolean -nm_platform_vlan_set_ingress_map (int ifindex, int from, int to) +nm_platform_vlan_set_ingress_map (NMPlatform *self, int ifindex, int from, int to) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); - g_assert (platform); g_return_val_if_fail (klass->vlan_set_ingress_map, FALSE); - debug ("link: setting vlan ingress map for %d from %d to %d", ifindex, from, to); - return klass->vlan_set_ingress_map (platform, ifindex, from, to); + _LOGD ("link: setting vlan ingress map for %d from %d to %d", ifindex, from, to); + return klass->vlan_set_ingress_map (self, ifindex, from, to); } gboolean -nm_platform_vlan_set_egress_map (int ifindex, int from, int to) +nm_platform_vlan_set_egress_map (NMPlatform *self, int ifindex, int from, int to) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); - g_assert (platform); g_return_val_if_fail (klass->vlan_set_egress_map, FALSE); - debug ("link: setting vlan egress map for %d from %d to %d", ifindex, from, to); - return klass->vlan_set_egress_map (platform, ifindex, from, to); + _LOGD ("link: setting vlan egress map for %d from %d to %d", ifindex, from, to); + return klass->vlan_set_egress_map (self, ifindex, from, to); } -gboolean -nm_platform_infiniband_partition_add (int parent, int p_key) +NMPlatformError +nm_platform_infiniband_partition_add (NMPlatform *self, int parent, int p_key, NMPlatformLink *out_link) { - const char *parent_name; - char *name; + gs_free char *parent_name = NULL; + gs_free char *name = NULL; + NMPlatformError plerr; - reset_error (); + _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); - g_return_val_if_fail (parent >= 0, FALSE); - g_return_val_if_fail (p_key >= 0, FALSE); - g_return_val_if_fail (klass->infiniband_partition_add, FALSE); + g_return_val_if_fail (parent >= 0, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (p_key >= 0, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (klass->infiniband_partition_add, NM_PLATFORM_ERROR_BUG); - if (nm_platform_link_get_type (parent) != NM_LINK_TYPE_INFINIBAND) { - platform->error = NM_PLATFORM_ERROR_WRONG_TYPE; - return FALSE; - } + parent_name = g_strdup (nm_platform_link_get_name (self, parent)); + if ( !parent_name + || nm_platform_link_get_type (self, parent) != NM_LINK_TYPE_INFINIBAND) + return NM_PLATFORM_ERROR_WRONG_TYPE; - parent_name = nm_platform_link_get_name (parent); name = g_strdup_printf ("%s.%04x", parent_name, p_key); - if (nm_platform_link_exists (name)) { - debug ("infiniband: already exists"); - platform->error = NM_PLATFORM_ERROR_EXISTS; - g_free (name); - return FALSE; - } - g_free (name); + plerr = _link_add_check_existing (self, name, NM_LINK_TYPE_INFINIBAND, out_link); + if (plerr != NM_PLATFORM_ERROR_SUCCESS) + return plerr; - return klass->infiniband_partition_add (platform, parent, p_key); + _LOGD ("link: adding infiniband partition %s for parent '%s' (%d), key %d", + name, parent_name, parent, p_key); + if (!klass->infiniband_partition_add (self, parent, p_key, out_link)) + return NM_PLATFORM_ERROR_UNSPECIFIED; + return NM_PLATFORM_ERROR_SUCCESS; } gboolean -nm_platform_veth_get_properties (int ifindex, NMPlatformVethProperties *props) +nm_platform_infiniband_get_info (NMPlatform *self, + int ifindex, + int *parent, + int *p_key, + const char **mode) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); + + g_return_val_if_fail (ifindex > 0, FALSE); + g_return_val_if_fail (klass->infiniband_get_info, FALSE); + + return klass->infiniband_get_info (self, ifindex, parent, p_key, mode); +} + +gboolean +nm_platform_veth_get_properties (NMPlatform *self, int ifindex, NMPlatformVethProperties *props) +{ + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (props != NULL, FALSE); - return klass->veth_get_properties (platform, ifindex, props); + return klass->veth_get_properties (self, ifindex, props); } gboolean -nm_platform_tun_get_properties (int ifindex, NMPlatformTunProperties *props) +nm_platform_tun_get_properties (NMPlatform *self, int ifindex, NMPlatformTunProperties *props) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (props != NULL, FALSE); - return klass->tun_get_properties (platform, ifindex, props); + return klass->tun_get_properties (self, ifindex, props); } gboolean -nm_platform_macvlan_get_properties (int ifindex, NMPlatformMacvlanProperties *props) +nm_platform_macvlan_get_properties (NMPlatform *self, int ifindex, NMPlatformMacvlanProperties *props) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (props != NULL, FALSE); - return klass->macvlan_get_properties (platform, ifindex, props); + return klass->macvlan_get_properties (self, ifindex, props); } gboolean -nm_platform_vxlan_get_properties (int ifindex, NMPlatformVxlanProperties *props) +nm_platform_vxlan_get_properties (NMPlatform *self, int ifindex, NMPlatformVxlanProperties *props) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (props != NULL, FALSE); - return klass->vxlan_get_properties (platform, ifindex, props); + return klass->vxlan_get_properties (self, ifindex, props); } gboolean -nm_platform_gre_get_properties (int ifindex, NMPlatformGreProperties *props) +nm_platform_gre_get_properties (NMPlatform *self, int ifindex, NMPlatformGreProperties *props) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (props != NULL, FALSE); - return klass->gre_get_properties (platform, ifindex, props); + return klass->gre_get_properties (self, ifindex, props); } gboolean -nm_platform_wifi_get_capabilities (int ifindex, NMDeviceWifiCapabilities *caps) +nm_platform_wifi_get_capabilities (NMPlatform *self, int ifindex, NMDeviceWifiCapabilities *caps) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); - return klass->wifi_get_capabilities (platform, ifindex, caps); + return klass->wifi_get_capabilities (self, ifindex, caps); } gboolean -nm_platform_wifi_get_bssid (int ifindex, guint8 *bssid) +nm_platform_wifi_get_bssid (NMPlatform *self, int ifindex, guint8 *bssid) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); - return klass->wifi_get_bssid (platform, ifindex, bssid); + return klass->wifi_get_bssid (self, ifindex, bssid); } GByteArray * -nm_platform_wifi_get_ssid (int ifindex) +nm_platform_wifi_get_ssid (NMPlatform *self, int ifindex) { - reset_error (); + _CHECK_SELF (self, klass, NULL); g_return_val_if_fail (ifindex > 0, NULL); - return klass->wifi_get_ssid (platform, ifindex); + return klass->wifi_get_ssid (self, ifindex); } guint32 -nm_platform_wifi_get_frequency (int ifindex) +nm_platform_wifi_get_frequency (NMPlatform *self, int ifindex) { - reset_error (); + _CHECK_SELF (self, klass, 0); g_return_val_if_fail (ifindex > 0, 0); - return klass->wifi_get_frequency (platform, ifindex); + return klass->wifi_get_frequency (self, ifindex); } int -nm_platform_wifi_get_quality (int ifindex) +nm_platform_wifi_get_quality (NMPlatform *self, int ifindex) { - reset_error (); + _CHECK_SELF (self, klass, 0); g_return_val_if_fail (ifindex > 0, 0); - return klass->wifi_get_quality (platform, ifindex); + return klass->wifi_get_quality (self, ifindex); } guint32 -nm_platform_wifi_get_rate (int ifindex) +nm_platform_wifi_get_rate (NMPlatform *self, int ifindex) { - reset_error (); + _CHECK_SELF (self, klass, 0); g_return_val_if_fail (ifindex > 0, 0); - return klass->wifi_get_rate (platform, ifindex); + return klass->wifi_get_rate (self, ifindex); } NM80211Mode -nm_platform_wifi_get_mode (int ifindex) +nm_platform_wifi_get_mode (NMPlatform *self, int ifindex) { - reset_error (); + _CHECK_SELF (self, klass, NM_802_11_MODE_UNKNOWN); g_return_val_if_fail (ifindex > 0, NM_802_11_MODE_UNKNOWN); - return klass->wifi_get_mode (platform, ifindex); + return klass->wifi_get_mode (self, ifindex); } void -nm_platform_wifi_set_mode (int ifindex, NM80211Mode mode) +nm_platform_wifi_set_mode (NMPlatform *self, int ifindex, NM80211Mode mode) { - reset_error (); + _CHECK_SELF_VOID (self, klass); g_return_if_fail (ifindex > 0); - klass->wifi_set_mode (platform, ifindex, mode); + klass->wifi_set_mode (self, ifindex, mode); } guint32 -nm_platform_wifi_find_frequency (int ifindex, const guint32 *freqs) +nm_platform_wifi_find_frequency (NMPlatform *self, int ifindex, const guint32 *freqs) { - reset_error (); + _CHECK_SELF (self, klass, 0); g_return_val_if_fail (ifindex > 0, 0); g_return_val_if_fail (freqs != NULL, 0); - return klass->wifi_find_frequency (platform, ifindex, freqs); + return klass->wifi_find_frequency (self, ifindex, freqs); } void -nm_platform_wifi_indicate_addressing_running (int ifindex, gboolean running) +nm_platform_wifi_indicate_addressing_running (NMPlatform *self, int ifindex, gboolean running) { - reset_error (); + _CHECK_SELF_VOID (self, klass); g_return_if_fail (ifindex > 0); - klass->wifi_indicate_addressing_running (platform, ifindex, running); + klass->wifi_indicate_addressing_running (self, ifindex, running); } guint32 -nm_platform_mesh_get_channel (int ifindex) +nm_platform_mesh_get_channel (NMPlatform *self, int ifindex) { - reset_error (); + _CHECK_SELF (self, klass, 0); g_return_val_if_fail (ifindex > 0, 0); - return klass->mesh_get_channel (platform, ifindex); + return klass->mesh_get_channel (self, ifindex); } gboolean -nm_platform_mesh_set_channel (int ifindex, guint32 channel) +nm_platform_mesh_set_channel (NMPlatform *self, int ifindex, guint32 channel) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); - return klass->mesh_set_channel (platform, ifindex, channel); + return klass->mesh_set_channel (self, ifindex, channel); } gboolean -nm_platform_mesh_set_ssid (int ifindex, const guint8 *ssid, gsize len) +nm_platform_mesh_set_ssid (NMPlatform *self, int ifindex, const guint8 *ssid, gsize len) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (ssid != NULL, FALSE); - return klass->mesh_set_ssid (platform, ifindex, ssid, len); + return klass->mesh_set_ssid (self, ifindex, ssid, len); } #define TO_STRING_DEV_BUF_SIZE (5+15+1) static const char * -_to_string_dev (int ifindex, char *buf, size_t size) +_to_string_dev (NMPlatform *self, int ifindex, char *buf, size_t size) { g_assert (buf && size >= TO_STRING_DEV_BUF_SIZE); if (ifindex) { - const char *name = ifindex > 0 ? nm_platform_link_get_name (ifindex) : NULL; + const char *name = ifindex > 0 && self ? nm_platform_link_get_name (self, ifindex) : NULL; char *buf2; strcpy (buf, " dev "); @@ -1538,29 +1833,30 @@ _to_string_dev (int ifindex, char *buf, size_t size) /******************************************************************/ GArray * -nm_platform_ip4_address_get_all (int ifindex) +nm_platform_ip4_address_get_all (NMPlatform *self, int ifindex) { - reset_error (); + _CHECK_SELF (self, klass, NULL); g_return_val_if_fail (ifindex > 0, NULL); g_return_val_if_fail (klass->ip4_address_get_all, NULL); - return klass->ip4_address_get_all (platform, ifindex); + return klass->ip4_address_get_all (self, ifindex); } GArray * -nm_platform_ip6_address_get_all (int ifindex) +nm_platform_ip6_address_get_all (NMPlatform *self, int ifindex) { - reset_error (); + _CHECK_SELF (self, klass, NULL); g_return_val_if_fail (ifindex > 0, NULL); g_return_val_if_fail (klass->ip6_address_get_all, NULL); - return klass->ip6_address_get_all (platform, ifindex); + return klass->ip6_address_get_all (self, ifindex); } gboolean -nm_platform_ip4_address_add (int ifindex, +nm_platform_ip4_address_add (NMPlatform *self, + int ifindex, in_addr_t address, in_addr_t peer_address, int plen, @@ -1568,7 +1864,7 @@ nm_platform_ip4_address_add (int ifindex, guint32 preferred, const char *label) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (plen > 0, FALSE); @@ -1590,13 +1886,14 @@ nm_platform_ip4_address_add (int ifindex, if (label) g_strlcpy (addr.label, label, sizeof (addr.label)); - debug ("address: adding or updating IPv4 address: %s", nm_platform_ip4_address_to_string (&addr)); + _LOGD ("address: adding or updating IPv4 address: %s", nm_platform_ip4_address_to_string (&addr)); } - return klass->ip4_address_add (platform, ifindex, address, peer_address, plen, lifetime, preferred, label); + return klass->ip4_address_add (self, ifindex, address, peer_address, plen, lifetime, preferred, label); } gboolean -nm_platform_ip6_address_add (int ifindex, +nm_platform_ip6_address_add (NMPlatform *self, + int ifindex, struct in6_addr address, struct in6_addr peer_address, int plen, @@ -1604,7 +1901,7 @@ nm_platform_ip6_address_add (int ifindex, guint32 preferred, guint flags) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (plen > 0, FALSE); @@ -1624,74 +1921,72 @@ nm_platform_ip6_address_add (int ifindex, addr.preferred = preferred; addr.flags = flags; - debug ("address: adding or updating IPv6 address: %s", nm_platform_ip6_address_to_string (&addr)); + _LOGD ("address: adding or updating IPv6 address: %s", nm_platform_ip6_address_to_string (&addr)); } - return klass->ip6_address_add (platform, ifindex, address, peer_address, plen, lifetime, preferred, flags); + return klass->ip6_address_add (self, ifindex, address, peer_address, plen, lifetime, preferred, flags); } gboolean -nm_platform_ip4_address_delete (int ifindex, in_addr_t address, int plen, in_addr_t peer_address) +nm_platform_ip4_address_delete (NMPlatform *self, int ifindex, in_addr_t address, int plen, in_addr_t peer_address) { char str_dev[TO_STRING_DEV_BUF_SIZE]; char str_peer[NM_UTILS_INET_ADDRSTRLEN]; - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (plen > 0, FALSE); g_return_val_if_fail (klass->ip4_address_delete, FALSE); - debug ("address: deleting IPv4 address %s/%d, %s%s%sifindex %d%s", + _LOGD ("address: deleting IPv4 address %s/%d, %s%s%sifindex %d%s", nm_utils_inet4_ntop (address, NULL), plen, peer_address ? "peer " : "", peer_address ? nm_utils_inet4_ntop (peer_address, str_peer) : "", peer_address ? ", " : "", ifindex, - _to_string_dev (ifindex, str_dev, sizeof (str_dev))); - return klass->ip4_address_delete (platform, ifindex, address, plen, peer_address); + _to_string_dev (self, ifindex, str_dev, sizeof (str_dev))); + return klass->ip4_address_delete (self, ifindex, address, plen, peer_address); } gboolean -nm_platform_ip6_address_delete (int ifindex, struct in6_addr address, int plen) +nm_platform_ip6_address_delete (NMPlatform *self, int ifindex, struct in6_addr address, int plen) { char str_dev[TO_STRING_DEV_BUF_SIZE]; - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (plen > 0, FALSE); g_return_val_if_fail (klass->ip6_address_delete, FALSE); - debug ("address: deleting IPv6 address %s/%d, ifindex %d%s", + _LOGD ("address: deleting IPv6 address %s/%d, ifindex %d%s", nm_utils_inet6_ntop (&address, NULL), plen, ifindex, - _to_string_dev (ifindex, str_dev, sizeof (str_dev))); - return klass->ip6_address_delete (platform, ifindex, address, plen); + _to_string_dev (self, ifindex, str_dev, sizeof (str_dev))); + return klass->ip6_address_delete (self, ifindex, address, plen); } -gboolean -nm_platform_ip4_address_exists (int ifindex, in_addr_t address, int plen) +const NMPlatformIP4Address * +nm_platform_ip4_address_get (NMPlatform *self, int ifindex, in_addr_t address, int plen) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (plen > 0, FALSE); - g_return_val_if_fail (klass->ip4_address_exists, FALSE); - return klass->ip4_address_exists (platform, ifindex, address, plen); + return klass->ip4_address_get (self, ifindex, address, plen); } -gboolean -nm_platform_ip6_address_exists (int ifindex, struct in6_addr address, int plen) +const NMPlatformIP6Address * +nm_platform_ip6_address_get (NMPlatform *self, int ifindex, struct in6_addr address, int plen) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (plen > 0, FALSE); - g_return_val_if_fail (klass->ip6_address_exists, FALSE); - return klass->ip6_address_exists (platform, ifindex, address, plen); + return klass->ip6_address_get (self, ifindex, address, plen); } static gboolean -array_contains_ip4_address (const GArray *addresses, const NMPlatformIP4Address *address) +array_contains_ip4_address (const GArray *addresses, const NMPlatformIP4Address *address, gint64 now, guint32 padding) { guint len = addresses ? addresses->len : 0; guint i; @@ -1699,15 +1994,20 @@ array_contains_ip4_address (const GArray *addresses, const NMPlatformIP4Address for (i = 0; i < len; i++) { NMPlatformIP4Address *candidate = &g_array_index (addresses, NMPlatformIP4Address, i); - if (candidate->address == address->address && candidate->plen == address->plen) - return TRUE; + if (candidate->address == address->address && candidate->plen == address->plen) { + guint32 lifetime, preferred; + + if (nmp_utils_lifetime_get (candidate->timestamp, candidate->lifetime, candidate->preferred, + now, padding, &lifetime, &preferred)) + return TRUE; + } } return FALSE; } static gboolean -array_contains_ip6_address (const GArray *addresses, const NMPlatformIP6Address *address) +array_contains_ip6_address (const GArray *addresses, const NMPlatformIP6Address *address, gint64 now, guint32 padding) { guint len = addresses ? addresses->len : 0; guint i; @@ -1715,108 +2015,27 @@ array_contains_ip6_address (const GArray *addresses, const NMPlatformIP6Address for (i = 0; i < len; i++) { NMPlatformIP6Address *candidate = &g_array_index (addresses, NMPlatformIP6Address, i); - if (IN6_ARE_ADDR_EQUAL (&candidate->address, &address->address) && candidate->plen == address->plen) - return TRUE; - } - - return FALSE; -} - -/** - * Takes a pair @timestamp and @duration, and returns the remaining duration based - * on the new timestamp @now. - */ -static guint32 -_rebase_relative_time_on_now (guint32 timestamp, guint32 duration, guint32 now, guint32 padding) -{ - gint64 t; - - if (duration == NM_PLATFORM_LIFETIME_PERMANENT) - return NM_PLATFORM_LIFETIME_PERMANENT; - - if (timestamp == 0) { - /* if the @timestamp is zero, assume it was just left unset and that the relative - * @duration starts counting from @now. This is convenient to construct an address - * and print it in nm_platform_ip4_address_to_string(). - * - * In general it does not make sense to set the @duration without anchoring at - * @timestamp because you don't know the absolute expiration time when looking - * at the address at a later moment. */ - timestamp = now; - } + if (IN6_ARE_ADDR_EQUAL (&candidate->address, &address->address) && candidate->plen == address->plen) { + guint32 lifetime, preferred; - /* For timestamp > now, just accept it and calculate the expected(?) result. */ - t = (gint64) timestamp + (gint64) duration - (gint64) now; - - /* Optional padding to avoid potential races. */ - t += (gint64) padding; - - if (t <= 0) - return 0; - if (t >= NM_PLATFORM_LIFETIME_PERMANENT) - return NM_PLATFORM_LIFETIME_PERMANENT - 1; - return t; -} - -static gboolean -_address_get_lifetime (const NMPlatformIPAddress *address, guint32 now, guint32 padding, guint32 *out_lifetime, guint32 *out_preferred) -{ - guint32 lifetime, preferred; - - if (address->lifetime == 0) { - *out_lifetime = NM_PLATFORM_LIFETIME_PERMANENT; - *out_preferred = NM_PLATFORM_LIFETIME_PERMANENT; - - /* We treat lifetime==0 as permanent addresses to allow easy creation of such addresses - * (without requiring to set the lifetime fields to NM_PLATFORM_LIFETIME_PERMANENT). - * In that case we also expect that the other fields (timestamp and preferred) are left unset. */ - g_return_val_if_fail (address->timestamp == 0 && address->preferred == 0, TRUE); - } else { - lifetime = _rebase_relative_time_on_now (address->timestamp, address->lifetime, now, padding); - if (!lifetime) - return FALSE; - preferred = _rebase_relative_time_on_now (address->timestamp, address->preferred, now, padding); - - *out_lifetime = lifetime; - *out_preferred = MIN (preferred, lifetime); - - /* Assert that non-permanent addresses have a (positive) @timestamp. _rebase_relative_time_on_now() - * treats addresses with timestamp 0 as *now*. Addresses passed to _address_get_lifetime() always - * should have a valid @timestamp, otherwise on every re-sync, their lifetime will be extended anew. - */ - g_return_val_if_fail ( address->timestamp != 0 - || ( address->lifetime == NM_PLATFORM_LIFETIME_PERMANENT - && address->preferred == NM_PLATFORM_LIFETIME_PERMANENT), TRUE); - g_return_val_if_fail (preferred <= lifetime, TRUE); - } - return TRUE; -} - -gboolean -nm_platform_ip4_check_reinstall_device_route (int ifindex, const NMPlatformIP4Address *address, guint32 device_route_metric) -{ - g_return_val_if_fail (address, FALSE); - - if ( ifindex <= 0 - || address->plen <= 0 - || address->plen >= 32) - return FALSE; - - if (device_route_metric == NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE) { - /* The automatically added route would be already our desired priority. - * Nothing to do. */ - return FALSE; + if (nmp_utils_lifetime_get (candidate->timestamp, candidate->lifetime, candidate->preferred, + now, padding, &lifetime, &preferred)) + return TRUE; + } } - return klass->ip4_check_reinstall_device_route (platform, ifindex, address, device_route_metric); + return FALSE; } /** * nm_platform_ip4_address_sync: + * @self: platform instance * @ifindex: Interface index * @known_addresses: List of addresses - * @device_route_metric: the route metric for adding subnet routes (replaces - * the kernel added routes). + * @out_added_addresses: (out): (allow-none): if not %NULL, return a #GPtrArray + * with the addresses added. The pointers point into @known_addresses. + * It possibly does not contain all addresses from @known_address because + * some addresses might be expired. * * A convenience function to synchronize addresses for a specific interface * with the least possible disturbance. It simply removes addresses that are @@ -1825,23 +2044,28 @@ nm_platform_ip4_check_reinstall_device_route (int ifindex, const NMPlatformIP4Ad * Returns: %TRUE on success. */ gboolean -nm_platform_ip4_address_sync (int ifindex, const GArray *known_addresses, guint32 device_route_metric) +nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, const GArray *known_addresses, GPtrArray **out_added_addresses) { GArray *addresses; NMPlatformIP4Address *address; guint32 now = nm_utils_get_monotonic_timestamp_s (); int i; + _CHECK_SELF (self, klass, FALSE); + /* Delete unknown addresses */ - addresses = nm_platform_ip4_address_get_all (ifindex); + addresses = nm_platform_ip4_address_get_all (self, ifindex); for (i = 0; i < addresses->len; i++) { address = &g_array_index (addresses, NMPlatformIP4Address, i); - if (!array_contains_ip4_address (known_addresses, address)) - nm_platform_ip4_address_delete (ifindex, address->address, address->plen, address->peer_address); + if (!array_contains_ip4_address (known_addresses, address, now, ADDRESS_LIFETIME_PADDING)) + nm_platform_ip4_address_delete (self, ifindex, address->address, address->plen, address->peer_address); } g_array_free (addresses, TRUE); + if (out_added_addresses) + *out_added_addresses = NULL; + if (!known_addresses) return TRUE; @@ -1849,33 +2073,18 @@ nm_platform_ip4_address_sync (int ifindex, const GArray *known_addresses, guint3 for (i = 0; i < known_addresses->len; i++) { const NMPlatformIP4Address *known_address = &g_array_index (known_addresses, NMPlatformIP4Address, i); guint32 lifetime, preferred; - guint32 network; - gboolean reinstall_device_route = FALSE; - /* add a padding of 5 seconds to avoid potential races. */ - if (!_address_get_lifetime ((NMPlatformIPAddress *) known_address, now, 5, &lifetime, &preferred)) + if (!nmp_utils_lifetime_get (known_address->timestamp, known_address->lifetime, known_address->preferred, + now, ADDRESS_LIFETIME_PADDING, &lifetime, &preferred)) continue; - if (nm_platform_ip4_check_reinstall_device_route (ifindex, known_address, device_route_metric)) - reinstall_device_route = TRUE; - - if (!nm_platform_ip4_address_add (ifindex, known_address->address, known_address->peer_address, known_address->plen, lifetime, preferred, known_address->label)) + if (!nm_platform_ip4_address_add (self, ifindex, known_address->address, known_address->peer_address, known_address->plen, lifetime, preferred, known_address->label)) return FALSE; - if (reinstall_device_route) { - /* Kernel automatically adds a device route for us with metric 0. That is not what we want. - * Remove it, and re-add it. - * - * In face of having the same subnets on two different interfaces with the same metric, - * this is a problem. Surprisingly, kernel is able to add two routes for the same subnet/prefix,metric - * to different interfaces. We cannot. Adding one, would replace the other. This is avoided - * by the above nm_platform_ip4_check_reinstall_device_route() check. - */ - network = nm_utils_ip4_address_clear_host_address (known_address->address, known_address->plen); - (void) nm_platform_ip4_route_add (ifindex, NM_IP_CONFIG_SOURCE_KERNEL, network, known_address->plen, - 0, known_address->address, device_route_metric, 0); - (void) nm_platform_ip4_route_delete (ifindex, network, known_address->plen, - NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE); + if (out_added_addresses) { + if (!*out_added_addresses) + *out_added_addresses = g_ptr_array_new (); + g_ptr_array_add (*out_added_addresses, (gpointer) known_address); } } @@ -1884,6 +2093,7 @@ nm_platform_ip4_address_sync (int ifindex, const GArray *known_addresses, guint3 /** * nm_platform_ip6_address_sync: + * @self: platform instance * @ifindex: Interface index * @known_addresses: List of addresses * @keep_link_local: Don't remove link-local address @@ -1895,7 +2105,7 @@ nm_platform_ip4_address_sync (int ifindex, const GArray *known_addresses, guint3 * Returns: %TRUE on success. */ gboolean -nm_platform_ip6_address_sync (int ifindex, const GArray *known_addresses, gboolean keep_link_local) +nm_platform_ip6_address_sync (NMPlatform *self, int ifindex, const GArray *known_addresses, gboolean keep_link_local) { GArray *addresses; NMPlatformIP6Address *address; @@ -1903,7 +2113,7 @@ nm_platform_ip6_address_sync (int ifindex, const GArray *known_addresses, gboole int i; /* Delete unknown addresses */ - addresses = nm_platform_ip6_address_get_all (ifindex); + addresses = nm_platform_ip6_address_get_all (self, ifindex); for (i = 0; i < addresses->len; i++) { address = &g_array_index (addresses, NMPlatformIP6Address, i); @@ -1911,8 +2121,8 @@ nm_platform_ip6_address_sync (int ifindex, const GArray *known_addresses, gboole if (keep_link_local && IN6_IS_ADDR_LINKLOCAL (&address->address)) continue; - if (!array_contains_ip6_address (known_addresses, address)) - nm_platform_ip6_address_delete (ifindex, address->address, address->plen); + if (!array_contains_ip6_address (known_addresses, address, now, ADDRESS_LIFETIME_PADDING)) + nm_platform_ip6_address_delete (self, ifindex, address->address, address->plen); } g_array_free (addresses, TRUE); @@ -1924,11 +2134,11 @@ nm_platform_ip6_address_sync (int ifindex, const GArray *known_addresses, gboole const NMPlatformIP6Address *known_address = &g_array_index (known_addresses, NMPlatformIP6Address, i); guint32 lifetime, preferred; - /* add a padding of 5 seconds to avoid potential races. */ - if (!_address_get_lifetime ((NMPlatformIPAddress *) known_address, now, 5, &lifetime, &preferred)) + if (!nmp_utils_lifetime_get (known_address->timestamp, known_address->lifetime, known_address->preferred, + now, ADDRESS_LIFETIME_PADDING, &lifetime, &preferred)) continue; - if (!nm_platform_ip6_address_add (ifindex, known_address->address, + if (!nm_platform_ip6_address_add (self, ifindex, known_address->address, known_address->peer_address, known_address->plen, lifetime, preferred, known_address->flags)) return FALSE; @@ -1938,51 +2148,50 @@ nm_platform_ip6_address_sync (int ifindex, const GArray *known_addresses, gboole } gboolean -nm_platform_address_flush (int ifindex) +nm_platform_address_flush (NMPlatform *self, int ifindex) { - return nm_platform_ip4_address_sync (ifindex, NULL, 0) - && nm_platform_ip6_address_sync (ifindex, NULL, FALSE); + _CHECK_SELF (self, klass, FALSE); + + return nm_platform_ip4_address_sync (self, ifindex, NULL, NULL) + && nm_platform_ip6_address_sync (self, ifindex, NULL, FALSE); } /******************************************************************/ GArray * -nm_platform_ip4_route_get_all (int ifindex, NMPlatformGetRouteMode mode) +nm_platform_ip4_route_get_all (NMPlatform *self, int ifindex, NMPlatformGetRouteFlags flags) { - reset_error (); + _CHECK_SELF (self, klass, NULL); g_return_val_if_fail (ifindex >= 0, NULL); - g_return_val_if_fail (klass->ip4_route_get_all, NULL); - return klass->ip4_route_get_all (platform, ifindex, mode); + return klass->ip4_route_get_all (self, ifindex, flags); } GArray * -nm_platform_ip6_route_get_all (int ifindex, NMPlatformGetRouteMode mode) +nm_platform_ip6_route_get_all (NMPlatform *self, int ifindex, NMPlatformGetRouteFlags flags) { - reset_error (); + _CHECK_SELF (self, klass, NULL); g_return_val_if_fail (ifindex >= 0, NULL); - g_return_val_if_fail (klass->ip6_route_get_all, NULL); - return klass->ip6_route_get_all (platform, ifindex, mode); + return klass->ip6_route_get_all (self, ifindex, flags); } gboolean -nm_platform_ip4_route_add (int ifindex, NMIPConfigSource source, +nm_platform_ip4_route_add (NMPlatform *self, + int ifindex, NMIPConfigSource source, in_addr_t network, int plen, in_addr_t gateway, guint32 pref_src, guint32 metric, guint32 mss) { - reset_error (); + _CHECK_SELF (self, klass, FALSE); - g_return_val_if_fail (platform, FALSE); g_return_val_if_fail (0 <= plen && plen <= 32, FALSE); g_return_val_if_fail (klass->ip4_route_add, FALSE); if (nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) { NMPlatformIP4Route route = { 0 }; - char pref_src_buf[NM_UTILS_INET_ADDRSTRLEN]; route.ifindex = ifindex; route.source = source; @@ -1991,21 +2200,21 @@ nm_platform_ip4_route_add (int ifindex, NMIPConfigSource source, route.gateway = gateway; route.metric = metric; route.mss = mss; + route.pref_src = pref_src; - debug ("route: adding or updating IPv4 route: %s%s%s%s", nm_platform_ip4_route_to_string (&route), - pref_src ? " (src: " : "", - pref_src ? nm_utils_inet4_ntop (pref_src, pref_src_buf) : "", - pref_src ? ")" : ""); + _LOGD ("route: adding or updating IPv4 route: %s", nm_platform_ip4_route_to_string (&route)); } - return klass->ip4_route_add (platform, ifindex, source, network, plen, gateway, pref_src, metric, mss); + return klass->ip4_route_add (self, ifindex, source, network, plen, gateway, pref_src, metric, mss); } gboolean -nm_platform_ip6_route_add (int ifindex, NMIPConfigSource source, +nm_platform_ip6_route_add (NMPlatform *self, + int ifindex, NMIPConfigSource source, struct in6_addr network, int plen, struct in6_addr gateway, guint32 metric, guint32 mss) { - g_return_val_if_fail (platform, FALSE); + _CHECK_SELF (self, klass, FALSE); + g_return_val_if_fail (0 <= plen && plen <= 128, FALSE); g_return_val_if_fail (klass->ip6_route_add, FALSE); @@ -2020,256 +2229,55 @@ nm_platform_ip6_route_add (int ifindex, NMIPConfigSource source, route.metric = metric; route.mss = mss; - debug ("route: adding or updating IPv6 route: %s", nm_platform_ip6_route_to_string (&route)); + _LOGD ("route: adding or updating IPv6 route: %s", nm_platform_ip6_route_to_string (&route)); } - return klass->ip6_route_add (platform, ifindex, source, network, plen, gateway, metric, mss); + return klass->ip6_route_add (self, ifindex, source, network, plen, gateway, metric, mss); } gboolean -nm_platform_ip4_route_delete (int ifindex, in_addr_t network, int plen, guint32 metric) +nm_platform_ip4_route_delete (NMPlatform *self, int ifindex, in_addr_t network, int plen, guint32 metric) { char str_dev[TO_STRING_DEV_BUF_SIZE]; - reset_error (); + _CHECK_SELF (self, klass, FALSE); - g_return_val_if_fail (platform, FALSE); g_return_val_if_fail (klass->ip4_route_delete, FALSE); - debug ("route: deleting IPv4 route %s/%d, metric=%"G_GUINT32_FORMAT", ifindex %d%s", + _LOGD ("route: deleting IPv4 route %s/%d, metric=%"G_GUINT32_FORMAT", ifindex %d%s", nm_utils_inet4_ntop (network, NULL), plen, metric, ifindex, - _to_string_dev (ifindex, str_dev, sizeof (str_dev))); - return klass->ip4_route_delete (platform, ifindex, network, plen, metric); + _to_string_dev (self, ifindex, str_dev, sizeof (str_dev))); + return klass->ip4_route_delete (self, ifindex, network, plen, metric); } gboolean -nm_platform_ip6_route_delete (int ifindex, struct in6_addr network, int plen, guint32 metric) +nm_platform_ip6_route_delete (NMPlatform *self, int ifindex, struct in6_addr network, int plen, guint32 metric) { char str_dev[TO_STRING_DEV_BUF_SIZE]; - reset_error (); + _CHECK_SELF (self, klass, FALSE); - g_return_val_if_fail (platform, FALSE); g_return_val_if_fail (klass->ip6_route_delete, FALSE); - debug ("route: deleting IPv6 route %s/%d, metric=%"G_GUINT32_FORMAT", ifindex %d%s", + _LOGD ("route: deleting IPv6 route %s/%d, metric=%"G_GUINT32_FORMAT", ifindex %d%s", nm_utils_inet6_ntop (&network, NULL), plen, metric, ifindex, - _to_string_dev (ifindex, str_dev, sizeof (str_dev))); - return klass->ip6_route_delete (platform, ifindex, network, plen, metric); -} - -gboolean -nm_platform_ip4_route_exists (int ifindex, in_addr_t network, int plen, guint32 metric) -{ - reset_error (); - - g_return_val_if_fail (platform, FALSE); - g_return_val_if_fail (klass->ip4_route_exists, FALSE); - - return klass->ip4_route_exists (platform,ifindex, network, plen, metric); -} - -gboolean -nm_platform_ip6_route_exists (int ifindex, struct in6_addr network, int plen, guint32 metric) -{ - reset_error (); - - g_return_val_if_fail (platform, FALSE); - g_return_val_if_fail (klass->ip6_route_exists, FALSE); - - return klass->ip6_route_exists (platform, ifindex, network, plen, metric); -} - -static gboolean -array_contains_ip4_route (const GArray *routes, const NMPlatformIP4Route *route) -{ - guint len = routes ? routes->len : 0; - guint i; - - for (i = 0; i < len; i++) { - NMPlatformIP4Route *c = &g_array_index (routes, NMPlatformIP4Route, i); - - if (route->network == c->network && - route->plen == c->plen && - route->gateway == c->gateway && - route->metric == c->metric) - return TRUE; - } - - return FALSE; + _to_string_dev (self, ifindex, str_dev, sizeof (str_dev))); + return klass->ip6_route_delete (self, ifindex, network, plen, metric); } -static gboolean -array_contains_ip6_route (const GArray *routes, const NMPlatformIP6Route *route) +const NMPlatformIP4Route * +nm_platform_ip4_route_get (NMPlatform *self, int ifindex, in_addr_t network, int plen, guint32 metric) { - guint len = routes ? routes->len : 0; - guint i; + _CHECK_SELF (self, klass, FALSE); - for (i = 0; i < len; i++) { - NMPlatformIP6Route *c = &g_array_index (routes, NMPlatformIP6Route, i); - - if (IN6_ARE_ADDR_EQUAL (&route->network, &c->network) && - route->plen == c->plen && - IN6_ARE_ADDR_EQUAL (&route->gateway, &c->gateway) && - route->metric == c->metric) - return TRUE; - } - - return FALSE; + return klass->ip4_route_get (self ,ifindex, network, plen, metric); } -/** - * nm_platform_ip4_route_sync: - * @ifindex: Interface index - * @known_routes: List of routes - * - * A convenience function to synchronize routes for a specific interface - * with the least possible disturbance. It simply removes routes that are - * not listed and adds routes that are. - * Default routes are ignored (both in @known_routes and those already - * configured on the device). - * - * Returns: %TRUE on success. - */ -gboolean -nm_platform_ip4_route_sync (int ifindex, const GArray *known_routes) +const NMPlatformIP6Route * +nm_platform_ip6_route_get (NMPlatform *self, int ifindex, struct in6_addr network, int plen, guint32 metric) { - GArray *routes; - NMPlatformIP4Route *route; - const NMPlatformIP4Route *known_route; - gboolean success; - int i, i_type; - - /* Delete unknown routes */ - routes = nm_platform_ip4_route_get_all (ifindex, NM_PLATFORM_GET_ROUTE_MODE_NO_DEFAULT); - for (i = 0; i < routes->len; i++) { - route = &g_array_index (routes, NMPlatformIP4Route, i); - - if (!array_contains_ip4_route (known_routes, route)) - (void) nm_platform_ip4_route_delete (ifindex, route->network, route->plen, route->metric); - } - - if (!known_routes) { - g_array_free (routes, TRUE); - return TRUE; - } - - /* Add missing routes */ - for (i_type = 0, success = TRUE; i_type < 2 && success; i_type++) { - for (i = 0; i < known_routes->len && success; i++) { - known_route = &g_array_index (known_routes, NMPlatformIP4Route, i); - - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (known_route)) - continue; + _CHECK_SELF (self, klass, FALSE); - if ( (i_type == 0 && known_route->gateway != 0) - || (i_type == 1 && known_route->gateway == 0)) { - /* Make two runs over the list of routes. On the first, only add - * device routes, on the second the others (gateway routes). */ - continue; - } - - /* Ignore routes that already exist */ - if (!array_contains_ip4_route (routes, known_route)) { - success = nm_platform_ip4_route_add (ifindex, - known_route->source, - known_route->network, - known_route->plen, - known_route->gateway, - 0, - known_route->metric, - known_route->mss); - if (!success && known_route->source < NM_IP_CONFIG_SOURCE_USER) { - nm_log_dbg (LOGD_PLATFORM, "ignore error adding IPv4 route to kernel: %s", - nm_platform_ip4_route_to_string (known_route)); - success = TRUE; - } - } - } - } - - g_array_free (routes, TRUE); - return success; -} - -/** - * nm_platform_ip6_route_sync: - * @ifindex: Interface index - * @known_routes: List of routes - * - * A convenience function to synchronize routes for a specific interface - * with the least possible disturbance. It simply removes routes that are - * not listed and adds routes that are. - * Default routes are ignored (both in @known_routes and those already - * configured on the device). - * - * Returns: %TRUE on success. - */ -gboolean -nm_platform_ip6_route_sync (int ifindex, const GArray *known_routes) -{ - GArray *routes; - NMPlatformIP6Route *route; - const NMPlatformIP6Route *known_route; - gboolean success; - int i, i_type; - - /* Delete unknown routes */ - routes = nm_platform_ip6_route_get_all (ifindex, NM_PLATFORM_GET_ROUTE_MODE_NO_DEFAULT); - for (i = 0; i < routes->len; i++) { - route = &g_array_index (routes, NMPlatformIP6Route, i); - route->ifindex = 0; - - if (!array_contains_ip6_route (known_routes, route)) - nm_platform_ip6_route_delete (ifindex, route->network, route->plen, route->metric); - } - - if (!known_routes) { - g_array_free (routes, TRUE); - return TRUE; - } - - /* Add missing routes */ - for (i_type = 0, success = TRUE; i_type < 2 && success; i_type++) { - for (i = 0; i < known_routes->len && success; i++) { - known_route = &g_array_index (known_routes, NMPlatformIP6Route, i); - - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (known_route)) - continue; - - if ( (i_type == 0 && !IN6_IS_ADDR_UNSPECIFIED (&known_route->gateway)) - || (i_type == 1 && IN6_IS_ADDR_UNSPECIFIED (&known_route->gateway))) { - /* Make two runs over the list of routes. On the first, only add - * device routes, on the second the others (gateway routes). */ - continue; - } - - /* Ignore routes that already exist */ - if (!array_contains_ip6_route (routes, known_route)) { - success = nm_platform_ip6_route_add (ifindex, - known_route->source, - known_route->network, - known_route->plen, - known_route->gateway, - known_route->metric, - known_route->mss); - if (!success && known_route->source < NM_IP_CONFIG_SOURCE_USER) { - nm_log_dbg (LOGD_PLATFORM, "ignore error adding IPv6 route to kernel: %s", - nm_platform_ip6_route_to_string (known_route)); - success = TRUE; - } - } - } - } - - g_array_free (routes, TRUE); - return success; -} - -gboolean -nm_platform_route_flush (int ifindex) -{ - return nm_platform_ip4_route_sync (ifindex, NULL) - && nm_platform_ip6_route_sync (ifindex, NULL); + return klass->ip6_route_get (self, ifindex, network, plen, metric); } /******************************************************************/ @@ -2278,6 +2286,10 @@ static const char * source_to_string (NMIPConfigSource source) { switch (source) { + case NM_IP_CONFIG_SOURCE_RTPROT_KERNEL: + return "rtprot-kernel"; + case _NM_IP_CONFIG_SOURCE_RTM_F_CLONED: + return "rtm-f-cloned"; case NM_IP_CONFIG_SOURCE_KERNEL: return "kernel"; case NM_IP_CONFIG_SOURCE_SHARED: @@ -2309,7 +2321,7 @@ _lifetime_to_string (guint32 timestamp, guint32 lifetime, gint32 now, char *buf, return "forever"; g_snprintf (buf, buf_size, "%usec", - _rebase_relative_time_on_now (timestamp, lifetime, now, 0)); + nmp_utils_lifetime_rebase_relative_time_on_now (timestamp, lifetime, now, 0)); return buf; } @@ -2322,28 +2334,38 @@ _lifetime_summary_to_string (gint32 now, guint32 timestamp, guint32 preferred, g return buf; } -static char to_string_buffer[256]; +char _nm_platform_to_string_buffer[256]; const char * nm_platform_link_to_string (const NMPlatformLink *link) { char master[20]; char parent[20]; - char *driver, *udi, *type; - GString *str; + char str_vlan[16]; + GString *str_flags; + char str_addrmode[30]; + gs_free char *str_addr = NULL; + gs_free char *str_inet6_token = NULL; if (!link) return "(unknown link)"; - str = g_string_new (NULL); - if (!link->arp) - g_string_append (str, "NOARP,"); - if (link->up) - g_string_append (str, "UP"); + str_flags = g_string_new (NULL); + if (NM_FLAGS_HAS (link->flags, IFF_NOARP)) + g_string_append (str_flags, "NOARP,"); + if (NM_FLAGS_HAS (link->flags, IFF_UP)) + g_string_append (str_flags, "UP"); else - g_string_append (str, "DOWN"); + g_string_append (str_flags, "DOWN"); if (link->connected) - g_string_append (str, ",LOWER_UP"); + g_string_append (str_flags, ",LOWER_UP"); + + if (link->flags) { + char str_flags_buf[64]; + + rtnl_link_flags2str (link->flags, str_flags_buf, sizeof (str_flags_buf)); + g_string_append_printf (str_flags, ";%s", str_flags_buf); + } if (link->master) g_snprintf (master, sizeof (master), " master %d", link->master); @@ -2355,19 +2377,69 @@ nm_platform_link_to_string (const NMPlatformLink *link) else parent[0] = 0; - driver = link->driver ? g_strdup_printf (" driver '%s'", link->driver) : NULL; - udi = link->udi ? g_strdup_printf (" udi '%s'", link->udi) : NULL; - type = link->type_name ? NULL : g_strdup_printf ("(%d)", link->type); - - g_snprintf (to_string_buffer, sizeof (to_string_buffer), "%d: %s%s <%s> mtu %d%s %s%s%s", - link->ifindex, link->name, parent, str->str, - link->mtu, master, link->type_name ? link->type_name : type, - driver ? driver : "", udi ? udi : ""); - g_string_free (str, TRUE); - g_free (driver); - g_free (udi); - g_free (type); - return to_string_buffer; + if (link->vlan_id) + g_snprintf (str_vlan, sizeof (str_vlan), " vlan %u", (guint) link->vlan_id); + else + str_vlan[0] = '\0'; + + if (link->inet6_addr_gen_mode_inv) { + switch (_nm_platform_uint8_inv (link->inet6_addr_gen_mode_inv)) { + case 0: + g_snprintf (str_addrmode, sizeof (str_addrmode), " addrgenmode eui64"); + break; + case 1: + g_snprintf (str_addrmode, sizeof (str_addrmode), " addrgenmode none"); + break; + default: + g_snprintf (str_addrmode, sizeof (str_addrmode), " addrgenmode %d", _nm_platform_uint8_inv (link->inet6_addr_gen_mode_inv)); + break; + } + } else + str_addrmode[0] = '\0'; + + if (link->addr.len) + str_addr = nm_utils_hwaddr_ntoa (link->addr.data, MIN (link->addr.len, sizeof (link->addr.data))); + if (link->inet6_token.is_valid) + str_inet6_token = nm_utils_hwaddr_ntoa (&link->inet6_token.iid, sizeof (link->inet6_token.iid)); + + g_snprintf (_nm_platform_to_string_buffer, sizeof (_nm_platform_to_string_buffer), + "%d: " /* ifindex */ + "%s" /* name */ + "%s" /* parent */ + " <%s>" /* flags */ + " mtu %d" + "%s" /* master */ + "%s" /* vlan */ + " arp %u" /* arptype */ + "%s%s" /* link->type */ + "%s%s" /* kind */ + "%s" /* is-in-udev */ + "%s" /* addr-gen-mode */ + "%s%s" /* addr */ + "%s%s" /* inet6_token */ + "%s%s" /* driver */ + , + link->ifindex, + link->name, + parent, + str_flags->str, + link->mtu, master, + str_vlan, + link->arptype, + nm_link_type_to_string (link->type) ? " " : "", + str_if_set (nm_link_type_to_string (link->type), "???"), + link->kind ? (g_strcmp0 (nm_link_type_to_string (link->type), link->kind) ? "/" : "*") : "", + link->kind && g_strcmp0 (nm_link_type_to_string (link->type), link->kind) ? link->kind : "", + link->initialized ? " init" : " not-init", + str_addrmode, + str_addr ? " addr " : "", + str_addr ? str_addr : "", + str_inet6_token ? " inet6token " : "", + str_inet6_token ? str_inet6_token : "", + link->driver ? " driver " : "", + link->driver ? link->driver : ""); + g_string_free (str_flags, TRUE); + return _nm_platform_to_string_buffer; } /** @@ -2403,7 +2475,7 @@ nm_platform_ip4_address_to_string (const NMPlatformIP4Address *address) str_peer = g_strconcat (" ptp ", s_peer, NULL); } - _to_string_dev (address->ifindex, str_dev, sizeof (str_dev)); + _to_string_dev (NULL, address->ifindex, str_dev, sizeof (str_dev)); if (*address->label) g_snprintf (str_label, sizeof (str_label), " label %s", address->label); @@ -2420,14 +2492,14 @@ nm_platform_ip4_address_to_string (const NMPlatformIP4Address *address) now, str_pref, sizeof (str_pref)) ); str_time_p = _lifetime_summary_to_string (now, address->timestamp, address->preferred, address->lifetime, str_time, sizeof (str_time)); - g_snprintf (to_string_buffer, sizeof (to_string_buffer), "%s/%d lft %s pref %s%s%s%s%s src %s", + g_snprintf (_nm_platform_to_string_buffer, sizeof (_nm_platform_to_string_buffer), "%s/%d lft %s pref %s%s%s%s%s src %s", s_address, address->plen, str_lft_p, str_pref_p, str_time_p, str_peer ? str_peer : "", str_dev, str_label, source_to_string (address->source)); g_free (str_peer); - return to_string_buffer; + return _nm_platform_to_string_buffer; } /** @@ -2488,7 +2560,7 @@ nm_platform_ip6_address_to_string (const NMPlatformIP6Address *address) str_peer = g_strconcat (" ptp ", s_peer, NULL); } - _to_string_dev (address->ifindex, str_dev, sizeof (str_dev)); + _to_string_dev (NULL, address->ifindex, str_dev, sizeof (str_dev)); nm_platform_addr_flags2str (address->flags, s_flags, sizeof (s_flags)); @@ -2504,7 +2576,7 @@ nm_platform_ip6_address_to_string (const NMPlatformIP6Address *address) now, str_pref, sizeof (str_pref)) ); str_time_p = _lifetime_summary_to_string (now, address->timestamp, address->preferred, address->lifetime, str_time, sizeof (str_time)); - g_snprintf (to_string_buffer, sizeof (to_string_buffer), "%s/%d lft %s pref %s%s%s%s%s src %s", + g_snprintf (_nm_platform_to_string_buffer, sizeof (_nm_platform_to_string_buffer), "%s/%d lft %s pref %s%s%s%s%s src %s", s_address, address->plen, str_lft_p, str_pref_p, str_time_p, str_peer ? str_peer : "", str_dev, @@ -2512,7 +2584,7 @@ nm_platform_ip6_address_to_string (const NMPlatformIP6Address *address) source_to_string (address->source)); g_free (str_flags); g_free (str_peer); - return to_string_buffer; + return _nm_platform_to_string_buffer; } /** @@ -2531,21 +2603,38 @@ const char * nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route) { char s_network[INET_ADDRSTRLEN], s_gateway[INET_ADDRSTRLEN]; + char s_pref_src[INET_ADDRSTRLEN]; char str_dev[TO_STRING_DEV_BUF_SIZE]; + char str_scope[30]; g_return_val_if_fail (route, "(unknown)"); inet_ntop (AF_INET, &route->network, s_network, sizeof(s_network)); inet_ntop (AF_INET, &route->gateway, s_gateway, sizeof(s_gateway)); - _to_string_dev (route->ifindex, str_dev, sizeof (str_dev)); - - g_snprintf (to_string_buffer, sizeof (to_string_buffer), "%s/%d via %s%s metric %"G_GUINT32_FORMAT" mss %"G_GUINT32_FORMAT" src %s", - s_network, route->plen, s_gateway, + _to_string_dev (NULL, route->ifindex, str_dev, sizeof (str_dev)); + + g_snprintf (_nm_platform_to_string_buffer, sizeof (_nm_platform_to_string_buffer), + "%s/%d" + " via %s" + "%s" + " metric %"G_GUINT32_FORMAT + " mss %"G_GUINT32_FORMAT + " src %s" /* source */ + "%s%s" /* scope */ + "%s%s" /* pref-src */ + "", + s_network, route->plen, + s_gateway, str_dev, - route->metric, route->mss, - source_to_string (route->source)); - return to_string_buffer; + route->metric, + route->mss, + source_to_string (route->source), + route->scope_inv ? " scope " : "", + route->scope_inv ? (rtnl_scope2str (nm_platform_route_scope_inv (route->scope_inv), str_scope, sizeof (str_scope))) : "", + route->pref_src ? " pref-src " : "", + route->pref_src ? inet_ntop (AF_INET, &route->pref_src, s_pref_src, sizeof(s_pref_src)) : ""); + return _nm_platform_to_string_buffer; } /** @@ -2571,14 +2660,23 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route) inet_ntop (AF_INET6, &route->network, s_network, sizeof(s_network)); inet_ntop (AF_INET6, &route->gateway, s_gateway, sizeof(s_gateway)); - _to_string_dev (route->ifindex, str_dev, sizeof (str_dev)); - - g_snprintf (to_string_buffer, sizeof (to_string_buffer), "%s/%d via %s%s metric %"G_GUINT32_FORMAT" mss %"G_GUINT32_FORMAT" src %s", - s_network, route->plen, s_gateway, + _to_string_dev (NULL, route->ifindex, str_dev, sizeof (str_dev)); + + g_snprintf (_nm_platform_to_string_buffer, sizeof (_nm_platform_to_string_buffer), + "%s/%d" + " via %s" + "%s" + " metric %"G_GUINT32_FORMAT + " mss %"G_GUINT32_FORMAT + " src %s" /* source */ + "", + s_network, route->plen, + s_gateway, str_dev, - route->metric, route->mss, + route->metric, + route->mss, source_to_string (route->source)); - return to_string_buffer; + return _nm_platform_to_string_buffer; } #define _CMP_POINTER(a, b) \ @@ -2597,6 +2695,12 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route) return (((a)->field) < ((b)->field)) ? -1 : 1; \ } G_STMT_END +#define _CMP_FIELD_BOOL(a, b, field) \ + G_STMT_START { \ + if ((!((a)->field)) != (!((b)->field))) \ + return ((!((a)->field)) < (!((b)->field))) ? -1 : 1; \ + } G_STMT_END + #define _CMP_FIELD_STR(a, b, field) \ G_STMT_START { \ int c = strcmp ((a)->field, (b)->field); \ @@ -2604,6 +2708,16 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route) return c < 0 ? -1 : 1; \ } G_STMT_END +#define _CMP_FIELD_STR_INTERNED(a, b, field) \ + G_STMT_START { \ + if (((a)->field) != ((b)->field)) { \ + /* just to be sure, also do a strcmp() if the pointers don't match */ \ + int c = g_strcmp0 ((a)->field, (b)->field); \ + if (c != 0) \ + return c < 0 ? -1 : 1; \ + } \ + } G_STMT_END + #define _CMP_FIELD_STR0(a, b, field) \ G_STMT_START { \ int c = g_strcmp0 ((a)->field, (b)->field); \ @@ -2611,6 +2725,14 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route) return c < 0 ? -1 : 1; \ } G_STMT_END +#define _CMP_FIELD_MEMCMP_LEN(a, b, field, len) \ + G_STMT_START { \ + int c = memcmp (&((a)->field), &((b)->field), \ + MIN (len, sizeof ((a)->field))); \ + if (c != 0) \ + return c < 0 ? -1 : 1; \ + } G_STMT_END + #define _CMP_FIELD_MEMCMP(a, b, field) \ G_STMT_START { \ int c = memcmp (&((a)->field), &((b)->field), \ @@ -2623,17 +2745,26 @@ int nm_platform_link_cmp (const NMPlatformLink *a, const NMPlatformLink *b) { _CMP_POINTER (a, b); + _CMP_FIELD (a, b, ifindex); _CMP_FIELD (a, b, type); _CMP_FIELD_STR (a, b, name); _CMP_FIELD (a, b, master); _CMP_FIELD (a, b, parent); - _CMP_FIELD (a, b, up); + _CMP_FIELD (a, b, vlan_id); + _CMP_FIELD (a, b, flags); _CMP_FIELD (a, b, connected); - _CMP_FIELD (a, b, arp); _CMP_FIELD (a, b, mtu); - _CMP_FIELD_STR0 (a, b, type_name); - _CMP_FIELD_STR0 (a, b, udi); - _CMP_FIELD_STR0 (a, b, driver); + _CMP_FIELD_BOOL (a, b, initialized); + _CMP_FIELD (a, b, arptype); + _CMP_FIELD (a, b, addr.len); + _CMP_FIELD (a, b, inet6_addr_gen_mode_inv); + _CMP_FIELD (a, b, inet6_token.is_valid); + _CMP_FIELD_STR_INTERNED (a, b, kind); + _CMP_FIELD_STR_INTERNED (a, b, driver); + if (a->addr.len) + _CMP_FIELD_MEMCMP_LEN (a, b, addr.data, a->addr.len); + if (a->inet6_token.is_valid) + _CMP_FIELD_MEMCMP (a, b, inet6_token.iid); return 0; } @@ -2680,6 +2811,8 @@ nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4Route _CMP_FIELD (a, b, gateway); _CMP_FIELD (a, b, metric); _CMP_FIELD (a, b, mss); + _CMP_FIELD (a, b, scope_inv); + _CMP_FIELD (a, b, pref_src); return 0; } @@ -2753,8 +2886,8 @@ nm_platform_ip_address_cmp_expiry (const NMPlatformIPAddress *a, const NMPlatfor #undef _CMP_POINTER -static const char * -_change_type_to_string (NMPlatformSignalChangeType change_type) +const char * +nm_platform_signal_change_type_to_string (NMPlatformSignalChangeType change_type) { switch (change_type) { case NM_PLATFORM_SIGNAL_ADDED: @@ -2769,76 +2902,80 @@ _change_type_to_string (NMPlatformSignalChangeType change_type) } static void -log_link (NMPlatform *p, int ifindex, NMPlatformLink *device, NMPlatformSignalChangeType change_type, gpointer user_data) +log_link (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformLink *device, NMPlatformSignalChangeType change_type, gpointer user_data) { - debug ("signal: link %7s: %s", _change_type_to_string (change_type), nm_platform_link_to_string (device)); + _LOGD ("signal: link %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_link_to_string (device)); } static void -log_ip4_address (NMPlatform *p, int ifindex, NMPlatformIP4Address *address, NMPlatformSignalChangeType change_type, gpointer user_data) +log_ip4_address (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformIP4Address *address, NMPlatformSignalChangeType change_type, gpointer user_data) { - debug ("signal: address 4 %7s: %s", _change_type_to_string (change_type), nm_platform_ip4_address_to_string (address)); + _LOGD ("signal: address 4 %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_ip4_address_to_string (address)); } static void -log_ip6_address (NMPlatform *p, int ifindex, NMPlatformIP6Address *address, NMPlatformSignalChangeType change_type, gpointer user_data) +log_ip6_address (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformIP6Address *address, NMPlatformSignalChangeType change_type, gpointer user_data) { - debug ("signal: address 6 %7s: %s", _change_type_to_string (change_type), nm_platform_ip6_address_to_string (address)); + _LOGD ("signal: address 6 %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_ip6_address_to_string (address)); } static void -log_ip4_route (NMPlatform *p, int ifindex, NMPlatformIP4Route *route, NMPlatformSignalChangeType change_type, gpointer user_data) +log_ip4_route (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformIP4Route *route, NMPlatformSignalChangeType change_type, gpointer user_data) { - debug ("signal: route 4 %7s: %s", _change_type_to_string (change_type), nm_platform_ip4_route_to_string (route)); + _LOGD ("signal: route 4 %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_ip4_route_to_string (route)); } static void -log_ip6_route (NMPlatform *p, int ifindex, NMPlatformIP6Route *route, NMPlatformSignalChangeType change_type, gpointer user_data) +log_ip6_route (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformIP6Route *route, NMPlatformSignalChangeType change_type, gpointer user_data) { - debug ("signal: route 6 %7s: %s", _change_type_to_string (change_type), nm_platform_ip6_route_to_string (route)); + _LOGD ("signal: route 6 %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_ip6_route_to_string (route)); } /******************************************************************/ static gboolean -_vtr_v4_route_add (int ifindex, const NMPlatformIPXRoute *route, guint32 v4_pref_src) +_vtr_v4_route_add (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route, gint64 metric) { - return nm_platform_ip4_route_add (ifindex > 0 ? ifindex : route->rx.ifindex, + return nm_platform_ip4_route_add (self, + ifindex > 0 ? ifindex : route->rx.ifindex, route->rx.source, route->r4.network, route->rx.plen, route->r4.gateway, - v4_pref_src, - route->rx.metric, + route->r4.pref_src, + metric >= 0 ? (guint32) metric : route->rx.metric, route->rx.mss); } static gboolean -_vtr_v6_route_add (int ifindex, const NMPlatformIPXRoute *route, guint32 v4_pref_src) +_vtr_v6_route_add (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route, gint64 metric) { - return nm_platform_ip6_route_add (ifindex > 0 ? ifindex : route->rx.ifindex, + return nm_platform_ip6_route_add (self, + ifindex > 0 ? ifindex : route->rx.ifindex, route->rx.source, route->r6.network, route->rx.plen, route->r6.gateway, - route->rx.metric, + metric >= 0 ? (guint32) metric : route->rx.metric, route->rx.mss); } static gboolean -_vtr_v4_route_delete (int ifindex, const NMPlatformIPXRoute *route) +_vtr_v4_route_delete (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route) { - return nm_platform_ip4_route_delete (ifindex > 0 ? ifindex : route->rx.ifindex, + return nm_platform_ip4_route_delete (self, + ifindex > 0 ? ifindex : route->rx.ifindex, route->r4.network, route->rx.plen, route->rx.metric); } static gboolean -_vtr_v6_route_delete (int ifindex, const NMPlatformIPXRoute *route) +_vtr_v6_route_delete (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route) { - return nm_platform_ip6_route_delete (ifindex > 0 ? ifindex : route->rx.ifindex, + return nm_platform_ip6_route_delete (self, + ifindex > 0 ? ifindex : route->rx.ifindex, route->r6.network, route->rx.plen, route->rx.metric); @@ -2851,15 +2988,15 @@ _vtr_v4_metric_normalize (guint32 metric) } static gboolean -_vtr_v4_route_delete_default (int ifindex, guint32 metric) +_vtr_v4_route_delete_default (NMPlatform *self, int ifindex, guint32 metric) { - return nm_platform_ip4_route_delete (ifindex, 0, 0, metric); + return nm_platform_ip4_route_delete (self, ifindex, 0, 0, metric); } static gboolean -_vtr_v6_route_delete_default (int ifindex, guint32 metric) +_vtr_v6_route_delete_default (NMPlatform *self, int ifindex, guint32 metric) { - return nm_platform_ip6_route_delete (ifindex, in6addr_any, 0, metric); + return nm_platform_ip6_route_delete (self, ifindex, in6addr_any, 0, metric); } /******************************************************************/ @@ -2893,6 +3030,35 @@ const NMPlatformVTableRoute nm_platform_vtable_route_v6 = { /******************************************************************/ static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_REGISTER_SINGLETON: + /* construct-only */ + priv->register_singleton = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +constructed (GObject *object) +{ + NMPlatform *self = NM_PLATFORM (object); + NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE (self); + + G_OBJECT_CLASS (nm_platform_parent_class)->constructed (object); + + if (priv->register_singleton) + nm_platform_setup (self); +} + +static void nm_platform_init (NMPlatform *object) { } @@ -2903,13 +3069,26 @@ nm_platform_init (NMPlatform *object) G_SIGNAL_RUN_FIRST, \ G_CALLBACK (method), \ NULL, NULL, NULL, \ - G_TYPE_NONE, 4, G_TYPE_INT, G_TYPE_POINTER, NM_TYPE_PLATFORM_SIGNAL_CHANGE_TYPE, NM_TYPE_PLATFORM_REASON); + G_TYPE_NONE, 5, NM_TYPE_POBJECT_TYPE, G_TYPE_INT, G_TYPE_POINTER, NM_TYPE_PLATFORM_SIGNAL_CHANGE_TYPE, NM_TYPE_PLATFORM_REASON); static void nm_platform_class_init (NMPlatformClass *platform_class) { GObjectClass *object_class = G_OBJECT_CLASS (platform_class); + g_type_class_add_private (object_class, sizeof (NMPlatformPrivate)); + + object_class->set_property = set_property; + object_class->constructed = constructed; + + g_object_class_install_property + (object_class, PROP_REGISTER_SINGLETON, + g_param_spec_boolean (NM_PLATFORM_REGISTER_SINGLETON, "", "", + FALSE, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /* Signals */ SIGNAL (SIGNAL_LINK_CHANGED, log_link) SIGNAL (SIGNAL_IP4_ADDRESS_CHANGED, log_ip4_address) diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index a49e0f11..4d254195 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -29,6 +29,7 @@ #include <nm-dbus-interface.h> #include "nm-types.h" +#include "NetworkManagerUtils.h" #define NM_TYPE_PLATFORM (nm_platform_get_type ()) #define NM_PLATFORM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_PLATFORM, NMPlatform)) @@ -39,6 +40,12 @@ /******************************************************************/ +#define NM_PLATFORM_REGISTER_SINGLETON "register-singleton" + +/******************************************************************/ + +typedef struct _NMPlatform NMPlatform; + /* workaround for older libnl version, that does not define these flags. */ #ifndef IFA_F_MANAGETEMPADDR #define IFA_F_MANAGETEMPADDR 0x100 @@ -47,19 +54,24 @@ #define IFA_F_NOPREFIXROUTE 0x200 #endif -typedef enum { - /* no error specified, sometimes this means the arguments were wrong */ - NM_PLATFORM_ERROR_NONE, - /* object was not found */ +typedef enum { /*< skip >*/ + + /* dummy value, to enforce that the enum type is signed and has a size + * to hold an integer. We want to encode errno from <errno.h> as negative + * values. */ + _NM_PLATFORM_ERROR_MININT = G_MININT, + + NM_PLATFORM_ERROR_SUCCESS = 0, + + NM_PLATFORM_ERROR_BUG, + + NM_PLATFORM_ERROR_UNSPECIFIED, + NM_PLATFORM_ERROR_NOT_FOUND, - /* object already exists */ NM_PLATFORM_ERROR_EXISTS, - /* object is wrong type */ NM_PLATFORM_ERROR_WRONG_TYPE, - /* object is not a slave */ NM_PLATFORM_ERROR_NOT_SLAVE, - /* firmware is not found */ - NM_PLATFORM_ERROR_NO_FIRMWARE + NM_PLATFORM_ERROR_NO_FIRMWARE, } NMPlatformError; typedef enum { @@ -83,18 +95,54 @@ struct _NMPlatformLink { __NMPlatformObject_COMMON; char name[IFNAMSIZ]; NMLinkType type; - const char *type_name; - const char *udi; + + /* rtnl_link_get_type(), IFLA_INFO_KIND. */ + /* NMPlatform initializes this field with a static string. */ + const char *kind; + + /* NMPlatform initializes this field with a static string. */ const char *driver; + + gboolean initialized; int master; int parent; - gboolean up; + + /* rtnl_link_get_arptype(), ifinfomsg.ifi_type. */ + guint32 arptype; + + /* rtnl_link_get_addr() */ + struct { + guint8 data[20]; /* NM_UTILS_HWADDR_LEN_MAX */ + guint8 len; + } addr; + + /* rtnl_link_inet6_get_token() */ + struct { + NMUtilsIPv6IfaceId iid; + guint8 is_valid; + } inet6_token; + + /* The bitwise inverse of rtnl_link_inet6_get_addr_gen_mode(). It is inverse + * to have a default of 0 -- meaning: unspecified. That way, a struct + * initialized with memset(0) has and unset value.*/ + guint8 inet6_addr_gen_mode_inv; + + /* rtnl_link_vlan_get_id(), IFLA_VLAN_ID */ + guint16 vlan_id; + + /* IFF_* flags as u32. Note that ifi_flags in 'struct ifinfomsg' is declared as 'unsigned', + * but libnl stores the flag internally as u32. */ + guint32 flags; + + /* @connected is mostly identical to (@flags & IFF_UP). Except for bridge/bond masters, + * where we coerce the link as disconnect if it has no slaves. */ gboolean connected; - gboolean arp; + guint mtu; }; typedef enum { + NM_PLATFORM_SIGNAL_NONE, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, @@ -102,11 +150,17 @@ typedef enum { #define NM_PLATFORM_LIFETIME_PERMANENT G_MAXUINT32 -typedef enum { - NM_PLATFORM_GET_ROUTE_MODE_ALL, - NM_PLATFORM_GET_ROUTE_MODE_NO_DEFAULT, - NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT, -} NMPlatformGetRouteMode; +typedef enum { /*< skip >*/ + NM_PLATFORM_GET_ROUTE_FLAGS_NONE = 0, + + /* Whether to include default-routes/non-default-routes. Omitting + * both WITH_DEFAULT and WITH_NON_DEFAULT, is equal to specifying + * both of them. */ + NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT = (1LL << 0), + NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT = (1LL << 1), + + NM_PLATFORM_GET_ROUTE_FLAGS_WITH_RTPROT_KERNEL = (1LL << 2), +} NMPlatformGetRouteFlags; typedef struct { __NMPlatformObject_COMMON; @@ -224,6 +278,14 @@ struct _NMPlatformIP4Route { __NMPlatformIPRoute_COMMON; in_addr_t network; in_addr_t gateway; + + /* The bitwise inverse of the route scope. It is inverted so that the + * default value (RT_SCOPE_NOWHERE) is nul. */ + guint8 scope_inv; + + /* RTA_PREFSRC/rtnl_route_get_pref_src(). A value of zero means that + * no pref-src is set. */ + guint32 pref_src; }; G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPRoute, network_ptr) == G_STRUCT_OFFSET (NMPlatformIP4Route, network)); @@ -252,16 +314,17 @@ typedef struct { gsize sizeof_route; int (*route_cmp) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b); const char *(*route_to_string) (const NMPlatformIPXRoute *route); - GArray *(*route_get_all) (int ifindex, NMPlatformGetRouteMode mode); - gboolean (*route_add) (int ifindex, const NMPlatformIPXRoute *route, guint32 v4_pref_src); - gboolean (*route_delete) (int ifindex, const NMPlatformIPXRoute *route); - gboolean (*route_delete_default) (int ifindex, guint32 metric); + GArray *(*route_get_all) (NMPlatform *self, int ifindex, NMPlatformGetRouteFlags flags); + gboolean (*route_add) (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route, gint64 metric); + gboolean (*route_delete) (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route); + gboolean (*route_delete_default) (NMPlatform *self, int ifindex, guint32 metric); guint32 (*metric_normalize) (guint32 metric); } NMPlatformVTableRoute; extern const NMPlatformVTableRoute nm_platform_vtable_route_v4; extern const NMPlatformVTableRoute nm_platform_vtable_route_v6; +extern char _nm_platform_to_string_buffer[256]; typedef struct { int peer; @@ -346,68 +409,81 @@ typedef struct { * network configuration daemons stopped. Look at the code first. */ -typedef struct { +struct _NMPlatform { GObject parent; - - NMPlatformError error; -} NMPlatform; +}; typedef struct { GObjectClass parent; - gboolean (*setup) (NMPlatform *); - gboolean (*sysctl_set) (NMPlatform *, const char *path, const char *value); char * (*sysctl_get) (NMPlatform *, const char *path); - gboolean (*link_get) (NMPlatform *platform, int ifindex, NMPlatformLink *link); + const NMPlatformLink *(*link_get) (NMPlatform *platform, int ifindex); + const NMPlatformLink *(*link_get_by_ifname) (NMPlatform *platform, const char *ifname); + const NMPlatformLink *(*link_get_by_address) (NMPlatform *platform, gconstpointer address, size_t length); + GArray *(*link_get_all) (NMPlatform *); - gboolean (*link_add) (NMPlatform *, const char *name, NMLinkType type, const void *address, size_t address_len); + gboolean (*link_add) (NMPlatform *, + const char *name, + NMLinkType type, + const void *address, + size_t address_len, + NMPlatformLink *out_link); gboolean (*link_delete) (NMPlatform *, int ifindex); - int (*link_get_ifindex) (NMPlatform *, const char *name); - const char *(*link_get_name) (NMPlatform *, int ifindex); - NMLinkType (*link_get_type) (NMPlatform *, int ifindex); const char *(*link_get_type_name) (NMPlatform *, int ifindex); + gboolean (*link_get_unmanaged) (NMPlatform *, int ifindex, gboolean *unmanaged); gboolean (*link_refresh) (NMPlatform *, int ifindex); + void (*process_events) (NMPlatform *self); - gboolean (*link_set_up) (NMPlatform *, int ifindex); + 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_is_up) (NMPlatform *, int ifindex); - gboolean (*link_is_connected) (NMPlatform *, int ifindex); - gboolean (*link_uses_arp) (NMPlatform *, int ifindex); - gboolean (*link_get_user_ipv6ll_enabled) (NMPlatform *, int ifindex); + const char *(*link_get_udi) (NMPlatform *self, int ifindex); + GObject *(*link_get_udev_device) (NMPlatform *self, int ifindex); + gboolean (*link_set_user_ipv6ll_enabled) (NMPlatform *, int ifindex, gboolean enabled); - gconstpointer (*link_get_address) (NMPlatform *, int ifindex, size_t *length); + gboolean (*link_get_permanent_address) (NMPlatform *, + int ifindex, + guint8 *buf, + size_t *length); gboolean (*link_set_address) (NMPlatform *, int ifindex, gconstpointer address, size_t length); - guint32 (*link_get_mtu) (NMPlatform *, int ifindex); gboolean (*link_set_mtu) (NMPlatform *, int ifindex, guint32 mtu); 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 *, + 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_enslave) (NMPlatform *, int master, int slave); gboolean (*link_release) (NMPlatform *, int master, int slave); - gboolean (*link_get_master) (NMPlatform *, int slave); gboolean (*master_set_option) (NMPlatform *, int ifindex, const char *option, const char *value); char * (*master_get_option) (NMPlatform *, int ifindex, const char *option); gboolean (*slave_set_option) (NMPlatform *, int ifindex, const char *option, const char *value); char * (*slave_get_option) (NMPlatform *, int ifindex, const char *option); - gboolean (*vlan_add) (NMPlatform *, const char *name, int parent, int vlanid, guint32 vlanflags); + gboolean (*vlan_add) (NMPlatform *, const char *name, int parent, int vlanid, guint32 vlanflags, NMPlatformLink *out_link); gboolean (*vlan_get_info) (NMPlatform *, int ifindex, int *parent, int *vlan_id); gboolean (*vlan_set_ingress_map) (NMPlatform *, int ifindex, int from, int to); gboolean (*vlan_set_egress_map) (NMPlatform *, int ifindex, int from, int to); - gboolean (*infiniband_partition_add) (NMPlatform *, int parent, int p_key); + gboolean (*infiniband_partition_add) (NMPlatform *, int parent, int p_key, NMPlatformLink *out_link); + gboolean (*infiniband_get_info) (NMPlatform *, + int ifindex, + int *parent, + int *p_key, + const char **mode); gboolean (*veth_get_properties) (NMPlatform *, int ifindex, NMPlatformVethProperties *properties); gboolean (*tun_get_properties) (NMPlatform *, int ifindex, NMPlatformTunProperties *properties); @@ -441,13 +517,11 @@ typedef struct { guint32 lifetime, guint32 preferred_lft, guint flags); gboolean (*ip4_address_delete) (NMPlatform *, int ifindex, in_addr_t address, int plen, in_addr_t peer_address); gboolean (*ip6_address_delete) (NMPlatform *, int ifindex, struct in6_addr address, int plen); - gboolean (*ip4_address_exists) (NMPlatform *, int ifindex, in_addr_t address, int plen); - gboolean (*ip6_address_exists) (NMPlatform *, int ifindex, struct in6_addr address, int plen); + const NMPlatformIP4Address *(*ip4_address_get) (NMPlatform *, int ifindex, in_addr_t address, int plen); + const NMPlatformIP6Address *(*ip6_address_get) (NMPlatform *, int ifindex, struct in6_addr address, int plen); - gboolean (*ip4_check_reinstall_device_route) (NMPlatform *, int ifindex, const NMPlatformIP4Address *address, guint32 device_route_metric); - - GArray * (*ip4_route_get_all) (NMPlatform *, int ifindex, NMPlatformGetRouteMode mode); - GArray * (*ip6_route_get_all) (NMPlatform *, int ifindex, NMPlatformGetRouteMode mode); + GArray * (*ip4_route_get_all) (NMPlatform *, int ifindex, NMPlatformGetRouteFlags flags); + GArray * (*ip6_route_get_all) (NMPlatform *, int ifindex, NMPlatformGetRouteFlags flags); gboolean (*ip4_route_add) (NMPlatform *, int ifindex, NMIPConfigSource source, in_addr_t network, int plen, in_addr_t gateway, guint32 pref_src, guint32 metric, guint32 mss); @@ -456,8 +530,8 @@ typedef struct { guint32 metric, guint32 mss); gboolean (*ip4_route_delete) (NMPlatform *, int ifindex, in_addr_t network, int plen, guint32 metric); gboolean (*ip6_route_delete) (NMPlatform *, int ifindex, struct in6_addr network, int plen, guint32 metric); - gboolean (*ip4_route_exists) (NMPlatform *, int ifindex, in_addr_t network, int plen, guint32 metric); - gboolean (*ip6_route_exists) (NMPlatform *, int ifindex, struct in6_addr network, int plen, guint32 metric); + const NMPlatformIP4Route *(*ip4_route_get) (NMPlatform *, int ifindex, in_addr_t network, int plen, guint32 metric); + const NMPlatformIP6Route *(*ip6_route_get) (NMPlatform *, int ifindex, struct in6_addr network, int plen, guint32 metric); gboolean (*check_support_kernel_extended_ifa_flags) (NMPlatform *); gboolean (*check_support_user_ipv6ll) (NMPlatform *); @@ -480,139 +554,172 @@ typedef struct { #define NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED "ip4-route-changed" #define NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED "ip6-route-changed" +const char *nm_platform_signal_change_type_to_string (NMPlatformSignalChangeType change_type); + /******************************************************************/ GType nm_platform_get_type (void); -void nm_platform_setup (GType type); +void nm_platform_setup (NMPlatform *instance); NMPlatform *nm_platform_get (void); -void nm_platform_free (void); +NMPlatform *nm_platform_try_get (void); + +#define NM_PLATFORM_GET (nm_platform_get ()) /******************************************************************/ -void nm_platform_set_error (NMPlatformError error); -NMPlatformError nm_platform_get_error (void); -const char *nm_platform_get_error_msg (void); - -void nm_platform_query_devices (void); - -gboolean nm_platform_sysctl_set (const char *path, const char *value); -char *nm_platform_sysctl_get (const char *path); -gint32 nm_platform_sysctl_get_int32 (const char *path, gint32 fallback); -gint64 nm_platform_sysctl_get_int_checked (const char *path, guint base, gint64 min, gint64 max, gint64 fallback); - -gboolean nm_platform_sysctl_set_ip6_hop_limit_safe (const char *iface, int value); - -gboolean nm_platform_link_get (int ifindex, NMPlatformLink *link); -GArray *nm_platform_link_get_all (void); -gboolean nm_platform_dummy_add (const char *name); -gboolean nm_platform_bridge_add (const char *name, const void *address, size_t address_len); -gboolean nm_platform_bond_add (const char *name); -gboolean nm_platform_team_add (const char *name); -gboolean nm_platform_link_exists (const char *name); -gboolean nm_platform_link_delete (int ifindex); -int nm_platform_link_get_ifindex (const char *name); -const char *nm_platform_link_get_name (int ifindex); -NMLinkType nm_platform_link_get_type (int ifindex); -const char *nm_platform_link_get_type_name (int ifindex); -gboolean nm_platform_link_is_software (int ifindex); -gboolean nm_platform_link_supports_slaves (int ifindex); - -gboolean nm_platform_link_refresh (int ifindex); - -gboolean nm_platform_link_set_up (int ifindex); -gboolean nm_platform_link_set_down (int ifindex); -gboolean nm_platform_link_set_arp (int ifindex); -gboolean nm_platform_link_set_noarp (int ifindex); -gboolean nm_platform_link_is_up (int ifindex); -gboolean nm_platform_link_is_connected (int ifindex); -gboolean nm_platform_link_uses_arp (int ifindex); - -gboolean nm_platform_link_get_user_ipv6ll_enabled (int ifindex); -gboolean nm_platform_link_set_user_ipv6ll_enabled (int ifindex, gboolean enabled); - -gconstpointer nm_platform_link_get_address (int ifindex, size_t *length); -gboolean nm_platform_link_set_address (int ifindex, const void *address, size_t length); -guint32 nm_platform_link_get_mtu (int ifindex); -gboolean nm_platform_link_set_mtu (int ifindex, guint32 mtu); - -char *nm_platform_link_get_physical_port_id (int ifindex); -guint nm_platform_link_get_dev_id (int ifindex); -gboolean nm_platform_link_get_wake_on_lan (int ifindex); - -gboolean nm_platform_link_supports_carrier_detect (int ifindex); -gboolean nm_platform_link_supports_vlans (int ifindex); - -gboolean nm_platform_link_enslave (int master, int slave); -gboolean nm_platform_link_release (int master, int slave); -int nm_platform_link_get_master (int slave); -gboolean nm_platform_master_set_option (int ifindex, const char *option, const char *value); -char *nm_platform_master_get_option (int ifindex, const char *option); -gboolean nm_platform_slave_set_option (int ifindex, const char *option, const char *value); -char *nm_platform_slave_get_option (int ifindex, const char *option); - -gboolean nm_platform_vlan_add (const char *name, int parent, int vlanid, guint32 vlanflags); -gboolean nm_platform_vlan_get_info (int ifindex, int *parent, int *vlanid); -gboolean nm_platform_vlan_set_ingress_map (int ifindex, int from, int to); -gboolean nm_platform_vlan_set_egress_map (int ifindex, int from, int to); - -gboolean nm_platform_infiniband_partition_add (int parent, int p_key); - -gboolean nm_platform_veth_get_properties (int ifindex, NMPlatformVethProperties *properties); -gboolean nm_platform_tun_get_properties (int ifindex, NMPlatformTunProperties *properties); -gboolean nm_platform_macvlan_get_properties (int ifindex, NMPlatformMacvlanProperties *props); -gboolean nm_platform_vxlan_get_properties (int ifindex, NMPlatformVxlanProperties *props); -gboolean nm_platform_gre_get_properties (int ifindex, NMPlatformGreProperties *props); - -gboolean nm_platform_wifi_get_capabilities (int ifindex, NMDeviceWifiCapabilities *caps); -gboolean nm_platform_wifi_get_bssid (int ifindex, guint8 *bssid); -GByteArray *nm_platform_wifi_get_ssid (int ifindex); -guint32 nm_platform_wifi_get_frequency (int ifindex); -int nm_platform_wifi_get_quality (int ifindex); -guint32 nm_platform_wifi_get_rate (int ifindex); -NM80211Mode nm_platform_wifi_get_mode (int ifindex); -void nm_platform_wifi_set_mode (int ifindex, NM80211Mode mode); -guint32 nm_platform_wifi_find_frequency (int ifindex, const guint32 *freqs); -void nm_platform_wifi_indicate_addressing_running (int ifindex, gboolean running); - -guint32 nm_platform_mesh_get_channel (int ifindex); -gboolean nm_platform_mesh_set_channel (int ifindex, guint32 channel); -gboolean nm_platform_mesh_set_ssid (int ifindex, const guint8 *ssid, gsize len); - -GArray *nm_platform_ip4_address_get_all (int ifindex); -GArray *nm_platform_ip6_address_get_all (int ifindex); -gboolean nm_platform_ip4_address_add (int ifindex, +/** + * nm_platform_route_scope_inv: + * @scope: the route scope, either its original value, or its inverse. + * + * This function is useful, because the constants such as RT_SCOPE_NOWHERE + * are 'int', so ~scope also gives an 'int'. This function gets the type + * casts to guint8 right. + * + * Returns: the bitwise inverse of the route scope. + * */ +#define nm_platform_route_scope_inv _nm_platform_uint8_inv +static inline guint8 +_nm_platform_uint8_inv (guint8 scope) +{ + return (guint8) ~scope; +} + +const char *nm_link_type_to_string (NMLinkType link_type); + +const char *nm_platform_error_to_string (NMPlatformError error); + +gboolean nm_platform_sysctl_set (NMPlatform *self, const char *path, const char *value); +char *nm_platform_sysctl_get (NMPlatform *self, const char *path); +gint32 nm_platform_sysctl_get_int32 (NMPlatform *self, const char *path, gint32 fallback); +gint64 nm_platform_sysctl_get_int_checked (NMPlatform *self, const char *path, guint base, gint64 min, gint64 max, gint64 fallback); + +gboolean nm_platform_sysctl_set_ip6_hop_limit_safe (NMPlatform *self, const char *iface, int value); + +const NMPlatformLink *nm_platform_link_get (NMPlatform *self, int ifindex); +const NMPlatformLink *nm_platform_link_get_by_ifname (NMPlatform *self, const char *ifname); +const NMPlatformLink *nm_platform_link_get_by_address (NMPlatform *self, gconstpointer address, size_t length); + +GArray *nm_platform_link_get_all (NMPlatform *self); +NMPlatformError nm_platform_dummy_add (NMPlatform *self, const char *name, NMPlatformLink *out_link); +NMPlatformError nm_platform_bridge_add (NMPlatform *self, const char *name, const void *address, size_t address_len, NMPlatformLink *out_link); +NMPlatformError nm_platform_bond_add (NMPlatform *self, const char *name, NMPlatformLink *out_link); +NMPlatformError nm_platform_team_add (NMPlatform *self, const char *name, NMPlatformLink *out_link); +gboolean nm_platform_link_delete (NMPlatform *self, int ifindex); + +/* convienience methods to lookup the link and access fields of NMPlatformLink. */ +int nm_platform_link_get_ifindex (NMPlatform *self, const char *name); +const char *nm_platform_link_get_name (NMPlatform *self, int ifindex); +NMLinkType nm_platform_link_get_type (NMPlatform *self, int ifindex); +gboolean nm_platform_link_is_software (NMPlatform *self, int ifindex); +gboolean nm_platform_link_is_up (NMPlatform *self, int ifindex); +gboolean nm_platform_link_is_connected (NMPlatform *self, int ifindex); +gboolean nm_platform_link_uses_arp (NMPlatform *self, int ifindex); +guint32 nm_platform_link_get_mtu (NMPlatform *self, int ifindex); +gboolean nm_platform_link_get_ipv6_token (NMPlatform *self, int ifindex, NMUtilsIPv6IfaceId *iid); +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); +int nm_platform_link_get_master (NMPlatform *self, int slave); + +gboolean nm_platform_link_get_unmanaged (NMPlatform *self, int ifindex, gboolean *unmanaged); +gboolean nm_platform_link_supports_slaves (NMPlatform *self, int ifindex); +const char *nm_platform_link_get_type_name (NMPlatform *self, int ifindex); + +gboolean nm_platform_link_refresh (NMPlatform *self, int ifindex); +void nm_platform_process_events (NMPlatform *self); + +gboolean nm_platform_link_set_up (NMPlatform *self, int ifindex, gboolean *out_no_firmware); +gboolean nm_platform_link_set_down (NMPlatform *self, int ifindex); +gboolean nm_platform_link_set_arp (NMPlatform *self, int ifindex); +gboolean nm_platform_link_set_noarp (NMPlatform *self, int ifindex); + +const char *nm_platform_link_get_udi (NMPlatform *self, int ifindex); + +GObject *nm_platform_link_get_udev_device (NMPlatform *self, int ifindex); + +gboolean nm_platform_link_set_user_ipv6ll_enabled (NMPlatform *self, int ifindex, gboolean enabled); + +gboolean nm_platform_link_get_permanent_address (NMPlatform *self, int ifindex, guint8 *buf, size_t *length); +gboolean nm_platform_link_set_address (NMPlatform *self, int ifindex, const void *address, size_t length); +gboolean nm_platform_link_set_mtu (NMPlatform *self, int ifindex, guint32 mtu); + +char *nm_platform_link_get_physical_port_id (NMPlatform *self, int ifindex); +guint nm_platform_link_get_dev_id (NMPlatform *self, int ifindex); +gboolean nm_platform_link_get_wake_on_lan (NMPlatform *self, int ifindex); +gboolean nm_platform_link_get_driver_info (NMPlatform *self, + int ifindex, + char **out_driver_name, + char **out_driver_version, + char **out_fw_version); + +gboolean nm_platform_link_supports_carrier_detect (NMPlatform *self, int ifindex); +gboolean nm_platform_link_supports_vlans (NMPlatform *self, int ifindex); + +gboolean nm_platform_link_enslave (NMPlatform *self, int master, int slave); +gboolean nm_platform_link_release (NMPlatform *self, int master, int slave); +gboolean nm_platform_master_set_option (NMPlatform *self, int ifindex, const char *option, const char *value); +char *nm_platform_master_get_option (NMPlatform *self, int ifindex, const char *option); +gboolean nm_platform_slave_set_option (NMPlatform *self, int ifindex, const char *option, const char *value); +char *nm_platform_slave_get_option (NMPlatform *self, int ifindex, const char *option); + +NMPlatformError nm_platform_vlan_add (NMPlatform *self, const char *name, int parent, int vlanid, guint32 vlanflags, NMPlatformLink *out_link); +gboolean nm_platform_vlan_get_info (NMPlatform *self, int ifindex, int *parent, int *vlanid); +gboolean nm_platform_vlan_set_ingress_map (NMPlatform *self, int ifindex, int from, int to); +gboolean nm_platform_vlan_set_egress_map (NMPlatform *self, int ifindex, int from, int to); + +NMPlatformError nm_platform_infiniband_partition_add (NMPlatform *self, int parent, int p_key, NMPlatformLink *out_link); +gboolean nm_platform_infiniband_get_info (NMPlatform *self, int ifindex, int *parent, int *p_key, const char **mode); + +gboolean nm_platform_veth_get_properties (NMPlatform *self, int ifindex, NMPlatformVethProperties *properties); +gboolean nm_platform_tun_get_properties (NMPlatform *self, int ifindex, NMPlatformTunProperties *properties); +gboolean nm_platform_macvlan_get_properties (NMPlatform *self, int ifindex, NMPlatformMacvlanProperties *props); +gboolean nm_platform_vxlan_get_properties (NMPlatform *self, int ifindex, NMPlatformVxlanProperties *props); +gboolean nm_platform_gre_get_properties (NMPlatform *self, int ifindex, NMPlatformGreProperties *props); + +gboolean nm_platform_wifi_get_capabilities (NMPlatform *self, int ifindex, NMDeviceWifiCapabilities *caps); +gboolean nm_platform_wifi_get_bssid (NMPlatform *self, int ifindex, guint8 *bssid); +GByteArray *nm_platform_wifi_get_ssid (NMPlatform *self, int ifindex); +guint32 nm_platform_wifi_get_frequency (NMPlatform *self, int ifindex); +int nm_platform_wifi_get_quality (NMPlatform *self, int ifindex); +guint32 nm_platform_wifi_get_rate (NMPlatform *self, int ifindex); +NM80211Mode nm_platform_wifi_get_mode (NMPlatform *self, int ifindex); +void nm_platform_wifi_set_mode (NMPlatform *self, int ifindex, NM80211Mode mode); +guint32 nm_platform_wifi_find_frequency (NMPlatform *self, int ifindex, const guint32 *freqs); +void nm_platform_wifi_indicate_addressing_running (NMPlatform *self, int ifindex, gboolean running); + +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); + +const NMPlatformIP4Address *nm_platform_ip4_address_get (NMPlatform *self, int ifindex, in_addr_t address, int plen); +const NMPlatformIP6Address *nm_platform_ip6_address_get (NMPlatform *self, int ifindex, struct in6_addr address, int plen); +GArray *nm_platform_ip4_address_get_all (NMPlatform *self, int ifindex); +GArray *nm_platform_ip6_address_get_all (NMPlatform *self, int ifindex); +gboolean nm_platform_ip4_address_add (NMPlatform *self, int ifindex, in_addr_t address, in_addr_t peer_address, int plen, guint32 lifetime, guint32 preferred_lft, const char *label); -gboolean nm_platform_ip6_address_add (int ifindex, +gboolean nm_platform_ip6_address_add (NMPlatform *self, int ifindex, struct in6_addr address, struct in6_addr peer_address, int plen, guint32 lifetime, guint32 preferred_lft, guint flags); -gboolean nm_platform_ip4_address_delete (int ifindex, in_addr_t address, int plen, in_addr_t peer_address); -gboolean nm_platform_ip6_address_delete (int ifindex, struct in6_addr address, int plen); -gboolean nm_platform_ip4_address_exists (int ifindex, in_addr_t address, int plen); -gboolean nm_platform_ip6_address_exists (int ifindex, struct in6_addr address, int plen); -gboolean nm_platform_ip4_address_sync (int ifindex, const GArray *known_addresses, guint32 device_route_metric); -gboolean nm_platform_ip6_address_sync (int ifindex, const GArray *known_addresses, gboolean keep_link_local); -gboolean nm_platform_address_flush (int ifindex); - -gboolean nm_platform_ip4_check_reinstall_device_route (int ifindex, const NMPlatformIP4Address *address, guint32 device_route_metric); - -GArray *nm_platform_ip4_route_get_all (int ifindex, NMPlatformGetRouteMode mode); -GArray *nm_platform_ip6_route_get_all (int ifindex, NMPlatformGetRouteMode mode); -gboolean nm_platform_ip4_route_add (int ifindex, NMIPConfigSource source, +gboolean nm_platform_ip4_address_delete (NMPlatform *self, int ifindex, in_addr_t address, int plen, in_addr_t peer_address); +gboolean nm_platform_ip6_address_delete (NMPlatform *self, int ifindex, struct in6_addr address, int plen); +gboolean nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, const GArray *known_addresses, GPtrArray **out_added_addresses); +gboolean nm_platform_ip6_address_sync (NMPlatform *self, int ifindex, const GArray *known_addresses, gboolean keep_link_local); +gboolean nm_platform_address_flush (NMPlatform *self, int ifindex); + +const NMPlatformIP4Route *nm_platform_ip4_route_get (NMPlatform *self, int ifindex, in_addr_t network, int plen, guint32 metric); +const NMPlatformIP6Route *nm_platform_ip6_route_get (NMPlatform *self, int ifindex, struct in6_addr network, int plen, guint32 metric); +GArray *nm_platform_ip4_route_get_all (NMPlatform *self, int ifindex, NMPlatformGetRouteFlags flags); +GArray *nm_platform_ip6_route_get_all (NMPlatform *self, int ifindex, NMPlatformGetRouteFlags flags); +gboolean nm_platform_ip4_route_add (NMPlatform *self, int ifindex, NMIPConfigSource source, in_addr_t network, int plen, in_addr_t gateway, guint32 pref_src, guint32 metric, guint32 mss); -gboolean nm_platform_ip6_route_add (int ifindex, NMIPConfigSource source, +gboolean nm_platform_ip6_route_add (NMPlatform *self, int ifindex, NMIPConfigSource source, struct in6_addr network, int plen, struct in6_addr gateway, guint32 metric, guint32 mss); -gboolean nm_platform_ip4_route_delete (int ifindex, in_addr_t network, int plen, guint32 metric); -gboolean nm_platform_ip6_route_delete (int ifindex, struct in6_addr network, int plen, guint32 metric); -gboolean nm_platform_ip4_route_exists (int ifindex, in_addr_t network, int plen, guint32 metric); -gboolean nm_platform_ip6_route_exists (int ifindex, struct in6_addr network, int plen, guint32 metric); -gboolean nm_platform_ip4_route_sync (int ifindex, const GArray *known_routes); -gboolean nm_platform_ip6_route_sync (int ifindex, const GArray *known_routes); -gboolean nm_platform_route_flush (int ifindex); +gboolean nm_platform_ip4_route_delete (NMPlatform *self, int ifindex, in_addr_t network, int plen, guint32 metric); +gboolean nm_platform_ip6_route_delete (NMPlatform *self, int ifindex, struct in6_addr network, int plen, guint32 metric); const char *nm_platform_link_to_string (const NMPlatformLink *link); const char *nm_platform_ip4_address_to_string (const NMPlatformIP4Address *address); @@ -627,8 +734,8 @@ int nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4R int nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b); gboolean nm_platform_check_support_libnl_extended_ifa_flags (void); -gboolean nm_platform_check_support_kernel_extended_ifa_flags (void); -gboolean nm_platform_check_support_user_ipv6ll (void); +gboolean nm_platform_check_support_kernel_extended_ifa_flags (NMPlatform *self); +gboolean nm_platform_check_support_user_ipv6ll (NMPlatform *self); void nm_platform_addr_flags2str (int flags, char *buf, size_t size); diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c new file mode 100644 index 00000000..51684b93 --- /dev/null +++ b/src/platform/nmp-object.c @@ -0,0 +1,1924 @@ +/* -*- 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 + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2015 Red Hat, Inc. + */ + +#include "nmp-object.h" + +#include <unistd.h> + +#include "nm-platform-utils.h" +#include "NetworkManagerUtils.h" +#include "nm-utils.h" +#include "nm-logging.h" + +/*********************************************************************************************/ + +#define _LOG_DOMAIN LOGD_PLATFORM + +#define _LOG(level, domain, obj, ...) \ + G_STMT_START { \ + const NMLogLevel __level = (level); \ + const NMLogDomain __domain = (domain); \ + \ + if (nm_logging_enabled (__level, __domain)) { \ + const NMPObject *const __obj = (obj); \ + \ + _nm_log (__level, __domain, 0, \ + "nmp-object[%p/%s]: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ + __obj, \ + (__obj ? NMP_OBJECT_GET_CLASS (__obj)->obj_type_name : "???") \ + _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ + } \ + } G_STMT_END +#define _LOG_LEVEL_ENABLED(level, domain) \ + ( nm_logging_enabled ((level), (domain)) ) + +#ifdef NM_MORE_LOGGING +#define _LOGT_ENABLED() _LOG_LEVEL_ENABLED (LOGL_TRACE, _LOG_DOMAIN) +#define _LOGT(obj, ...) _LOG (LOGL_TRACE, _LOG_DOMAIN, obj, __VA_ARGS__) +#else +#define _LOGT_ENABLED() FALSE +#define _LOGT(obj, ...) G_STMT_START { if (FALSE) { _LOG (LOGL_TRACE, _LOG_DOMAIN, obj, __VA_ARGS__); } } G_STMT_END +#endif + +#define _LOGD(obj, ...) _LOG (LOGL_DEBUG, _LOG_DOMAIN, obj, __VA_ARGS__) +#define _LOGI(obj, ...) _LOG (LOGL_INFO , _LOG_DOMAIN, obj, __VA_ARGS__) +#define _LOGW(obj, ...) _LOG (LOGL_WARN , _LOG_DOMAIN, obj, __VA_ARGS__) +#define _LOGE(obj, ...) _LOG (LOGL_ERR , _LOG_DOMAIN, obj, __VA_ARGS__) + +/*********************************************************************************************/ + +struct _NMPCache { + /* the cache contains only one hash table for all object types, and similarly + * it contains only one NMMultiIndex. + * This works, because different object types don't ever compare equal and + * because their index ids also don't overlap. + * + * For routes and addresses, the cache contains an address if (and only if) the + * object was reported via netlink. + * For links, the cache contain a link if it was reported by either netlink + * or udev. That means, a link object can be alive, even if it was already + * removed via netlink. + * + * This effectively merges the udev-device cache into the NMPCache. + */ + + GHashTable *idx_main; + NMMultiIndex *idx_multi; + + gboolean use_udev; +}; + +/******************************************************************/ + +static inline guint +_id_hash_ip6_addr (const struct in6_addr *addr) +{ + guint hash = (guint) 0x897da53981a13ULL; + int i; + + for (i = 0; i < sizeof (*addr); i++) + hash = (hash * 33) + ((const guint8 *) addr)[i]; + return hash; +} + +static const char * +_link_get_driver (GUdevDevice *udev_device, const char *kind, const char *ifname) +{ + const char *driver = NULL; + + nm_assert (kind == g_intern_string (kind)); + + if (udev_device) { + driver = nmp_utils_udev_get_driver (udev_device); + if (driver) + return driver; + } + + if (kind) + return kind; + + if (ifname) { + char *d; + + if (nmp_utils_ethtool_get_driver_info (ifname, &d, NULL, NULL)) { + driver = d && d[0] ? g_intern_string (d) : NULL; + g_free (d); + if (driver) + return driver; + } + } + + return "unknown"; +} + +void +_nmp_object_fixup_link_udev_fields (NMPObject *obj, gboolean use_udev) +{ + const char *driver = NULL; + gboolean initialized = FALSE; + + nm_assert (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_LINK); + + /* The link contains internal fields that are combined by + * properties from netlink and udev. Update those properties */ + + /* When a link is not in netlink, it's udev fields don't matter. */ + if (obj->_link.netlink.is_in_netlink) { + driver = _link_get_driver (obj->_link.udev.device, + obj->link.kind, + obj->link.name); + if (obj->_link.udev.device) + initialized = TRUE; + else if (!use_udev) { + /* If we don't use udev, we immediately mark the link as initialized. + * + * For that, we consult @use_udev argument, that is cached via + * nmp_cache_use_udev_get(). It is on purpose not to test + * for a writable /sys on every call. A minor reason for that is + * performance, but the real reason is reproducibility. + * + * If you want to support changing of whether udev is enabled, + * reset the value via nmp_cache_use_udev_set() carefully -- and + * possibly update the links in the cache accordingly. + * */ + initialized = TRUE; + } + } + + obj->link.driver = driver; + obj->link.initialized = initialized; +} + +static void +_nmp_object_fixup_link_master_connected (NMPObject *obj, const NMPCache *cache) +{ + nm_assert (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_LINK); + + if (nmp_cache_link_connected_needs_toggle (cache, obj, NULL, NULL)) + obj->link.connected = !obj->link.connected; +} + +/******************************************************************/ + +const NMPClass * +nmp_class_from_type (NMPObjectType obj_type) +{ + g_return_val_if_fail (obj_type > NMP_OBJECT_TYPE_UNKNOWN && obj_type <= NMP_OBJECT_TYPE_MAX, NULL); + + return &_nmp_classes[obj_type - 1]; +} + +/******************************************************************/ + +NMPObject * +nmp_object_ref (NMPObject *obj) +{ + g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj), NULL); + g_return_val_if_fail (obj->_ref_count != NMP_REF_COUNT_STACKINIT, NULL); + obj->_ref_count++; + + _LOGT (obj, "ref: %d", obj->_ref_count); + + return obj; +} + +void +nmp_object_unref (NMPObject *obj) +{ + if (obj) { + g_return_if_fail (obj->_ref_count > 0); + g_return_if_fail (obj->_ref_count != NMP_REF_COUNT_STACKINIT); + _LOGT (obj, "%s: %d", + obj->_ref_count <= 1 ? "destroy" : "unref", + obj->_ref_count - 1); + if (--obj->_ref_count <= 0) { + const NMPClass *klass = obj->_class; + + nm_assert (!obj->is_cached); + if (klass->cmd_obj_dispose) + klass->cmd_obj_dispose (obj); + g_slice_free1 (klass->sizeof_data + G_STRUCT_OFFSET (NMPObject, object), obj); + } + } +} + +static void +_vt_cmd_obj_dispose_link (NMPObject *obj) +{ + g_clear_object (&obj->_link.udev.device); +} + +static NMPObject * +_nmp_object_new_from_class (const NMPClass *klass) +{ + NMPObject *obj; + + nm_assert (klass); + nm_assert (klass->sizeof_data > 0); + nm_assert (klass->sizeof_public > 0 && klass->sizeof_public <= klass->sizeof_data); + + obj = g_slice_alloc0 (klass->sizeof_data + G_STRUCT_OFFSET (NMPObject, object)); + obj->_class = klass; + obj->_ref_count = 1; + _LOGT (obj, "new"); + return obj; +} + +NMPObject * +nmp_object_new (NMPObjectType obj_type, const NMPlatformObject *plobj) +{ + const NMPClass *klass = nmp_class_from_type (obj_type); + NMPObject *obj; + + obj = _nmp_object_new_from_class (klass); + if (plobj) + memcpy (&obj->object, plobj, klass->sizeof_public); + return obj; +} + +NMPObject * +nmp_object_new_link (int ifindex) +{ + NMPObject *obj; + + obj = nmp_object_new (NMP_OBJECT_TYPE_LINK, NULL); + obj->link.ifindex = ifindex; + return obj; +} + +/******************************************************************/ + +static const NMPObject * +_nmp_object_stackinit_from_class (NMPObject *obj, const NMPClass *klass) +{ + nm_assert (klass); + + memset (obj, 0, sizeof (NMPObject)); + obj->_class = klass; + obj->_ref_count = NMP_REF_COUNT_STACKINIT; + return obj; +} + +const NMPObject * +nmp_object_stackinit (NMPObject *obj, NMPObjectType obj_type, const NMPlatformObject *plobj) +{ + const NMPClass *klass = nmp_class_from_type (obj_type); + + _nmp_object_stackinit_from_class (obj, klass); + if (plobj) + memcpy (&obj->object, plobj, klass->sizeof_public); + return obj; +} + +const NMPObject * +nmp_object_stackinit_id (NMPObject *obj, const NMPObject *src) +{ + nm_assert (NMP_OBJECT_IS_VALID (src)); + nm_assert (obj); + + NMP_OBJECT_GET_CLASS (src)->cmd_obj_stackinit_id (obj, src); + return obj; +} + +const NMPObject * +nmp_object_stackinit_id_link (NMPObject *obj, int ifindex) +{ + nmp_object_stackinit (obj, NMP_OBJECT_TYPE_LINK, NULL); + obj->link.ifindex = ifindex; + return obj; +} + +static void +_vt_cmd_obj_stackinit_id_link (NMPObject *obj, const NMPObject *src) +{ + nmp_object_stackinit_id_link (obj, src->link.ifindex); +} + +const NMPObject * +nmp_object_stackinit_id_ip4_address (NMPObject *obj, int ifindex, guint32 address, int plen) +{ + nmp_object_stackinit (obj, NMP_OBJECT_TYPE_IP4_ADDRESS, NULL); + obj->ip4_address.ifindex = ifindex; + obj->ip4_address.address = address; + obj->ip4_address.plen = plen; + return obj; +} + +static void +_vt_cmd_obj_stackinit_id_ip4_address (NMPObject *obj, const NMPObject *src) +{ + nmp_object_stackinit_id_ip4_address (obj, src->ip_address.ifindex, src->ip4_address.address, src->ip_address.plen); +} + +const NMPObject * +nmp_object_stackinit_id_ip6_address (NMPObject *obj, int ifindex, const struct in6_addr *address, int plen) +{ + nmp_object_stackinit (obj, NMP_OBJECT_TYPE_IP6_ADDRESS, NULL); + obj->ip4_address.ifindex = ifindex; + if (address) + obj->ip6_address.address = *address; + obj->ip6_address.plen = plen; + return obj; +} + +static void +_vt_cmd_obj_stackinit_id_ip6_address (NMPObject *obj, const NMPObject *src) +{ + nmp_object_stackinit_id_ip6_address (obj, src->ip_address.ifindex, &src->ip6_address.address, src->ip_address.plen); +} + +const NMPObject * +nmp_object_stackinit_id_ip4_route (NMPObject *obj, int ifindex, guint32 network, int plen, guint32 metric) +{ + nmp_object_stackinit (obj, NMP_OBJECT_TYPE_IP4_ROUTE, NULL); + obj->ip4_route.ifindex = ifindex; + obj->ip4_route.network = network; + obj->ip4_route.plen = plen; + obj->ip4_route.metric = metric; + return obj; +} + +static void +_vt_cmd_obj_stackinit_id_ip4_route (NMPObject *obj, const NMPObject *src) +{ + nmp_object_stackinit_id_ip4_route (obj, src->ip_route.ifindex, src->ip4_route.network, src->ip_route.plen, src->ip_route.metric); +} + +const NMPObject * +nmp_object_stackinit_id_ip6_route (NMPObject *obj, int ifindex, const struct in6_addr *network, int plen, guint32 metric) +{ + nmp_object_stackinit (obj, NMP_OBJECT_TYPE_IP6_ROUTE, NULL); + obj->ip6_route.ifindex = ifindex; + if (network) + obj->ip6_route.network = *network; + obj->ip6_route.plen = plen; + obj->ip6_route.metric = metric; + return obj; +} + +static void +_vt_cmd_obj_stackinit_id_ip6_route (NMPObject *obj, const NMPObject *src) +{ + nmp_object_stackinit_id_ip6_route (obj, src->ip_route.ifindex, &src->ip6_route.network, src->ip_route.plen, src->ip_route.metric); +} + +/******************************************************************/ + +const char * +nmp_object_to_string (const NMPObject *obj, NMPObjectToStringMode to_string_mode, char *buf, gsize buf_size) +{ + const NMPClass *klass; + char buf2[sizeof (_nm_platform_to_string_buffer)]; + char buf3[sizeof (_nm_platform_to_string_buffer)]; + const char *str; + + if (!buf) { + buf = _nm_platform_to_string_buffer; + buf_size = sizeof (_nm_platform_to_string_buffer); + } + + if (!obj) { + g_strlcpy (buf, "NULL", buf_size); + return buf; + } + + g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj), NULL); + + klass = NMP_OBJECT_GET_CLASS (obj); + + switch (to_string_mode) { + case NMP_OBJECT_TO_STRING_ID: + return klass->cmd_plobj_to_string_id (&obj->object, buf, buf_size); + case NMP_OBJECT_TO_STRING_ALL: + g_strlcpy (buf2, NMP_OBJECT_GET_CLASS (obj)->cmd_plobj_to_string (&obj->object), sizeof (buf2)); + + if (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_LINK) { + g_snprintf (buf3, sizeof (buf3), + ",%cin-nl,%p", + obj->_link.netlink.is_in_netlink ? '+' : '-', + obj->_link.udev.device); + } else + buf3[0] = '\0'; + + g_snprintf (buf, buf_size, + "[%s,%p,%d,%ccache,%calive,%cvisible%s; %s]", + klass->obj_type_name, obj, obj->_ref_count, + obj->is_cached ? '+' : '-', + nmp_object_is_alive (obj) ? '+' : '-', + nmp_object_is_visible (obj) ? '+' : '-', + buf3, buf2); + return buf; + case NMP_OBJECT_TO_STRING_PUBLIC: + str = NMP_OBJECT_GET_CLASS (obj)->cmd_plobj_to_string (&obj->object); + if (str != buf) + g_strlcpy (buf, str, buf_size); + return buf; + default: + g_return_val_if_reached ("ERROR"); + } +} + +#define _vt_cmd_plobj_to_string_id(type, plat_type, ...) \ +static const char * \ +_vt_cmd_plobj_to_string_id_##type (const NMPlatformObject *_obj, char *buf, gsize buf_len) \ +{ \ + plat_type *const obj = (plat_type *) _obj; \ + char buf1[NM_UTILS_INET_ADDRSTRLEN]; \ + \ + (void) buf1; \ + g_snprintf (buf, buf_len, \ + __VA_ARGS__); \ + return buf; \ +} +_vt_cmd_plobj_to_string_id (link, NMPlatformLink, "%d", obj->ifindex); +_vt_cmd_plobj_to_string_id (ip4_address, NMPlatformIP4Address, "%d: %s/%d", obj->ifindex, nm_utils_inet4_ntop ( obj->address, buf1), obj->plen); +_vt_cmd_plobj_to_string_id (ip6_address, NMPlatformIP6Address, "%d: %s/%d", obj->ifindex, nm_utils_inet6_ntop (&obj->address, buf1), obj->plen); +_vt_cmd_plobj_to_string_id (ip4_route, NMPlatformIP4Route, "%d: %s/%d %d", obj->ifindex, nm_utils_inet4_ntop ( obj->network, buf1), obj->plen, obj->metric); +_vt_cmd_plobj_to_string_id (ip6_route, NMPlatformIP6Route, "%d: %s/%d %d", obj->ifindex, nm_utils_inet6_ntop (&obj->network, buf1), obj->plen, obj->metric); + +int +nmp_object_cmp (const NMPObject *obj1, const NMPObject *obj2) +{ + if (obj1 == obj2) + return 0; + if (!obj1) + return -1; + if (!obj2) + return 1; + + g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj1), -1); + g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj2), 1); + + if (NMP_OBJECT_GET_CLASS (obj1) != NMP_OBJECT_GET_CLASS (obj2)) + return NMP_OBJECT_GET_CLASS (obj1) < NMP_OBJECT_GET_CLASS (obj2) ? -1 : 1; + + return NMP_OBJECT_GET_CLASS (obj1)->cmd_plobj_cmp (&obj1->object, &obj2->object); +} + +gboolean +nmp_object_equal (const NMPObject *obj1, const NMPObject *obj2) +{ + const NMPClass *klass; + + g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj1), FALSE); + g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj2), FALSE); + + if (obj1 == obj2) + return TRUE; + + klass = NMP_OBJECT_GET_CLASS (obj1); + + if (klass != NMP_OBJECT_GET_CLASS (obj2)) + return FALSE; + + return klass->cmd_obj_equal (obj1, obj2); +} + +static gboolean +_vt_cmd_obj_equal_plain (const NMPObject *obj1, const NMPObject *obj2) +{ + return NMP_OBJECT_GET_CLASS (obj1)->cmd_plobj_cmp (&obj1->object, &obj2->object) == 0; +} + +static gboolean +_vt_cmd_obj_equal_link (const NMPObject *obj1, const NMPObject *obj2) +{ + const NMPClass *klass = NMP_OBJECT_GET_CLASS (obj1); + + if (klass->cmd_plobj_cmp (&obj1->object, &obj2->object) != 0) + return FALSE; + if (obj1->_link.netlink.is_in_netlink != obj2->_link.netlink.is_in_netlink) + return FALSE; + if (obj1->_link.udev.device != obj2->_link.udev.device) + return FALSE; + return TRUE; +} + +/* @src is a const object, which is not entirely correct for link types, where + * we increase the ref count for src->_link.udev.device. + * Hence, nmp_object_copy() can violate the const promise of @src. + * */ +void +nmp_object_copy (NMPObject *dst, const NMPObject *src, gboolean id_only) +{ + g_return_if_fail (NMP_OBJECT_IS_VALID (dst)); + g_return_if_fail (NMP_OBJECT_IS_VALID (src)); + g_return_if_fail (!NMP_OBJECT_IS_STACKINIT (dst)); + + if (src != dst) { + const NMPClass *klass = NMP_OBJECT_GET_CLASS (dst); + + g_return_if_fail (klass == NMP_OBJECT_GET_CLASS (src)); + + if (id_only) + klass->cmd_plobj_id_copy (&dst->object, &src->object); + else + klass->cmd_obj_copy (dst, src); + } +} + +#define _vt_cmd_plobj_id_copy(type, plat_type, cmd) \ +static void \ +_vt_cmd_plobj_id_copy_##type (NMPlatformObject *_dst, const NMPlatformObject *_src) \ +{ \ + plat_type *const dst = (plat_type *) _dst; \ + const plat_type *const src = (const plat_type *) _src; \ + { cmd } \ +} +_vt_cmd_plobj_id_copy (link, NMPlatformLink, { + dst->ifindex = src->ifindex; +}); +_vt_cmd_plobj_id_copy (ip4_address, NMPlatformIP4Address, { + dst->ifindex = src->ifindex; + dst->plen = src->plen; + dst->address = src->address; +}); +_vt_cmd_plobj_id_copy (ip6_address, NMPlatformIP6Address, { + dst->ifindex = src->ifindex; + dst->plen = src->plen; + dst->address = src->address; +}); +_vt_cmd_plobj_id_copy (ip4_route, NMPlatformIP4Route, { + dst->ifindex = src->ifindex; + dst->plen = src->plen; + dst->metric = src->metric; + dst->network = src->network; +}); +_vt_cmd_plobj_id_copy (ip6_route, NMPlatformIP6Route, { + dst->ifindex = src->ifindex; + dst->plen = src->plen; + dst->metric = src->metric; + dst->network = src->network; +}); + +static void +_vt_cmd_obj_copy_plain (NMPObject *dst, const NMPObject *src) +{ + memcpy (&dst->object, &src->object, NMP_OBJECT_GET_CLASS (dst)->sizeof_data); +} + +static void +_vt_cmd_obj_copy_link (NMPObject *dst, const NMPObject *src) +{ + if (dst->_link.udev.device != src->_link.udev.device) { + if (dst->_link.udev.device) + g_object_unref (dst->_link.udev.device); + if (src->_link.udev.device) + g_object_ref (src->_link.udev.device); + } + dst->_link = src->_link; +} + +/* Uses internally nmp_object_copy(), hence it also violates the const + * promise for @obj. + * */ +NMPObject * +nmp_object_clone (const NMPObject *obj, gboolean id_only) +{ + NMPObject *dst; + + if (!obj) + return NULL; + + g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj), NULL); + + dst = _nmp_object_new_from_class (NMP_OBJECT_GET_CLASS (obj)); + nmp_object_copy (dst, obj, id_only); + return dst; +} + +gboolean +nmp_object_id_equal (const NMPObject *obj1, const NMPObject *obj2) +{ + const NMPClass *klass; + + if (obj1 == obj2) + return TRUE; + if (!obj1 || !obj2) + return FALSE; + + g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj1), FALSE); + g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj2), FALSE); + + klass = NMP_OBJECT_GET_CLASS (obj1); + return klass == NMP_OBJECT_GET_CLASS (obj2) + && klass->cmd_plobj_id_equal (&obj1->object, &obj2->object); +} + +#define _vt_cmd_plobj_id_equal(type, plat_type, cmd) \ +static gboolean \ +_vt_cmd_plobj_id_equal_##type (const NMPlatformObject *_obj1, const NMPlatformObject *_obj2) \ +{ \ + const plat_type *const obj1 = (const plat_type *) _obj1; \ + const plat_type *const obj2 = (const plat_type *) _obj2; \ + return (cmd); \ +} +_vt_cmd_plobj_id_equal (link, NMPlatformLink, + obj1->ifindex == obj2->ifindex); +_vt_cmd_plobj_id_equal (ip4_address, NMPlatformIP4Address, + obj1->ifindex == obj2->ifindex + && obj1->plen == obj2->plen + && obj1->address == obj2->address); +_vt_cmd_plobj_id_equal (ip6_address, NMPlatformIP6Address, + obj1->ifindex == obj2->ifindex + && obj1->plen == obj2->plen + && IN6_ARE_ADDR_EQUAL (&obj1->address, &obj2->address)); +_vt_cmd_plobj_id_equal (ip4_route, NMPlatformIP4Route, + obj1->ifindex == obj2->ifindex + && obj1->plen == obj2->plen + && obj1->metric == obj2->metric + && obj1->network == obj2->network); +_vt_cmd_plobj_id_equal (ip6_route, NMPlatformIP6Route, + obj1->ifindex == obj2->ifindex + && obj1->plen == obj2->plen + && obj1->metric == obj2->metric + && IN6_ARE_ADDR_EQUAL( &obj1->network, &obj2->network)); + +guint +nmp_object_id_hash (const NMPObject *obj) +{ + if (!obj) + return 0; + + g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj), 0); + return NMP_OBJECT_GET_CLASS (obj)->cmd_plobj_id_hash (&obj->object); +} + +#define _vt_cmd_plobj_id_hash(type, plat_type, cmd) \ +static guint \ +_vt_cmd_plobj_id_hash_##type (const NMPlatformObject *_obj) \ +{ \ + const plat_type *const obj = (const plat_type *) _obj; \ + guint hash; \ + { cmd; } \ + return hash; \ +} +_vt_cmd_plobj_id_hash (link, NMPlatformLink, { + /* libnl considers: + * .oo_id_attrs = LINK_ATTR_IFINDEX | LINK_ATTR_FAMILY, + */ + hash = (guint) 3982791431u; + hash = hash + ((guint) obj->ifindex); +}) +_vt_cmd_plobj_id_hash (ip4_address, NMPlatformIP4Address, { + /* libnl considers: + * .oo_id_attrs = (ADDR_ATTR_FAMILY | ADDR_ATTR_IFINDEX | + * ADDR_ATTR_LOCAL | ADDR_ATTR_PREFIXLEN), + */ + hash = (guint) 3591309853u; + hash = hash + ((guint) obj->ifindex); + hash = hash * 33 + ((guint) obj->plen); + hash = hash * 33 + ((guint) obj->address); +}) +_vt_cmd_plobj_id_hash (ip6_address, NMPlatformIP6Address, { + hash = (guint) 2907861637u; + hash = hash + ((guint) obj->ifindex); + hash = hash * 33 + ((guint) obj->plen); + hash = hash * 33 + _id_hash_ip6_addr (&obj->address); +}) +_vt_cmd_plobj_id_hash (ip4_route, NMPlatformIP4Route, { + /* libnl considers: + * .oo_id_attrs = (ROUTE_ATTR_FAMILY | ROUTE_ATTR_TOS | + * ROUTE_ATTR_TABLE | ROUTE_ATTR_DST | + * ROUTE_ATTR_PRIO), + */ + hash = (guint) 2569857221u; + hash = hash + ((guint) obj->ifindex); + hash = hash * 33 + ((guint) obj->plen); + hash = hash * 33 + ((guint) obj->metric); + hash = hash * 33 + ((guint) obj->network); +}) +_vt_cmd_plobj_id_hash (ip6_route, NMPlatformIP6Route, { + hash = (guint) 3999787007u; + hash = hash + ((guint) obj->ifindex); + hash = hash * 33 + ((guint) obj->plen); + hash = hash * 33 + ((guint) obj->metric); + hash = hash * 33 + _id_hash_ip6_addr (&obj->network); +}) + +gboolean +nmp_object_is_alive (const NMPObject *obj) +{ + /* for convenience, allow NULL. */ + if (!obj) + return FALSE; + + return NMP_OBJECT_GET_CLASS (obj)->cmd_obj_is_alive (obj); +} + +static gboolean +_vt_cmd_obj_is_alive_link (const NMPObject *obj) +{ + return obj->object.ifindex > 0 && (obj->_link.netlink.is_in_netlink || obj->_link.udev.device); +} + +static gboolean +_vt_cmd_obj_is_alive_ipx_address (const NMPObject *obj) +{ + return obj->object.ifindex > 0; +} + +static gboolean +_vt_cmd_obj_is_alive_ipx_route (const NMPObject *obj) +{ + /* We want to ignore routes that are RTM_F_CLONED but we still + * let nmp_object_from_nl() create such route objects, instead of + * returning NULL right away. + * + * The idea is, that if we have the same route (according to its id) + * in the cache with !RTM_F_CLONED, an update that changes the route + * to be RTM_F_CLONED must remove the instance. + * + * If nmp_object_from_nl() would just return NULL, we couldn't look + * into the cache to see if it contains a route that now disappears + * (because it is cloned). + * + * Instead we create a dead object, and nmp_cache_update_netlink() + * will remove the old version of the update. + **/ + return obj->object.ifindex > 0 && (obj->ip_route.source != _NM_IP_CONFIG_SOURCE_RTM_F_CLONED); +} + +gboolean +nmp_object_is_visible (const NMPObject *obj) + +{ + /* for convenience, allow NULL. */ + if (!obj) + return FALSE; + + return NMP_OBJECT_GET_CLASS (obj)->cmd_obj_is_visible (obj); +} + +static gboolean +_vt_cmd_obj_is_visible_link (const NMPObject *obj) +{ + return obj->object.ifindex > 0 + && obj->_link.netlink.is_in_netlink + && obj->link.name[0]; +} + +static gboolean +_vt_cmd_obj_is_visible_ipx_address (const NMPObject *obj) +{ + return obj->object.ifindex > 0; +} + +static gboolean +_vt_cmd_obj_is_visible_ipx_route (const NMPObject *obj) +{ + NMIPConfigSource source = obj->ip_route.source; + + return obj->object.ifindex > 0 && source != _NM_IP_CONFIG_SOURCE_RTM_F_CLONED; +} + +/******************************************************************/ + +/** + * nmp_object_from_nl: + * @platform: platform instance (needed to lookup sysctl) + * @nlo: + * @id_only: if %TRUE, only fill the id fields of the object and leave the + * other fields unset. This is useful to create a needle to lookup a matching + * item in the cache. + * @complete_from_cache: sometimes the netlink object doesn't contain all information. + * If true, look them up in the cache and preserve the original value. + * + * Convert a libnl object to a platform object. + * Returns: a NMPObject containing @nlo. If @id_only is %TRUE, only the id fields + * are defined. + **/ +NMPObject * +nmp_object_from_nl (NMPlatform *platform, const struct nl_object *nlo, gboolean id_only, gboolean complete_from_cache) +{ + NMPObjectType obj_type = _nlo_get_object_type (nlo); + NMPObject *obj; + + if (obj_type == NMP_OBJECT_TYPE_UNKNOWN) + return NULL; + + obj = nmp_object_new (obj_type, NULL); + + if (!NMP_OBJECT_GET_CLASS (obj)->cmd_plobj_init_from_nl (platform, &obj->object, nlo, id_only, complete_from_cache)) { + nmp_object_unref (obj); + return NULL; + } + return obj; +} + +struct nl_object * +nmp_object_to_nl (NMPlatform *platform, const NMPObject *obj, gboolean id_only) +{ + return NMP_OBJECT_GET_CLASS (obj)->cmd_plobj_to_nl (platform, &obj->object, id_only); +} + +/******************************************************************/ + +gboolean +nmp_cache_id_equal (const NMPCacheId *a, const NMPCacheId *b) +{ + /* just memcmp() the entire id. This is potentially dangerous, because + * the struct is not __attribute__((packed)) and not all types have the + * same size. It is important, to memset() the entire struct to 0, + * not only the relevant fields. + * + * You anyway should use the nmp_cache_id_init_*() functions on a stack-allocated + * struct. */ + return memcmp (a, b, sizeof (NMPCacheId)) == 0; +} + +guint +nmp_cache_id_hash (const NMPCacheId *id) +{ + guint hash = 5381; + guint i; + + for (i = 0; i < sizeof (NMPCacheId); i++) + hash = ((hash << 5) + hash) + ((char *) id)[i]; /* hash * 33 + c */ + return hash; +} + +NMPCacheId * +nmp_cache_id_clone (const NMPCacheId *id) +{ + NMPCacheId *id2; + + id2 = g_slice_new (NMPCacheId); + memcpy (id2, id, sizeof (NMPCacheId)); + return id2; +} + +void +nmp_cache_id_destroy (NMPCacheId *id) +{ + g_slice_free (NMPCacheId, id); +} + +/******************************************************************/ + +NMPCacheId _nmp_cache_id_static; + +static NMPCacheId * +_nmp_cache_id_init (NMPCacheId *id, NMPCacheIdType id_type) +{ + memset (id, 0, sizeof (NMPCacheId)); + id->_id_type = id_type; + return id; +} + +NMPCacheId * +nmp_cache_id_init_object_type (NMPCacheId *id, NMPObjectType obj_type, gboolean visible_only) +{ + _nmp_cache_id_init (id, visible_only + ? NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY + : NMP_CACHE_ID_TYPE_OBJECT_TYPE); + id->object_type.obj_type = obj_type; + return id; +} + +NMPCacheId * +nmp_cache_id_init_addrroute_visible_by_ifindex (NMPCacheId *id, + NMPObjectType obj_type, + int ifindex) +{ + g_return_val_if_fail (NM_IN_SET (obj_type, + NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ADDRESS, NMP_OBJECT_TYPE_IP6_ROUTE), NULL); + + if (ifindex <= 0) + return nmp_cache_id_init_object_type (id, obj_type, TRUE); + + _nmp_cache_id_init (id, NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX); + id->object_type_by_ifindex.obj_type = obj_type; + id->object_type_by_ifindex.ifindex = ifindex; + return id; +} + +NMPCacheId * +nmp_cache_id_init_routes_visible (NMPCacheId *id, + NMPObjectType obj_type, + gboolean with_default, + gboolean with_non_default, + int ifindex) +{ + g_return_val_if_fail (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE), NULL); + + if (with_default && with_non_default) { + if (ifindex <= 0) + return nmp_cache_id_init_object_type (id, obj_type, TRUE); + return nmp_cache_id_init_addrroute_visible_by_ifindex (id, obj_type, ifindex); + } + + if (with_default) + _nmp_cache_id_init (id, NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_ONLY_DEFAULT); + else if (with_non_default) + _nmp_cache_id_init (id, NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_NO_DEFAULT); + else + g_return_val_if_reached (NULL); + + id->object_type_by_ifindex.obj_type = obj_type; + id->object_type_by_ifindex.ifindex = ifindex; + return id; +} + +/******************************************************************/ + +static gboolean +_nmp_object_init_cache_id (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id) +{ + const NMPClass *klass = NMP_OBJECT_GET_CLASS (obj); + + switch (id_type) { + case NMP_CACHE_ID_TYPE_OBJECT_TYPE: + *out_id = nmp_cache_id_init_object_type (id, klass->obj_type, FALSE); + return TRUE; + case NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY: + if (nmp_object_is_visible (obj)) + *out_id = nmp_cache_id_init_object_type (id, klass->obj_type, TRUE); + else + *out_id = NULL; + return TRUE; + default: + return klass->cmd_obj_init_cache_id (obj, id_type, id, out_id); + } +} + +static gboolean +_vt_cmd_obj_init_cache_id_link (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id) +{ + return FALSE; +} + +static gboolean +_vt_cmd_obj_init_cache_id_ipx_address (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id) +{ + switch (id_type) { + case NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX: + if (_vt_cmd_obj_is_visible_ipx_address (obj)) { + nm_assert (obj->object.ifindex > 0); + *out_id = nmp_cache_id_init_addrroute_visible_by_ifindex (id, NMP_OBJECT_GET_TYPE (obj), obj->object.ifindex); + return TRUE; + } + break; + default: + return FALSE; + } + *out_id = NULL; + return TRUE; +} + +static gboolean +_vt_cmd_obj_init_cache_id_ipx_route (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id) +{ + switch (id_type) { + case NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX: + if (_vt_cmd_obj_is_visible_ipx_route (obj)) { + nm_assert (obj->object.ifindex > 0); + *out_id = nmp_cache_id_init_addrroute_visible_by_ifindex (id, NMP_OBJECT_GET_TYPE (obj), obj->object.ifindex); + return TRUE; + } + break; + case NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT: + if ( _vt_cmd_obj_is_visible_ipx_route (obj) + && !NM_PLATFORM_IP_ROUTE_IS_DEFAULT (&obj->ip_route)) { + nm_assert (obj->object.ifindex > 0); + *out_id = nmp_cache_id_init_routes_visible (id, NMP_OBJECT_GET_TYPE (obj), FALSE, TRUE, 0); + return TRUE; + } + break; + case NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ONLY_DEFAULT: + if ( _vt_cmd_obj_is_visible_ipx_route (obj) + && NM_PLATFORM_IP_ROUTE_IS_DEFAULT (&obj->ip_route)) { + nm_assert (obj->object.ifindex > 0); + *out_id = nmp_cache_id_init_routes_visible (id, NMP_OBJECT_GET_TYPE (obj), TRUE, FALSE, 0); + return TRUE; + } + break; + case NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_NO_DEFAULT: + if ( _vt_cmd_obj_is_visible_ipx_route (obj) + && !NM_PLATFORM_IP_ROUTE_IS_DEFAULT (&obj->ip_route)) { + nm_assert (obj->object.ifindex > 0); + *out_id = nmp_cache_id_init_routes_visible (id, NMP_OBJECT_GET_TYPE (obj), FALSE, TRUE, obj->object.ifindex); + return TRUE; + } + break; + case NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_ONLY_DEFAULT: + if ( _vt_cmd_obj_is_visible_ipx_route (obj) + && NM_PLATFORM_IP_ROUTE_IS_DEFAULT (&obj->ip_route)) { + nm_assert (obj->object.ifindex > 0); + *out_id = nmp_cache_id_init_routes_visible (id, NMP_OBJECT_GET_TYPE (obj), TRUE, FALSE, obj->object.ifindex); + return TRUE; + } + break; + default: + return FALSE; + } + *out_id = NULL; + return TRUE; +} + +/******************************************************************/ + +gboolean +nmp_cache_use_udev_detect () +{ + return access ("/sys", W_OK) == 0; +} + +gboolean +nmp_cache_use_udev_get (const NMPCache *cache) +{ + g_return_val_if_fail (cache, TRUE); + + return cache->use_udev; +} + +gboolean +nmp_cache_use_udev_set (NMPCache *cache, gboolean use_udev) +{ + g_return_val_if_fail (cache, FALSE); + + use_udev = !!use_udev; + if (use_udev == cache->use_udev) + return FALSE; + + cache->use_udev = use_udev; + return TRUE; +} + +/******************************************************************/ + +/** + * nmp_cache_link_connected_needs_toggle: + * @cache: the platform cache + * @master: the link object, that is checked whether its connected property + * needs to be toggled. + * @potential_slave: (allow-none): an additional link object that is treated + * as if it was inside @cache. If given, it shaddows a link in the cache + * with the same ifindex. + * @ignore_slave: (allow-none): if set, the check will pretend that @ignore_slave + * is not in the cache. + * + * NMPlatformLink has two connected flags: (master->link.flags&IFF_LOWER_UP) (as reported + * from netlink) and master->link.connected. For bond and bridge master, kernel reports + * those links as IFF_LOWER_UP if they have no slaves attached. We want to present instead + * a combined @connected flag that shows masters without slaves as down. + * + * Check if the connected flag of @master should be toggled according to the content + * of @cache (including @potential_slave). + * + * Returns: %TRUE, if @master->link.connected should be flipped/toggled. + **/ +gboolean +nmp_cache_link_connected_needs_toggle (const NMPCache *cache, const NMPObject *master, const NMPObject *potential_slave, const NMPObject *ignore_slave) +{ + const NMPlatformLink *const *links; + gboolean is_lower_up = FALSE; + guint len, i; + + if ( !master + || NMP_OBJECT_GET_TYPE (master) != NMP_OBJECT_TYPE_LINK + || master->link.ifindex <= 0 + || !nmp_object_is_visible (master) + || !NM_IN_SET (master->link.type, NM_LINK_TYPE_BRIDGE, NM_LINK_TYPE_BOND)) + return FALSE; + + /* if native IFF_LOWER_UP is down, link.connected must also be down + * regardless of the slaves. */ + if (!NM_FLAGS_HAS (master->link.flags, IFF_LOWER_UP)) + return !!master->link.connected; + + if (potential_slave && NMP_OBJECT_GET_TYPE (potential_slave) != NMP_OBJECT_TYPE_LINK) + potential_slave = NULL; + + if ( potential_slave + && nmp_object_is_visible (potential_slave) + && potential_slave->link.ifindex > 0 + && potential_slave->link.master == master->link.ifindex + && potential_slave->link.connected) { + is_lower_up = TRUE; + } else { + links = (const NMPlatformLink *const *) nmp_cache_lookup_multi (cache, nmp_cache_id_init_object_type (NMP_CACHE_ID_STATIC, NMP_OBJECT_TYPE_LINK, FALSE), &len); + for (i = 0; i < len; i++) { + const NMPlatformLink *link = links[i]; + const NMPObject *obj = NMP_OBJECT_UP_CAST ((NMPlatformObject *) link); + + nm_assert (NMP_OBJECT_GET_TYPE (NMP_OBJECT_UP_CAST ((NMPlatformObject *) link)) == NMP_OBJECT_TYPE_LINK); + + if ( (!potential_slave || potential_slave->link.ifindex != link->ifindex) + && ignore_slave != obj + && link->ifindex > 0 + && link->master == master->link.ifindex + && nmp_object_is_visible (obj) + && link->connected) { + is_lower_up = TRUE; + break; + } + } + } + return !!master->link.connected != is_lower_up; +} + +/** + * nmp_cache_link_connected_needs_toggle_by_ifindex: + * @cache: + * @master_ifindex: the ifindex of a potential master that should be checked + * whether it needs toggling. + * @potential_slave: (allow-none): passed to nmp_cache_link_connected_needs_toggle(). + * It considers @potential_slave as being inside the cache, replacing an existing + * link with the same ifindex. + * @ignore_slave: (allow-onne): passed to nmp_cache_link_connected_needs_toggle(). + * + * The flag obj->link.connected depends on the state of other links in the + * @cache. See also nmp_cache_link_connected_needs_toggle(). Given an ifindex + * of a master, check if the cache contains such a master link that needs + * toogling of the connected flag. + * + * Returns: NULL if there is no master link with ifindex @master_ifindex that should be toggled. + * Otherwise, return the link object from inside the cache with the given ifindex. + * The connected flag of that master should be toggled. + */ +const NMPObject * +nmp_cache_link_connected_needs_toggle_by_ifindex (const NMPCache *cache, int master_ifindex, const NMPObject *potential_slave, const NMPObject *ignore_slave) +{ + const NMPObject *master; + + if (master_ifindex > 0) { + master = nmp_cache_lookup_link (cache, master_ifindex); + if (nmp_cache_link_connected_needs_toggle (cache, master, potential_slave, ignore_slave)) + return master; + } + return NULL; +} + +/******************************************************************/ + +const NMPlatformObject *const * +nmp_cache_lookup_multi (const NMPCache *cache, const NMPCacheId *cache_id, guint *out_len) +{ + return (const NMPlatformObject *const *) nm_multi_index_lookup (cache->idx_multi, + (const NMMultiIndexId *) cache_id, + out_len); +} + +GArray * +nmp_cache_lookup_multi_to_array (const NMPCache *cache, NMPObjectType obj_type, const NMPCacheId *cache_id) +{ + const NMPClass *klass = nmp_class_from_type (obj_type); + guint len, i; + const NMPlatformObject *const *objects; + GArray *array; + + g_return_val_if_fail (klass, NULL); + + objects = nmp_cache_lookup_multi (cache, cache_id, &len); + array = g_array_sized_new (FALSE, FALSE, klass->sizeof_public, len); + + for (i = 0; i < len; i++) { + nm_assert (NMP_OBJECT_GET_CLASS (NMP_OBJECT_UP_CAST (objects[i])) == klass); + g_array_append_vals (array, objects[i], 1); + } + return array; +} + +const NMPObject * +nmp_cache_lookup_obj (const NMPCache *cache, const NMPObject *obj) +{ + g_return_val_if_fail (obj, NULL); + + return g_hash_table_lookup (cache->idx_main, obj); +} + +const NMPObject * +nmp_cache_lookup_link (const NMPCache *cache, int ifindex) +{ + NMPObject obj_needle; + + return nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&obj_needle, ifindex)); +} + +const NMPObject * +nmp_cache_lookup_link_full (const NMPCache *cache, + int ifindex, + const char *ifname, + gboolean visible_only, + NMLinkType link_type, + NMPObjectMatchFn match_fn, + gpointer user_data) +{ + NMPObject obj_needle; + const NMPObject *obj; + const NMPlatformObject *const *list; + guint i, len; + NMPCacheId cache_id; + + if (ifindex > 0) { + obj = nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&obj_needle, ifindex)); + + if ( !obj + || (visible_only && !nmp_object_is_visible (obj)) + || (link_type != NM_LINK_TYPE_NONE && obj->link.type != link_type) + || (ifname && strcmp (obj->link.name, ifname)) + || (match_fn && !match_fn (obj, user_data))) + return NULL; + return obj; + } else if (!ifname && !match_fn) + return NULL; + else { + list = nmp_cache_lookup_multi (cache, nmp_cache_id_init_object_type (&cache_id, NMP_OBJECT_TYPE_LINK, visible_only), &len); + for (i = 0; i < len; i++) { + obj = NMP_OBJECT_UP_CAST (list[i]); + + if (link_type != NM_LINK_TYPE_NONE && obj->link.type != link_type) + continue; + if (ifname && strcmp (ifname, obj->link.name)) + continue; + if (match_fn && !match_fn (obj, user_data)) + continue; + + return obj; + } + return NULL; + } +} + +GHashTable * +nmp_cache_lookup_all_to_hash (const NMPCache *cache, + NMPCacheId *cache_id, + GHashTable *hash) +{ + NMMultiIndexIdIter iter; + gpointer plobj; + + nm_multi_index_id_iter_init (&iter, cache->idx_multi, (const NMMultiIndexId *) cache_id); + + if (nm_multi_index_id_iter_next (&iter, &plobj)) { + if (!hash) + hash = g_hash_table_new_full (NULL, NULL, (GDestroyNotify) nmp_object_unref, NULL); + + do { + g_hash_table_add (hash, nmp_object_ref (NMP_OBJECT_UP_CAST (plobj))); + } while (nm_multi_index_id_iter_next (&iter, &plobj)); + } + + return hash; +} + +/******************************************************************/ + +static void +_nmp_cache_update_cache (NMPCache *cache, NMPObject *obj, gboolean remove) +{ + NMPCacheIdType id_type; + + for (id_type = 0; id_type <= NMP_CACHE_ID_TYPE_MAX; id_type++) { + NMPCacheId cache_id_storage; + const NMPCacheId *cache_id; + + if (!_nmp_object_init_cache_id (obj, id_type, &cache_id_storage, &cache_id)) + continue; + if (!cache_id) + continue; + + /* We don't put @obj itself into the multi index, but &obj->object. As of now, all + * users expect a pointer to NMPlatformObject, not NMPObject. + * You can use NMP_OBJECT_UP_CAST() to retrieve the original @obj pointer. + * + * If need be, we could determine based on @id_type which pointer we want to store. */ + + if (remove) { + if (!nm_multi_index_remove (cache->idx_multi, &cache_id->base, &obj->object)) + g_assert_not_reached (); + } else { + if (!nm_multi_index_add (cache->idx_multi, &cache_id->base, &obj->object)) + g_assert_not_reached (); + } + } +} + +static void +_nmp_cache_update_add (NMPCache *cache, NMPObject *obj) +{ + nm_assert (!obj->is_cached); + nmp_object_ref (obj); + nm_assert (!nm_multi_index_lookup_first_by_value (cache->idx_multi, &obj->object)); +#if GLIB_CHECK_VERSION(2, 40, 0) + if (!g_hash_table_add (cache->idx_main, obj)) + g_assert_not_reached (); +#else + g_hash_table_add (cache->idx_main, obj); +#endif + obj->is_cached = TRUE; + _nmp_cache_update_cache (cache, obj, FALSE); +} + +static void +_nmp_cache_update_remove (NMPCache *cache, NMPObject *obj) +{ + nm_assert (obj->is_cached); + _nmp_cache_update_cache (cache, obj, TRUE); + obj->is_cached = FALSE; + if (!g_hash_table_remove (cache->idx_main, obj)) + g_assert_not_reached (); + + /* @obj is possibly a dangling pointer at this point. No problem, multi-index doesn't dereference. */ + nm_assert (!nm_multi_index_lookup_first_by_value (cache->idx_multi, &obj->object)); +} + +static void +_nmp_cache_update_update (NMPCache *cache, NMPObject *obj, const NMPObject *new) +{ + NMPCacheIdType id_type; + + nm_assert (NMP_OBJECT_GET_CLASS (obj) == NMP_OBJECT_GET_CLASS (new)); + nm_assert (obj->is_cached); + nm_assert (!new->is_cached); + + for (id_type = 0; id_type <= NMP_CACHE_ID_TYPE_MAX; id_type++) { + NMPCacheId cache_id_storage_obj, cache_id_storage_new; + const NMPCacheId *cache_id_obj, *cache_id_new; + + if (!_nmp_object_init_cache_id (obj, id_type, &cache_id_storage_obj, &cache_id_obj)) + continue; + if (!_nmp_object_init_cache_id (new, id_type, &cache_id_storage_new, &cache_id_new)) + g_assert_not_reached (); + if (!nm_multi_index_move (cache->idx_multi, (NMMultiIndexId *) cache_id_obj, (NMMultiIndexId *) cache_id_new, &obj->object)) + g_assert_not_reached (); + } + nmp_object_copy (obj, new, FALSE); +} + +NMPCacheOpsType +nmp_cache_remove (NMPCache *cache, const NMPObject *obj, gboolean equals_by_ptr, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data) +{ + NMPObject *old; + + nm_assert (NMP_OBJECT_IS_VALID (obj)); + + old = g_hash_table_lookup (cache->idx_main, obj); + if (!old) { + if (out_obj) + *out_obj = NULL; + if (out_was_visible) + *out_was_visible = FALSE; + return NMP_CACHE_OPS_UNCHANGED; + } + + if (out_obj) + *out_obj = nmp_object_ref (old); + if (out_was_visible) + *out_was_visible = nmp_object_is_visible (old); + if (equals_by_ptr && old != obj) { + /* We found an identical object, but we only delete it if it's the same pointer as + * @obj. */ + return NMP_CACHE_OPS_UNCHANGED; + } + if (pre_hook) + pre_hook (cache, old, NULL, NMP_CACHE_OPS_REMOVED, user_data); + _nmp_cache_update_remove (cache, old); + return NMP_CACHE_OPS_REMOVED; +} + +NMPCacheOpsType +nmp_cache_remove_netlink (NMPCache *cache, const NMPObject *obj_needle, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data) +{ + if (NMP_OBJECT_GET_TYPE (obj_needle) == NMP_OBJECT_TYPE_LINK) { + NMPObject *old; + auto_nmp_obj NMPObject *obj = NULL; + + /* For nmp_cache_remove_netlink() we have an incomplete @obj_needle instance to be + * removed from netlink. Link objects are alive without being in netlink when they + * have a udev-device. All we want to do in this case is clear the netlink.is_in_netlink + * flag. */ + + old = (NMPObject *) nmp_cache_lookup_link (cache, obj_needle->link.ifindex); + if (!old) { + if (out_obj) + *out_obj = NULL; + if (out_was_visible) + *out_was_visible = FALSE; + return NMP_CACHE_OPS_UNCHANGED; + } + + if (out_obj) + *out_obj = nmp_object_ref (old); + if (out_was_visible) + *out_was_visible = nmp_object_is_visible (old); + + if (!old->_link.netlink.is_in_netlink) { + nm_assert (old->_link.udev.device); + return NMP_CACHE_OPS_UNCHANGED; + } + + if (!old->_link.udev.device) { + /* the update would make @old invalid. Remove it. */ + if (pre_hook) + pre_hook (cache, old, NULL, NMP_CACHE_OPS_REMOVED, user_data); + _nmp_cache_update_remove (cache, old); + return NMP_CACHE_OPS_REMOVED; + } + + obj = nmp_object_clone (old, FALSE); + obj->_link.netlink.is_in_netlink = FALSE; + + _nmp_object_fixup_link_master_connected (obj, cache); + _nmp_object_fixup_link_udev_fields (obj, cache->use_udev); + + if (pre_hook) + pre_hook (cache, old, obj, NMP_CACHE_OPS_UPDATED, user_data); + _nmp_cache_update_update (cache, old, obj); + return NMP_CACHE_OPS_UPDATED; + } else + return nmp_cache_remove (cache, obj_needle, FALSE, out_obj, out_was_visible, pre_hook, user_data); +} + +/** + * nmp_cache_update_netlink: + * @cache: the platform cache + * @obj: a #NMPObject instance as received from netlink and created via + * nmp_object_from_nl(). Especially for link, it must not have the udev + * replated fields set. + * This instance will be modified and might be put into the cache. When + * calling nmp_cache_update_netlink() you hand @obj over to the cache. + * Except, that the cache will increment the ref count as appropriate. You + * must still unref the obj to release your part of the ownership. + * @out_obj: (allow-none): (out): return the object instance that is inside + * the cache. If you specify non %NULL, you must always unref the returned + * instance. If the return value indicates that the object was removed, + * the object is no longer in the cache. Even if the return value indicates + * that the object was unchanged, it will still return @out_obj -- if + * such an object is in the cache. + * @out_was_visible: (allow-none): (out): whether the object was visible before + * the update operation. + * @pre_hook: (allow-none): a callback *before* the object gets updated. You cannot + * influence the outcome and must not do anything beyong inspecting the changes. + * @user_data: + * + * Returns: how the cache changed. + **/ +NMPCacheOpsType +nmp_cache_update_netlink (NMPCache *cache, NMPObject *obj, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data) +{ + NMPObject *old; + + nm_assert (NMP_OBJECT_IS_VALID (obj)); + nm_assert (!NMP_OBJECT_IS_STACKINIT (obj)); + nm_assert (!obj->is_cached); + + /* A link object from netlink must have the udev related fields unset. + * We could implement to handle that, but there is no need to support such + * a use-case */ + nm_assert (NMP_OBJECT_GET_TYPE (obj) != NMP_OBJECT_TYPE_LINK || + ( !obj->_link.udev.device + && !obj->link.driver)); + + old = g_hash_table_lookup (cache->idx_main, obj); + + if (out_obj) + *out_obj = NULL; + if (out_was_visible) + *out_was_visible = FALSE; + + if (!old) { + if (!nmp_object_is_alive (obj)) + return NMP_CACHE_OPS_UNCHANGED; + + if (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_LINK) { + _nmp_object_fixup_link_master_connected (obj, cache); + _nmp_object_fixup_link_udev_fields (obj, cache->use_udev); + } + + if (out_obj) + *out_obj = nmp_object_ref (obj); + + if (pre_hook) + pre_hook (cache, NULL, obj, NMP_CACHE_OPS_ADDED, user_data); + _nmp_cache_update_add (cache, obj); + return NMP_CACHE_OPS_ADDED; + } else if (old == obj) { + /* updating a cached object inplace is not supported because the object contributes to hash-key + * for NMMultiIndex. Modifying an object that is inside NMMultiIndex means that these + * keys change. + * The problem is, that for a given object NMMultiIndex does not support (efficient) + * reverse lookup to get all the NMPCacheIds to which it belongs. If that would be implemented, + * it would be possible to implement inplace-update. + * + * There is an un-optimized reverse lookup via nm_multi_index_iter_init(), but we don't want + * that because we might have a large number of indexes to search. + * + * We could add efficient reverse lookup by adding a reverse index to NMMultiIndex. But that + * also adds some cost to support an (uncommon?) usage pattern. + * + * Instead we just don't support it. Actually, we expect the user to + * create a new instance with nmp_object_from_nl(). That is what nmp_cache_update_netlink(). + * + * TL;DR: a cached object must never be modified. + */ + g_assert_not_reached (); + } else { + gboolean is_alive = FALSE; + + nm_assert (old->is_cached); + + if (out_obj) + *out_obj = nmp_object_ref (old); + if (out_was_visible) + *out_was_visible = nmp_object_is_visible (old); + + if (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_LINK) { + if (!obj->_link.netlink.is_in_netlink) { + if (!old->_link.netlink.is_in_netlink) { + nm_assert (old->_link.udev.device); + return NMP_CACHE_OPS_UNCHANGED; + } + if (old->_link.udev.device) { + /* @obj is not in netlink. + * + * This is similar to nmp_cache_remove_netlink(), but there we preserve the + * preexisting netlink properties. The use case of that is when kernel_get_object() + * cannot load an object (based on the id of a needle). + * + * Here we keep the data provided from @obj. The usecase is when receiving + * a valid @obj instance from netlink with RTM_DELROUTE. + */ + is_alive = TRUE; + } + } else + is_alive = TRUE; + + if (is_alive) { + _nmp_object_fixup_link_master_connected (obj, cache); + + /* Merge the netlink parts with what we have from udev. */ + g_clear_object (&obj->_link.udev.device); + obj->_link.udev.device = old->_link.udev.device ? g_object_ref (old->_link.udev.device) : NULL; + _nmp_object_fixup_link_udev_fields (obj, cache->use_udev); + } + } else + is_alive = nmp_object_is_alive (obj); + + if (!is_alive) { + /* the update would make @old invalid. Remove it. */ + if (pre_hook) + pre_hook (cache, old, NULL, NMP_CACHE_OPS_REMOVED, user_data); + _nmp_cache_update_remove (cache, old); + return NMP_CACHE_OPS_REMOVED; + } + + if (nmp_object_equal (old, obj)) + return NMP_CACHE_OPS_UNCHANGED; + + if (pre_hook) + pre_hook (cache, old, obj, NMP_CACHE_OPS_UPDATED, user_data); + _nmp_cache_update_update (cache, old, obj); + return NMP_CACHE_OPS_UPDATED; + } +} + +NMPCacheOpsType +nmp_cache_update_link_udev (NMPCache *cache, int ifindex, GUdevDevice *udev_device, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data) +{ + NMPObject *old; + auto_nmp_obj NMPObject *obj = NULL; + + old = (NMPObject *) nmp_cache_lookup_link (cache, ifindex); + + if (out_obj) + *out_obj = NULL; + if (out_was_visible) + *out_was_visible = FALSE; + + if (!old) { + if (!udev_device) + return NMP_CACHE_OPS_UNCHANGED; + + obj = nmp_object_new (NMP_OBJECT_TYPE_LINK, NULL); + obj->link.ifindex = ifindex; + obj->_link.udev.device = g_object_ref (udev_device); + + _nmp_object_fixup_link_udev_fields (obj, cache->use_udev); + + nm_assert (nmp_object_is_alive (obj)); + + if (out_obj) + *out_obj = nmp_object_ref (obj); + + if (pre_hook) + pre_hook (cache, NULL, obj, NMP_CACHE_OPS_ADDED, user_data); + _nmp_cache_update_add (cache, obj); + return NMP_CACHE_OPS_ADDED; + } else { + nm_assert (old->is_cached); + + if (out_obj) + *out_obj = nmp_object_ref (old); + if (out_was_visible) + *out_was_visible = nmp_object_is_visible (old); + + if (old->_link.udev.device == udev_device) + return NMP_CACHE_OPS_UNCHANGED; + + if (!udev_device && !old->_link.netlink.is_in_netlink) { + /* the update would make @old invalid. Remove it. */ + if (pre_hook) + pre_hook (cache, old, NULL, NMP_CACHE_OPS_REMOVED, user_data); + _nmp_cache_update_remove (cache, old); + return NMP_CACHE_OPS_REMOVED; + } + + obj = nmp_object_clone (old, FALSE); + + g_clear_object (&obj->_link.udev.device); + obj->_link.udev.device = udev_device ? g_object_ref (udev_device) : NULL; + + _nmp_object_fixup_link_udev_fields (obj, cache->use_udev); + + nm_assert (nmp_object_is_alive (obj)); + + if (pre_hook) + pre_hook (cache, old, obj, NMP_CACHE_OPS_UPDATED, user_data); + _nmp_cache_update_update (cache, old, obj); + return NMP_CACHE_OPS_UPDATED; + } +} + +NMPCacheOpsType +nmp_cache_update_link_master_connected (NMPCache *cache, int ifindex, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data) +{ + NMPObject *old; + auto_nmp_obj NMPObject *obj = NULL; + + old = (NMPObject *) nmp_cache_lookup_link (cache, ifindex); + + if (!old) { + if (out_obj) + *out_obj = NULL; + if (out_was_visible) + *out_was_visible = FALSE; + + return NMP_CACHE_OPS_UNCHANGED; + } + + nm_assert (old->is_cached); + + if (out_obj) + *out_obj = nmp_object_ref (old); + if (out_was_visible) + *out_was_visible = nmp_object_is_visible (old); + + if (!nmp_cache_link_connected_needs_toggle (cache, old, NULL, NULL)) + return NMP_CACHE_OPS_UNCHANGED; + + obj = nmp_object_clone (old, FALSE); + obj->link.connected = !old->link.connected; + + nm_assert (nmp_object_is_alive (obj)); + + if (pre_hook) + pre_hook (cache, old, obj, NMP_CACHE_OPS_UPDATED, user_data); + _nmp_cache_update_update (cache, old, obj); + return NMP_CACHE_OPS_UPDATED; +} + +/******************************************************************/ + +NMPCache * +nmp_cache_new () +{ + NMPCache *cache = g_new (NMPCache, 1); + + cache->idx_main = g_hash_table_new_full ((GHashFunc) nmp_object_id_hash, + (GEqualFunc) nmp_object_id_equal, + (GDestroyNotify) nmp_object_unref, + NULL); + cache->idx_multi = nm_multi_index_new ((NMMultiIndexFuncHash) nmp_cache_id_hash, + (NMMultiIndexFuncEqual) nmp_cache_id_equal, + (NMMultiIndexFuncClone) nmp_cache_id_clone, + (NMMultiIndexFuncDestroy) nmp_cache_id_destroy); + cache->use_udev = nmp_cache_use_udev_detect (); + return cache; +} + +void +nmp_cache_free (NMPCache *cache) +{ + GHashTableIter iter; + NMPObject *obj; + + /* No need to cumbersomely remove the objects properly. They are not hooked up + * in a complicated way, we can just unref them together with cache->idx_main. + * + * But we must clear the @is_cached flag. */ + g_hash_table_iter_init (&iter, cache->idx_main); + while (g_hash_table_iter_next (&iter, (gpointer *) &obj, NULL)) { + nm_assert (obj->is_cached); + obj->is_cached = FALSE; + } + + nm_multi_index_free (cache->idx_multi); + g_hash_table_unref (cache->idx_main); + + g_free (cache); +} + +/******************************************************************/ + +void +ASSERT_nmp_cache_is_consistent (const NMPCache *cache) +{ +#ifdef NM_MORE_ASSERTS + NMMultiIndexIter iter_multi; + GHashTableIter iter_hash; + guint i, len; + NMPCacheId cache_id_storage; + const NMPCacheId *cache_id, *cache_id2; + const NMPlatformObject *const *objects; + const NMPObject *obj; + + g_assert (cache); + + g_hash_table_iter_init (&iter_hash, cache->idx_main); + while (g_hash_table_iter_next (&iter_hash, (gpointer *) &obj, NULL)) { + NMPCacheIdType id_type; + + g_assert (NMP_OBJECT_IS_VALID (obj)); + g_assert (nmp_object_is_alive (obj)); + + for (id_type = 0; id_type <= NMP_CACHE_ID_TYPE_MAX; id_type++) { + if (!_nmp_object_init_cache_id (obj, id_type, &cache_id_storage, &cache_id)) + continue; + if (!cache_id) + continue; + g_assert (nm_multi_index_contains (cache->idx_multi, &cache_id->base, &obj->object)); + } + } + + nm_multi_index_iter_init (&iter_multi, cache->idx_multi, NULL); + while (nm_multi_index_iter_next (&iter_multi, + (const NMMultiIndexId **) &cache_id, + (void *const**) &objects, + &len)) { + g_assert (len > 0 && objects && objects[len] == NULL); + + for (i = 0; i < len; i++) { + g_assert (objects[i]); + obj = NMP_OBJECT_UP_CAST (objects[i]); + g_assert (NMP_OBJECT_IS_VALID (obj)); + + /* for now, enforce that all objects for a certain index are of the same type. */ + g_assert (NMP_OBJECT_GET_CLASS (obj) == NMP_OBJECT_GET_CLASS (NMP_OBJECT_UP_CAST (objects[0]))); + + if (!_nmp_object_init_cache_id (obj, cache_id->_id_type, &cache_id_storage, &cache_id2)) + g_assert_not_reached (); + g_assert (cache_id2); + g_assert (nmp_cache_id_equal (cache_id, cache_id2)); + g_assert_cmpint (nmp_cache_id_hash (cache_id), ==, nmp_cache_id_hash (cache_id2)); + + g_assert (obj == g_hash_table_lookup (cache->idx_main, obj)); + } + } +#endif +} +/******************************************************************/ + +const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { + [NMP_OBJECT_TYPE_LINK - 1] = { + .obj_type = NMP_OBJECT_TYPE_LINK, + .sizeof_data = sizeof (NMPObjectLink), + .sizeof_public = sizeof (NMPlatformLink), + .obj_type_name = "link", + .nl_type = "route/link", + .addr_family = AF_UNSPEC, + .rtm_gettype = RTM_GETLINK, + .signal_type = NM_PLATFORM_SIGNAL_LINK_CHANGED, + .cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_link, + .cmd_obj_equal = _vt_cmd_obj_equal_link, + .cmd_obj_copy = _vt_cmd_obj_copy_link, + .cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_link, + .cmd_obj_dispose = _vt_cmd_obj_dispose_link, + .cmd_obj_is_alive = _vt_cmd_obj_is_alive_link, + .cmd_obj_is_visible = _vt_cmd_obj_is_visible_link, + .cmd_plobj_init_from_nl = _nmp_vt_cmd_plobj_init_from_nl_link, + .cmd_plobj_to_nl = _nmp_vt_cmd_plobj_to_nl_link, + .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_link, + .cmd_plobj_id_equal = _vt_cmd_plobj_id_equal_link, + .cmd_plobj_id_hash = _vt_cmd_plobj_id_hash_link, + .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_link, + .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj)) nm_platform_link_to_string, + .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_link_cmp, + }, + [NMP_OBJECT_TYPE_IP4_ADDRESS - 1] = { + .obj_type = NMP_OBJECT_TYPE_IP4_ADDRESS, + .sizeof_data = sizeof (NMPObjectIP4Address), + .sizeof_public = sizeof (NMPlatformIP4Address), + .obj_type_name = "ip4-address", + .nl_type = "route/addr", + .addr_family = AF_INET, + .rtm_gettype = RTM_GETADDR, + .signal_type = NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, + .cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_ipx_address, + .cmd_obj_equal = _vt_cmd_obj_equal_plain, + .cmd_obj_copy = _vt_cmd_obj_copy_plain, + .cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_ip4_address, + .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_address, + .cmd_obj_is_visible = _vt_cmd_obj_is_visible_ipx_address, + .cmd_plobj_init_from_nl = _nmp_vt_cmd_plobj_init_from_nl_ip4_address, + .cmd_plobj_to_nl = _nmp_vt_cmd_plobj_to_nl_ip4_address, + .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip4_address, + .cmd_plobj_id_equal = _vt_cmd_plobj_id_equal_ip4_address, + .cmd_plobj_id_hash = _vt_cmd_plobj_id_hash_ip4_address, + .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip4_address, + .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj)) nm_platform_ip4_address_to_string, + .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip4_address_cmp, + }, + [NMP_OBJECT_TYPE_IP6_ADDRESS - 1] = { + .obj_type = NMP_OBJECT_TYPE_IP6_ADDRESS, + .sizeof_data = sizeof (NMPObjectIP6Address), + .sizeof_public = sizeof (NMPlatformIP6Address), + .obj_type_name = "ip6-address", + .nl_type = "route/addr", + .addr_family = AF_INET6, + .rtm_gettype = RTM_GETADDR, + .signal_type = NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, + .cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_ipx_address, + .cmd_obj_equal = _vt_cmd_obj_equal_plain, + .cmd_obj_copy = _vt_cmd_obj_copy_plain, + .cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_ip6_address, + .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_address, + .cmd_obj_is_visible = _vt_cmd_obj_is_visible_ipx_address, + .cmd_plobj_init_from_nl = _nmp_vt_cmd_plobj_init_from_nl_ip6_address, + .cmd_plobj_to_nl = _nmp_vt_cmd_plobj_to_nl_ip6_address, + .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip6_address, + .cmd_plobj_id_equal = _vt_cmd_plobj_id_equal_ip6_address, + .cmd_plobj_id_hash = _vt_cmd_plobj_id_hash_ip6_address, + .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip6_address, + .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj)) nm_platform_ip6_address_to_string, + .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip6_address_cmp + }, + [NMP_OBJECT_TYPE_IP4_ROUTE - 1] = { + .obj_type = NMP_OBJECT_TYPE_IP4_ROUTE, + .sizeof_data = sizeof (NMPObjectIP4Route), + .sizeof_public = sizeof (NMPlatformIP4Route), + .obj_type_name = "ip4-route", + .nl_type = "route/route", + .addr_family = AF_INET, + .rtm_gettype = RTM_GETROUTE, + .signal_type = NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, + .cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_ipx_route, + .cmd_obj_equal = _vt_cmd_obj_equal_plain, + .cmd_obj_copy = _vt_cmd_obj_copy_plain, + .cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_ip4_route, + .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_route, + .cmd_obj_is_visible = _vt_cmd_obj_is_visible_ipx_route, + .cmd_plobj_init_from_nl = _nmp_vt_cmd_plobj_init_from_nl_ip4_route, + .cmd_plobj_to_nl = _nmp_vt_cmd_plobj_to_nl_ip4_route, + .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip4_route, + .cmd_plobj_id_equal = _vt_cmd_plobj_id_equal_ip4_route, + .cmd_plobj_id_hash = _vt_cmd_plobj_id_hash_ip4_route, + .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip4_route, + .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj)) nm_platform_ip4_route_to_string, + .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip4_route_cmp, + }, + [NMP_OBJECT_TYPE_IP6_ROUTE - 1] = { + .obj_type = NMP_OBJECT_TYPE_IP6_ROUTE, + .sizeof_data = sizeof (NMPObjectIP6Route), + .sizeof_public = sizeof (NMPlatformIP6Route), + .obj_type_name = "ip6-route", + .nl_type = "route/route", + .addr_family = AF_INET6, + .rtm_gettype = RTM_GETROUTE, + .signal_type = NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, + .cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_ipx_route, + .cmd_obj_equal = _vt_cmd_obj_equal_plain, + .cmd_obj_copy = _vt_cmd_obj_copy_plain, + .cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_ip6_route, + .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_route, + .cmd_obj_is_visible = _vt_cmd_obj_is_visible_ipx_route, + .cmd_plobj_init_from_nl = _nmp_vt_cmd_plobj_init_from_nl_ip6_route, + .cmd_plobj_to_nl = _nmp_vt_cmd_plobj_to_nl_ip6_route, + .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip6_route, + .cmd_plobj_id_equal = _vt_cmd_plobj_id_equal_ip6_route, + .cmd_plobj_id_hash = _vt_cmd_plobj_id_hash_ip6_route, + .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip6_route, + .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj)) nm_platform_ip6_route_to_string, + .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip6_route_cmp, + }, +}; + diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h new file mode 100644 index 00000000..5b1887b8 --- /dev/null +++ b/src/platform/nmp-object.h @@ -0,0 +1,361 @@ +/* -*- 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 + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2015 Red Hat, Inc. + */ + +#ifndef __NMP_OBJECT_H__ +#define __NMP_OBJECT_H__ + +#include "config.h" + +#include "nm-platform.h" +#include "nm-multi-index.h" +#include "nm-macros-internal.h" + +#include <netlink/netlink.h> +#include <gudev/gudev.h> + + +typedef enum { /*< skip >*/ + NMP_OBJECT_TO_STRING_ID, + NMP_OBJECT_TO_STRING_PUBLIC, + NMP_OBJECT_TO_STRING_ALL, +} NMPObjectToStringMode; + +typedef enum { /*< skip >*/ + NMP_CACHE_OPS_UNCHANGED = NM_PLATFORM_SIGNAL_NONE, + NMP_CACHE_OPS_UPDATED = NM_PLATFORM_SIGNAL_CHANGED, + NMP_CACHE_OPS_ADDED = NM_PLATFORM_SIGNAL_ADDED, + NMP_CACHE_OPS_REMOVED = NM_PLATFORM_SIGNAL_REMOVED, +} NMPCacheOpsType; + +/* The NMPCacheIdType are the different index types. + * + * An object of a certain object-type, can be candidate to being + * indexed by a certain NMPCacheIdType or not. For example, all + * objects are indexed via an index of type NMP_CACHE_ID_TYPE_OBJECT_TYPE, + * but only route objects can be indexed by NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT. + * + * Of one index type, there can be multiple indexes or not. + * For example, of the index type NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX there + * are multiple instances (for different route/addresses, v4/v6, per-ifindex). + * + * But one object, can only be indexed by one particular index of a + * type. For example, a certain address instance is only indexed by + * the index NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX with + * matching v4/v6 and ifindex -- or maybe not at all if it isn't visible. + * */ +typedef enum { /*< skip >*/ + /* all the objects of a certain type */ + NMP_CACHE_ID_TYPE_OBJECT_TYPE, + + /* all the visible objects of a certain type */ + NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY, + + /* indeces for the visible routes, ignoring ifindex. */ + NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT, + NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ONLY_DEFAULT, + + /* all the visible addresses/routes (by object-type) for an ifindex. */ + NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX, + + /* three indeces for the visible routes, per ifindex. */ + NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_NO_DEFAULT, + NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_ONLY_DEFAULT, + + __NMP_CACHE_ID_TYPE_MAX, + NMP_CACHE_ID_TYPE_MAX = __NMP_CACHE_ID_TYPE_MAX - 1, +} NMPCacheIdType; + +typedef struct _NMPObject NMPObject; + +typedef struct { + union { + NMMultiIndexId base; + guint8 _id_type; /* NMPCacheIdType as guint8 */ + struct { + /* NMP_CACHE_ID_TYPE_OBJECT_TYPE */ + /* NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY */ + /* NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT */ + /* NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_ONLY_DEFAULT */ + guint8 _id_type; + guint8 obj_type; /* NMPObjectType as guint8 */ + } object_type; + struct { + /* NMP_CACHE_ID_TYPE_ADDRROUTE_VISIBLE_BY_IFINDEX */ + /* NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_NO_DEFAULT */ + /* NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_BY_IFINDEX_ONLY_DEFAULT */ + guint8 _id_type; + guint8 obj_type; /* NMPObjectType as guint8 */ + int ifindex; + } object_type_by_ifindex; + }; +} NMPCacheId; + +extern NMPCacheId _nmp_cache_id_static; +#define NMP_CACHE_ID_STATIC (&_nmp_cache_id_static) + +typedef struct { + NMPObjectType obj_type; + int addr_family; + int rtm_gettype; + int sizeof_data; + int sizeof_public; + const char *obj_type_name; + const char *nl_type; + const char *signal_type; + + /* returns %FALSE, if the obj type would never have an entry for index type @id_type. If @obj has an index, + * initialize @id and set @out_id to it. Otherwise, @out_id is NULL. */ + gboolean (*cmd_obj_init_cache_id) (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id); + + gboolean (*cmd_obj_equal) (const NMPObject *obj1, const NMPObject *obj2); + void (*cmd_obj_copy) (NMPObject *dst, const NMPObject *src); + void (*cmd_obj_stackinit_id) (NMPObject *obj, const NMPObject *src); + void (*cmd_obj_dispose) (NMPObject *obj); + gboolean (*cmd_obj_is_alive) (const NMPObject *obj); + gboolean (*cmd_obj_is_visible) (const NMPObject *obj); + + /* functions that operate on NMPlatformObject */ + gboolean (*cmd_plobj_init_from_nl) (NMPlatform *platform, NMPlatformObject *obj, const struct nl_object *nlo, gboolean id_only, gboolean complete_from_cache); + struct nl_object *(*cmd_plobj_to_nl) (NMPlatform *platform, const NMPlatformObject *obj, gboolean id_only); + void (*cmd_plobj_id_copy) (NMPlatformObject *dst, const NMPlatformObject *src); + gboolean (*cmd_plobj_id_equal) (const NMPlatformObject *obj1, const NMPlatformObject *obj2); + guint (*cmd_plobj_id_hash) (const NMPlatformObject *obj); + const char *(*cmd_plobj_to_string_id) (const NMPlatformObject *obj, char *buf, gsize buf_size); + const char *(*cmd_plobj_to_string) (const NMPlatformObject *obj); + int (*cmd_plobj_cmp) (const NMPlatformObject *obj1, const NMPlatformObject *obj2); +} NMPClass; + +extern const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX]; + +typedef struct { + NMPlatformLink _public; + + struct { + guint8 is_in_netlink; + } netlink; + + struct { + GUdevDevice *device; + } udev; +} NMPObjectLink; + +typedef struct { + NMPlatformIP4Address _public; +} NMPObjectIP4Address; + +typedef struct { + NMPlatformIP4Route _public; +} NMPObjectIP4Route; + +typedef struct { + NMPlatformIP6Address _public; +} NMPObjectIP6Address; + +typedef struct { + NMPlatformIP6Route _public; +} NMPObjectIP6Route; + +struct _NMPObject { + const NMPClass *_class; + int _ref_count; + guint8 is_cached; + union { + NMPlatformObject object; + + NMPlatformLink link; + NMPObjectLink _link; + + NMPlatformIPAddress ip_address; + NMPlatformIPXAddress ipx_address; + NMPlatformIP4Address ip4_address; + NMPlatformIP6Address ip6_address; + NMPObjectIP4Address _ip4_address; + NMPObjectIP6Address _ip6_address; + + NMPlatformIPRoute ip_route; + NMPlatformIPXRoute ipx_route; + NMPlatformIP4Route ip4_route; + NMPlatformIP6Route ip6_route; + NMPObjectIP4Route _ip4_route; + NMPObjectIP6Route _ip6_route; + }; +}; + +static inline gboolean +NMP_CLASS_IS_VALID (const NMPClass *klass) +{ + return klass >= &_nmp_classes[0] + && klass <= &_nmp_classes[G_N_ELEMENTS (_nmp_classes)] + && ((((char *) klass) - ((char *) NULL)) % (&_nmp_classes[1] - &_nmp_classes[0])) == 0; +} + +#define NMP_REF_COUNT_STACKINIT (G_MAXINT) + +static inline NMPObject * +NMP_OBJECT_UP_CAST(const NMPlatformObject *plobj) +{ + NMPObject *obj; + + obj = plobj + ? (NMPObject *) ( &(((char *) plobj)[-((int) G_STRUCT_OFFSET (NMPObject, object))]) ) + : NULL; + nm_assert (!obj || (obj->_ref_count > 0 && NMP_CLASS_IS_VALID (obj->_class))); + return obj; +} +#define NMP_OBJECT_UP_CAST(plobj) (NMP_OBJECT_UP_CAST ((const NMPlatformObject *) (plobj))) + +static inline gboolean +NMP_OBJECT_IS_VALID (const NMPObject *obj) +{ + nm_assert (!obj || ( obj + && obj->_ref_count > 0 + && NMP_CLASS_IS_VALID (obj->_class))); + + /* There isn't really much to check. Either @obj is NULL, or we must + * assume that it points to valid memory. */ + return obj != NULL; +} + +static inline gboolean +NMP_OBJECT_IS_STACKINIT (const NMPObject *obj) +{ + nm_assert (!obj || NMP_OBJECT_IS_VALID (obj)); + + return obj && obj->_ref_count == NMP_REF_COUNT_STACKINIT; +} + +static inline const NMPClass * +NMP_OBJECT_GET_CLASS (const NMPObject *obj) +{ + nm_assert (NMP_OBJECT_IS_VALID (obj)); + + return obj->_class; +} + +static inline NMPObjectType +NMP_OBJECT_GET_TYPE (const NMPObject *obj) +{ + nm_assert (!obj || NMP_OBJECT_IS_VALID (obj)); + + return obj ? obj->_class->obj_type : NMP_OBJECT_TYPE_UNKNOWN; +} + + + +const NMPClass *nmp_class_from_type (NMPObjectType obj_type); + +NMPObject *nmp_object_ref (NMPObject *object); +void nmp_object_unref (NMPObject *object); +NMPObject *nmp_object_new (NMPObjectType obj_type, const NMPlatformObject *plob); +NMPObject *nmp_object_new_link (int ifindex); + +const NMPObject *nmp_object_stackinit (NMPObject *obj, NMPObjectType obj_type, const NMPlatformObject *plobj); +const NMPObject *nmp_object_stackinit_id (NMPObject *obj, const NMPObject *src); +const NMPObject *nmp_object_stackinit_id_link (NMPObject *obj, int ifindex); +const NMPObject *nmp_object_stackinit_id_ip4_address (NMPObject *obj, int ifindex, guint32 address, int plen); +const NMPObject *nmp_object_stackinit_id_ip6_address (NMPObject *obj, int ifindex, const struct in6_addr *address, int plen); +const NMPObject *nmp_object_stackinit_id_ip4_route (NMPObject *obj, int ifindex, guint32 network, int plen, guint32 metric); +const NMPObject *nmp_object_stackinit_id_ip6_route (NMPObject *obj, int ifindex, const struct in6_addr *network, int plen, guint32 metric); + +const char *nmp_object_to_string (const NMPObject *obj, NMPObjectToStringMode to_string_mode, char *buf, gsize buf_size); +int nmp_object_cmp (const NMPObject *obj1, const NMPObject *obj2); +gboolean nmp_object_equal (const NMPObject *obj1, const NMPObject *obj2); +void nmp_object_copy (NMPObject *dst, const NMPObject *src, gboolean id_only); +NMPObject *nmp_object_clone (const NMPObject *obj, gboolean id_only); +gboolean nmp_object_id_equal (const NMPObject *obj1, const NMPObject *obj2); +guint nmp_object_id_hash (const NMPObject *obj); +gboolean nmp_object_is_alive (const NMPObject *obj); +gboolean nmp_object_is_visible (const NMPObject *obj); + +void _nmp_object_fixup_link_udev_fields (NMPObject *obj, gboolean use_udev); + +#define auto_nmp_obj __attribute__((cleanup(_nmp_auto_obj_cleanup))) +static inline void +_nmp_auto_obj_cleanup (NMPObject **pobj) +{ + nmp_object_unref (*pobj); +} + +typedef struct _NMPCache NMPCache; + +typedef void (*NMPCachePreHook) (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data); +typedef gboolean (*NMPObjectMatchFn) (const NMPObject *obj, gpointer user_data); + +gboolean nmp_cache_id_equal (const NMPCacheId *a, const NMPCacheId *b); +guint nmp_cache_id_hash (const NMPCacheId *id); +NMPCacheId *nmp_cache_id_clone (const NMPCacheId *id); +void nmp_cache_id_destroy (NMPCacheId *id); + +NMPCacheId *nmp_cache_id_init_object_type (NMPCacheId *id, NMPObjectType obj_type, gboolean visible_only); +NMPCacheId *nmp_cache_id_init_addrroute_visible_by_ifindex (NMPCacheId *id, NMPObjectType obj_type, int ifindex); +NMPCacheId *nmp_cache_id_init_routes_visible (NMPCacheId *id, NMPObjectType obj_type, gboolean with_default, gboolean with_non_default, int ifindex); + +const NMPlatformObject *const *nmp_cache_lookup_multi (const NMPCache *cache, const NMPCacheId *cache_id, guint *out_len); +GArray *nmp_cache_lookup_multi_to_array (const NMPCache *cache, NMPObjectType obj_type, const NMPCacheId *cache_id); +const NMPObject *nmp_cache_lookup_obj (const NMPCache *cache, const NMPObject *obj); +const NMPObject *nmp_cache_lookup_link (const NMPCache *cache, int ifindex); + +const NMPObject *nmp_cache_lookup_link_full (const NMPCache *cache, + int ifindex, + const char *ifname, + gboolean visible_only, + NMLinkType link_type, + NMPObjectMatchFn match_fn, + gpointer user_data); +GHashTable *nmp_cache_lookup_all_to_hash (const NMPCache *cache, + NMPCacheId *cache_id, + GHashTable *hash); + +gboolean nmp_cache_link_connected_needs_toggle (const NMPCache *cache, const NMPObject *master, const NMPObject *potential_slave, const NMPObject *ignore_slave); +const NMPObject *nmp_cache_link_connected_needs_toggle_by_ifindex (const NMPCache *cache, int master_ifindex, const NMPObject *potential_slave, const NMPObject *ignore_slave); + +gboolean nmp_cache_use_udev_detect (void); +gboolean nmp_cache_use_udev_get (const NMPCache *cache); +gboolean nmp_cache_use_udev_set (NMPCache *cache, gboolean use_udev); + +void ASSERT_nmp_cache_is_consistent (const NMPCache *cache); + +NMPCacheOpsType nmp_cache_remove (NMPCache *cache, const NMPObject *obj, gboolean equals_by_ptr, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data); +NMPCacheOpsType nmp_cache_remove_netlink (NMPCache *cache, const NMPObject *obj, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data); +NMPCacheOpsType nmp_cache_update_netlink (NMPCache *cache, NMPObject *obj, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data); +NMPCacheOpsType nmp_cache_update_link_udev (NMPCache *cache, int ifindex, GUdevDevice *udev_device, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data); +NMPCacheOpsType nmp_cache_update_link_master_connected (NMPCache *cache, int ifindex, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data); + +NMPCache *nmp_cache_new (void); +void nmp_cache_free (NMPCache *cache); + +NMPObject *nmp_object_from_nl (NMPlatform *platform, const struct nl_object *nlo, gboolean id_only, gboolean complete_from_cache); +struct nl_object *nmp_object_to_nl (NMPlatform *platform, const NMPObject *obj, gboolean id_only); + +/* the following functions are currently implemented inside nm-linux-platform, because + * they depend on utility functions there. */ +NMPObjectType _nlo_get_object_type (const struct nl_object *nlo); +gboolean _nmp_vt_cmd_plobj_init_from_nl_link (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache); +gboolean _nmp_vt_cmd_plobj_init_from_nl_ip4_address (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache); +gboolean _nmp_vt_cmd_plobj_init_from_nl_ip6_address (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache); +gboolean _nmp_vt_cmd_plobj_init_from_nl_ip4_route (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache); +gboolean _nmp_vt_cmd_plobj_init_from_nl_ip6_route (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache); +struct nl_object *_nmp_vt_cmd_plobj_to_nl_link (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only); +struct nl_object *_nmp_vt_cmd_plobj_to_nl_ip4_address (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only); +struct nl_object *_nmp_vt_cmd_plobj_to_nl_ip6_address (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only); +struct nl_object *_nmp_vt_cmd_plobj_to_nl_ip4_route (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only); +struct nl_object *_nmp_vt_cmd_plobj_to_nl_ip6_route (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only); + +#endif /* __NMP_OBJECT_H__ */ diff --git a/src/platform/tests/Makefile.am b/src/platform/tests/Makefile.am index c10600eb..54844ec1 100644 --- a/src/platform/tests/Makefile.am +++ b/src/platform/tests/Makefile.am @@ -37,6 +37,8 @@ noinst_PROGRAMS = \ test-link-linux \ test-address-fake \ test-address-linux \ + test-general \ + test-nmp-object \ test-route-fake \ test-route-linux \ test-cleanup-fake \ @@ -109,7 +111,27 @@ test_cleanup_linux_CPPFLAGS = \ -DKERNEL_HACKS=1 test_cleanup_linux_LDADD = $(PLATFORM_LDADD) -@VALGRIND_RULES@ -TESTS = test-link-fake test-address-fake test-route-fake test-cleanup-fake test-address-linux test-route-linux test-cleanup-linux +test_nmp_object_SOURCES = \ + test-nmp-object.c +test_nmp_object_LDADD = \ + $(top_builddir)/src/libNetworkManager.la + +test_general_SOURCES = \ + test-general.c +test_general_LDADD = \ + $(top_builddir)/src/libNetworkManager.la + +@VALGRIND_RULES@ +TESTS = \ + test-address-fake \ + test-address-linux \ + test-cleanup-fake \ + test-cleanup-linux \ + test-general \ + test-link-fake \ + test-link-linux \ + test-nmp-object \ + test-route-fake \ + test-route-linux diff --git a/src/platform/tests/Makefile.in b/src/platform/tests/Makefile.in index 16a48430..a9333539 100644 --- a/src/platform/tests/Makefile.in +++ b/src/platform/tests/Makefile.in @@ -92,12 +92,14 @@ host_triplet = @host@ noinst_PROGRAMS = dump$(EXEEXT) monitor$(EXEEXT) platform$(EXEEXT) \ test-link-fake$(EXEEXT) test-link-linux$(EXEEXT) \ test-address-fake$(EXEEXT) test-address-linux$(EXEEXT) \ + test-general$(EXEEXT) test-nmp-object$(EXEEXT) \ test-route-fake$(EXEEXT) test-route-linux$(EXEEXT) \ test-cleanup-fake$(EXEEXT) test-cleanup-linux$(EXEEXT) -TESTS = test-link-fake$(EXEEXT) test-address-fake$(EXEEXT) \ - test-route-fake$(EXEEXT) test-cleanup-fake$(EXEEXT) \ - test-address-linux$(EXEEXT) test-route-linux$(EXEEXT) \ - test-cleanup-linux$(EXEEXT) +TESTS = test-address-fake$(EXEEXT) test-address-linux$(EXEEXT) \ + test-cleanup-fake$(EXEEXT) test-cleanup-linux$(EXEEXT) \ + test-general$(EXEEXT) test-link-fake$(EXEEXT) \ + test-link-linux$(EXEEXT) test-nmp-object$(EXEEXT) \ + test-route-fake$(EXEEXT) test-route-linux$(EXEEXT) subdir = src/platform/tests ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \ @@ -172,6 +174,9 @@ am_test_cleanup_linux_OBJECTS = \ test_cleanup_linux-test-cleanup.$(OBJEXT) $(am__objects_9) test_cleanup_linux_OBJECTS = $(am_test_cleanup_linux_OBJECTS) test_cleanup_linux_DEPENDENCIES = $(PLATFORM_LDADD) +am_test_general_OBJECTS = test-general.$(OBJEXT) +test_general_OBJECTS = $(am_test_general_OBJECTS) +test_general_DEPENDENCIES = $(top_builddir)/src/libNetworkManager.la am__objects_10 = test_link_fake-nm-platform.$(OBJEXT) \ test_link_fake-nm-fake-platform.$(OBJEXT) \ test_link_fake-nm-linux-platform.$(OBJEXT) @@ -190,6 +195,10 @@ am_test_link_linux_OBJECTS = test_link_linux-test-link.$(OBJEXT) \ $(am__objects_13) test_link_linux_OBJECTS = $(am_test_link_linux_OBJECTS) test_link_linux_DEPENDENCIES = $(PLATFORM_LDADD) +am_test_nmp_object_OBJECTS = test-nmp-object.$(OBJEXT) +test_nmp_object_OBJECTS = $(am_test_nmp_object_OBJECTS) +test_nmp_object_DEPENDENCIES = \ + $(top_builddir)/src/libNetworkManager.la am__objects_14 = test_route_fake-nm-platform.$(OBJEXT) \ test_route_fake-nm-fake-platform.$(OBJEXT) \ test_route_fake-nm-linux-platform.$(OBJEXT) @@ -245,12 +254,14 @@ am__v_CCLD_1 = SOURCES = $(dump_SOURCES) $(monitor_SOURCES) $(platform_SOURCES) \ $(test_address_fake_SOURCES) $(test_address_linux_SOURCES) \ $(test_cleanup_fake_SOURCES) $(test_cleanup_linux_SOURCES) \ - $(test_link_fake_SOURCES) $(test_link_linux_SOURCES) \ + $(test_general_SOURCES) $(test_link_fake_SOURCES) \ + $(test_link_linux_SOURCES) $(test_nmp_object_SOURCES) \ $(test_route_fake_SOURCES) $(test_route_linux_SOURCES) DIST_SOURCES = $(dump_SOURCES) $(monitor_SOURCES) $(platform_SOURCES) \ $(test_address_fake_SOURCES) $(test_address_linux_SOURCES) \ $(test_cleanup_fake_SOURCES) $(test_cleanup_linux_SOURCES) \ - $(test_link_fake_SOURCES) $(test_link_linux_SOURCES) \ + $(test_general_SOURCES) $(test_link_fake_SOURCES) \ + $(test_link_linux_SOURCES) $(test_nmp_object_SOURCES) \ $(test_route_fake_SOURCES) $(test_route_linux_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ @@ -298,13 +309,196 @@ am__tty_colors = { \ std='[m'; \ fi; \ } +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__recheck_rx = ^[ ]*:recheck:[ ]* +am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* +am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* +# A command that, given a newline-separated list of test names on the +# standard input, print the name of the tests that are to be re-run +# upon "make recheck". +am__list_recheck_tests = $(AWK) '{ \ + recheck = 1; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + { \ + if ((getline line2 < ($$0 ".log")) < 0) \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ + { \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ + { \ + break; \ + } \ + }; \ + if (recheck) \ + print $$0; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# A command that, given a newline-separated list of test names on the +# standard input, create the global log from their .trs and .log files. +am__create_global_log = $(AWK) ' \ +function fatal(msg) \ +{ \ + print "fatal: making $@: " msg | "cat >&2"; \ + exit 1; \ +} \ +function rst_section(header) \ +{ \ + print header; \ + len = length(header); \ + for (i = 1; i <= len; i = i + 1) \ + printf "="; \ + printf "\n\n"; \ +} \ +{ \ + copy_in_global_log = 1; \ + global_test_result = "RUN"; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".trs"); \ + if (line ~ /$(am__global_test_result_rx)/) \ + { \ + sub("$(am__global_test_result_rx)", "", line); \ + sub("[ ]*$$", "", line); \ + global_test_result = line; \ + } \ + else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ + copy_in_global_log = 0; \ + }; \ + if (copy_in_global_log) \ + { \ + rst_section(global_test_result ": " $$0); \ + while ((rc = (getline line < ($$0 ".log"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".log"); \ + print line; \ + }; \ + printf "\n"; \ + }; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# Restructured Text title. +am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } +# Solaris 10 'make', and several other traditional 'make' implementations, +# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it +# by disabling -e (using the XSI extension "set +e") if it's set. +am__sh_e_setup = case $$- in *e*) set +e;; esac +# Default flags passed to test drivers. +am__common_driver_flags = \ + --color-tests "$$am__color_tests" \ + --enable-hard-errors "$$am__enable_hard_errors" \ + --expect-failure "$$am__expect_failure" +# To be inserted before the command running the test. Creates the +# directory for the log if needed. Stores in $dir the directory +# containing $f, in $tst the test, in $log the log. Executes the +# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and +# passes TESTS_ENVIRONMENT. Set up options for the wrapper that +# will run the test scripts (or their associated LOG_COMPILER, if +# thy have one). +am__check_pre = \ +$(am__sh_e_setup); \ +$(am__vpath_adj_setup) $(am__vpath_adj) \ +$(am__tty_colors); \ +srcdir=$(srcdir); export srcdir; \ +case "$@" in \ + */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ + *) am__odir=.;; \ +esac; \ +test "x$$am__odir" = x"." || test -d "$$am__odir" \ + || $(MKDIR_P) "$$am__odir" || exit $$?; \ +if test -f "./$$f"; then dir=./; \ +elif test -f "$$f"; then dir=; \ +else dir="$(srcdir)/"; fi; \ +tst=$$dir$$f; log='$@'; \ +if test -n '$(DISABLE_HARD_ERRORS)'; then \ + am__enable_hard_errors=no; \ +else \ + am__enable_hard_errors=yes; \ +fi; \ +case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ + am__expect_failure=yes;; \ + *) \ + am__expect_failure=no;; \ +esac; \ +$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) +# A shell command to get the names of the tests scripts with any registered +# extension removed (i.e., equivalently, the names of the test logs, with +# the '.log' extension removed). The result is saved in the shell variable +# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, +# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", +# since that might cause problem with VPATH rewrites for suffix-less tests. +# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. +am__set_TESTS_bases = \ + bases='$(TEST_LOGS)'; \ + bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ + bases=`echo $$bases` +RECHECK_LOGS = $(TEST_LOGS) +AM_RECURSIVE_TARGETS = check recheck +TEST_SUITE_LOG = test-suite.log +TEST_EXTENSIONS = @EXEEXT@ .test +LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) +am__set_b = \ + case '$@' in \ + */*) \ + case '$*' in \ + */*) b='$*';; \ + *) b=`echo '$@' | sed 's/\.log$$//'`; \ + esac;; \ + *) \ + b='$*';; \ + esac +am__test_logs1 = $(TESTS:=.log) +am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) +TEST_LOGS = $(am__test_logs2:.test.log=.log) +TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/build-aux/test-driver +TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ + $(TEST_LOG_FLAGS) am__DIST_COMMON = $(srcdir)/Makefile.in \ - $(top_srcdir)/build-aux/depcomp + $(top_srcdir)/build-aux/depcomp \ + $(top_srcdir)/build-aux/test-driver DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -418,6 +612,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ +LOG_DRIVER = @LOG_DRIVER@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ @@ -642,10 +837,22 @@ test_cleanup_linux_CPPFLAGS = \ -DKERNEL_HACKS=1 test_cleanup_linux_LDADD = $(PLATFORM_LDADD) +test_nmp_object_SOURCES = \ + test-nmp-object.c + +test_nmp_object_LDADD = \ + $(top_builddir)/src/libNetworkManager.la + +test_general_SOURCES = \ + test-general.c + +test_general_LDADD = \ + $(top_builddir)/src/libNetworkManager.la + all: all-am .SUFFIXES: -.SUFFIXES: .c .lo .o .obj +.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ @@ -713,6 +920,10 @@ test-cleanup-linux$(EXEEXT): $(test_cleanup_linux_OBJECTS) $(test_cleanup_linux_ @rm -f test-cleanup-linux$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_cleanup_linux_OBJECTS) $(test_cleanup_linux_LDADD) $(LIBS) +test-general$(EXEEXT): $(test_general_OBJECTS) $(test_general_DEPENDENCIES) $(EXTRA_test_general_DEPENDENCIES) + @rm -f test-general$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_general_OBJECTS) $(test_general_LDADD) $(LIBS) + test-link-fake$(EXEEXT): $(test_link_fake_OBJECTS) $(test_link_fake_DEPENDENCIES) $(EXTRA_test_link_fake_DEPENDENCIES) @rm -f test-link-fake$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_link_fake_OBJECTS) $(test_link_fake_LDADD) $(LIBS) @@ -721,6 +932,10 @@ test-link-linux$(EXEEXT): $(test_link_linux_OBJECTS) $(test_link_linux_DEPENDENC @rm -f test-link-linux$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_link_linux_OBJECTS) $(test_link_linux_LDADD) $(LIBS) +test-nmp-object$(EXEEXT): $(test_nmp_object_OBJECTS) $(test_nmp_object_DEPENDENCIES) $(EXTRA_test_nmp_object_DEPENDENCIES) + @rm -f test-nmp-object$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_nmp_object_OBJECTS) $(test_nmp_object_LDADD) $(LIBS) + test-route-fake$(EXEEXT): $(test_route_fake_OBJECTS) $(test_route_fake_DEPENDENCIES) $(EXTRA_test_route_fake_DEPENDENCIES) @rm -f test-route-fake$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_route_fake_OBJECTS) $(test_route_fake_LDADD) $(LIBS) @@ -741,6 +956,8 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-linux-platform.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-platform.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/platform.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-general.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-nmp-object.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_address_fake-nm-fake-platform.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_address_fake-nm-linux-platform.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_address_fake-nm-platform.Po@am__quote@ @@ -1463,98 +1680,231 @@ cscopelist-am: $(am__tagged_files) distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -check-TESTS: $(TESTS) - @failed=0; all=0; xfail=0; xpass=0; skip=0; \ - srcdir=$(srcdir); export srcdir; \ - list=' $(TESTS) '; \ - $(am__tty_colors); \ - if test -n "$$list"; then \ - for tst in $$list; do \ - if test -f ./$$tst; then dir=./; \ - elif test -f $$tst; then dir=; \ - else dir="$(srcdir)/"; fi; \ - if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \ - all=`expr $$all + 1`; \ - case " $(XFAIL_TESTS) " in \ - *[\ \ ]$$tst[\ \ ]*) \ - xpass=`expr $$xpass + 1`; \ - failed=`expr $$failed + 1`; \ - col=$$red; res=XPASS; \ - ;; \ - *) \ - col=$$grn; res=PASS; \ - ;; \ - esac; \ - elif test $$? -ne 77; then \ - all=`expr $$all + 1`; \ - case " $(XFAIL_TESTS) " in \ - *[\ \ ]$$tst[\ \ ]*) \ - xfail=`expr $$xfail + 1`; \ - col=$$lgn; res=XFAIL; \ - ;; \ - *) \ - failed=`expr $$failed + 1`; \ - col=$$red; res=FAIL; \ - ;; \ - esac; \ - else \ - skip=`expr $$skip + 1`; \ - col=$$blu; res=SKIP; \ - fi; \ - echo "$${col}$$res$${std}: $$tst"; \ - done; \ - if test "$$all" -eq 1; then \ - tests="test"; \ - All=""; \ - else \ - tests="tests"; \ - All="All "; \ +# Recover from deleted '.trs' file; this should ensure that +# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create +# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells +# to avoid problems with "make -n". +.log.trs: + rm -f $< $@ + $(MAKE) $(AM_MAKEFLAGS) $< + +# Leading 'am--fnord' is there to ensure the list of targets does not +# expand to empty, as could happen e.g. with make check TESTS=''. +am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) +am--force-recheck: + @: + +$(TEST_SUITE_LOG): $(TEST_LOGS) + @$(am__set_TESTS_bases); \ + am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ + redo_bases=`for i in $$bases; do \ + am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ + done`; \ + if test -n "$$redo_bases"; then \ + redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ + redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ + if $(am__make_dryrun); then :; else \ + rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ fi; \ - if test "$$failed" -eq 0; then \ - if test "$$xfail" -eq 0; then \ - banner="$$All$$all $$tests passed"; \ - else \ - if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ - banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ - fi; \ - else \ - if test "$$xpass" -eq 0; then \ - banner="$$failed of $$all $$tests failed"; \ + fi; \ + if test -n "$$am__remaking_logs"; then \ + echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ + "recursion detected" >&2; \ + elif test -n "$$redo_logs"; then \ + am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ + fi; \ + if $(am__make_dryrun); then :; else \ + st=0; \ + errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ + for i in $$redo_bases; do \ + test -f $$i.trs && test -r $$i.trs \ + || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ + test -f $$i.log && test -r $$i.log \ + || { echo "$$errmsg $$i.log" >&2; st=1; }; \ + done; \ + test $$st -eq 0 || exit 1; \ + fi + @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ + ws='[ ]'; \ + results=`for b in $$bases; do echo $$b.trs; done`; \ + test -n "$$results" || results=/dev/null; \ + all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ + pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ + fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ + skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ + xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ + xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ + error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ + if test `expr $$fail + $$xpass + $$error` -eq 0; then \ + success=true; \ + else \ + success=false; \ + fi; \ + br='==================='; br=$$br$$br$$br$$br; \ + result_count () \ + { \ + if test x"$$1" = x"--maybe-color"; then \ + maybe_colorize=yes; \ + elif test x"$$1" = x"--no-color"; then \ + maybe_colorize=no; \ else \ - if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ - banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ + echo "$@: invalid 'result_count' usage" >&2; exit 4; \ fi; \ - fi; \ - dashes="$$banner"; \ - skipped=""; \ - if test "$$skip" -ne 0; then \ - if test "$$skip" -eq 1; then \ - skipped="($$skip test was not run)"; \ + shift; \ + desc=$$1 count=$$2; \ + if test $$maybe_colorize = yes && test $$count -gt 0; then \ + color_start=$$3 color_end=$$std; \ else \ - skipped="($$skip tests were not run)"; \ + color_start= color_end=; \ fi; \ - test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ - dashes="$$skipped"; \ - fi; \ - report=""; \ - if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ - report="Please report to $(PACKAGE_BUGREPORT)"; \ - test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ - dashes="$$report"; \ - fi; \ - dashes=`echo "$$dashes" | sed s/./=/g`; \ - if test "$$failed" -eq 0; then \ - col="$$grn"; \ - else \ - col="$$red"; \ - fi; \ - echo "$${col}$$dashes$${std}"; \ - echo "$${col}$$banner$${std}"; \ - test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \ - test -z "$$report" || echo "$${col}$$report$${std}"; \ - echo "$${col}$$dashes$${std}"; \ - test "$$failed" -eq 0; \ - else :; fi + echo "$${color_start}# $$desc $$count$${color_end}"; \ + }; \ + create_testsuite_report () \ + { \ + result_count $$1 "TOTAL:" $$all "$$brg"; \ + result_count $$1 "PASS: " $$pass "$$grn"; \ + result_count $$1 "SKIP: " $$skip "$$blu"; \ + result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ + result_count $$1 "FAIL: " $$fail "$$red"; \ + result_count $$1 "XPASS:" $$xpass "$$red"; \ + result_count $$1 "ERROR:" $$error "$$mgn"; \ + }; \ + { \ + echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ + $(am__rst_title); \ + create_testsuite_report --no-color; \ + echo; \ + echo ".. contents:: :depth: 2"; \ + echo; \ + for b in $$bases; do echo $$b; done \ + | $(am__create_global_log); \ + } >$(TEST_SUITE_LOG).tmp || exit 1; \ + mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ + if $$success; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ + fi; \ + echo "$${col}$$br$${std}"; \ + echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \ + echo "$${col}$$br$${std}"; \ + create_testsuite_report --maybe-color; \ + echo "$$col$$br$$std"; \ + if $$success; then :; else \ + echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ + if test -n "$(PACKAGE_BUGREPORT)"; then \ + echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ + fi; \ + echo "$$col$$br$$std"; \ + fi; \ + $$success || exit 1 + +check-TESTS: + @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list + @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + trs_list=`for i in $$bases; do echo $$i.trs; done`; \ + log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ + exit $$?; +recheck: all + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + bases=`for i in $$bases; do echo $$i; done \ + | $(am__list_recheck_tests)` || exit 1; \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + log_list=`echo $$log_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ + am__force_recheck=am--force-recheck \ + TEST_LOGS="$$log_list"; \ + exit $$? +test-address-fake.log: test-address-fake$(EXEEXT) + @p='test-address-fake$(EXEEXT)'; \ + b='test-address-fake'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +test-address-linux.log: test-address-linux$(EXEEXT) + @p='test-address-linux$(EXEEXT)'; \ + b='test-address-linux'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +test-cleanup-fake.log: test-cleanup-fake$(EXEEXT) + @p='test-cleanup-fake$(EXEEXT)'; \ + b='test-cleanup-fake'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +test-cleanup-linux.log: test-cleanup-linux$(EXEEXT) + @p='test-cleanup-linux$(EXEEXT)'; \ + b='test-cleanup-linux'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +test-general.log: test-general$(EXEEXT) + @p='test-general$(EXEEXT)'; \ + b='test-general'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +test-link-fake.log: test-link-fake$(EXEEXT) + @p='test-link-fake$(EXEEXT)'; \ + b='test-link-fake'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +test-link-linux.log: test-link-linux$(EXEEXT) + @p='test-link-linux$(EXEEXT)'; \ + b='test-link-linux'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +test-nmp-object.log: test-nmp-object$(EXEEXT) + @p='test-nmp-object$(EXEEXT)'; \ + b='test-nmp-object'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +test-route-fake.log: test-route-fake$(EXEEXT) + @p='test-route-fake$(EXEEXT)'; \ + b='test-route-fake'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +test-route-linux.log: test-route-linux$(EXEEXT) + @p='test-route-linux$(EXEEXT)'; \ + b='test-route-linux'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +.test.log: + @p='$<'; \ + $(am__set_b); \ + $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +@am__EXEEXT_TRUE@.test$(EXEEXT).log: +@am__EXEEXT_TRUE@ @p='$<'; \ +@am__EXEEXT_TRUE@ $(am__set_b); \ +@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ +@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ +@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ +@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ @@ -1611,6 +1961,9 @@ install-strip: "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: + -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) + -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) + -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) clean-generic: @@ -1706,7 +2059,7 @@ uninstall-am: installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ - tags tags-am uninstall uninstall-am + recheck tags tags-am uninstall uninstall-am .PRECIOUS: Makefile diff --git a/src/platform/tests/dump.c b/src/platform/tests/dump.c index 3bb61da4..54de1da7 100644 --- a/src/platform/tests/dump.c +++ b/src/platform/tests/dump.c @@ -7,6 +7,7 @@ #include "nm-platform.h" #include "nm-linux-platform.h" #include "nm-fake-platform.h" +#include "nm-macros-internal.h" static void dump_interface (NMPlatformLink *link) @@ -26,14 +27,14 @@ dump_interface (NMPlatformLink *link) size_t addrlen; int i; - g_assert (link->up || !link->connected); + g_assert (NM_FLAGS_HAS (link->flags, IFF_UP) || !link->connected); - printf ("%d: %s: %s", link->ifindex, link->name, link->type_name); - if (link->up) + printf ("%d: %s: %s", link->ifindex, link->name, nm_link_type_to_string (link->type)); + if (NM_FLAGS_HAS (link->flags, IFF_UP)) printf (" %s", link->connected ? "CONNECTED" : "DISCONNECTED"); else printf (" DOWN"); - if (!link->arp) + if (NM_FLAGS_HAS (link->flags, IFF_NOARP)) printf (" noarp"); if (link->master) printf (" master %d", link->master); @@ -43,22 +44,22 @@ dump_interface (NMPlatformLink *link) printf ("\n"); if (link->driver) printf (" driver: %s\n", link->driver); - printf (" UDI: %s\n", link->udi); - if (!nm_platform_vlan_get_info (link->ifindex, &vlan_parent, &vlan_id)) + printf (" UDI: %s\n", nm_platform_link_get_udi (NM_PLATFORM_GET, link->ifindex)); + if (!nm_platform_vlan_get_info (NM_PLATFORM_GET, link->ifindex, &vlan_parent, &vlan_id)) g_assert_not_reached (); if (vlan_parent) printf (" vlan parent %d id %d\n", vlan_parent, vlan_id); - if (nm_platform_link_is_software (link->ifindex)) + if (nm_platform_link_is_software (NM_PLATFORM_GET, link->ifindex)) printf (" class software\n"); - if (nm_platform_link_supports_slaves (link->ifindex)) + if (nm_platform_link_supports_slaves (NM_PLATFORM_GET, link->ifindex)) printf (" class supports-slaves\n"); - if (nm_platform_link_supports_carrier_detect (link->ifindex)) + if (nm_platform_link_supports_carrier_detect (NM_PLATFORM_GET, link->ifindex)) printf (" feature carrier-detect\n"); - if (nm_platform_link_supports_vlans (link->ifindex)) + if (nm_platform_link_supports_vlans (NM_PLATFORM_GET, link->ifindex)) printf (" feature vlans\n"); - address = nm_platform_link_get_address (link->ifindex, &addrlen); + address = nm_platform_link_get_address (NM_PLATFORM_GET, link->ifindex, &addrlen); if (address) { printf (" link-address "); for (i = 0; i < addrlen; i++) @@ -66,8 +67,8 @@ dump_interface (NMPlatformLink *link) printf ("\n"); } - ip4_addresses = nm_platform_ip4_address_get_all (link->ifindex); - ip6_addresses = nm_platform_ip6_address_get_all (link->ifindex); + ip4_addresses = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, link->ifindex); + ip6_addresses = nm_platform_ip6_address_get_all (NM_PLATFORM_GET, link->ifindex); g_assert (ip4_addresses); g_assert (ip6_addresses); @@ -85,8 +86,8 @@ dump_interface (NMPlatformLink *link) g_array_unref (ip4_addresses); g_array_unref (ip6_addresses); - ip4_routes = nm_platform_ip4_route_get_all (link->ifindex, NM_PLATFORM_GET_ROUTE_MODE_ALL); - ip6_routes = nm_platform_ip6_route_get_all (link->ifindex, NM_PLATFORM_GET_ROUTE_MODE_ALL); + ip4_routes = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, link->ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); + ip6_routes = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, link->ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); g_assert (ip4_routes); g_assert (ip6_routes); @@ -112,7 +113,7 @@ dump_interface (NMPlatformLink *link) static void dump_all (void) { - GArray *links = nm_platform_link_get_all (); + GArray *links = nm_platform_link_get_all (NM_PLATFORM_GET); int i; for (i = 0; i < links->len; i++) diff --git a/src/platform/tests/platform.c b/src/platform/tests/platform.c index eea6610d..24e828c1 100644 --- a/src/platform/tests/platform.c +++ b/src/platform/tests/platform.c @@ -44,13 +44,13 @@ typedef const char *string_t; static gboolean do_sysctl_set (char **argv) { - return nm_platform_sysctl_set (argv[0], argv[1]); + return nm_platform_sysctl_set (NM_PLATFORM_GET, argv[0], argv[1]); } static gboolean do_sysctl_get (char **argv) { - gs_free char *value = nm_platform_sysctl_get (argv[0]); + gs_free char *value = nm_platform_sysctl_get (NM_PLATFORM_GET, argv[0]); printf ("%s\n", value); @@ -66,7 +66,7 @@ parse_ifindex (const char *str) ifindex = strtol (str, &endptr, 10); if (*endptr) { - ifindex = nm_platform_link_get_ifindex (str); + ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, str); } return ifindex; @@ -79,7 +79,7 @@ do_link_get_all (char **argv) NMPlatformLink *device; int i; - links = nm_platform_link_get_all (); + links = nm_platform_link_get_all (NM_PLATFORM_GET); for (i = 0; i < links->len; i++) { device = &g_array_index (links, NMPlatformLink, i); @@ -93,25 +93,25 @@ do_link_get_all (char **argv) static gboolean do_dummy_add (char **argv) { - return nm_platform_dummy_add (argv[0]); + return nm_platform_dummy_add (NM_PLATFORM_GET, argv[0], NULL) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean do_bridge_add (char **argv) { - return nm_platform_bridge_add (argv[0], NULL, 0); + return nm_platform_bridge_add (NM_PLATFORM_GET, argv[0], NULL, 0, NULL) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean do_bond_add (char **argv) { - return nm_platform_bond_add (argv[0]); + return nm_platform_bond_add (NM_PLATFORM_GET, argv[0], NULL) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean do_team_add (char **argv) { - return nm_platform_team_add (argv[0]); + return nm_platform_team_add (NM_PLATFORM_GET, argv[0], NULL) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean @@ -122,13 +122,13 @@ do_vlan_add (char **argv) int vlanid = strtol (*argv++, NULL, 10); guint32 vlan_flags = strtol (*argv++, NULL, 10); - return nm_platform_vlan_add (name, parent, vlanid, vlan_flags); + return nm_platform_vlan_add (NM_PLATFORM_GET, name, parent, vlanid, vlan_flags, NULL) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean do_link_exists (char **argv) { - gboolean value = nm_platform_link_exists (argv[0]); + gboolean value = !!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, argv[0]); print_boolean (value); @@ -140,7 +140,7 @@ do_link_exists (char **argv) do_link_##cmdname (char **argv) \ { \ int ifindex = parse_ifindex (argv[0]); \ - return ifindex ? nm_platform_link_##cmdname (ifindex) : FALSE; \ + return ifindex ? nm_platform_link_##cmdname (NM_PLATFORM_GET, ifindex) : FALSE; \ } #define LINK_CMD_GET_FULL(cmdname, type, cond) \ @@ -149,7 +149,7 @@ do_link_exists (char **argv) { \ int ifindex = parse_ifindex (argv[0]); \ if (ifindex) { \ - type##_t value = nm_platform_link_##cmdname (ifindex); \ + type##_t value = nm_platform_link_##cmdname (NM_PLATFORM_GET, ifindex); \ if (cond) { \ print_##type (value); \ return TRUE; \ @@ -169,7 +169,7 @@ LINK_CMD (delete) static gboolean do_link_get_ifindex (char **argv) { - int ifindex = nm_platform_link_get_ifindex (argv[0]); + int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, argv[0]); if (ifindex) printf ("%d\n", ifindex); @@ -182,7 +182,14 @@ LINK_CMD_GET_FULL (get_type, decimal, value > 0) LINK_CMD_GET (is_software, boolean) LINK_CMD_GET (supports_slaves, boolean) -LINK_CMD (set_up) +static gboolean +do_link_set_up (char **argv) +{ + int ifindex = parse_ifindex (argv[0]); + + return ifindex ? nm_platform_link_set_up (NM_PLATFORM_GET, ifindex, NULL) : FALSE; +} + LINK_CMD (set_down) LINK_CMD (set_arp) LINK_CMD (set_noarp) @@ -213,7 +220,7 @@ do_link_set_address (char **argv) g_assert (!*endptr); } - return nm_platform_link_set_address (ifindex, address, sizeof (address)); + return nm_platform_link_set_address (NM_PLATFORM_GET, ifindex, address, sizeof (address)); } static gboolean @@ -224,7 +231,7 @@ do_link_get_address (char **argv) size_t length; int i; - address = nm_platform_link_get_address (ifindex, &length); + address = nm_platform_link_get_address (NM_PLATFORM_GET, ifindex, &length); if (!address || length <= 0) return FALSE; @@ -242,7 +249,7 @@ do_link_set_mtu (char **argv) int ifindex = parse_ifindex (*argv++); int mtu = strtoul (*argv++, NULL, 10); - return nm_platform_link_set_mtu (ifindex, mtu); + return nm_platform_link_set_mtu (NM_PLATFORM_GET, ifindex, mtu); } LINK_CMD_GET (get_mtu, decimal); @@ -255,7 +262,7 @@ do_link_enslave (char **argv) int master = parse_ifindex (*argv++); int slave = parse_ifindex (*argv++); - return nm_platform_link_enslave (master, slave); + return nm_platform_link_enslave (NM_PLATFORM_GET, master, slave); } static gboolean @@ -264,7 +271,7 @@ do_link_release (char **argv) int master = parse_ifindex (*argv++); int slave = parse_ifindex (*argv++); - return nm_platform_link_release (master, slave); + return nm_platform_link_release (NM_PLATFORM_GET, master, slave); } LINK_CMD_GET (get_master, decimal) @@ -276,7 +283,7 @@ do_master_set_option (char **argv) const char *option = *argv++; const char *value = *argv++; - return nm_platform_master_set_option (ifindex, option, value); + return nm_platform_master_set_option (NM_PLATFORM_GET, ifindex, option, value); } static gboolean @@ -284,7 +291,7 @@ do_master_get_option (char **argv) { int ifindex = parse_ifindex (*argv++); const char *option = *argv++; - gs_free char *value = nm_platform_master_get_option (ifindex, option); + gs_free char *value = nm_platform_master_get_option (NM_PLATFORM_GET, ifindex, option); printf ("%s\n", value); @@ -298,7 +305,7 @@ do_slave_set_option (char **argv) const char *option = *argv++; const char *value = *argv++; - return nm_platform_slave_set_option (ifindex, option, value); + return nm_platform_slave_set_option (NM_PLATFORM_GET, ifindex, option, value); } static gboolean @@ -306,7 +313,7 @@ do_slave_get_option (char **argv) { int ifindex = parse_ifindex (*argv++); const char *option = *argv++; - gs_free char *value = nm_platform_slave_get_option (ifindex, option); + gs_free char *value = nm_platform_slave_get_option (NM_PLATFORM_GET, ifindex, option); printf ("%s\n", value); @@ -320,7 +327,7 @@ do_vlan_get_info (char **argv) int parent; int vlanid; - if (!nm_platform_vlan_get_info (ifindex, &parent, &vlanid)) + if (!nm_platform_vlan_get_info (NM_PLATFORM_GET, ifindex, &parent, &vlanid)) return FALSE; printf ("%d %d\n", parent, vlanid); @@ -335,7 +342,7 @@ do_vlan_set_ingress_map (char **argv) int from = strtol (*argv++, NULL, 10); int to = strtol (*argv++, NULL, 10); - return nm_platform_vlan_set_ingress_map (ifindex, from, to); + return nm_platform_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, from, to); } static gboolean @@ -345,7 +352,7 @@ do_vlan_set_egress_map (char **argv) int from = strtol (*argv++, NULL, 10); int to = strtol (*argv++, NULL, 10); - return nm_platform_vlan_set_egress_map (ifindex, from, to); + return nm_platform_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, from, to); } static gboolean @@ -354,7 +361,7 @@ do_veth_get_properties (char **argv) int ifindex = parse_ifindex (*argv++); NMPlatformVethProperties props; - if (!nm_platform_veth_get_properties (ifindex, &props)) + if (!nm_platform_veth_get_properties (NM_PLATFORM_GET, ifindex, &props)) return FALSE; printf ("peer: %d\n", props.peer); @@ -368,7 +375,7 @@ do_tun_get_properties (char **argv) int ifindex = parse_ifindex (*argv++); NMPlatformTunProperties props; - if (!nm_platform_tun_get_properties (ifindex, &props)) + if (!nm_platform_tun_get_properties (NM_PLATFORM_GET, ifindex, &props)) return FALSE; printf ("mode: %s\n", props.mode); @@ -396,7 +403,7 @@ do_macvlan_get_properties (char **argv) int ifindex = parse_ifindex (*argv++); NMPlatformMacvlanProperties props; - if (!nm_platform_macvlan_get_properties (ifindex, &props)) + if (!nm_platform_macvlan_get_properties (NM_PLATFORM_GET, ifindex, &props)) return FALSE; printf ("parent: %d\n", props.parent_ifindex); @@ -413,7 +420,7 @@ do_vxlan_get_properties (char **argv) NMPlatformVxlanProperties props; char addrstr[INET6_ADDRSTRLEN]; - if (!nm_platform_vxlan_get_properties (ifindex, &props)) + if (!nm_platform_vxlan_get_properties (NM_PLATFORM_GET, ifindex, &props)) return FALSE; printf ("parent-ifindex: %u\n", props.parent_ifindex); @@ -460,7 +467,7 @@ do_gre_get_properties (char **argv) NMPlatformGreProperties props; char addrstr[INET_ADDRSTRLEN]; - if (!nm_platform_gre_get_properties (ifindex, &props)) + if (!nm_platform_gre_get_properties (NM_PLATFORM_GET, ifindex, &props)) return FALSE; printf ("parent-ifindex: %u\n", props.parent_ifindex); @@ -496,7 +503,7 @@ do_ip4_address_get_all (char **argv) int i; if (ifindex) { - addresses = nm_platform_ip4_address_get_all (ifindex); + addresses = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); for (i = 0; i < addresses->len; i++) { address = &g_array_index (addresses, NMPlatformIP4Address, i); inet_ntop (AF_INET, &address->address, addrstr, sizeof (addrstr)); @@ -518,7 +525,7 @@ do_ip6_address_get_all (char **argv) int i; if (ifindex) { - addresses = nm_platform_ip6_address_get_all (ifindex); + addresses = nm_platform_ip6_address_get_all (NM_PLATFORM_GET, ifindex); for (i = 0; i < addresses->len; i++) { address = &g_array_index (addresses, NMPlatformIP6Address, i); inet_ntop (AF_INET6, &address->address, addrstr, sizeof (addrstr)); @@ -576,7 +583,7 @@ do_ip4_address_add (char **argv) guint32 lifetime = strtol (*argv++, NULL, 10); guint32 preferred = strtol (*argv++, NULL, 10); - gboolean value = nm_platform_ip4_address_add (ifindex, address, 0, plen, lifetime, preferred, NULL); + gboolean value = nm_platform_ip4_address_add (NM_PLATFORM_GET, ifindex, address, 0, plen, lifetime, preferred, NULL); return value; } else return FALSE; @@ -594,7 +601,7 @@ do_ip6_address_add (char **argv) guint32 preferred = strtol (*argv++, NULL, 10); guint flags = (*argv) ? rtnl_addr_str2flags (*argv++) : 0; - gboolean value = nm_platform_ip6_address_add (ifindex, address, in6addr_any, plen, lifetime, preferred, flags); + gboolean value = nm_platform_ip6_address_add (NM_PLATFORM_GET, ifindex, address, in6addr_any, plen, lifetime, preferred, flags); return value; } else return FALSE; @@ -608,7 +615,7 @@ do_ip6_address_add (char **argv) v##_t address; \ int plen; \ if (ifindex && parse_##v##_address (*argv++, &address, &plen)) { \ - gboolean value = nm_platform_##v##_address_##cmdname (ifindex, address, plen, ##__VA_ARGS__); \ + gboolean value = !!nm_platform_##v##_address_##cmdname (NM_PLATFORM_GET, ifindex, address, plen, ##__VA_ARGS__); \ if (print) { \ print_boolean (value); \ return TRUE; \ @@ -621,7 +628,7 @@ do_ip6_address_add (char **argv) #define ADDR_CMD_PRINT(cmdname) ADDR_CMD_FULL (ip4, cmdname, TRUE) ADDR_CMD_FULL (ip6, cmdname, TRUE) ADDR_CMD (delete) -ADDR_CMD_PRINT (exists) +ADDR_CMD_PRINT (get) static gboolean do_ip4_route_get_all (char **argv) @@ -633,7 +640,7 @@ do_ip4_route_get_all (char **argv) int i; if (ifindex) { - routes = nm_platform_ip4_route_get_all (ifindex, NM_PLATFORM_GET_ROUTE_MODE_ALL); + routes = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); for (i = 0; i < routes->len; i++) { route = &g_array_index (routes, NMPlatformIP4Route, i); inet_ntop (AF_INET, &route->network, networkstr, sizeof (networkstr)); @@ -657,7 +664,7 @@ do_ip6_route_get_all (char **argv) int i; if (ifindex) { - routes = nm_platform_ip6_route_get_all (ifindex, NM_PLATFORM_GET_ROUTE_MODE_ALL); + routes = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); for (i = 0; i < routes->len; i++) { route = &g_array_index (routes, NMPlatformIP6Route, i); inet_ntop (AF_INET6, &route->network, networkstr, sizeof (networkstr)); @@ -683,7 +690,7 @@ do_ip4_route_add (char **argv) metric = strtol (*argv++, NULL, 10); mss = strtol (*argv++, NULL, 10); - return nm_platform_ip4_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, + return nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, 0, metric, mss); } @@ -699,7 +706,7 @@ do_ip6_route_add (char **argv) parse_ip6_address (*argv++, &gateway, NULL); metric = strtol (*argv++, NULL, 10); mss = strtol (*argv++, NULL, 10); - return nm_platform_ip6_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, + return nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, metric, mss); } @@ -714,7 +721,7 @@ do_ip4_route_delete (char **argv) parse_ip4_address (*argv++, &network, &plen); metric = strtol (*argv++, NULL, 10); - return nm_platform_ip4_route_delete (ifindex, network, plen, metric); + return nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric); } static gboolean @@ -727,11 +734,11 @@ do_ip6_route_delete (char **argv) parse_ip6_address (*argv++, &network, &plen); metric = strtol (*argv++, NULL, 10); - return nm_platform_ip6_route_delete (ifindex, network, plen, metric); + return nm_platform_ip6_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric); } static gboolean -do_ip4_route_exists (char **argv) +do_ip4_route_get (char **argv) { int ifindex = parse_ifindex (*argv++); in_addr_t network; @@ -740,12 +747,12 @@ do_ip4_route_exists (char **argv) parse_ip4_address (*argv++, &network, &plen); metric = strtol (*argv++, NULL, 10); - print_boolean (nm_platform_ip4_route_exists (ifindex, network, plen, metric)); + print_boolean (!!nm_platform_ip4_route_get (NM_PLATFORM_GET, ifindex, network, plen, metric)); return TRUE; } static gboolean -do_ip6_route_exists (char **argv) +do_ip6_route_get (char **argv) { int ifindex = parse_ifindex (*argv++); struct in6_addr network; @@ -754,7 +761,7 @@ do_ip6_route_exists (char **argv) parse_ip6_address (*argv++, &network, &plen); metric = strtol (*argv++, NULL, 10); - print_boolean (nm_platform_ip6_route_exists (ifindex, network, plen, metric)); + print_boolean (!!nm_platform_ip6_route_get (NM_PLATFORM_GET, ifindex, network, plen, metric)); return TRUE; } @@ -831,9 +838,9 @@ static const command_t commands[] = { "<ifname/ifindex> <address>/<plen>" }, { "ip6-address-delete", "delete IPv6 address", do_ip6_address_delete, 2, "<ifname/ifindex> <address>/<plen>" }, - { "ip4-address-exists", "check for existence of IPv4 address", do_ip4_address_exists, 2, + { "ip4-address-exists", "check for existence of IPv4 address", do_ip4_address_get, 2, "<ifname/ifindex> <address>/<plen>" }, - { "ip6-address-exists", "check for existence of IPv6 address", do_ip6_address_exists, 2, + { "ip6-address-exists", "check for existence of IPv6 address", do_ip6_address_get, 2, "<ifname/ifindex> <address>/<plen>" }, { "ip4-route-get-all", "print all IPv4 routes", do_ip4_route_get_all, 1, "<ifname/ifindex>" }, { "ip6-route-get-all", "print all IPv6 routes", do_ip6_route_get_all, 1, "<ifname/ifindex>" }, @@ -845,9 +852,9 @@ static const command_t commands[] = { "<ifname/ifindex> <network>/<plen> <metric>" }, { "ip6-route-delete", "delete IPv6 route", do_ip6_route_delete, 3, "<ifname/ifindex> <network>/<plen> <metric>" }, - { "ip4-route-exists", "check for existence of IPv4 route", do_ip4_route_exists, 3, + { "ip4-route-exists", "check for existence of IPv4 route", do_ip4_route_get, 3, "<ifname/ifindex> <network>/<plen> <metric>" }, - { "ip6-route-exists", "check for existence of IPv6 route", do_ip6_route_exists, 3, + { "ip6-route-exists", "check for existence of IPv6 route", do_ip6_route_get, 3, "<ifname/ifindex> <network>/<plen> <metric>" }, { NULL, NULL, NULL, 0, NULL }, }; @@ -858,7 +865,6 @@ main (int argc, char **argv) const char *arg0 = *argv++; const command_t *command = NULL; gboolean status = TRUE; - int error; #if !GLIB_CHECK_VERSION (2, 35, 0) g_type_init (); @@ -892,12 +898,5 @@ main (int argc, char **argv) error ("\n"); } - error = nm_platform_get_error (); - if (error) { - const char *msg = nm_platform_get_error_msg (); - - error ("nm-platform: %s\n", msg); - } - - return !!error; + return EXIT_SUCCESS; } diff --git a/src/platform/tests/test-address.c b/src/platform/tests/test-address.c index 5561038d..d6f346b3 100644 --- a/src/platform/tests/test-address.c +++ b/src/platform/tests/test-address.c @@ -9,7 +9,7 @@ #define IP6_PLEN 64 static void -ip4_address_callback (NMPlatform *platform, int ifindex, NMPlatformIP4Address *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data) +ip4_address_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, NMPlatformIP4Address *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data) { g_assert (received); g_assert_cmpint (received->ifindex, ==, ifindex); @@ -24,14 +24,12 @@ ip4_address_callback (NMPlatform *platform, int ifindex, NMPlatformIP4Address *r if (data->loop) g_main_loop_quit (data->loop); - if (data->received) - g_error ("Received signal '%s' a second time.", data->name); - - data->received = TRUE; + data->received_count++; + debug ("Received signal '%s' %dth time.", data->name, data->received_count); } static void -ip6_address_callback (NMPlatform *platform, int ifindex, NMPlatformIP6Address *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data) +ip6_address_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, NMPlatformIP6Address *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data) { g_assert (received); g_assert_cmpint (received->ifindex, ==, ifindex); @@ -46,16 +44,14 @@ ip6_address_callback (NMPlatform *platform, int ifindex, NMPlatformIP6Address *r if (data->loop) g_main_loop_quit (data->loop); - if (data->received) - g_error ("Received signal '%s' a second time.", data->name); - - data->received = TRUE; + data->received_count++; + debug ("Received signal '%s' %dth time.", data->name, data->received_count); } static void test_ip4_address (void) { - int ifindex = nm_platform_link_get_ifindex (DEVICE_NAME); + int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); SignalData *address_added = add_signal_ifindex (NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_ADDED, ip4_address_callback, ifindex); SignalData *address_changed = add_signal_ifindex (NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, ip4_address_callback, ifindex); SignalData *address_removed = add_signal_ifindex (NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, ip4_address_callback, ifindex); @@ -68,23 +64,18 @@ test_ip4_address (void) inet_pton (AF_INET, IP4_ADDRESS, &addr); /* Add address */ - g_assert (!nm_platform_ip4_address_exists (ifindex, addr, IP4_PLEN)); - no_error (); - g_assert (nm_platform_ip4_address_add (ifindex, addr, 0, IP4_PLEN, lifetime, preferred, NULL)); - no_error (); - g_assert (nm_platform_ip4_address_exists (ifindex, addr, IP4_PLEN)); - no_error (); + g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN)); + g_assert (nm_platform_ip4_address_add (NM_PLATFORM_GET, ifindex, addr, 0, IP4_PLEN, lifetime, preferred, NULL)); + g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN)); accept_signal (address_added); /* Add address again (aka update) */ - g_assert (nm_platform_ip4_address_add (ifindex, addr, 0, IP4_PLEN, lifetime, preferred, NULL)); - no_error (); - accept_signal (address_changed); + g_assert (nm_platform_ip4_address_add (NM_PLATFORM_GET, ifindex, addr, 0, IP4_PLEN, lifetime, preferred, NULL)); + accept_signals (address_changed, 0, 1); /* Test address listing */ - addresses = nm_platform_ip4_address_get_all (ifindex); + addresses = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); g_assert (addresses); - no_error (); g_assert_cmpint (addresses->len, ==, 1); address = &g_array_index (addresses, NMPlatformIP4Address, 0); g_assert_cmpint (address->ifindex, ==, ifindex); @@ -93,14 +84,12 @@ test_ip4_address (void) g_array_unref (addresses); /* Remove address */ - g_assert (nm_platform_ip4_address_delete (ifindex, addr, IP4_PLEN, 0)); - no_error (); - g_assert (!nm_platform_ip4_address_exists (ifindex, addr, IP4_PLEN)); + g_assert (nm_platform_ip4_address_delete (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, 0)); + g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN)); accept_signal (address_removed); /* Remove address again */ - g_assert (nm_platform_ip4_address_delete (ifindex, addr, IP4_PLEN, 0)); - no_error (); + g_assert (nm_platform_ip4_address_delete (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, 0)); free_signal (address_added); free_signal (address_changed); @@ -110,7 +99,7 @@ test_ip4_address (void) static void test_ip6_address (void) { - int ifindex = nm_platform_link_get_ifindex (DEVICE_NAME); + int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); SignalData *address_added = add_signal_ifindex (NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_ADDED, ip6_address_callback, ifindex); SignalData *address_changed = add_signal_ifindex (NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, ip6_address_callback, ifindex); SignalData *address_removed = add_signal_ifindex (NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, ip6_address_callback, ifindex); @@ -124,23 +113,18 @@ test_ip6_address (void) inet_pton (AF_INET6, IP6_ADDRESS, &addr); /* Add address */ - g_assert (!nm_platform_ip6_address_exists (ifindex, addr, IP6_PLEN)); - no_error (); - g_assert (nm_platform_ip6_address_add (ifindex, addr, in6addr_any, IP6_PLEN, lifetime, preferred, flags)); - no_error (); - g_assert (nm_platform_ip6_address_exists (ifindex, addr, IP6_PLEN)); - no_error (); + g_assert (!nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); + g_assert (nm_platform_ip6_address_add (NM_PLATFORM_GET, ifindex, addr, in6addr_any, IP6_PLEN, lifetime, preferred, flags)); + g_assert (nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); accept_signal (address_added); /* Add address again (aka update) */ - g_assert (nm_platform_ip6_address_add (ifindex, addr, in6addr_any, IP6_PLEN, lifetime, preferred, flags)); - no_error (); - accept_signal (address_changed); + g_assert (nm_platform_ip6_address_add (NM_PLATFORM_GET, ifindex, addr, in6addr_any, IP6_PLEN, lifetime, preferred, flags)); + accept_signals (address_changed, 0, 1); /* Test address listing */ - addresses = nm_platform_ip6_address_get_all (ifindex); + addresses = nm_platform_ip6_address_get_all (NM_PLATFORM_GET, ifindex); g_assert (addresses); - no_error (); g_assert_cmpint (addresses->len, ==, 1); address = &g_array_index (addresses, NMPlatformIP6Address, 0); g_assert_cmpint (address->ifindex, ==, ifindex); @@ -149,14 +133,12 @@ test_ip6_address (void) g_array_unref (addresses); /* Remove address */ - g_assert (nm_platform_ip6_address_delete (ifindex, addr, IP6_PLEN)); - no_error (); - g_assert (!nm_platform_ip6_address_exists (ifindex, addr, IP6_PLEN)); + g_assert (nm_platform_ip6_address_delete (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); + g_assert (!nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); accept_signal (address_removed); /* Remove address again */ - g_assert (nm_platform_ip6_address_delete (ifindex, addr, IP6_PLEN)); - no_error (); + g_assert (nm_platform_ip6_address_delete (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); free_signal (address_added); free_signal (address_changed); @@ -168,7 +150,7 @@ test_ip4_address_external (void) { SignalData *address_added = add_signal (NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_ADDED, ip4_address_callback); SignalData *address_removed = add_signal (NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, ip4_address_callback); - int ifindex = nm_platform_link_get_ifindex (DEVICE_NAME); + int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); in_addr_t addr; guint32 lifetime = 2000; guint32 preferred = 1000; @@ -179,28 +161,26 @@ test_ip4_address_external (void) /* Looks like addresses are not announced by kerenl when the interface * is down. Link-local IPv6 address is automatically added. */ - g_assert (nm_platform_link_set_up (nm_platform_link_get_ifindex (DEVICE_NAME))); + g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME), NULL)); /* Add/delete notification */ run_command ("ip address add %s/%d dev %s valid_lft %d preferred_lft %d", IP4_ADDRESS, IP4_PLEN, DEVICE_NAME, lifetime, preferred); wait_signal (address_added); - g_assert (nm_platform_ip4_address_exists (ifindex, addr, IP4_PLEN)); + g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN)); run_command ("ip address delete %s/%d dev %s", IP4_ADDRESS, IP4_PLEN, DEVICE_NAME); wait_signal (address_removed); - g_assert (!nm_platform_ip4_address_exists (ifindex, addr, IP4_PLEN)); + g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN)); /* Add/delete conflict */ run_command ("ip address add %s/%d dev %s valid_lft %d preferred_lft %d", IP4_ADDRESS, IP4_PLEN, DEVICE_NAME, lifetime, preferred); - g_assert (nm_platform_ip4_address_add (ifindex, addr, 0, IP4_PLEN, lifetime, preferred, NULL)); - no_error (); - g_assert (nm_platform_ip4_address_exists (ifindex, addr, IP4_PLEN)); + g_assert (nm_platform_ip4_address_add (NM_PLATFORM_GET, ifindex, addr, 0, IP4_PLEN, lifetime, preferred, NULL)); + g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN)); accept_signal (address_added); /*run_command ("ip address delete %s/%d dev %s", IP4_ADDRESS, IP4_PLEN, DEVICE_NAME); g_assert (nm_platform_ip4_address_delete (ifindex, addr, IP4_PLEN, 0)); - no_error (); - g_assert (!nm_platform_ip4_address_exists (ifindex, addr, IP4_PLEN)); + g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN)); accept_signal (address_removed);*/ free_signal (address_added); @@ -212,7 +192,7 @@ test_ip6_address_external (void) { SignalData *address_added = add_signal (NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_ADDED, ip6_address_callback); SignalData *address_removed = add_signal (NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, ip6_address_callback); - int ifindex = nm_platform_link_get_ifindex (DEVICE_NAME); + int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); struct in6_addr addr; guint32 lifetime = 2000; guint32 preferred = 1000; @@ -224,22 +204,20 @@ test_ip6_address_external (void) run_command ("ip address add %s/%d dev %s valid_lft %d preferred_lft %d", IP6_ADDRESS, IP6_PLEN, DEVICE_NAME, lifetime, preferred); wait_signal (address_added); - g_assert (nm_platform_ip6_address_exists (ifindex, addr, IP6_PLEN)); + g_assert (nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); run_command ("ip address delete %s/%d dev %s", IP6_ADDRESS, IP6_PLEN, DEVICE_NAME); wait_signal (address_removed); - g_assert (!nm_platform_ip6_address_exists (ifindex, addr, IP6_PLEN)); + g_assert (!nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); /* Add/delete conflict */ run_command ("ip address add %s/%d dev %s valid_lft %d preferred_lft %d", IP6_ADDRESS, IP6_PLEN, DEVICE_NAME, lifetime, preferred); - g_assert (nm_platform_ip6_address_add (ifindex, addr, in6addr_any, IP6_PLEN, lifetime, preferred, flags)); - no_error (); - g_assert (nm_platform_ip6_address_exists (ifindex, addr, IP6_PLEN)); + g_assert (nm_platform_ip6_address_add (NM_PLATFORM_GET, ifindex, addr, in6addr_any, IP6_PLEN, lifetime, preferred, flags)); + g_assert (nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); accept_signal (address_added); /*run_command ("ip address delete %s/%d dev %s", IP6_ADDRESS, IP6_PLEN, DEVICE_NAME); - g_assert (nm_platform_ip6_address_delete (ifindex, addr, IP6_PLEN)); - no_error (); - g_assert (!nm_platform_ip6_address_exists (ifindex, addr, IP6_PLEN)); + g_assert (nm_platform_ip6_address_delete (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); + g_assert (!nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); wait_signal (address_removed);*/ free_signal (address_added); @@ -257,9 +235,9 @@ setup_tests (void) { SignalData *link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, DEVICE_NAME); - nm_platform_link_delete (nm_platform_link_get_ifindex (DEVICE_NAME)); - g_assert (!nm_platform_link_exists (DEVICE_NAME)); - g_assert (nm_platform_dummy_add (DEVICE_NAME)); + nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); + g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME)); + g_assert (nm_platform_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_SUCCESS); accept_signal (link_added); free_signal (link_added); diff --git a/src/platform/tests/test-cleanup.c b/src/platform/tests/test-cleanup.c index 0d0f3f63..0b825114 100644 --- a/src/platform/tests/test-cleanup.c +++ b/src/platform/tests/test-cleanup.c @@ -35,30 +35,30 @@ test_cleanup_internal (void) inet_pton (AF_INET6, "2001:db8:e:f:1:2:3:4", &gateway6); /* Create and set up device */ - g_assert (nm_platform_dummy_add (DEVICE_NAME)); + g_assert (nm_platform_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_SUCCESS); accept_signal (link_added); free_signal (link_added); - g_assert (nm_platform_link_set_up (nm_platform_link_get_ifindex (DEVICE_NAME))); - ifindex = nm_platform_link_get_ifindex (DEVICE_NAME); + g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME), NULL)); + ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); g_assert (ifindex > 0); /* Add routes and addresses */ - g_assert (nm_platform_ip4_address_add (ifindex, addr4, 0, plen4, lifetime, preferred, NULL)); - g_assert (nm_platform_ip6_address_add (ifindex, addr6, in6addr_any, plen6, lifetime, preferred, flags)); - g_assert (nm_platform_ip4_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, gateway4, 32, INADDR_ANY, 0, metric, mss)); - g_assert (nm_platform_ip4_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, network4, plen4, gateway4, 0, metric, mss)); - g_assert (nm_platform_ip4_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, 0, 0, gateway4, 0, metric, mss)); - g_assert (nm_platform_ip6_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, gateway6, 128, in6addr_any, metric, mss)); - g_assert (nm_platform_ip6_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, network6, plen6, gateway6, metric, mss)); - g_assert (nm_platform_ip6_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, in6addr_any, 0, gateway6, metric, mss)); - - addresses4 = nm_platform_ip4_address_get_all (ifindex); - addresses6 = nm_platform_ip6_address_get_all (ifindex); - routes4 = nm_platform_ip4_route_get_all (ifindex, NM_PLATFORM_GET_ROUTE_MODE_ALL); - routes6 = nm_platform_ip6_route_get_all (ifindex, NM_PLATFORM_GET_ROUTE_MODE_ALL); + g_assert (nm_platform_ip4_address_add (NM_PLATFORM_GET, ifindex, addr4, 0, plen4, lifetime, preferred, NULL)); + g_assert (nm_platform_ip6_address_add (NM_PLATFORM_GET, ifindex, addr6, in6addr_any, plen6, lifetime, preferred, flags)); + g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, gateway4, 32, INADDR_ANY, 0, metric, mss)); + g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network4, plen4, gateway4, 0, metric, mss)); + g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, 0, 0, gateway4, 0, metric, mss)); + g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, gateway6, 128, in6addr_any, metric, mss)); + g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network6, plen6, gateway6, metric, mss)); + g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, in6addr_any, 0, gateway6, metric, mss)); + + addresses4 = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); + addresses6 = nm_platform_ip6_address_get_all (NM_PLATFORM_GET, ifindex); + routes4 = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); + routes6 = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); g_assert_cmpint (addresses4->len, ==, 1); - g_assert_cmpint (addresses6->len, ==, 1); + g_assert_cmpint (addresses6->len, ==, 2); /* also has a IPv6 LL address. */ g_assert_cmpint (routes4->len, ==, 3); g_assert_cmpint (routes6->len, ==, 3); @@ -68,12 +68,12 @@ test_cleanup_internal (void) g_array_unref (routes6); /* Delete interface with all addresses and routes */ - g_assert (nm_platform_link_delete (ifindex)); + g_assert (nm_platform_link_delete (NM_PLATFORM_GET, ifindex)); - addresses4 = nm_platform_ip4_address_get_all (ifindex); - addresses6 = nm_platform_ip6_address_get_all (ifindex); - routes4 = nm_platform_ip4_route_get_all (ifindex, NM_PLATFORM_GET_ROUTE_MODE_ALL); - routes6 = nm_platform_ip6_route_get_all (ifindex, NM_PLATFORM_GET_ROUTE_MODE_ALL); + addresses4 = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); + addresses6 = nm_platform_ip6_address_get_all (NM_PLATFORM_GET, ifindex); + routes4 = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); + routes6 = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); g_assert_cmpint (addresses4->len, ==, 0); g_assert_cmpint (addresses6->len, ==, 0); @@ -95,8 +95,8 @@ init_tests (int *argc, char ***argv) void setup_tests (void) { - nm_platform_link_delete (nm_platform_link_get_ifindex (DEVICE_NAME)); - g_assert (!nm_platform_link_exists (DEVICE_NAME)); + nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); + g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME)); g_test_add_func ("/internal", test_cleanup_internal); /* FIXME: add external cleanup check */ diff --git a/src/platform/tests/test-common.c b/src/platform/tests/test-common.c index 1980282d..b0eae63b 100644 --- a/src/platform/tests/test-common.c +++ b/src/platform/tests/test-common.c @@ -1,9 +1,31 @@ #include "config.h" +#include <sys/mount.h> +#include <sched.h> + #include "test-common.h" #include "nm-test-utils.h" +#define SIGNAL_DATA_FMT "'%s-%s' ifindex %d%s%s%s (%d times received)" +#define SIGNAL_DATA_ARG(data) (data)->name, nm_platform_signal_change_type_to_string ((data)->change_type), (data)->ifindex, (data)->ifname ? " ifname '" : "", (data)->ifname ? (data)->ifname : "", (data)->ifname ? "'" : "", (data)->received_count + + +gboolean +nmtst_platform_is_root_test (void) +{ + NM_PRAGMA_WARNING_DISABLE("-Wtautological-compare") + return (SETUP == nm_linux_platform_setup); + NM_PRAGMA_WARNING_REENABLE +} + +gboolean +nmtst_platform_is_sysfs_writable (void) +{ + return !nmtst_platform_is_root_test () + || (access ("/sys/devices", W_OK) == 0); +} + SignalData * add_signal_full (const char *name, NMPlatformSignalChangeType change_type, GCallback callback, int ifindex, const char *ifname) { @@ -11,7 +33,7 @@ add_signal_full (const char *name, NMPlatformSignalChangeType change_type, GCall data->name = name; data->change_type = change_type; - data->received = FALSE; + data->received_count = 0; data->handler_id = g_signal_connect (nm_platform_get (), name, callback, data); data->ifindex = ifindex; data->ifname = ifname; @@ -21,58 +43,60 @@ add_signal_full (const char *name, NMPlatformSignalChangeType change_type, GCall return data; } -static const char * -_change_type_to_string (NMPlatformSignalChangeType change_type) +void +_accept_signal (const char *file, int line, const char *func, SignalData *data) { - switch (change_type) { - case NM_PLATFORM_SIGNAL_ADDED: - return "added"; - case NM_PLATFORM_SIGNAL_CHANGED: - return "changed"; - case NM_PLATFORM_SIGNAL_REMOVED: - return "removed"; - default: - g_return_val_if_reached ("UNKNOWN"); - } + debug ("NMPlatformSignalAssert: %s:%d, %s(): Accepting signal one time: "SIGNAL_DATA_FMT, file, line, func, SIGNAL_DATA_ARG (data)); + if (data->received_count != 1) + g_error ("NMPlatformSignalAssert: %s:%d, %s(): failure to accept signal one time: "SIGNAL_DATA_FMT, file, line, func, SIGNAL_DATA_ARG (data)); + data->received_count = 0; } void -accept_signal (SignalData *data) +_accept_signals (const char *file, int line, const char *func, SignalData *data, int min, int max) { - debug ("Accepting signal '%s-%s' ifindex %d ifname %s.", data->name, _change_type_to_string (data->change_type), data->ifindex, data->ifname); - if (!data->received) - g_error ("Attemted to accept a non-received signal '%s-%s'.", data->name, _change_type_to_string (data->change_type)); + debug ("NMPlatformSignalAssert: %s:%d, %s(): Accepting signal [%d,%d] times: "SIGNAL_DATA_FMT, file, line, func, min, max, SIGNAL_DATA_ARG (data)); + if (data->received_count < min || data->received_count > max) + g_error ("NMPlatformSignalAssert: %s:%d, %s(): failure to accept signal [%d,%d] times: "SIGNAL_DATA_FMT, file, line, func, min, max, SIGNAL_DATA_ARG (data)); + data->received_count = 0; +} - data->received = FALSE; +void +_ensure_no_signal (const char *file, int line, const char *func, SignalData *data) +{ + debug ("NMPlatformSignalAssert: %s:%d, %s(): Accepting signal 0 times: "SIGNAL_DATA_FMT, file, line, func, SIGNAL_DATA_ARG (data)); + if (data->received_count > 0) + g_error ("NMPlatformSignalAssert: %s:%d, %s(): failure to accept signal 0 times: "SIGNAL_DATA_FMT, file, line, func, SIGNAL_DATA_ARG (data)); } void -wait_signal (SignalData *data) +_wait_signal (const char *file, int line, const char *func, SignalData *data) { - if (data->received) - g_error ("Signal '%s' received before waiting for it.", data->name); + debug ("NMPlatformSignalAssert: %s:%d, %s(): wait signal: "SIGNAL_DATA_FMT, file, line, func, SIGNAL_DATA_ARG (data)); + if (data->received_count) + g_error ("NMPlatformSignalAssert: %s:%d, %s(): failure to wait for signal: "SIGNAL_DATA_FMT, file, line, func, SIGNAL_DATA_ARG (data)); data->loop = g_main_loop_new (NULL, FALSE); g_main_loop_run (data->loop); g_clear_pointer (&data->loop, g_main_loop_unref); - accept_signal (data); + _accept_signal (file, line, func, data); } void -free_signal (SignalData *data) +_free_signal (const char *file, int line, const char *func, SignalData *data) { - if (data->received) - g_error ("Attempted to free received but not accepted signal '%s-%s'.", data->name, _change_type_to_string (data->change_type)); + debug ("NMPlatformSignalAssert: %s:%d, %s(): free signal: "SIGNAL_DATA_FMT, file, line, func, SIGNAL_DATA_ARG (data)); + if (data->received_count != 0) + g_error ("NMPlatformSignalAssert: %s:%d, %s(): failure to free non-accepted signal: "SIGNAL_DATA_FMT, file, line, func, SIGNAL_DATA_ARG (data)); g_signal_handler_disconnect (nm_platform_get (), data->handler_id); g_free (data); } void -link_callback (NMPlatform *platform, int ifindex, NMPlatformLink *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data) +link_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, NMPlatformLink *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data) { - GArray *links; NMPlatformLink *cached; int i; @@ -84,7 +108,7 @@ link_callback (NMPlatform *platform, int ifindex, NMPlatformLink *received, NMPl if (data->ifindex && data->ifindex != received->ifindex) return; - if (data->ifname && g_strcmp0 (data->ifname, nm_platform_link_get_name (ifindex)) != 0) + if (data->ifname && g_strcmp0 (data->ifname, nm_platform_link_get_name (NM_PLATFORM_GET, ifindex)) != 0) return; if (change_type != data->change_type) return; @@ -94,20 +118,17 @@ link_callback (NMPlatform *platform, int ifindex, NMPlatformLink *received, NMPl g_main_loop_quit (data->loop); } - if (data->received) - g_error ("Received signal '%s-%s' a second time.", data->name, _change_type_to_string (data->change_type)); - - debug ("Received signal '%s-%s' ifindex %d ifname '%s'.", data->name, _change_type_to_string (data->change_type), ifindex, received->name); - data->received = TRUE; + data->received_count++; + debug ("Received signal '%s-%s' ifindex %d ifname '%s' %dth time.", data->name, nm_platform_signal_change_type_to_string (data->change_type), ifindex, received->name, data->received_count); if (change_type == NM_PLATFORM_SIGNAL_REMOVED) - g_assert (!nm_platform_link_get_name (ifindex)); + g_assert (!nm_platform_link_get_name (NM_PLATFORM_GET, ifindex)); else - g_assert (nm_platform_link_get_name (ifindex)); + g_assert (nm_platform_link_get_name (NM_PLATFORM_GET, ifindex)); /* Check the data */ g_assert (received->ifindex > 0); - links = nm_platform_link_get_all (); + links = nm_platform_link_get_all (NM_PLATFORM_GET); for (i = 0; i < links->len; i++) { cached = &g_array_index (links, NMPlatformLink, i); if (cached->ifindex == received->ifindex) { @@ -221,9 +242,9 @@ _assert_ip4_route_exists (const char *file, guint line, const char *func, gboole exists ? "doesn't" : "does"); } - ifindex = nm_platform_link_get_ifindex (ifname); + ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, ifname); g_assert (ifindex > 0); - if (!nm_platform_ip4_route_exists (ifindex, network, plen, metric) != !exists) { + if (!nm_platform_ip4_route_get (NM_PLATFORM_GET, ifindex, network, plen, metric) != !exists) { g_error ("[%s:%u] %s(): The ip4 route %s/%d metric %u %s, but platform thinks %s", file, line, func, nm_utils_inet4_ntop (network, NULL), plen, metric, @@ -249,6 +270,46 @@ run_command (const char *format, ...) NMTST_DEFINE(); +static gboolean +unshare_user (void) +{ + FILE *f; + uid_t uid = geteuid (); + gid_t gid = getegid (); + + /* Already a root? */ + if (gid == 0 && uid == 0) + return TRUE; + + /* Become a root in new user NS. */ + if (unshare (CLONE_NEWUSER) != 0) + return FALSE; + + /* Since Linux 3.19 we have to disable setgroups() in order to map users. + * Just proceed if the file is not there. */ + f = fopen ("/proc/self/setgroups", "w"); + if (f) { + fprintf (f, "deny"); + fclose (f); + } + + /* Map current UID to root in NS to be created. */ + f = fopen ("/proc/self/uid_map", "w"); + if (!f) + return FALSE; + fprintf (f, "0 %d 1", uid); + fclose (f); + + /* Map current GID to root in NS to be created. */ + f = fopen ("/proc/self/gid_map", "w"); + if (!f) + return FALSE; + fprintf (f, "0 %d 1", gid); + fclose (f); + + return TRUE; +} + int main (int argc, char **argv) { @@ -257,20 +318,62 @@ main (int argc, char **argv) init_tests (&argc, &argv); - NM_PRAGMA_WARNING_DISABLE("-Wtautological-compare") - if (SETUP == nm_linux_platform_setup && getuid() != 0) { - /* Try to exec as sudo, this function does not return, if a sudo-cmd is set. */ - nmtst_reexec_sudo (); + if ( nmtst_platform_is_root_test () + && (geteuid () != 0 || getegid () != 0)) { + if ( g_getenv ("NMTST_FORCE_REAL_ROOT") + || !unshare_user ()) { + /* Try to exec as sudo, this function does not return, if a sudo-cmd is set. */ + nmtst_reexec_sudo (); #ifdef REQUIRE_ROOT_TESTS - g_print ("Fail test: requires root privileges (%s)\n", program); - return EXIT_FAILURE; + g_print ("Fail test: requires root privileges (%s)\n", program); + return EXIT_FAILURE; #else - g_print ("Skipping test: requires root privileges (%s)\n", program); - return 77; + g_print ("Skipping test: requires root privileges (%s)\n", program); + return g_test_run (); #endif + } + } + + if (nmtst_platform_is_root_test () && !g_getenv ("NMTST_NO_UNSHARE")) { + int errsv; + + if (unshare (CLONE_NEWNET | CLONE_NEWNS) != 0) { + errsv = errno; + g_error ("unshare(CLONE_NEWNET|CLONE_NEWNS) failed with %s (%d)", strerror (errsv), errsv); + } + + /* Mount our /sys instance, so that gudev sees only our devices. + * Needs to be read-only, because we don't run udev. */ + if (mount (NULL, "/sys", "sysfs", MS_SLAVE, NULL) != 0) { + errsv = errno; + g_error ("mount(\"/\", MS_SLAVE) failed with %s (%d)", strerror (errsv), errsv); + } + if (mount ("sys", "/sys", "sysfs", MS_RDONLY, NULL) != 0) { + errsv = errno; + g_error ("mount(\"/sys\") failed with %s (%d)", strerror (errsv), errsv); + } + + /* Create a writable /sys/devices tree. This makes it possible to run tests + * that modify values via sysfs (such as bridge forward delay). */ + if (mount ("sys", "/sys/devices", "sysfs", 0, NULL) != 0) { + errsv = errno; + g_error ("mount(\"/sys/devices\") failed with %s (%d)", strerror (errsv), errsv); + } + if (mount (NULL, "/sys/devices", "sysfs", MS_REMOUNT, NULL) != 0) { + /* Read-write remount failed. Never mind, we're probably just a root in + * our user NS. */ + if (umount ("/sys/devices") != 0) { + errsv = errno; + g_error ("umount(\"/sys/devices\") failed with %s (%d)", strerror (errsv), errsv); + } + } else { + if (mount ("/sys/devices/devices", "/sys/devices", "sysfs", MS_BIND, NULL) != 0) { + errsv = errno; + g_error ("mount(\"/sys\") failed with %s (%d)", strerror (errsv), errsv); + } + } } - NM_PRAGMA_WARNING_REENABLE SETUP (); @@ -278,8 +381,8 @@ main (int argc, char **argv) result = g_test_run (); - nm_platform_link_delete (nm_platform_link_get_ifindex (DEVICE_NAME)); + nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); - nm_platform_free (); + g_object_unref (nm_platform_get ()); return result; } diff --git a/src/platform/tests/test-common.h b/src/platform/tests/test-common.h index 367833d1..3d647543 100644 --- a/src/platform/tests/test-common.h +++ b/src/platform/tests/test-common.h @@ -15,33 +15,40 @@ #define debug(...) nm_log_dbg (LOGD_PLATFORM, __VA_ARGS__) -#define error(err) g_assert (nm_platform_get_error () == err) -#define no_error() error (NM_PLATFORM_ERROR_NONE) - typedef struct { int handler_id; const char *name; NMPlatformSignalChangeType change_type; - gboolean received; + gint received_count; GMainLoop *loop; int ifindex; const char *ifname; } SignalData; +gboolean nmtst_platform_is_root_test (void); +gboolean nmtst_platform_is_sysfs_writable (void); + SignalData *add_signal_full (const char *name, NMPlatformSignalChangeType change_type, GCallback callback, int ifindex, const char *ifname); #define add_signal(name, change_type, callback) add_signal_full (name, change_type, (GCallback) callback, 0, NULL) #define add_signal_ifindex(name, change_type, callback, ifindex) add_signal_full (name, change_type, (GCallback) callback, ifindex, NULL) #define add_signal_ifname(name, change_type, callback, ifname) add_signal_full (name, change_type, (GCallback) callback, 0, ifname) -void accept_signal (SignalData *data); -void wait_signal (SignalData *data); -void free_signal (SignalData *data); +void _accept_signal (const char *file, int line, const char *func, SignalData *data); +void _accept_signals (const char *file, int line, const char *func, SignalData *data, int min, int max); +void _wait_signal (const char *file, int line, const char *func, SignalData *data); +void _ensure_no_signal (const char *file, int line, const char *func, SignalData *data); +void _free_signal (const char *file, int line, const char *func, SignalData *data); +#define accept_signal(data) _accept_signal(__FILE__, __LINE__, G_STRFUNC, data) +#define accept_signals(data, min, max) _accept_signals(__FILE__, __LINE__, G_STRFUNC, data, min, max) +#define wait_signal(data) _wait_signal(__FILE__, __LINE__, G_STRFUNC, data) +#define ensure_no_signal(data) _ensure_no_signal(__FILE__, __LINE__, G_STRFUNC, data) +#define free_signal(data) _free_signal(__FILE__, __LINE__, G_STRFUNC, data) gboolean ip4_route_exists (const char *ifname, guint32 network, int plen, guint32 metric); void _assert_ip4_route_exists (const char *file, guint line, const char *func, gboolean exists, const char *ifname, guint32 network, int plen, guint32 metric); #define assert_ip4_route_exists(exists, ifname, network, plen, metric) _assert_ip4_route_exists (__FILE__, __LINE__, G_STRFUNC, exists, ifname, network, plen, metric) -void link_callback (NMPlatform *platform, int ifindex, NMPlatformLink *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data); +void link_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, NMPlatformLink *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data); void run_command (const char *format, ...); diff --git a/src/platform/tests/test-general.c b/src/platform/tests/test-general.c new file mode 100644 index 00000000..0a397b37 --- /dev/null +++ b/src/platform/tests/test-general.c @@ -0,0 +1,67 @@ +/* -*- 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 + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2015 Red Hat, Inc. + */ + +#include "nm-platform-utils.h" + +#include <linux/rtnetlink.h> + +#include "nm-linux-platform.h" +#include "nm-logging.h" + +#include "nm-test-utils.h" + + +/******************************************************************/ + +static void +test_init_linux_platform (void) +{ + gs_unref_object NMPlatform *platform = NULL; + + platform = g_object_new (NM_TYPE_LINUX_PLATFORM, NULL); +} + +/******************************************************************/ + +static void +test_link_get_all (void) +{ + gs_unref_object NMPlatform *platform = NULL; + gs_unref_array GArray *links = NULL; + + platform = g_object_new (NM_TYPE_LINUX_PLATFORM, NULL); + + links = nm_platform_link_get_all (platform); +} + +/******************************************************************/ + +NMTST_DEFINE (); + +int +main (int argc, char **argv) +{ + nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT"); + + g_test_add_func ("/general/init_linux_platform", test_init_linux_platform); + g_test_add_func ("/general/link_get_all", test_link_get_all); + + return g_test_run (); +} diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c index 99556e0a..dc7b8397 100644 --- a/src/platform/tests/test-link.c +++ b/src/platform/tests/test-link.c @@ -21,112 +21,94 @@ test_bogus(void) { size_t addrlen; - g_assert (!nm_platform_link_exists (BOGUS_NAME)); - no_error (); - g_assert (!nm_platform_link_delete (BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); - g_assert (!nm_platform_link_get_ifindex (BOGUS_NAME)); - error (NM_PLATFORM_ERROR_NOT_FOUND); - g_assert (!nm_platform_link_get_name (BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); - g_assert (!nm_platform_link_get_type (BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); - g_assert (!nm_platform_link_get_type_name (BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); - - g_assert (!nm_platform_link_set_up (BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); - g_assert (!nm_platform_link_set_down (BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); - g_assert (!nm_platform_link_set_arp (BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); - g_assert (!nm_platform_link_set_noarp (BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); - g_assert (!nm_platform_link_is_up (BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); - g_assert (!nm_platform_link_is_connected (BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); - g_assert (!nm_platform_link_uses_arp (BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); - - g_assert (!nm_platform_link_get_address (BOGUS_IFINDEX, &addrlen)); + g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, BOGUS_NAME)); + g_assert (!nm_platform_link_delete (NM_PLATFORM_GET, BOGUS_IFINDEX)); + g_assert (!nm_platform_link_get_ifindex (NM_PLATFORM_GET, BOGUS_NAME)); + g_assert (!nm_platform_link_get_name (NM_PLATFORM_GET, BOGUS_IFINDEX)); + g_assert (!nm_platform_link_get_type (NM_PLATFORM_GET, BOGUS_IFINDEX)); + g_assert (!nm_platform_link_get_type_name (NM_PLATFORM_GET, BOGUS_IFINDEX)); + + g_assert (!nm_platform_link_set_up (NM_PLATFORM_GET, BOGUS_IFINDEX, NULL)); + g_assert (!nm_platform_link_set_down (NM_PLATFORM_GET, BOGUS_IFINDEX)); + g_assert (!nm_platform_link_set_arp (NM_PLATFORM_GET, BOGUS_IFINDEX)); + g_assert (!nm_platform_link_set_noarp (NM_PLATFORM_GET, BOGUS_IFINDEX)); + g_assert (!nm_platform_link_is_up (NM_PLATFORM_GET, BOGUS_IFINDEX)); + g_assert (!nm_platform_link_is_connected (NM_PLATFORM_GET, BOGUS_IFINDEX)); + g_assert (!nm_platform_link_uses_arp (NM_PLATFORM_GET, BOGUS_IFINDEX)); + + g_assert (!nm_platform_link_get_address (NM_PLATFORM_GET, BOGUS_IFINDEX, &addrlen)); g_assert (!addrlen); - error (NM_PLATFORM_ERROR_NOT_FOUND); - g_assert (!nm_platform_link_get_address (BOGUS_IFINDEX, NULL)); - error (NM_PLATFORM_ERROR_NOT_FOUND); - g_assert (!nm_platform_link_set_mtu (BOGUS_IFINDEX, MTU)); - error (NM_PLATFORM_ERROR_NOT_FOUND); - g_assert (!nm_platform_link_get_mtu (BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); - - g_assert (!nm_platform_link_supports_carrier_detect (BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); - g_assert (!nm_platform_link_supports_vlans (BOGUS_IFINDEX)); - error (NM_PLATFORM_ERROR_NOT_FOUND); - - g_assert (!nm_platform_vlan_get_info (BOGUS_IFINDEX, NULL, NULL)); - error (NM_PLATFORM_ERROR_NOT_FOUND); - g_assert (!nm_platform_vlan_set_ingress_map (BOGUS_IFINDEX, 0, 0)); - error (NM_PLATFORM_ERROR_NOT_FOUND); - g_assert (!nm_platform_vlan_set_egress_map (BOGUS_IFINDEX, 0, 0)); - error (NM_PLATFORM_ERROR_NOT_FOUND); + g_assert (!nm_platform_link_get_address (NM_PLATFORM_GET, BOGUS_IFINDEX, NULL)); + g_assert (!nm_platform_link_set_mtu (NM_PLATFORM_GET, BOGUS_IFINDEX, MTU)); + g_assert (!nm_platform_link_get_mtu (NM_PLATFORM_GET, BOGUS_IFINDEX)); + + g_assert (!nm_platform_link_supports_carrier_detect (NM_PLATFORM_GET, BOGUS_IFINDEX)); + g_assert (!nm_platform_link_supports_vlans (NM_PLATFORM_GET, BOGUS_IFINDEX)); + + g_assert (!nm_platform_vlan_get_info (NM_PLATFORM_GET, BOGUS_IFINDEX, NULL, NULL)); + g_assert (!nm_platform_vlan_set_ingress_map (NM_PLATFORM_GET, BOGUS_IFINDEX, 0, 0)); + g_assert (!nm_platform_vlan_set_egress_map (NM_PLATFORM_GET, BOGUS_IFINDEX, 0, 0)); } static void test_loopback (void) { - g_assert (nm_platform_link_exists (LO_NAME)); - g_assert_cmpint (nm_platform_link_get_type (LO_INDEX), ==, NM_LINK_TYPE_LOOPBACK); - g_assert_cmpint (nm_platform_link_get_ifindex (LO_NAME), ==, LO_INDEX); - g_assert_cmpstr (nm_platform_link_get_name (LO_INDEX), ==, LO_NAME); - g_assert_cmpstr (nm_platform_link_get_type_name (LO_INDEX), ==, LO_TYPEDESC); - - g_assert (nm_platform_link_supports_carrier_detect (LO_INDEX)); - g_assert (!nm_platform_link_supports_vlans (LO_INDEX)); + g_assert (nm_platform_link_get_by_ifname (NM_PLATFORM_GET, LO_NAME)); + g_assert_cmpint (nm_platform_link_get_type (NM_PLATFORM_GET, LO_INDEX), ==, NM_LINK_TYPE_LOOPBACK); + g_assert_cmpint (nm_platform_link_get_ifindex (NM_PLATFORM_GET, LO_NAME), ==, LO_INDEX); + g_assert_cmpstr (nm_platform_link_get_name (NM_PLATFORM_GET, LO_INDEX), ==, LO_NAME); + g_assert_cmpstr (nm_platform_link_get_type_name (NM_PLATFORM_GET, LO_INDEX), ==, LO_TYPEDESC); + + g_assert (nm_platform_link_supports_carrier_detect (NM_PLATFORM_GET, LO_INDEX)); + g_assert (!nm_platform_link_supports_vlans (NM_PLATFORM_GET, LO_INDEX)); } -static int +static gboolean software_add (NMLinkType link_type, const char *name) { switch (link_type) { case NM_LINK_TYPE_DUMMY: - return nm_platform_dummy_add (name); + return nm_platform_dummy_add (NM_PLATFORM_GET, name, NULL) == NM_PLATFORM_ERROR_SUCCESS; case NM_LINK_TYPE_BRIDGE: - return nm_platform_bridge_add (name, NULL, 0); + return nm_platform_bridge_add (NM_PLATFORM_GET, name, NULL, 0, NULL) == NM_PLATFORM_ERROR_SUCCESS; case NM_LINK_TYPE_BOND: { - gboolean bond0_exists = nm_platform_link_exists ("bond0"); - gboolean result = nm_platform_bond_add (name); - NMPlatformError error = nm_platform_get_error (); + gboolean bond0_exists = !!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "bond0"); + NMPlatformError plerr; + + plerr = nm_platform_bond_add (NM_PLATFORM_GET, name, NULL); /* Check that bond0 is *not* automatically created. */ if (!bond0_exists) - g_assert (!nm_platform_link_exists ("bond0")); - - nm_platform_set_error (error); - return result; + g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "bond0")); + return plerr == NM_PLATFORM_ERROR_SUCCESS; } case NM_LINK_TYPE_TEAM: - return nm_platform_team_add (name); + return nm_platform_team_add (NM_PLATFORM_GET, name, NULL) == NM_PLATFORM_ERROR_SUCCESS; case NM_LINK_TYPE_VLAN: { SignalData *parent_added; SignalData *parent_changed; /* Don't call link_callback for the bridge interface */ parent_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, PARENT_NAME); - if (nm_platform_bridge_add (PARENT_NAME, NULL, 0)) + if (nm_platform_bridge_add (NM_PLATFORM_GET, PARENT_NAME, NULL, 0, NULL) == NM_PLATFORM_ERROR_SUCCESS) accept_signal (parent_added); free_signal (parent_added); { - int parent_ifindex = nm_platform_link_get_ifindex (PARENT_NAME); + int parent_ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, PARENT_NAME); + gboolean was_up = nm_platform_link_is_up (NM_PLATFORM_GET, parent_ifindex); parent_changed = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, link_callback, parent_ifindex); - g_assert (nm_platform_link_set_up (parent_ifindex)); - accept_signal (parent_changed); + g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, parent_ifindex, NULL)); + if (was_up) { + /* when NM is running in the background, it will mess with addrgenmode which might cause additional signals. */ + accept_signals (parent_changed, 0, 1); + } else + accept_signal (parent_changed); free_signal (parent_changed); - return nm_platform_vlan_add (name, parent_ifindex, VLAN_ID, 0); + return nm_platform_vlan_add (NM_PLATFORM_GET, name, parent_ifindex, VLAN_ID, 0, NULL) == NM_PLATFORM_ERROR_SUCCESS; } } default: @@ -142,9 +124,12 @@ test_slave (int master, int type, SignalData *master_changed) SignalData *link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, SLAVE_NAME); SignalData *link_changed, *link_removed; char *value; + NMLinkType link_type = nm_platform_link_get_type (NM_PLATFORM_GET, master); + + g_assert (NM_IN_SET (link_type, NM_LINK_TYPE_TEAM, NM_LINK_TYPE_BOND, NM_LINK_TYPE_BRIDGE)); g_assert (software_add (type, SLAVE_NAME)); - ifindex = nm_platform_link_get_ifindex (SLAVE_NAME); + ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, SLAVE_NAME); g_assert (ifindex > 0); link_changed = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, link_callback, ifindex); link_removed = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, link_callback, ifindex); @@ -154,46 +139,55 @@ test_slave (int master, int type, SignalData *master_changed) * * See https://bugzilla.redhat.com/show_bug.cgi?id=910348 */ - g_assert (nm_platform_link_set_down (ifindex)); - g_assert (!nm_platform_link_is_up (ifindex)); - accept_signal (link_changed); + g_assert (!nm_platform_link_is_up (NM_PLATFORM_GET, ifindex)); + g_assert (nm_platform_link_set_down (NM_PLATFORM_GET, ifindex)); + g_assert (!nm_platform_link_is_up (NM_PLATFORM_GET, ifindex)); + ensure_no_signal (link_changed); /* Enslave */ link_changed->ifindex = ifindex; - g_assert (nm_platform_link_enslave (master, ifindex)); no_error (); - g_assert_cmpint (nm_platform_link_get_master (ifindex), ==, master); no_error (); - accept_signal (link_changed); - accept_signal (master_changed); + g_assert (nm_platform_link_enslave (NM_PLATFORM_GET, master, ifindex)); + g_assert_cmpint (nm_platform_link_get_master (NM_PLATFORM_GET, ifindex), ==, master); + + accept_signals (link_changed, 1, 3); + accept_signals (master_changed, 0, 1); + + /* enslaveing brings put the slave */ + if (NM_IN_SET (link_type, NM_LINK_TYPE_BOND, NM_LINK_TYPE_TEAM)) + g_assert (nm_platform_link_is_up (NM_PLATFORM_GET, ifindex)); + else + g_assert (!nm_platform_link_is_up (NM_PLATFORM_GET, ifindex)); /* Set master up */ - g_assert (nm_platform_link_set_up (master)); - accept_signal (master_changed); + 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); /* Master with a disconnected slave is disconnected * * For some reason, bonding and teaming slaves are automatically set up. We * need to set them back down for this test. */ - switch (nm_platform_link_get_type (master)) { + switch (nm_platform_link_get_type (NM_PLATFORM_GET, master)) { case NM_LINK_TYPE_BOND: case NM_LINK_TYPE_TEAM: - g_assert (nm_platform_link_set_down (ifindex)); + g_assert (nm_platform_link_set_down (NM_PLATFORM_GET, ifindex)); accept_signal (link_changed); - accept_signal (master_changed); + accept_signals (master_changed, 0, 2); break; default: break; } - g_assert (!nm_platform_link_is_up (ifindex)); - g_assert (!nm_platform_link_is_connected (ifindex)); - if (nm_platform_link_is_connected (master)) { - if (nm_platform_link_get_type (master) == NM_LINK_TYPE_TEAM) { + g_assert (!nm_platform_link_is_up (NM_PLATFORM_GET, ifindex)); + g_assert (!nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex)); + if (nm_platform_link_is_connected (NM_PLATFORM_GET, master)) { + if (nm_platform_link_get_type (NM_PLATFORM_GET, master) == NM_LINK_TYPE_TEAM) { /* Older team versions (e.g. Fedora 17) have a bug that team master stays * IFF_LOWER_UP even if its slave is down. Double check it with iproute2 and if * `ip link` also claims master to be up, accept it. */ char *stdout_str = NULL; - nmtst_spawn_sync (NULL, &stdout_str, NULL, 0, "/sbin/ip", "link", "show", "dev", nm_platform_link_get_name (master)); + nmtst_spawn_sync (NULL, &stdout_str, NULL, 0, "/sbin/ip", "link", "show", "dev", nm_platform_link_get_name (NM_PLATFORM_GET, master)); g_assert (strstr (stdout_str, "LOWER_UP")); g_free (stdout_str); @@ -202,47 +196,59 @@ test_slave (int master, int type, SignalData *master_changed) } /* Set slave up and see if master gets up too */ - g_assert (nm_platform_link_set_up (ifindex)); no_error (); - g_assert (nm_platform_link_is_connected (ifindex)); - g_assert (nm_platform_link_is_connected (master)); - accept_signal (link_changed); - accept_signal (master_changed); + g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, ifindex, NULL)); + g_assert (nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex)); + 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, 1, 2); /* Enslave again * * Gracefully succeed if already enslaved. */ - g_assert (nm_platform_link_enslave (master, ifindex)); no_error (); - accept_signal (link_changed); - accept_signal (master_changed); + ensure_no_signal (link_changed); + g_assert (nm_platform_link_enslave (NM_PLATFORM_GET, master, ifindex)); + accept_signals (link_changed, 0, 2); + ensure_no_signal (master_changed); /* Set slave option */ switch (type) { case NM_LINK_TYPE_BRIDGE: - g_assert (nm_platform_slave_set_option (ifindex, "priority", "789")); - no_error (); - value = nm_platform_slave_get_option (ifindex, "priority"); - no_error (); - g_assert_cmpstr (value, ==, "789"); - g_free (value); + if (nmtst_platform_is_sysfs_writable ()) { + g_assert (nm_platform_slave_set_option (NM_PLATFORM_GET, ifindex, "priority", "789")); + value = nm_platform_slave_get_option (NM_PLATFORM_GET, ifindex, "priority"); + g_assert_cmpstr (value, ==, "789"); + g_free (value); + } break; default: break; } /* Release */ - g_assert (nm_platform_link_release (master, ifindex)); - g_assert_cmpint (nm_platform_link_get_master (ifindex), ==, 0); no_error (); - accept_signal (link_changed); - accept_signal (master_changed); + ensure_no_signal (link_changed); + g_assert (nm_platform_link_release (NM_PLATFORM_GET, master, ifindex)); + g_assert_cmpint (nm_platform_link_get_master (NM_PLATFORM_GET, ifindex), ==, 0); + accept_signals (link_changed, 1, 3); + if (link_type != NM_LINK_TYPE_TEAM) + accept_signals (master_changed, 1, 2); + else + accept_signals (master_changed, 1, 1); + + ensure_no_signal (master_changed); /* Release again */ - g_assert (!nm_platform_link_release (master, ifindex)); - error (NM_PLATFORM_ERROR_NOT_SLAVE); + ensure_no_signal (link_changed); + g_assert (!nm_platform_link_release (NM_PLATFORM_GET, master, ifindex)); + + ensure_no_signal (master_changed); /* Remove */ - g_assert (nm_platform_link_delete (ifindex)); - no_error (); + ensure_no_signal (link_changed); + g_assert (nm_platform_link_delete (NM_PLATFORM_GET, ifindex)); + accept_signals (master_changed, 0, 1); + accept_signals (link_changed, 0, 1); accept_signal (link_removed); free_signal (link_added); @@ -262,53 +268,50 @@ test_software (NMLinkType link_type, const char *link_typename) /* Add */ link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, DEVICE_NAME); g_assert (software_add (link_type, DEVICE_NAME)); - no_error (); accept_signal (link_added); - g_assert (nm_platform_link_exists (DEVICE_NAME)); - ifindex = nm_platform_link_get_ifindex (DEVICE_NAME); + g_assert (nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME)); + ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); g_assert (ifindex >= 0); - g_assert_cmpint (nm_platform_link_get_type (ifindex), ==, link_type); - g_assert_cmpstr (nm_platform_link_get_type_name (ifindex), ==, link_typename); + g_assert_cmpint (nm_platform_link_get_type (NM_PLATFORM_GET, ifindex), ==, link_type); + g_assert_cmpstr (nm_platform_link_get_type_name (NM_PLATFORM_GET, ifindex), ==, link_typename); link_changed = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, link_callback, ifindex); link_removed = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, link_callback, ifindex); if (link_type == NM_LINK_TYPE_VLAN) { - g_assert (nm_platform_vlan_get_info (ifindex, &vlan_parent, &vlan_id)); - g_assert_cmpint (vlan_parent, ==, nm_platform_link_get_ifindex (PARENT_NAME)); + g_assert (nm_platform_vlan_get_info (NM_PLATFORM_GET, ifindex, &vlan_parent, &vlan_id)); + g_assert_cmpint (vlan_parent, ==, nm_platform_link_get_ifindex (NM_PLATFORM_GET, PARENT_NAME)); g_assert_cmpint (vlan_id, ==, VLAN_ID); - no_error (); } /* Add again */ g_assert (!software_add (link_type, DEVICE_NAME)); - error (NM_PLATFORM_ERROR_EXISTS); /* Set ARP/NOARP */ - g_assert (nm_platform_link_uses_arp (ifindex)); - g_assert (nm_platform_link_set_noarp (ifindex)); - g_assert (!nm_platform_link_uses_arp (ifindex)); - accept_signal (link_changed); - g_assert (nm_platform_link_set_arp (ifindex)); - g_assert (nm_platform_link_uses_arp (ifindex)); + g_assert (nm_platform_link_uses_arp (NM_PLATFORM_GET, ifindex)); + g_assert (nm_platform_link_set_noarp (NM_PLATFORM_GET, ifindex)); + g_assert (!nm_platform_link_uses_arp (NM_PLATFORM_GET, ifindex)); + accept_signals (link_changed, 1, 2); + g_assert (nm_platform_link_set_arp (NM_PLATFORM_GET, ifindex)); + g_assert (nm_platform_link_uses_arp (NM_PLATFORM_GET, ifindex)); accept_signal (link_changed); /* Set master option */ switch (link_type) { case NM_LINK_TYPE_BRIDGE: - g_assert (nm_platform_master_set_option (ifindex, "forward_delay", "789")); - no_error (); - value = nm_platform_master_get_option (ifindex, "forward_delay"); - no_error (); - g_assert_cmpstr (value, ==, "789"); - g_free (value); + if (nmtst_platform_is_sysfs_writable ()) { + g_assert (nm_platform_master_set_option (NM_PLATFORM_GET, ifindex, "forward_delay", "789")); + value = nm_platform_master_get_option (NM_PLATFORM_GET, ifindex, "forward_delay"); + g_assert_cmpstr (value, ==, "789"); + g_free (value); + } break; case NM_LINK_TYPE_BOND: - g_assert (nm_platform_master_set_option (ifindex, "mode", "active-backup")); - no_error (); - value = nm_platform_master_get_option (ifindex, "mode"); - no_error (); - /* When reading back, the output looks slightly different. */ - g_assert (g_str_has_prefix (value, "active-backup")); - g_free (value); + if (nmtst_platform_is_sysfs_writable ()) { + g_assert (nm_platform_master_set_option (NM_PLATFORM_GET, ifindex, "mode", "active-backup")); + value = nm_platform_master_get_option (NM_PLATFORM_GET, ifindex, "mode"); + /* When reading back, the output looks slightly different. */ + g_assert (g_str_has_prefix (value, "active-backup")); + g_free (value); + } break; default: break; @@ -326,33 +329,29 @@ test_software (NMLinkType link_type, const char *link_typename) default: break; } + free_signal (link_changed); /* Delete */ - g_assert (nm_platform_link_delete (ifindex)); - no_error (); - g_assert (!nm_platform_link_exists (DEVICE_NAME)); no_error (); - g_assert_cmpint (nm_platform_link_get_type (ifindex), ==, NM_LINK_TYPE_NONE); - error (NM_PLATFORM_ERROR_NOT_FOUND); - g_assert (!nm_platform_link_get_type (ifindex)); - error (NM_PLATFORM_ERROR_NOT_FOUND); + g_assert (nm_platform_link_delete (NM_PLATFORM_GET, ifindex)); + g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME)); + g_assert_cmpint (nm_platform_link_get_type (NM_PLATFORM_GET, ifindex), ==, NM_LINK_TYPE_NONE); + g_assert (!nm_platform_link_get_type (NM_PLATFORM_GET, ifindex)); accept_signal (link_removed); /* Delete again */ - g_assert (!nm_platform_link_delete (nm_platform_link_get_ifindex (DEVICE_NAME))); - error (NM_PLATFORM_ERROR_NOT_FOUND); + g_assert (!nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME))); /* VLAN: Delete parent */ if (link_type == NM_LINK_TYPE_VLAN) { SignalData *link_removed_parent = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, link_callback, vlan_parent); - g_assert (nm_platform_link_delete (vlan_parent)); + g_assert (nm_platform_link_delete (NM_PLATFORM_GET, vlan_parent)); accept_signal (link_removed_parent); free_signal (link_removed_parent); } /* No pending signal */ free_signal (link_added); - free_signal (link_changed); free_signal (link_removed); } @@ -365,14 +364,12 @@ test_bridge (void) static void test_bond (void) { - NM_PRAGMA_WARNING_DISABLE("-Wtautological-compare") - if (SETUP == nm_linux_platform_setup && + if (nmtst_platform_is_root_test () && !g_file_test ("/proc/1/net/bonding", G_FILE_TEST_IS_DIR) && system("modprobe --show bonding") != 0) { g_test_skip ("Skipping test for bonding: bonding module not available"); return; } - NM_PRAGMA_WARNING_REENABLE test_software (NM_LINK_TYPE_BOND, "bond"); } @@ -400,76 +397,70 @@ test_internal (void) int ifindex; /* Check the functions for non-existent devices */ - g_assert (!nm_platform_link_exists (DEVICE_NAME)); no_error (); - g_assert (!nm_platform_link_get_ifindex (DEVICE_NAME)); - error (NM_PLATFORM_ERROR_NOT_FOUND); + g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME)); + g_assert (!nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); /* Add device */ - g_assert (nm_platform_dummy_add (DEVICE_NAME)); - no_error (); + g_assert (nm_platform_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_SUCCESS); accept_signal (link_added); /* Try to add again */ - g_assert (!nm_platform_dummy_add (DEVICE_NAME)); - error (NM_PLATFORM_ERROR_EXISTS); + g_assert (nm_platform_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_EXISTS); /* Check device index, name and type */ - ifindex = nm_platform_link_get_ifindex (DEVICE_NAME); + ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); g_assert (ifindex > 0); - g_assert_cmpstr (nm_platform_link_get_name (ifindex), ==, DEVICE_NAME); - g_assert_cmpint (nm_platform_link_get_type (ifindex), ==, NM_LINK_TYPE_DUMMY); - g_assert_cmpstr (nm_platform_link_get_type_name (ifindex), ==, DUMMY_TYPEDESC); + g_assert_cmpstr (nm_platform_link_get_name (NM_PLATFORM_GET, ifindex), ==, DEVICE_NAME); + g_assert_cmpint (nm_platform_link_get_type (NM_PLATFORM_GET, ifindex), ==, NM_LINK_TYPE_DUMMY); + g_assert_cmpstr (nm_platform_link_get_type_name (NM_PLATFORM_GET, ifindex), ==, DUMMY_TYPEDESC); link_changed = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, link_callback, ifindex); link_removed = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, link_callback, ifindex); /* Up/connected */ - g_assert (!nm_platform_link_is_up (ifindex)); no_error (); - g_assert (!nm_platform_link_is_connected (ifindex)); no_error (); - g_assert (nm_platform_link_set_up (ifindex)); no_error (); - g_assert (nm_platform_link_is_up (ifindex)); no_error (); - g_assert (nm_platform_link_is_connected (ifindex)); no_error (); + g_assert (!nm_platform_link_is_up (NM_PLATFORM_GET, ifindex)); + g_assert (!nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex)); + g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, ifindex, NULL)); + g_assert (nm_platform_link_is_up (NM_PLATFORM_GET, ifindex)); + g_assert (nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex)); accept_signal (link_changed); - g_assert (nm_platform_link_set_down (ifindex)); no_error (); - g_assert (!nm_platform_link_is_up (ifindex)); no_error (); - g_assert (!nm_platform_link_is_connected (ifindex)); no_error (); + g_assert (nm_platform_link_set_down (NM_PLATFORM_GET, ifindex)); + g_assert (!nm_platform_link_is_up (NM_PLATFORM_GET, ifindex)); + g_assert (!nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex)); accept_signal (link_changed); /* arp/noarp */ - g_assert (!nm_platform_link_uses_arp (ifindex)); - g_assert (nm_platform_link_set_arp (ifindex)); - g_assert (nm_platform_link_uses_arp (ifindex)); + g_assert (!nm_platform_link_uses_arp (NM_PLATFORM_GET, ifindex)); + g_assert (nm_platform_link_set_arp (NM_PLATFORM_GET, ifindex)); + g_assert (nm_platform_link_uses_arp (NM_PLATFORM_GET, ifindex)); accept_signal (link_changed); - g_assert (nm_platform_link_set_noarp (ifindex)); - g_assert (!nm_platform_link_uses_arp (ifindex)); + g_assert (nm_platform_link_set_noarp (NM_PLATFORM_GET, ifindex)); + g_assert (!nm_platform_link_uses_arp (NM_PLATFORM_GET, ifindex)); accept_signal (link_changed); /* Features */ - g_assert (!nm_platform_link_supports_carrier_detect (ifindex)); - g_assert (nm_platform_link_supports_vlans (ifindex)); + g_assert (!nm_platform_link_supports_carrier_detect (NM_PLATFORM_GET, ifindex)); + g_assert (nm_platform_link_supports_vlans (NM_PLATFORM_GET, ifindex)); /* Set MAC address */ - g_assert (nm_platform_link_set_address (ifindex, mac, sizeof (mac))); - address = nm_platform_link_get_address (ifindex, &addrlen); + g_assert (nm_platform_link_set_address (NM_PLATFORM_GET, ifindex, mac, sizeof (mac))); + address = nm_platform_link_get_address (NM_PLATFORM_GET, ifindex, &addrlen); g_assert (addrlen == sizeof(mac)); g_assert (!memcmp (address, mac, addrlen)); - address = nm_platform_link_get_address (ifindex, NULL); + address = nm_platform_link_get_address (NM_PLATFORM_GET, ifindex, NULL); g_assert (!memcmp (address, mac, addrlen)); accept_signal (link_changed); /* Set MTU */ - g_assert (nm_platform_link_set_mtu (ifindex, MTU)); - no_error (); - g_assert_cmpint (nm_platform_link_get_mtu (ifindex), ==, MTU); + g_assert (nm_platform_link_set_mtu (NM_PLATFORM_GET, ifindex, MTU)); + g_assert_cmpint (nm_platform_link_get_mtu (NM_PLATFORM_GET, ifindex), ==, MTU); accept_signal (link_changed); /* Delete device */ - g_assert (nm_platform_link_delete (ifindex)); - no_error (); + g_assert (nm_platform_link_delete (NM_PLATFORM_GET, ifindex)); accept_signal (link_removed); /* Try to delete again */ - g_assert (!nm_platform_link_delete (ifindex)); - error (NM_PLATFORM_ERROR_NOT_FOUND); + g_assert (!nm_platform_link_delete (NM_PLATFORM_GET, ifindex)); free_signal (link_added); free_signal (link_changed); @@ -479,60 +470,56 @@ test_internal (void) static void test_external (void) { - NMPlatformLink link; + const NMPlatformLink *pllink; SignalData *link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, DEVICE_NAME); SignalData *link_changed, *link_removed; int ifindex; - gboolean success; run_command ("ip link add %s type %s", DEVICE_NAME, "dummy"); wait_signal (link_added); - g_assert (nm_platform_link_exists (DEVICE_NAME)); - ifindex = nm_platform_link_get_ifindex (DEVICE_NAME); + + g_assert (nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME)); + ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); g_assert (ifindex > 0); - g_assert_cmpstr (nm_platform_link_get_name (ifindex), ==, DEVICE_NAME); - g_assert_cmpint (nm_platform_link_get_type (ifindex), ==, NM_LINK_TYPE_DUMMY); - g_assert_cmpstr (nm_platform_link_get_type_name (ifindex), ==, DUMMY_TYPEDESC); + g_assert_cmpstr (nm_platform_link_get_name (NM_PLATFORM_GET, ifindex), ==, DEVICE_NAME); + g_assert_cmpint (nm_platform_link_get_type (NM_PLATFORM_GET, ifindex), ==, NM_LINK_TYPE_DUMMY); + g_assert_cmpstr (nm_platform_link_get_type_name (NM_PLATFORM_GET, ifindex), ==, DUMMY_TYPEDESC); link_changed = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, link_callback, ifindex); link_removed = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, link_callback, ifindex); - success = nm_platform_link_get (ifindex, &link); - g_assert (success); - if (!link.driver) { + pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex); + g_assert (pllink); + if (!pllink->initialized) { /* we still lack the notification via UDEV. Expect another link changed signal. */ wait_signal (link_changed); } /* Up/connected/arp */ - g_assert (!nm_platform_link_is_up (ifindex)); - g_assert (!nm_platform_link_is_connected (ifindex)); - g_assert (!nm_platform_link_uses_arp (ifindex)); + g_assert (!nm_platform_link_is_up (NM_PLATFORM_GET, ifindex)); + g_assert (!nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex)); + g_assert (!nm_platform_link_uses_arp (NM_PLATFORM_GET, ifindex)); + run_command ("ip link set %s up", DEVICE_NAME); wait_signal (link_changed); - g_assert (nm_platform_link_is_up (ifindex)); - g_assert (nm_platform_link_is_connected (ifindex)); + + g_assert (nm_platform_link_is_up (NM_PLATFORM_GET, ifindex)); + g_assert (nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex)); run_command ("ip link set %s down", DEVICE_NAME); wait_signal (link_changed); - g_assert (!nm_platform_link_is_up (ifindex)); - g_assert (!nm_platform_link_is_connected (ifindex)); - /* This test doesn't trigger a netlink event at least on - * 3.8.2-206.fc18.x86_64. Disabling the waiting and checking code - * because of that. - */ + g_assert (!nm_platform_link_is_up (NM_PLATFORM_GET, ifindex)); + g_assert (!nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex)); + run_command ("ip link set %s arp on", DEVICE_NAME); -#if 0 wait_signal (link_changed); - g_assert (nm_platform_link_uses_arp (ifindex)); -#endif + g_assert (nm_platform_link_uses_arp (NM_PLATFORM_GET, ifindex)); run_command ("ip link set %s arp off", DEVICE_NAME); -#if 0 wait_signal (link_changed); - g_assert (!nm_platform_link_uses_arp (ifindex)); -#endif + g_assert (!nm_platform_link_uses_arp (NM_PLATFORM_GET, ifindex)); run_command ("ip link del %s", DEVICE_NAME); wait_signal (link_removed); - g_assert (!nm_platform_link_exists (DEVICE_NAME)); + accept_signals (link_changed, 0, 1); + g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME)); free_signal (link_added); free_signal (link_changed); @@ -548,12 +535,12 @@ init_tests (int *argc, char ***argv) void setup_tests (void) { - nm_platform_link_delete (nm_platform_link_get_ifindex (DEVICE_NAME)); - nm_platform_link_delete (nm_platform_link_get_ifindex (SLAVE_NAME)); - nm_platform_link_delete (nm_platform_link_get_ifindex (PARENT_NAME)); - g_assert (!nm_platform_link_exists (DEVICE_NAME)); - g_assert (!nm_platform_link_exists (SLAVE_NAME)); - g_assert (!nm_platform_link_exists (PARENT_NAME)); + nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); + nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, SLAVE_NAME)); + nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, PARENT_NAME)); + g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME)); + g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, SLAVE_NAME)); + g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, PARENT_NAME)); g_test_add_func ("/link/bogus", test_bogus); g_test_add_func ("/link/loopback", test_loopback); diff --git a/src/platform/tests/test-nmp-object.c b/src/platform/tests/test-nmp-object.c new file mode 100644 index 00000000..88bb0707 --- /dev/null +++ b/src/platform/tests/test-nmp-object.c @@ -0,0 +1,425 @@ +/* -*- 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 + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2015 Red Hat, Inc. + */ + +#include "nmp-object.h" + +#include "nm-logging.h" + +#include "nm-test-utils.h" + +struct { + GList *udev_devices; +} global; + +/******************************************************************/ + +static gboolean +_nmp_object_id_equal (const NMPObject *a, const NMPObject *b) +{ + gboolean a_b = nmp_object_id_equal (a, b); + + g_assert (NM_IN_SET (a_b, FALSE, TRUE) && a_b == nmp_object_id_equal (b, a)); + return a_b; +} +#define nmp_object_id_equal _nmp_object_id_equal + +static gboolean +_nmp_object_equal (const NMPObject *a, const NMPObject *b) +{ + gboolean a_b = nmp_object_equal (a, b); + + g_assert (NM_IN_SET (a_b, FALSE, TRUE) && a_b == nmp_object_equal (b, a)); + return a_b; +} +#define nmp_object_equal _nmp_object_equal + +/******************************************************************/ + +static void +_assert_cache_multi_lookup_contains (const NMPCache *cache, const NMPCacheId *cache_id, const NMPObject *obj, gboolean contains) +{ + const NMPlatformObject *const *objects; + guint i, len; + gboolean found; + + g_assert (cache_id); + g_assert (NMP_OBJECT_IS_VALID (obj)); + + g_assert (nmp_cache_lookup_obj (cache, obj) == obj); + + objects = nmp_cache_lookup_multi (cache, cache_id, &len); + + g_assert ((len == 0 && !objects) || (len > 0 && objects && !objects[len])); + + found = FALSE; + for (i = 0; i < len; i++) { + NMPObject *o; + + g_assert (objects[i]); + o = NMP_OBJECT_UP_CAST (objects[i]); + g_assert (NMP_OBJECT_IS_VALID (o)); + + if (obj == o) { + g_assert (!found); + found = TRUE; + } + } + + g_assert (!!contains == found); +} + +/******************************************************************/ + +typedef struct { + NMPCache *cache; + NMPCacheOpsType expected_ops_type; + const NMPObject *obj_clone; + NMPObject *new_clone; + gboolean was_visible; + gboolean called; +} _NMPCacheUpdateData; + +static void +_nmp_cache_update_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data) +{ + _NMPCacheUpdateData *data = user_data; + + g_assert (data); + g_assert (!data->called); + g_assert (data->cache == cache); + + g_assert_cmpint (data->expected_ops_type, ==, ops_type); + + switch (ops_type) { + case NMP_CACHE_OPS_ADDED: + g_assert (!old); + g_assert (NMP_OBJECT_IS_VALID (new)); + g_assert (nmp_object_is_alive (new)); + g_assert (nmp_object_id_equal (data->obj_clone, new)); + g_assert (nmp_object_equal (data->obj_clone, new)); + break; + case NMP_CACHE_OPS_UPDATED: + g_assert (NMP_OBJECT_IS_VALID (old)); + g_assert (NMP_OBJECT_IS_VALID (new)); + g_assert (nmp_object_is_alive (old)); + g_assert (nmp_object_is_alive (new)); + g_assert (nmp_object_id_equal (data->obj_clone, new)); + g_assert (nmp_object_id_equal (data->obj_clone, old)); + g_assert (nmp_object_id_equal (old, new)); + g_assert (nmp_object_equal (data->obj_clone, new)); + g_assert (!nmp_object_equal (data->obj_clone, old)); + g_assert (!nmp_object_equal (old, new)); + break; + case NMP_CACHE_OPS_REMOVED: + g_assert (!new); + g_assert (NMP_OBJECT_IS_VALID (old)); + g_assert (nmp_object_is_alive (old)); + g_assert (nmp_object_id_equal (data->obj_clone, old)); + break; + default: + g_assert_not_reached (); + } + + data->was_visible = old ? nmp_object_is_visible (old) : FALSE; + data->new_clone = new ? nmp_object_clone (new, FALSE) : NULL; + data->called = TRUE; +} + +static void +_nmp_cache_update_netlink (NMPCache *cache, NMPObject *obj, NMPObject **out_obj, gboolean *out_was_visible, NMPCacheOpsType expected_ops_type) +{ + NMPCacheOpsType ops_type; + NMPObject *obj2; + gboolean was_visible; + auto_nmp_obj NMPObject *obj_clone = nmp_object_clone (obj, FALSE); + auto_nmp_obj NMPObject *new_clone = NULL; + const NMPObject *obj_old; + _NMPCacheUpdateData data = { + .cache = cache, + .expected_ops_type = expected_ops_type, + .obj_clone = obj_clone, + }; + + obj_old = nmp_cache_lookup_link (cache, obj->object.ifindex); + if (obj_old && obj_old->_link.udev.device) + obj_clone->_link.udev.device = g_object_ref (obj_old->_link.udev.device); + _nmp_object_fixup_link_udev_fields (obj_clone, nmp_cache_use_udev_get (cache)); + + g_assert (cache); + g_assert (NMP_OBJECT_IS_VALID (obj)); + + ops_type = nmp_cache_update_netlink (cache, obj, &obj2, &was_visible, _nmp_cache_update_hook, &data); + + new_clone = data.new_clone; + + g_assert_cmpint (ops_type, ==, expected_ops_type); + + if (ops_type != NMP_CACHE_OPS_UNCHANGED) { + g_assert (NMP_OBJECT_IS_VALID (obj2)); + g_assert (data.called); + g_assert_cmpint (data.was_visible, ==, was_visible); + + if (ops_type == NMP_CACHE_OPS_REMOVED) + g_assert (!data.new_clone); + else { + g_assert (data.new_clone); + g_assert (nmp_object_equal (obj2, data.new_clone)); + } + } else { + g_assert (!data.called); + g_assert (!obj2 || was_visible == nmp_object_is_visible (obj2)); + } + + g_assert (!obj2 || nmp_object_id_equal (obj, obj2)); + if (ops_type != NMP_CACHE_OPS_REMOVED && obj2) + g_assert (nmp_object_equal (obj, obj2)); + + if (out_obj) + *out_obj = obj2; + else + nmp_object_unref (obj2); + if (out_was_visible) + *out_was_visible = was_visible; +} + +static const NMPlatformLink pl_link_2 = { + .ifindex = 2, + .name = "eth0", + .type = NM_LINK_TYPE_ETHERNET, +}; + +static const NMPlatformLink pl_link_3 = { + .ifindex = 3, + .name = "wlan0", + .type = NM_LINK_TYPE_WIFI, +}; + +static void +test_cache_link (void) +{ + NMPCache *cache; + NMPObject *obj1, *obj2; + NMPObject objs1; + gboolean was_visible; + NMPCacheId cache_id_storage; + GUdevDevice *udev_device_2 = g_list_nth_data (global.udev_devices, 0); + GUdevDevice *udev_device_3 = g_list_nth_data (global.udev_devices, 0); + NMPCacheOpsType ops_type; + + cache = nmp_cache_new (); + + nmp_cache_use_udev_set (cache, g_rand_int_range (nmtst_get_rand (), 0, 2)); + + /* if we have a link, and don't set is_in_netlink, adding it has no effect. */ + obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); + g_assert (NMP_OBJECT_UP_CAST (&obj1->object) == obj1); + g_assert (!nmp_object_is_alive (obj1)); + _nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_UNCHANGED); + ASSERT_nmp_cache_is_consistent (cache); + g_assert (!obj2); + g_assert (!was_visible); + g_assert (!nmp_cache_lookup_obj (cache, obj1)); + g_assert (!nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex))); + nmp_object_unref (obj1); + + /* Only when setting @is_in_netlink the link is added. */ + obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); + obj1->_link.netlink.is_in_netlink = TRUE; + g_assert (nmp_object_is_alive (obj1)); + _nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_ADDED); + ASSERT_nmp_cache_is_consistent (cache); + g_assert (nmp_object_equal (obj1, obj2)); + g_assert (!was_visible); + g_assert (nmp_cache_lookup_obj (cache, obj1) == obj2); + g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj2); + g_assert (nmp_object_is_visible (obj2)); + _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, TRUE); + _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE); + nmp_object_unref (obj1); + nmp_object_unref (obj2); + + /* updating the same link with identical value, has no effect. */ + obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); + obj1->_link.netlink.is_in_netlink = TRUE; + g_assert (nmp_object_is_alive (obj1)); + _nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_UNCHANGED); + ASSERT_nmp_cache_is_consistent (cache); + g_assert (obj2 != obj1); + g_assert (nmp_object_equal (obj1, obj2)); + g_assert (was_visible); + g_assert (nmp_cache_lookup_obj (cache, obj1) == obj2); + g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj2); + nmp_object_unref (obj1); + nmp_object_unref (obj2); + + /* remove the link from netlink */ + obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); + g_assert (!nmp_object_is_alive (obj1)); + _nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_REMOVED); + ASSERT_nmp_cache_is_consistent (cache); + g_assert (obj2 != obj1); + g_assert (was_visible); + g_assert (!nmp_cache_lookup_obj (cache, obj1)); + g_assert (!nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex))); + nmp_object_unref (obj1); + nmp_object_unref (obj2); + + if (udev_device_2) { + /* now add the link only with aspect UDEV. */ + ops_type = nmp_cache_update_link_udev (cache, pl_link_2.ifindex, udev_device_2, &obj2, &was_visible, NULL, NULL); + ASSERT_nmp_cache_is_consistent (cache); + g_assert_cmpint (ops_type, ==, NMP_CACHE_OPS_ADDED); + g_assert (!was_visible); + g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj2); + g_assert (!nmp_object_is_visible (obj2)); + _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, FALSE); + _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE); + nmp_object_unref (obj2); + } + + /* add it in netlink too. */ + obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); + obj1->_link.netlink.is_in_netlink = TRUE; + g_assert (nmp_object_is_alive (obj1)); + _nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, udev_device_2 ? NMP_CACHE_OPS_UPDATED : NMP_CACHE_OPS_ADDED); + ASSERT_nmp_cache_is_consistent (cache); + g_assert (nmp_object_equal (obj1, obj2)); + g_assert (!was_visible); + g_assert (nmp_cache_lookup_obj (cache, obj1) == obj2); + g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj2); + g_assert (nmp_object_is_visible (obj2)); + _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, TRUE); + _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE); + nmp_object_unref (obj1); + nmp_object_unref (obj2); + + /* remove again from netlink. */ + obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); + obj1->_link.netlink.is_in_netlink = FALSE; + g_assert (!nmp_object_is_alive (obj1)); + _nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, udev_device_2 ? NMP_CACHE_OPS_UPDATED : NMP_CACHE_OPS_REMOVED); + ASSERT_nmp_cache_is_consistent (cache); + g_assert (obj2 != obj1); + g_assert (was_visible); + if (udev_device_2) { + g_assert (nmp_cache_lookup_obj (cache, obj1) == obj2); + g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == obj2); + g_assert (!nmp_object_is_visible (obj2)); + _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, FALSE); + _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE); + } else { + g_assert (nmp_cache_lookup_obj (cache, obj1) == NULL); + g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_2.ifindex)) == NULL); + g_assert (nmp_object_is_visible (obj2)); + } + nmp_object_unref (obj1); + nmp_object_unref (obj2); + + /* now another link only with aspect UDEV. */ + if (udev_device_3) { + /* now add the link only with aspect UDEV. */ + ops_type = nmp_cache_update_link_udev (cache, pl_link_3.ifindex, udev_device_3, &obj2, &was_visible, NULL, NULL); + g_assert_cmpint (ops_type, ==, NMP_CACHE_OPS_ADDED); + ASSERT_nmp_cache_is_consistent (cache); + g_assert (NMP_OBJECT_IS_VALID (obj2)); + g_assert (!was_visible); + g_assert (!nmp_object_is_visible (obj2)); + g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_3.ifindex)) == obj2); + _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, FALSE); + _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE); + g_assert_cmpint (obj2->_link.netlink.is_in_netlink, ==, FALSE); + g_assert_cmpint (obj2->link.initialized, ==, FALSE); + nmp_object_unref (obj2); + + /* add it in netlink too. */ + obj1 = nmp_object_new (NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_3); + obj1->_link.netlink.is_in_netlink = TRUE; + g_assert (nmp_object_is_alive (obj1)); + _nmp_cache_update_netlink (cache, obj1, &obj2, &was_visible, NMP_CACHE_OPS_UPDATED); + ASSERT_nmp_cache_is_consistent (cache); + g_assert (obj2 != obj1); + g_assert (nmp_object_equal (obj1, obj2)); + g_assert (!was_visible); + g_assert (nmp_cache_lookup_obj (cache, obj1) == obj2); + g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_3.ifindex)) == obj2); + g_assert (nmp_object_is_visible (obj2)); + _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, TRUE); + _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE); + g_assert_cmpint (obj2->_link.netlink.is_in_netlink, ==, TRUE); + g_assert_cmpint (obj2->link.initialized, ==, TRUE); + nmp_object_unref (obj1); + nmp_object_unref (obj2); + + /* remove UDEV. */ + ops_type = nmp_cache_update_link_udev (cache, pl_link_3.ifindex, NULL, &obj2, &was_visible, NULL, NULL); + g_assert_cmpint (ops_type, ==, NMP_CACHE_OPS_UPDATED); + ASSERT_nmp_cache_is_consistent (cache); + g_assert (was_visible); + g_assert (nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&objs1, pl_link_3.ifindex)) == obj2); + g_assert (nmp_object_is_visible (obj2)); + _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, TRUE), obj2, TRUE); + _assert_cache_multi_lookup_contains (cache, nmp_cache_id_init_object_type (&cache_id_storage, NMP_OBJECT_TYPE_LINK, FALSE), obj2, TRUE); + g_assert_cmpint (obj2->_link.netlink.is_in_netlink, ==, TRUE); + g_assert_cmpint (obj2->link.initialized, ==, !nmp_cache_use_udev_get (cache)); + nmp_object_unref (obj2); + } + + nmp_cache_free (cache); +} + +/******************************************************************/ + +NMTST_DEFINE (); + +int +main (int argc, char **argv) +{ + int result; + gs_unref_object GUdevClient *udev_client = NULL; + + nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT"); + + udev_client = g_udev_client_new ((const char *[]) { "net", NULL }); + { + gs_unref_object GUdevEnumerator *udev_enumerator = g_udev_enumerator_new (udev_client); + + g_udev_enumerator_add_match_subsystem (udev_enumerator, "net"); + + /* Demand that the device is initialized (udev rules ran, + * device has a stable name now) in case udev is running + * (not in a container). */ + if (access ("/sys", W_OK) == 0) + g_udev_enumerator_add_match_is_initialized (udev_enumerator); + + global.udev_devices = g_udev_enumerator_execute (udev_enumerator); + } + + g_test_add_func ("/nmp-object/cache_link", test_cache_link); + + result = g_test_run (); + + while (global.udev_devices) { + g_object_unref (global.udev_devices->data); + global.udev_devices = g_list_remove (global.udev_devices, global.udev_devices->data); + } + + return result; +} + diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c index 2d3caaf1..cf322dec 100644 --- a/src/platform/tests/test-route.c +++ b/src/platform/tests/test-route.c @@ -1,5 +1,7 @@ #include "config.h" +#include <linux/rtnetlink.h> + #include "test-common.h" #include "nm-test-utils.h" #include "NetworkManagerUtils.h" @@ -7,7 +9,7 @@ #define DEVICE_NAME "nm-test-device" static void -ip4_route_callback (NMPlatform *platform, int ifindex, NMPlatformIP4Route *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data) +ip4_route_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, const NMPlatformIP4Route *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data) { g_assert (received); g_assert_cmpint (received->ifindex, ==, ifindex); @@ -22,14 +24,12 @@ ip4_route_callback (NMPlatform *platform, int ifindex, NMPlatformIP4Route *recei if (data->loop) g_main_loop_quit (data->loop); - if (data->received) - g_error ("Received signal '%s' a second time.", data->name); - - data->received = TRUE; + data->received_count++; + debug ("Received signal '%s' %dth time.", data->name, data->received_count); } static void -ip6_route_callback (NMPlatform *platform, int ifindex, NMPlatformIP6Route *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data) +ip6_route_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, const NMPlatformIP6Route *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data) { g_assert (received); g_assert_cmpint (received->ifindex, ==, ifindex); @@ -44,16 +44,14 @@ ip6_route_callback (NMPlatform *platform, int ifindex, NMPlatformIP6Route *recei if (data->loop) g_main_loop_quit (data->loop); - if (data->received) - g_error ("Received signal '%s' a second time.", data->name); - - data->received = TRUE; + data->received_count++; + debug ("Received signal '%s' %dth time.", data->name, data->received_count); } static void test_ip4_route_metric0 (void) { - int ifindex = nm_platform_link_get_ifindex (DEVICE_NAME); + int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); SignalData *route_added = add_signal (NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_ADDED, ip4_route_callback); SignalData *route_changed = add_signal (NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, ip4_route_callback); SignalData *route_removed = add_signal (NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, ip4_route_callback); @@ -67,48 +65,42 @@ test_ip4_route_metric0 (void) assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, metric); /* add the first route */ - g_assert (nm_platform_ip4_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, INADDR_ANY, 0, metric, mss)); - no_error (); + g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, INADDR_ANY, 0, metric, mss)); accept_signal (route_added); assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, 0); assert_ip4_route_exists (TRUE, DEVICE_NAME, network, plen, metric); /* Deleting route with metric 0 does nothing */ - g_assert (nm_platform_ip4_route_delete (ifindex, network, plen, 0)); - no_error (); - g_assert (!route_removed->received); + g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, 0)); + ensure_no_signal (route_removed); assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, 0); assert_ip4_route_exists (TRUE, DEVICE_NAME, network, plen, metric); /* add the second route */ - g_assert (nm_platform_ip4_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, INADDR_ANY, 0, 0, mss)); - no_error (); + g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, INADDR_ANY, 0, 0, mss)); accept_signal (route_added); assert_ip4_route_exists (TRUE, DEVICE_NAME, network, plen, 0); assert_ip4_route_exists (TRUE, DEVICE_NAME, network, plen, metric); /* Delete route with metric 0 */ - g_assert (nm_platform_ip4_route_delete (ifindex, network, plen, 0)); - no_error (); + g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, 0)); accept_signal (route_removed); assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, 0); assert_ip4_route_exists (TRUE, DEVICE_NAME, network, plen, metric); /* Delete route with metric 0 again (we expect nothing to happen) */ - g_assert (nm_platform_ip4_route_delete (ifindex, network, plen, 0)); - no_error (); - g_assert (!route_removed->received); + g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, 0)); + ensure_no_signal (route_removed); assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, 0); assert_ip4_route_exists (TRUE, DEVICE_NAME, network, plen, metric); /* Delete the other route */ - g_assert (nm_platform_ip4_route_delete (ifindex, network, plen, metric)); - no_error (); + g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); accept_signal (route_removed); assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, 0); @@ -122,7 +114,7 @@ test_ip4_route_metric0 (void) static void test_ip4_route (void) { - int ifindex = nm_platform_link_get_ifindex (DEVICE_NAME); + int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); SignalData *route_added = add_signal (NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_ADDED, ip4_route_callback); SignalData *route_changed = add_signal (NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, ip4_route_callback); SignalData *route_removed = add_signal (NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, ip4_route_callback); @@ -139,40 +131,31 @@ test_ip4_route (void) inet_pton (AF_INET, "198.51.100.1", &gateway); /* Add route to gateway */ - g_assert (nm_platform_ip4_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, gateway, 32, INADDR_ANY, 0, metric, mss)); - no_error (); + g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, gateway, 32, INADDR_ANY, 0, metric, mss)); accept_signal (route_added); /* Add route */ assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, metric); - no_error (); - g_assert (nm_platform_ip4_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, 0, metric, mss)); - no_error (); + g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, 0, metric, mss)); assert_ip4_route_exists (TRUE, DEVICE_NAME, network, plen, metric); - no_error (); accept_signal (route_added); /* Add route again */ - g_assert (nm_platform_ip4_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, 0, metric, mss)); - no_error (); - accept_signal (route_changed); + g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, 0, metric, mss)); + accept_signals (route_changed, 0, 1); /* Add default route */ assert_ip4_route_exists (FALSE, DEVICE_NAME, 0, 0, metric); - no_error (); - g_assert (nm_platform_ip4_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, 0, 0, gateway, 0, metric, mss)); - no_error (); + g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, 0, 0, gateway, 0, metric, mss)); assert_ip4_route_exists (TRUE, DEVICE_NAME, 0, 0, metric); - no_error (); accept_signal (route_added); /* Add default route again */ - g_assert (nm_platform_ip4_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, 0, 0, gateway, 0, metric, mss)); - no_error (); - accept_signal (route_changed); + g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, 0, 0, gateway, 0, metric, mss)); + accept_signals (route_changed, 0, 1); /* Test route listing */ - routes = nm_platform_ip4_route_get_all (ifindex, NM_PLATFORM_GET_ROUTE_MODE_ALL); + routes = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); memset (rts, 0, sizeof (rts)); rts[0].source = NM_IP_CONFIG_SOURCE_USER; rts[0].network = gateway; @@ -181,6 +164,7 @@ test_ip4_route (void) rts[0].gateway = INADDR_ANY; rts[0].metric = metric; rts[0].mss = mss; + rts[0].scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK); rts[1].source = NM_IP_CONFIG_SOURCE_USER; rts[1].network = network; rts[1].plen = plen; @@ -188,6 +172,7 @@ test_ip4_route (void) rts[1].gateway = gateway; rts[1].metric = metric; rts[1].mss = mss; + rts[1].scope_inv = nm_platform_route_scope_inv (RT_SCOPE_UNIVERSE); rts[2].source = NM_IP_CONFIG_SOURCE_USER; rts[2].network = 0; rts[2].plen = 0; @@ -195,20 +180,18 @@ test_ip4_route (void) rts[2].gateway = gateway; rts[2].metric = metric; rts[2].mss = mss; + rts[2].scope_inv = nm_platform_route_scope_inv (RT_SCOPE_UNIVERSE); g_assert_cmpint (routes->len, ==, 3); - g_assert (!memcmp (routes->data, rts, sizeof (rts))); nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, rts, routes->len, TRUE); g_array_unref (routes); /* Remove route */ - g_assert (nm_platform_ip4_route_delete (ifindex, network, plen, metric)); - no_error (); + g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); assert_ip4_route_exists (FALSE, DEVICE_NAME, network, plen, metric); accept_signal (route_removed); /* Remove route again */ - g_assert (nm_platform_ip4_route_delete (ifindex, network, plen, metric)); - no_error (); + g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); free_signal (route_added); free_signal (route_changed); @@ -218,7 +201,7 @@ test_ip4_route (void) static void test_ip6_route (void) { - int ifindex = nm_platform_link_get_ifindex (DEVICE_NAME); + int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); SignalData *route_added = add_signal (NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_ADDED, ip6_route_callback); SignalData *route_changed = add_signal (NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, ip6_route_callback); SignalData *route_removed = add_signal (NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, ip6_route_callback); @@ -235,40 +218,31 @@ test_ip6_route (void) inet_pton (AF_INET6, "2001:db8:c:d:1:2:3:4", &gateway); /* Add route to gateway */ - g_assert (nm_platform_ip6_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, gateway, 128, in6addr_any, metric, mss)); - no_error (); + g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, gateway, 128, in6addr_any, metric, mss)); accept_signal (route_added); /* Add route */ - g_assert (!nm_platform_ip6_route_exists (ifindex, network, plen, metric)); - no_error (); - g_assert (nm_platform_ip6_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, metric, mss)); - no_error (); - g_assert (nm_platform_ip6_route_exists (ifindex, network, plen, metric)); - no_error (); + g_assert (!nm_platform_ip6_route_get (NM_PLATFORM_GET, ifindex, network, plen, metric)); + g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, metric, mss)); + g_assert (nm_platform_ip6_route_get (NM_PLATFORM_GET, ifindex, network, plen, metric)); accept_signal (route_added); /* Add route again */ - g_assert (nm_platform_ip6_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, metric, mss)); - no_error (); - accept_signal (route_changed); + g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, metric, mss)); + accept_signals (route_changed, 0, 1); /* Add default route */ - g_assert (!nm_platform_ip6_route_exists (ifindex, in6addr_any, 0, metric)); - no_error (); - g_assert (nm_platform_ip6_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, in6addr_any, 0, gateway, metric, mss)); - no_error (); - g_assert (nm_platform_ip6_route_exists (ifindex, in6addr_any, 0, metric)); - no_error (); + g_assert (!nm_platform_ip6_route_get (NM_PLATFORM_GET, ifindex, in6addr_any, 0, metric)); + g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, in6addr_any, 0, gateway, metric, mss)); + g_assert (nm_platform_ip6_route_get (NM_PLATFORM_GET, ifindex, in6addr_any, 0, metric)); accept_signal (route_added); /* Add default route again */ - g_assert (nm_platform_ip6_route_add (ifindex, NM_IP_CONFIG_SOURCE_USER, in6addr_any, 0, gateway, metric, mss)); - no_error (); - accept_signal (route_changed); + g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, in6addr_any, 0, gateway, metric, mss)); + accept_signals (route_changed, 0, 1); /* Test route listing */ - routes = nm_platform_ip6_route_get_all (ifindex, NM_PLATFORM_GET_ROUTE_MODE_ALL); + routes = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); memset (rts, 0, sizeof (rts)); rts[0].source = NM_IP_CONFIG_SOURCE_USER; rts[0].network = gateway; @@ -292,19 +266,16 @@ test_ip6_route (void) rts[2].metric = nm_utils_ip6_route_metric_normalize (metric); rts[2].mss = mss; g_assert_cmpint (routes->len, ==, 3); - g_assert (!memcmp (routes->data, rts, sizeof (rts))); nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, rts, routes->len, TRUE); g_array_unref (routes); /* Remove route */ - g_assert (nm_platform_ip6_route_delete (ifindex, network, plen, metric)); - no_error (); - g_assert (!nm_platform_ip6_route_exists (ifindex, network, plen, metric)); + g_assert (nm_platform_ip6_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); + g_assert (!nm_platform_ip6_route_get (NM_PLATFORM_GET, ifindex, network, plen, metric)); accept_signal (route_removed); /* Remove route again */ - g_assert (nm_platform_ip6_route_delete (ifindex, network, plen, metric)); - no_error (); + g_assert (nm_platform_ip6_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); free_signal (route_added); free_signal (route_changed); @@ -322,13 +293,13 @@ setup_tests (void) { SignalData *link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, DEVICE_NAME); - nm_platform_link_delete (nm_platform_link_get_ifindex (DEVICE_NAME)); - g_assert (!nm_platform_link_exists (DEVICE_NAME)); - g_assert (nm_platform_dummy_add (DEVICE_NAME)); + nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); + g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME)); + g_assert (nm_platform_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_SUCCESS); accept_signal (link_added); free_signal (link_added); - g_assert (nm_platform_link_set_up (nm_platform_link_get_ifindex (DEVICE_NAME))); + g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME), NULL)); g_test_add_func ("/route/ip4", test_ip4_route); g_test_add_func ("/route/ip6", test_ip6_route); diff --git a/src/platform/wifi/wifi-utils.c b/src/platform/wifi/wifi-utils.c index daef881b..068c404e 100644 --- a/src/platform/wifi/wifi-utils.c +++ b/src/platform/wifi/wifi-utils.c @@ -162,20 +162,13 @@ wifi_utils_deinit (WifiData *data) } gboolean -wifi_utils_is_wifi (const char *iface, const char *sysfs_path, const char *devtype) +wifi_utils_is_wifi (const char *iface, const char *sysfs_path) { char phy80211_path[255]; struct stat s; g_return_val_if_fail (iface != NULL, FALSE); - if (g_strcmp0 (devtype, "wlan") == 0) { - /* All Wi-Fi drivers should set DEVTYPE=wlan. Since the kernel's - * cfg80211/nl80211 stack does, this check should match any nl80211 - * capable driver (including mac82011-based ones). */ - return TRUE; - } - if (sysfs_path) { /* Check for nl80211 sysfs paths */ g_snprintf (phy80211_path, sizeof (phy80211_path), "%s/phy80211", sysfs_path); diff --git a/src/platform/wifi/wifi-utils.h b/src/platform/wifi/wifi-utils.h index eb37bc22..588386ab 100644 --- a/src/platform/wifi/wifi-utils.h +++ b/src/platform/wifi/wifi-utils.h @@ -29,7 +29,7 @@ typedef struct WifiData WifiData; -gboolean wifi_utils_is_wifi (const char *iface, const char *sysfs_path, const char *devtype); +gboolean wifi_utils_is_wifi (const char *iface, const char *sysfs_path); WifiData *wifi_utils_init (const char *iface, int ifindex, gboolean check_scan); diff --git a/src/ppp-manager/Makefile.in b/src/ppp-manager/Makefile.in index ddbb7de0..26698e16 100644 --- a/src/ppp-manager/Makefile.in +++ b/src/ppp-manager/Makefile.in @@ -221,6 +221,7 @@ ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -334,6 +335,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ +LOG_DRIVER = @LOG_DRIVER@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ diff --git a/src/ppp-manager/nm-ppp-manager.c b/src/ppp-manager/nm-ppp-manager.c index 65993deb..73528aa9 100644 --- a/src/ppp-manager/nm-ppp-manager.c +++ b/src/ppp-manager/nm-ppp-manager.c @@ -39,6 +39,7 @@ #ifndef aligned_u64 #define aligned_u64 unsigned long long __attribute__((aligned(8))) #endif +#include <linux/if.h> #include <linux/if_ppp.h> #include "NetworkManagerUtils.h" @@ -46,7 +47,6 @@ #include "nm-ppp-manager.h" #include "nm-dbus-manager.h" #include "nm-logging.h" -#include "nm-posix-signals.h" #include "nm-platform.h" #include "nm-core-internal.h" @@ -1030,20 +1030,6 @@ create_pppd_cmd_line (NMPPPManager *self, } static void -pppd_child_setup (gpointer user_data G_GNUC_UNUSED) -{ - /* We are in the child process at this point */ - pid_t pid = getpid (); - setpgid (pid, pid); - - /* - * We blocked signals in main(). We need to restore original signal - * mask for pppd here so that it can receive signals. - */ - nm_unblock_posix_signals (NULL); -} - -static void pppoe_fill_defaults (NMSettingPpp *setting) { if (!nm_setting_ppp_get_mtu (setting)) @@ -1086,7 +1072,6 @@ nm_ppp_manager_start (NMPPPManager *manager, NMCmdLine *ppp_cmd; char *cmd_str; struct stat st; - int ignored; g_return_val_if_fail (NM_IS_PPP_MANAGER (manager), FALSE); g_return_val_if_fail (NM_IS_ACT_REQUEST (req), FALSE); @@ -1106,7 +1091,7 @@ nm_ppp_manager_start (NMPPPManager *manager, /* Make sure /dev/ppp exists (bgo #533064) */ if (stat ("/dev/ppp", &st) || !S_ISCHR (st.st_mode)) - ignored = system ("/sbin/modprobe ppp_generic"); + nm_utils_modprobe (NULL, FALSE, "ppp_generic", NULL); connection = nm_act_request_get_connection (req); g_assert (connection); @@ -1141,8 +1126,8 @@ nm_ppp_manager_start (NMPPPManager *manager, priv->pid = 0; if (!g_spawn_async (NULL, (char **) ppp_cmd->array->pdata, NULL, G_SPAWN_DO_NOT_REAP_CHILD, - pppd_child_setup, - NULL, &priv->pid, err)) { + nm_utils_setpgid, NULL, + &priv->pid, err)) { goto out; } diff --git a/src/rdisc/Makefile.in b/src/rdisc/Makefile.in index 349616f7..4713fa9b 100644 --- a/src/rdisc/Makefile.in +++ b/src/rdisc/Makefile.in @@ -195,6 +195,7 @@ ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -308,6 +309,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ +LOG_DRIVER = @LOG_DRIVER@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ diff --git a/src/rdisc/nm-lndp-rdisc.c b/src/rdisc/nm-lndp-rdisc.c index cadbfdfa..46f81476 100644 --- a/src/rdisc/nm-lndp-rdisc.c +++ b/src/rdisc/nm-lndp-rdisc.c @@ -298,7 +298,7 @@ start (NMRDisc *rdisc) static inline gint32 ipv6_sysctl_get (const char *ifname, const char *property, gint32 defval) { - return nm_platform_sysctl_get_int32 (nm_utils_ip6_property_path (ifname, property), defval); + return nm_platform_sysctl_get_int32 (NM_PLATFORM_GET, nm_utils_ip6_property_path (ifname, property), defval); } NMRDisc * diff --git a/src/rdisc/nm-rdisc.c b/src/rdisc/nm-rdisc.c index 07271a89..a73ab7d0 100644 --- a/src/rdisc/nm-rdisc.c +++ b/src/rdisc/nm-rdisc.c @@ -83,7 +83,7 @@ nm_rdisc_add_gateway (NMRDisc *rdisc, const NMRDiscGateway *new) } if (new->lifetime) - g_array_insert_val (rdisc->gateways, CLAMP (insert_idx, 0, G_MAXINT), *new); + g_array_insert_val (rdisc->gateways, MAX (insert_idx, 0), *new); return !!new->lifetime; } @@ -241,12 +241,38 @@ clear_rs_timeout (NMRDisc *rdisc) } } -void +/** + * nm_rdisc_set_iid: + * @rdisc: the #NMRDisc + * @iid: the new interface ID + * + * Sets the "Modified EUI-64" interface ID to be used when generating + * IPv6 addresses using received prefixes. Identifiers are either generated + * from the hardware addresses or manually set by the operator with + * "ip token" command. + * + * Upon token change (or initial setting) all addresses generated using + * the old identifier are removed. The caller should ensure the addresses + * will be reset by soliciting router advertisements. + * + * Returns: %TRUE if the token was changed, %FALSE otherwise. + **/ +gboolean nm_rdisc_set_iid (NMRDisc *rdisc, const NMUtilsIPv6IfaceId iid) { - g_return_if_fail (NM_IS_RDISC (rdisc)); + g_return_val_if_fail (NM_IS_RDISC (rdisc), FALSE); + + if (rdisc->iid.id != iid.id) { + rdisc->iid = iid; + if (rdisc->addresses->len) { + debug ("(%s) IPv6 interface identifier changed, flushing addresses", rdisc->ifname); + g_array_remove_range (rdisc->addresses, 0, rdisc->addresses->len); + g_signal_emit_by_name (rdisc, NM_RDISC_CONFIG_CHANGED, NM_RDISC_CONFIG_ADDRESSES); + } + return TRUE; + } - rdisc->iid = iid; + return FALSE; } static gboolean diff --git a/src/rdisc/nm-rdisc.h b/src/rdisc/nm-rdisc.h index a79e6c96..463e1912 100644 --- a/src/rdisc/nm-rdisc.h +++ b/src/rdisc/nm-rdisc.h @@ -141,7 +141,7 @@ typedef struct { GType nm_rdisc_get_type (void); -void nm_rdisc_set_iid (NMRDisc *rdisc, const NMUtilsIPv6IfaceId iid); +gboolean nm_rdisc_set_iid (NMRDisc *rdisc, const NMUtilsIPv6IfaceId iid); void nm_rdisc_start (NMRDisc *rdisc); #endif /* __NETWORKMANAGER_RDISC_H__ */ diff --git a/src/rdisc/tests/Makefile.in b/src/rdisc/tests/Makefile.in index f241fcbf..8fedb770 100644 --- a/src/rdisc/tests/Makefile.in +++ b/src/rdisc/tests/Makefile.in @@ -207,13 +207,196 @@ am__tty_colors = { \ std='[m'; \ fi; \ } +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__recheck_rx = ^[ ]*:recheck:[ ]* +am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* +am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* +# A command that, given a newline-separated list of test names on the +# standard input, print the name of the tests that are to be re-run +# upon "make recheck". +am__list_recheck_tests = $(AWK) '{ \ + recheck = 1; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + { \ + if ((getline line2 < ($$0 ".log")) < 0) \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ + { \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ + { \ + break; \ + } \ + }; \ + if (recheck) \ + print $$0; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# A command that, given a newline-separated list of test names on the +# standard input, create the global log from their .trs and .log files. +am__create_global_log = $(AWK) ' \ +function fatal(msg) \ +{ \ + print "fatal: making $@: " msg | "cat >&2"; \ + exit 1; \ +} \ +function rst_section(header) \ +{ \ + print header; \ + len = length(header); \ + for (i = 1; i <= len; i = i + 1) \ + printf "="; \ + printf "\n\n"; \ +} \ +{ \ + copy_in_global_log = 1; \ + global_test_result = "RUN"; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".trs"); \ + if (line ~ /$(am__global_test_result_rx)/) \ + { \ + sub("$(am__global_test_result_rx)", "", line); \ + sub("[ ]*$$", "", line); \ + global_test_result = line; \ + } \ + else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ + copy_in_global_log = 0; \ + }; \ + if (copy_in_global_log) \ + { \ + rst_section(global_test_result ": " $$0); \ + while ((rc = (getline line < ($$0 ".log"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".log"); \ + print line; \ + }; \ + printf "\n"; \ + }; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# Restructured Text title. +am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } +# Solaris 10 'make', and several other traditional 'make' implementations, +# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it +# by disabling -e (using the XSI extension "set +e") if it's set. +am__sh_e_setup = case $$- in *e*) set +e;; esac +# Default flags passed to test drivers. +am__common_driver_flags = \ + --color-tests "$$am__color_tests" \ + --enable-hard-errors "$$am__enable_hard_errors" \ + --expect-failure "$$am__expect_failure" +# To be inserted before the command running the test. Creates the +# directory for the log if needed. Stores in $dir the directory +# containing $f, in $tst the test, in $log the log. Executes the +# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and +# passes TESTS_ENVIRONMENT. Set up options for the wrapper that +# will run the test scripts (or their associated LOG_COMPILER, if +# thy have one). +am__check_pre = \ +$(am__sh_e_setup); \ +$(am__vpath_adj_setup) $(am__vpath_adj) \ +$(am__tty_colors); \ +srcdir=$(srcdir); export srcdir; \ +case "$@" in \ + */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ + *) am__odir=.;; \ +esac; \ +test "x$$am__odir" = x"." || test -d "$$am__odir" \ + || $(MKDIR_P) "$$am__odir" || exit $$?; \ +if test -f "./$$f"; then dir=./; \ +elif test -f "$$f"; then dir=; \ +else dir="$(srcdir)/"; fi; \ +tst=$$dir$$f; log='$@'; \ +if test -n '$(DISABLE_HARD_ERRORS)'; then \ + am__enable_hard_errors=no; \ +else \ + am__enable_hard_errors=yes; \ +fi; \ +case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ + am__expect_failure=yes;; \ + *) \ + am__expect_failure=no;; \ +esac; \ +$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) +# A shell command to get the names of the tests scripts with any registered +# extension removed (i.e., equivalently, the names of the test logs, with +# the '.log' extension removed). The result is saved in the shell variable +# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, +# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", +# since that might cause problem with VPATH rewrites for suffix-less tests. +# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. +am__set_TESTS_bases = \ + bases='$(TEST_LOGS)'; \ + bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ + bases=`echo $$bases` +RECHECK_LOGS = $(TEST_LOGS) +AM_RECURSIVE_TARGETS = check recheck +TEST_SUITE_LOG = test-suite.log +TEST_EXTENSIONS = @EXEEXT@ .test +LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) +am__set_b = \ + case '$@' in \ + */*) \ + case '$*' in \ + */*) b='$*';; \ + *) b=`echo '$@' | sed 's/\.log$$//'`; \ + esac;; \ + *) \ + b='$*';; \ + esac +am__test_logs1 = $(TESTS:=.log) +am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) +TEST_LOGS = $(am__test_logs2:.test.log=.log) +TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/build-aux/test-driver +TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ + $(TEST_LOG_FLAGS) am__DIST_COMMON = $(srcdir)/Makefile.in \ - $(top_srcdir)/build-aux/depcomp + $(top_srcdir)/build-aux/depcomp \ + $(top_srcdir)/build-aux/test-driver DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -327,6 +510,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ +LOG_DRIVER = @LOG_DRIVER@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ @@ -505,7 +689,7 @@ test_rdisc_fake_LDADD = \ all: all-am .SUFFIXES: -.SUFFIXES: .c .lo .o .obj +.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ @@ -641,98 +825,168 @@ cscopelist-am: $(am__tagged_files) distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -check-TESTS: $(TESTS) - @failed=0; all=0; xfail=0; xpass=0; skip=0; \ - srcdir=$(srcdir); export srcdir; \ - list=' $(TESTS) '; \ - $(am__tty_colors); \ - if test -n "$$list"; then \ - for tst in $$list; do \ - if test -f ./$$tst; then dir=./; \ - elif test -f $$tst; then dir=; \ - else dir="$(srcdir)/"; fi; \ - if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \ - all=`expr $$all + 1`; \ - case " $(XFAIL_TESTS) " in \ - *[\ \ ]$$tst[\ \ ]*) \ - xpass=`expr $$xpass + 1`; \ - failed=`expr $$failed + 1`; \ - col=$$red; res=XPASS; \ - ;; \ - *) \ - col=$$grn; res=PASS; \ - ;; \ - esac; \ - elif test $$? -ne 77; then \ - all=`expr $$all + 1`; \ - case " $(XFAIL_TESTS) " in \ - *[\ \ ]$$tst[\ \ ]*) \ - xfail=`expr $$xfail + 1`; \ - col=$$lgn; res=XFAIL; \ - ;; \ - *) \ - failed=`expr $$failed + 1`; \ - col=$$red; res=FAIL; \ - ;; \ - esac; \ - else \ - skip=`expr $$skip + 1`; \ - col=$$blu; res=SKIP; \ - fi; \ - echo "$${col}$$res$${std}: $$tst"; \ - done; \ - if test "$$all" -eq 1; then \ - tests="test"; \ - All=""; \ - else \ - tests="tests"; \ - All="All "; \ +# Recover from deleted '.trs' file; this should ensure that +# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create +# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells +# to avoid problems with "make -n". +.log.trs: + rm -f $< $@ + $(MAKE) $(AM_MAKEFLAGS) $< + +# Leading 'am--fnord' is there to ensure the list of targets does not +# expand to empty, as could happen e.g. with make check TESTS=''. +am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) +am--force-recheck: + @: + +$(TEST_SUITE_LOG): $(TEST_LOGS) + @$(am__set_TESTS_bases); \ + am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ + redo_bases=`for i in $$bases; do \ + am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ + done`; \ + if test -n "$$redo_bases"; then \ + redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ + redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ + if $(am__make_dryrun); then :; else \ + rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ fi; \ - if test "$$failed" -eq 0; then \ - if test "$$xfail" -eq 0; then \ - banner="$$All$$all $$tests passed"; \ - else \ - if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ - banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ - fi; \ - else \ - if test "$$xpass" -eq 0; then \ - banner="$$failed of $$all $$tests failed"; \ + fi; \ + if test -n "$$am__remaking_logs"; then \ + echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ + "recursion detected" >&2; \ + elif test -n "$$redo_logs"; then \ + am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ + fi; \ + if $(am__make_dryrun); then :; else \ + st=0; \ + errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ + for i in $$redo_bases; do \ + test -f $$i.trs && test -r $$i.trs \ + || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ + test -f $$i.log && test -r $$i.log \ + || { echo "$$errmsg $$i.log" >&2; st=1; }; \ + done; \ + test $$st -eq 0 || exit 1; \ + fi + @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ + ws='[ ]'; \ + results=`for b in $$bases; do echo $$b.trs; done`; \ + test -n "$$results" || results=/dev/null; \ + all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ + pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ + fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ + skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ + xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ + xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ + error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ + if test `expr $$fail + $$xpass + $$error` -eq 0; then \ + success=true; \ + else \ + success=false; \ + fi; \ + br='==================='; br=$$br$$br$$br$$br; \ + result_count () \ + { \ + if test x"$$1" = x"--maybe-color"; then \ + maybe_colorize=yes; \ + elif test x"$$1" = x"--no-color"; then \ + maybe_colorize=no; \ else \ - if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ - banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ + echo "$@: invalid 'result_count' usage" >&2; exit 4; \ fi; \ - fi; \ - dashes="$$banner"; \ - skipped=""; \ - if test "$$skip" -ne 0; then \ - if test "$$skip" -eq 1; then \ - skipped="($$skip test was not run)"; \ + shift; \ + desc=$$1 count=$$2; \ + if test $$maybe_colorize = yes && test $$count -gt 0; then \ + color_start=$$3 color_end=$$std; \ else \ - skipped="($$skip tests were not run)"; \ + color_start= color_end=; \ fi; \ - test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ - dashes="$$skipped"; \ - fi; \ - report=""; \ - if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ - report="Please report to $(PACKAGE_BUGREPORT)"; \ - test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ - dashes="$$report"; \ - fi; \ - dashes=`echo "$$dashes" | sed s/./=/g`; \ - if test "$$failed" -eq 0; then \ - col="$$grn"; \ - else \ - col="$$red"; \ - fi; \ - echo "$${col}$$dashes$${std}"; \ - echo "$${col}$$banner$${std}"; \ - test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \ - test -z "$$report" || echo "$${col}$$report$${std}"; \ - echo "$${col}$$dashes$${std}"; \ - test "$$failed" -eq 0; \ - else :; fi + echo "$${color_start}# $$desc $$count$${color_end}"; \ + }; \ + create_testsuite_report () \ + { \ + result_count $$1 "TOTAL:" $$all "$$brg"; \ + result_count $$1 "PASS: " $$pass "$$grn"; \ + result_count $$1 "SKIP: " $$skip "$$blu"; \ + result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ + result_count $$1 "FAIL: " $$fail "$$red"; \ + result_count $$1 "XPASS:" $$xpass "$$red"; \ + result_count $$1 "ERROR:" $$error "$$mgn"; \ + }; \ + { \ + echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ + $(am__rst_title); \ + create_testsuite_report --no-color; \ + echo; \ + echo ".. contents:: :depth: 2"; \ + echo; \ + for b in $$bases; do echo $$b; done \ + | $(am__create_global_log); \ + } >$(TEST_SUITE_LOG).tmp || exit 1; \ + mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ + if $$success; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ + fi; \ + echo "$${col}$$br$${std}"; \ + echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \ + echo "$${col}$$br$${std}"; \ + create_testsuite_report --maybe-color; \ + echo "$$col$$br$$std"; \ + if $$success; then :; else \ + echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ + if test -n "$(PACKAGE_BUGREPORT)"; then \ + echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ + fi; \ + echo "$$col$$br$$std"; \ + fi; \ + $$success || exit 1 + +check-TESTS: + @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list + @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + trs_list=`for i in $$bases; do echo $$i.trs; done`; \ + log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ + exit $$?; +recheck: all + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + bases=`for i in $$bases; do echo $$i; done \ + | $(am__list_recheck_tests)` || exit 1; \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + log_list=`echo $$log_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ + am__force_recheck=am--force-recheck \ + TEST_LOGS="$$log_list"; \ + exit $$? +test-rdisc-fake.log: test-rdisc-fake$(EXEEXT) + @p='test-rdisc-fake$(EXEEXT)'; \ + b='test-rdisc-fake'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +.test.log: + @p='$<'; \ + $(am__set_b); \ + $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +@am__EXEEXT_TRUE@.test$(EXEEXT).log: +@am__EXEEXT_TRUE@ @p='$<'; \ +@am__EXEEXT_TRUE@ $(am__set_b); \ +@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ +@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ +@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ +@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ @@ -789,6 +1043,9 @@ install-strip: "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: + -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) + -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) + -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) clean-generic: @@ -884,7 +1141,7 @@ uninstall-am: installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ - tags tags-am uninstall uninstall-am + recheck tags tags-am uninstall uninstall-am .PRECIOUS: Makefile diff --git a/src/rdisc/tests/test-rdisc-fake.c b/src/rdisc/tests/test-rdisc-fake.c index 8b7bd37a..33672637 100644 --- a/src/rdisc/tests/test-rdisc-fake.c +++ b/src/rdisc/tests/test-rdisc-fake.c @@ -36,7 +36,7 @@ rdisc_new (void) { NMRDisc *rdisc; const int ifindex = 1; - const char *ifname = nm_platform_link_get_name (ifindex); + const char *ifname = nm_platform_link_get_name (NM_PLATFORM_GET, ifindex); rdisc = nm_fake_rdisc_new (ifindex, ifname); g_assert (rdisc); @@ -439,7 +439,7 @@ main (int argc, char **argv) if (nmtst_test_quick ()) { g_print ("Skipping test: don't run long running test %s (NMTST_DEBUG=slow)\n", str_if_set (g_get_prgname (), "test-rdisc-fake")); - return EXIT_SKIP; + return g_test_run (); } nm_fake_platform_setup (); diff --git a/src/rdisc/tests/test-rdisc-linux.c b/src/rdisc/tests/test-rdisc-linux.c index 9b2ccafd..eddb3b0e 100644 --- a/src/rdisc/tests/test-rdisc-linux.c +++ b/src/rdisc/tests/test-rdisc-linux.c @@ -54,7 +54,7 @@ main (int argc, char **argv) if (argv[1]) { ifname = argv[1]; - ifindex = nm_platform_link_get_ifindex (ifname); + ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, ifname); } else { g_print ("Missing command line argument \"interface-name\"\n"); return EXIT_FAILURE; diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c index ec2052f0..0f67a216 100644 --- a/src/settings/nm-settings-connection.c +++ b/src/settings/nm-settings-connection.c @@ -96,6 +96,8 @@ enum { static guint signals[LAST_SIGNAL] = { 0 }; typedef struct { + gboolean removed; + NMAgentManager *agent_mgr; NMSessionMonitor *session_monitor; guint session_changed_id; @@ -1742,6 +1744,12 @@ impl_settings_connection_clear_secrets (NMSettingsConnection *self, void nm_settings_connection_signal_remove (NMSettingsConnection *self) { + NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self); + + if (priv->removed) + g_return_if_reached (); + priv->removed = TRUE; + /* Emit removed first */ g_signal_emit_by_name (self, NM_SETTINGS_CONNECTION_REMOVED); diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c index 9d40b06e..4d7d697d 100644 --- a/src/settings/nm-settings.c +++ b/src/settings/nm-settings.c @@ -94,9 +94,10 @@ EXPORT(nm_settings_connection_replace_settings) EXPORT(nm_settings_connection_replace_and_commit) /* END LINKER CRACKROCK */ +#define PLUGIN_MODULE_PATH "plugin-module-path" + static void claim_connection (NMSettings *self, - NMSettingsConnection *connection, - gboolean do_export); + NMSettingsConnection *connection); static gboolean impl_settings_list_connections (NMSettings *self, GPtrArray **connections, @@ -218,7 +219,7 @@ plugin_connection_added (NMSystemConfigInterface *config, NMSettingsConnection *connection, gpointer user_data) { - claim_connection (NM_SETTINGS (user_data), connection, TRUE); + claim_connection (NM_SETTINGS (user_data), connection); } static void @@ -239,7 +240,7 @@ load_connections (NMSettings *self) // priority plugin. for (elt = plugin_connections; elt; elt = g_slist_next (elt)) - claim_connection (self, NM_SETTINGS_CONNECTION (elt->data), TRUE); + claim_connection (self, NM_SETTINGS_CONNECTION (elt->data)); g_slist_free (plugin_connections); @@ -468,6 +469,21 @@ notify (GObject *object, GParamSpec *pspec) g_slice_free (GValue, value); } +gboolean +nm_settings_has_connection (NMSettings *self, NMConnection *connection) +{ + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + GHashTableIter iter; + gpointer data; + + g_hash_table_iter_init (&iter, priv->connections); + while (g_hash_table_iter_next (&iter, NULL, &data)) + if (data == connection) + return TRUE; + + return FALSE; +} + const GSList * nm_settings_get_unmanaged_specs (NMSettings *self) { @@ -489,7 +505,7 @@ get_plugin (NMSettings *self, guint32 capability) NMSystemConfigInterfaceCapabilities caps = NM_SYSTEM_CONFIG_INTERFACE_CAP_NONE; g_object_get (G_OBJECT (iter->data), NM_SYSTEM_CONFIG_INTERFACE_CAPABILITIES, &caps, NULL); - if (caps & capability) + if (NM_FLAGS_ALL (caps, capability)) return NM_SYSTEM_CONFIG_INTERFACE (iter->data); } @@ -596,6 +612,7 @@ add_plugin (NMSettings *self, NMSystemConfigInterface *plugin) NMSettingsPrivate *priv; char *pname = NULL; char *pinfo = NULL; + const char *path; g_return_if_fail (NM_IS_SETTINGS (self)); g_return_if_fail (NM_IS_SYSTEM_CONFIG_INTERFACE (plugin)); @@ -613,7 +630,10 @@ add_plugin (NMSettings *self, NMSystemConfigInterface *plugin) NM_SYSTEM_CONFIG_INTERFACE_INFO, &pinfo, NULL); - nm_log_info (LOGD_SETTINGS, "Loaded plugin %s: %s", pname, pinfo); + path = g_object_get_data (G_OBJECT (plugin), PLUGIN_MODULE_PATH); + + nm_log_info (LOGD_SETTINGS, "Loaded settings plugin %s: %s%s%s%s", pname, pinfo, + NM_PRINT_FMT_QUOTED (path, " (", path, ")", "")); g_free (pname); g_free (pinfo); } @@ -665,17 +685,13 @@ load_plugins (NMSettings *self, const char **plugins, GError **error) GModule *plugin; gs_free char *full_name = NULL; gs_free char *path = NULL; - gs_free char *pname = NULL; + const char *pname; GObject *obj; GObject * (*factory_func) (void); struct stat st; int errsv; - pname = g_strdup (*iter); - g_strstrip (pname); - - if (!*pname) - continue; + pname = *iter; if (!*pname || strchr (pname, '/')) { LOG (LOGL_WARN, "ignore invalid plugin \"%s\"", pname); @@ -719,7 +735,7 @@ load_plugins (NMSettings *self, const char **plugins, GError **error) plugin = g_module_open (path, G_MODULE_BIND_LOCAL); if (!plugin) { LOG (LOGL_WARN, "Could not load plugin '%s' from file '%s': %s", - pname, full_name, g_module_error ()); + pname, path, g_module_error ()); continue; } @@ -730,6 +746,7 @@ load_plugins (NMSettings *self, const char **plugins, GError **error) "Could not find plugin '%s' factory function.", pname); success = FALSE; + g_module_close (plugin); break; } @@ -739,11 +756,14 @@ load_plugins (NMSettings *self, const char **plugins, GError **error) "Plugin '%s' returned invalid system config object.", pname); success = FALSE; + g_module_close (plugin); break; } g_module_make_resident (plugin); g_object_weak_ref (obj, (GWeakNotify) g_module_close, plugin); + g_object_set_data_full (obj, PLUGIN_MODULE_PATH, path, g_free); + path = NULL; add_plugin (self, NM_SYSTEM_CONFIG_INTERFACE (obj)); list = g_slist_append (list, obj); } @@ -794,6 +814,11 @@ static void connection_removed (NMSettingsConnection *connection, gpointer user_data) { NMSettings *self = NM_SETTINGS (user_data); + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + const char *cpath = nm_connection_get_path (NM_CONNECTION (connection)); + + if (!g_hash_table_lookup (priv->connections, cpath)) + g_return_if_reached (); g_object_ref (connection); @@ -809,8 +834,7 @@ connection_removed (NMSettingsConnection *connection, gpointer user_data) g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_ready_changed), self); /* Forget about the connection internally */ - g_hash_table_remove (NM_SETTINGS_GET_PRIVATE (user_data)->connections, - (gpointer) nm_connection_get_path (NM_CONNECTION (connection))); + g_hash_table_remove (priv->connections, (gpointer) cpath); /* Notify D-Bus */ g_signal_emit (self, signals[CONNECTION_REMOVED], 0, connection); @@ -877,9 +901,7 @@ openconnect_migrate_hack (NMConnection *connection) } static void -claim_connection (NMSettings *self, - NMSettingsConnection *connection, - gboolean do_export) +claim_connection (NMSettings *self, NMSettingsConnection *connection) { NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); static guint32 ec_counter = 0; @@ -1030,7 +1052,7 @@ nm_settings_add_connection (NMSettings *self, added = nm_system_config_interface_add_connection (plugin, connection, save_to_disk, &add_error); if (added) { - claim_connection (self, added, TRUE); + claim_connection (self, added); return added; } nm_log_dbg (LOGD_SETTINGS, "Failed to add %s/'%s': %s", @@ -1576,6 +1598,9 @@ have_connection_for_device (NMSettings *self, NMDevice *device) NMConnection *connection = NM_CONNECTION (data); const char *ctype, *iface; + if (!nm_device_check_connection_compatible (device, connection)) + continue; + s_con = nm_connection_get_setting_connection (connection); iface = nm_setting_connection_get_interface_name (s_con); @@ -1671,7 +1696,7 @@ default_wired_clear_tag (NMSettings *self, g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (default_wired_connection_updated_by_user_cb), self); if (add_to_no_auto_default) - nm_config_set_ethernet_no_auto_default (NM_SETTINGS_GET_PRIVATE (self)->config, device); + nm_config_set_no_auto_default_for_device (NM_SETTINGS_GET_PRIVATE (self)->config, device); } void diff --git a/src/settings/nm-settings.h b/src/settings/nm-settings.h index 9c0a4c83..d196efe6 100644 --- a/src/settings/nm-settings.h +++ b/src/settings/nm-settings.h @@ -111,6 +111,8 @@ NMSettingsConnection *nm_settings_get_connection_by_path (NMSettings *settings, NMSettingsConnection *nm_settings_get_connection_by_uuid (NMSettings *settings, const char *uuid); +gboolean nm_settings_has_connection (NMSettings *self, NMConnection *connection); + const GSList *nm_settings_get_unmanaged_specs (NMSettings *self); char *nm_settings_get_hostname (NMSettings *self); diff --git a/src/settings/plugins/Makefile.in b/src/settings/plugins/Makefile.in index ea45e2a5..d7b7ab14 100644 --- a/src/settings/plugins/Makefile.in +++ b/src/settings/plugins/Makefile.in @@ -200,6 +200,7 @@ ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -313,6 +314,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ +LOG_DRIVER = @LOG_DRIVER@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ diff --git a/src/settings/plugins/example/Makefile.in b/src/settings/plugins/example/Makefile.in index dabbdc74..3b7a1551 100644 --- a/src/settings/plugins/example/Makefile.in +++ b/src/settings/plugins/example/Makefile.in @@ -191,6 +191,7 @@ ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -304,6 +305,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ +LOG_DRIVER = @LOG_DRIVER@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ diff --git a/src/settings/plugins/example/plugin.c b/src/settings/plugins/example/plugin.c index 9945e13b..0bcb38fc 100644 --- a/src/settings/plugins/example/plugin.c +++ b/src/settings/plugins/example/plugin.c @@ -847,7 +847,7 @@ nm_system_config_factory (void) priv = SC_PLUGIN_EXAMPLE_GET_PRIVATE (singleton); /* Cache the config file path */ - priv->conf_file = nm_config_get_path (nm_config_get ()); + priv->conf_file = nm_config_data_get_config_main_file (nm_config_get_data (nm_config_get ())); } else { /* This function should never be called twice */ g_assert_not_reached (); diff --git a/src/settings/plugins/ibft/Makefile.in b/src/settings/plugins/ibft/Makefile.in index 405c213b..de9be286 100644 --- a/src/settings/plugins/ibft/Makefile.in +++ b/src/settings/plugins/ibft/Makefile.in @@ -266,6 +266,7 @@ ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -379,6 +380,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ +LOG_DRIVER = @LOG_DRIVER@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ diff --git a/src/settings/plugins/ibft/reader.c b/src/settings/plugins/ibft/reader.c index e89d9b08..9bc63462 100644 --- a/src/settings/plugins/ibft/reader.c +++ b/src/settings/plugins/ibft/reader.c @@ -36,7 +36,6 @@ #include "nm-core-internal.h" #include "nm-platform.h" -#include "nm-posix-signals.h" #include "NetworkManagerUtils.h" #include "nm-logging.h" @@ -44,22 +43,6 @@ #define PARSE_WARNING(msg...) nm_log_warn (LOGD_SETTINGS, " " msg) -static void -iscsiadm_child_setup (gpointer user_data G_GNUC_UNUSED) -{ - /* We are in the child process here; set a different process group to - * ensure signal isolation between child and parent. - */ - pid_t pid = getpid (); - setpgid (pid, pid); - - /* - * We blocked signals in main(). We need to restore original signal - * mask for iscsiadm here so that it can receive signals. - */ - nm_unblock_posix_signals (NULL); -} - /* Removes trailing whitespace and whitespace before and immediately after the '=' */ static char * remove_most_whitespace (const char *src) @@ -126,7 +109,7 @@ read_ibft_blocks (const char *iscsiadm_path, g_return_val_if_fail (out_blocks != NULL && *out_blocks == NULL, FALSE); if (!g_spawn_sync ("/", (char **) argv, (char **) envp, 0, - iscsiadm_child_setup, NULL, &out, &err, &status, error)) + NULL, NULL, &out, &err, &status, error)) goto done; if (!WIFEXITED (status)) { @@ -136,6 +119,15 @@ read_ibft_blocks (const char *iscsiadm_path, } 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 ? err : "(none)"); @@ -405,13 +397,13 @@ connection_setting_add (const GPtrArray *block, prefix ? prefix : "", iface); - uuid = nm_utils_uuid_generate_from_strings ("ibft", - s_hwaddr, - s_vlanid ? "V" : "v", - s_vlanid ? s_vlanid : "", - s_ip4addr ? "A" : "DHCP", - s_ip4addr ? s_ip4addr : "", - NULL); + uuid = _nm_utils_uuid_generate_from_strings ("ibft", + s_hwaddr, + s_vlanid ? "V" : "v", + s_vlanid ? s_vlanid : "", + s_ip4addr ? "A" : "DHCP", + s_ip4addr ? s_ip4addr : "", + NULL); s_con = nm_setting_connection_new (); g_object_set (s_con, @@ -439,7 +431,7 @@ is_ibft_vlan_device (const GPtrArray *block) /* 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) + if (_nm_utils_ascii_str_to_int64 (s_vlan_id, 10, 1, 4095, -1) != -1) return TRUE; } return FALSE; @@ -466,7 +458,7 @@ vlan_setting_add_from_block (const GPtrArray *block, 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); + 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); diff --git a/src/settings/plugins/ibft/tests/Makefile.in b/src/settings/plugins/ibft/tests/Makefile.in index d682318a..266497b6 100644 --- a/src/settings/plugins/ibft/tests/Makefile.in +++ b/src/settings/plugins/ibft/tests/Makefile.in @@ -205,13 +205,196 @@ am__tty_colors = { \ std='[m'; \ fi; \ } +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__recheck_rx = ^[ ]*:recheck:[ ]* +am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* +am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* +# A command that, given a newline-separated list of test names on the +# standard input, print the name of the tests that are to be re-run +# upon "make recheck". +am__list_recheck_tests = $(AWK) '{ \ + recheck = 1; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + { \ + if ((getline line2 < ($$0 ".log")) < 0) \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ + { \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ + { \ + break; \ + } \ + }; \ + if (recheck) \ + print $$0; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# A command that, given a newline-separated list of test names on the +# standard input, create the global log from their .trs and .log files. +am__create_global_log = $(AWK) ' \ +function fatal(msg) \ +{ \ + print "fatal: making $@: " msg | "cat >&2"; \ + exit 1; \ +} \ +function rst_section(header) \ +{ \ + print header; \ + len = length(header); \ + for (i = 1; i <= len; i = i + 1) \ + printf "="; \ + printf "\n\n"; \ +} \ +{ \ + copy_in_global_log = 1; \ + global_test_result = "RUN"; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".trs"); \ + if (line ~ /$(am__global_test_result_rx)/) \ + { \ + sub("$(am__global_test_result_rx)", "", line); \ + sub("[ ]*$$", "", line); \ + global_test_result = line; \ + } \ + else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ + copy_in_global_log = 0; \ + }; \ + if (copy_in_global_log) \ + { \ + rst_section(global_test_result ": " $$0); \ + while ((rc = (getline line < ($$0 ".log"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".log"); \ + print line; \ + }; \ + printf "\n"; \ + }; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# Restructured Text title. +am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } +# Solaris 10 'make', and several other traditional 'make' implementations, +# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it +# by disabling -e (using the XSI extension "set +e") if it's set. +am__sh_e_setup = case $$- in *e*) set +e;; esac +# Default flags passed to test drivers. +am__common_driver_flags = \ + --color-tests "$$am__color_tests" \ + --enable-hard-errors "$$am__enable_hard_errors" \ + --expect-failure "$$am__expect_failure" +# To be inserted before the command running the test. Creates the +# directory for the log if needed. Stores in $dir the directory +# containing $f, in $tst the test, in $log the log. Executes the +# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and +# passes TESTS_ENVIRONMENT. Set up options for the wrapper that +# will run the test scripts (or their associated LOG_COMPILER, if +# thy have one). +am__check_pre = \ +$(am__sh_e_setup); \ +$(am__vpath_adj_setup) $(am__vpath_adj) \ +$(am__tty_colors); \ +srcdir=$(srcdir); export srcdir; \ +case "$@" in \ + */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ + *) am__odir=.;; \ +esac; \ +test "x$$am__odir" = x"." || test -d "$$am__odir" \ + || $(MKDIR_P) "$$am__odir" || exit $$?; \ +if test -f "./$$f"; then dir=./; \ +elif test -f "$$f"; then dir=; \ +else dir="$(srcdir)/"; fi; \ +tst=$$dir$$f; log='$@'; \ +if test -n '$(DISABLE_HARD_ERRORS)'; then \ + am__enable_hard_errors=no; \ +else \ + am__enable_hard_errors=yes; \ +fi; \ +case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ + am__expect_failure=yes;; \ + *) \ + am__expect_failure=no;; \ +esac; \ +$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) +# A shell command to get the names of the tests scripts with any registered +# extension removed (i.e., equivalently, the names of the test logs, with +# the '.log' extension removed). The result is saved in the shell variable +# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, +# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", +# since that might cause problem with VPATH rewrites for suffix-less tests. +# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. +am__set_TESTS_bases = \ + bases='$(TEST_LOGS)'; \ + bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ + bases=`echo $$bases` +RECHECK_LOGS = $(TEST_LOGS) +AM_RECURSIVE_TARGETS = check recheck +TEST_SUITE_LOG = test-suite.log +TEST_EXTENSIONS = @EXEEXT@ .test +LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) +am__set_b = \ + case '$@' in \ + */*) \ + case '$*' in \ + */*) b='$*';; \ + *) b=`echo '$@' | sed 's/\.log$$//'`; \ + esac;; \ + *) \ + b='$*';; \ + esac +am__test_logs1 = $(TESTS:=.log) +am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) +TEST_LOGS = $(am__test_logs2:.test.log=.log) +TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/build-aux/test-driver +TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ + $(TEST_LOG_FLAGS) am__DIST_COMMON = $(srcdir)/Makefile.in \ - $(top_srcdir)/build-aux/depcomp + $(top_srcdir)/build-aux/depcomp \ + $(top_srcdir)/build-aux/test-driver DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -325,6 +508,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ +LOG_DRIVER = @LOG_DRIVER@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ @@ -510,7 +694,7 @@ EXTRA_DIST = \ all: all-am .SUFFIXES: -.SUFFIXES: .c .lo .o .obj +.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ @@ -656,98 +840,168 @@ cscopelist-am: $(am__tagged_files) distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -check-TESTS: $(TESTS) - @failed=0; all=0; xfail=0; xpass=0; skip=0; \ - srcdir=$(srcdir); export srcdir; \ - list=' $(TESTS) '; \ - $(am__tty_colors); \ - if test -n "$$list"; then \ - for tst in $$list; do \ - if test -f ./$$tst; then dir=./; \ - elif test -f $$tst; then dir=; \ - else dir="$(srcdir)/"; fi; \ - if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \ - all=`expr $$all + 1`; \ - case " $(XFAIL_TESTS) " in \ - *[\ \ ]$$tst[\ \ ]*) \ - xpass=`expr $$xpass + 1`; \ - failed=`expr $$failed + 1`; \ - col=$$red; res=XPASS; \ - ;; \ - *) \ - col=$$grn; res=PASS; \ - ;; \ - esac; \ - elif test $$? -ne 77; then \ - all=`expr $$all + 1`; \ - case " $(XFAIL_TESTS) " in \ - *[\ \ ]$$tst[\ \ ]*) \ - xfail=`expr $$xfail + 1`; \ - col=$$lgn; res=XFAIL; \ - ;; \ - *) \ - failed=`expr $$failed + 1`; \ - col=$$red; res=FAIL; \ - ;; \ - esac; \ - else \ - skip=`expr $$skip + 1`; \ - col=$$blu; res=SKIP; \ - fi; \ - echo "$${col}$$res$${std}: $$tst"; \ - done; \ - if test "$$all" -eq 1; then \ - tests="test"; \ - All=""; \ - else \ - tests="tests"; \ - All="All "; \ +# Recover from deleted '.trs' file; this should ensure that +# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create +# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells +# to avoid problems with "make -n". +.log.trs: + rm -f $< $@ + $(MAKE) $(AM_MAKEFLAGS) $< + +# Leading 'am--fnord' is there to ensure the list of targets does not +# expand to empty, as could happen e.g. with make check TESTS=''. +am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) +am--force-recheck: + @: + +$(TEST_SUITE_LOG): $(TEST_LOGS) + @$(am__set_TESTS_bases); \ + am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ + redo_bases=`for i in $$bases; do \ + am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ + done`; \ + if test -n "$$redo_bases"; then \ + redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ + redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ + if $(am__make_dryrun); then :; else \ + rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ fi; \ - if test "$$failed" -eq 0; then \ - if test "$$xfail" -eq 0; then \ - banner="$$All$$all $$tests passed"; \ - else \ - if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ - banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ - fi; \ - else \ - if test "$$xpass" -eq 0; then \ - banner="$$failed of $$all $$tests failed"; \ + fi; \ + if test -n "$$am__remaking_logs"; then \ + echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ + "recursion detected" >&2; \ + elif test -n "$$redo_logs"; then \ + am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ + fi; \ + if $(am__make_dryrun); then :; else \ + st=0; \ + errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ + for i in $$redo_bases; do \ + test -f $$i.trs && test -r $$i.trs \ + || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ + test -f $$i.log && test -r $$i.log \ + || { echo "$$errmsg $$i.log" >&2; st=1; }; \ + done; \ + test $$st -eq 0 || exit 1; \ + fi + @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ + ws='[ ]'; \ + results=`for b in $$bases; do echo $$b.trs; done`; \ + test -n "$$results" || results=/dev/null; \ + all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ + pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ + fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ + skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ + xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ + xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ + error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ + if test `expr $$fail + $$xpass + $$error` -eq 0; then \ + success=true; \ + else \ + success=false; \ + fi; \ + br='==================='; br=$$br$$br$$br$$br; \ + result_count () \ + { \ + if test x"$$1" = x"--maybe-color"; then \ + maybe_colorize=yes; \ + elif test x"$$1" = x"--no-color"; then \ + maybe_colorize=no; \ else \ - if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ - banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ + echo "$@: invalid 'result_count' usage" >&2; exit 4; \ fi; \ - fi; \ - dashes="$$banner"; \ - skipped=""; \ - if test "$$skip" -ne 0; then \ - if test "$$skip" -eq 1; then \ - skipped="($$skip test was not run)"; \ + shift; \ + desc=$$1 count=$$2; \ + if test $$maybe_colorize = yes && test $$count -gt 0; then \ + color_start=$$3 color_end=$$std; \ else \ - skipped="($$skip tests were not run)"; \ + color_start= color_end=; \ fi; \ - test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ - dashes="$$skipped"; \ - fi; \ - report=""; \ - if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ - report="Please report to $(PACKAGE_BUGREPORT)"; \ - test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ - dashes="$$report"; \ - fi; \ - dashes=`echo "$$dashes" | sed s/./=/g`; \ - if test "$$failed" -eq 0; then \ - col="$$grn"; \ - else \ - col="$$red"; \ - fi; \ - echo "$${col}$$dashes$${std}"; \ - echo "$${col}$$banner$${std}"; \ - test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \ - test -z "$$report" || echo "$${col}$$report$${std}"; \ - echo "$${col}$$dashes$${std}"; \ - test "$$failed" -eq 0; \ - else :; fi + echo "$${color_start}# $$desc $$count$${color_end}"; \ + }; \ + create_testsuite_report () \ + { \ + result_count $$1 "TOTAL:" $$all "$$brg"; \ + result_count $$1 "PASS: " $$pass "$$grn"; \ + result_count $$1 "SKIP: " $$skip "$$blu"; \ + result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ + result_count $$1 "FAIL: " $$fail "$$red"; \ + result_count $$1 "XPASS:" $$xpass "$$red"; \ + result_count $$1 "ERROR:" $$error "$$mgn"; \ + }; \ + { \ + echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ + $(am__rst_title); \ + create_testsuite_report --no-color; \ + echo; \ + echo ".. contents:: :depth: 2"; \ + echo; \ + for b in $$bases; do echo $$b; done \ + | $(am__create_global_log); \ + } >$(TEST_SUITE_LOG).tmp || exit 1; \ + mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ + if $$success; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ + fi; \ + echo "$${col}$$br$${std}"; \ + echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \ + echo "$${col}$$br$${std}"; \ + create_testsuite_report --maybe-color; \ + echo "$$col$$br$$std"; \ + if $$success; then :; else \ + echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ + if test -n "$(PACKAGE_BUGREPORT)"; then \ + echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ + fi; \ + echo "$$col$$br$$std"; \ + fi; \ + $$success || exit 1 + +check-TESTS: + @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list + @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + trs_list=`for i in $$bases; do echo $$i.trs; done`; \ + log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ + exit $$?; +recheck: all + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + bases=`for i in $$bases; do echo $$i; done \ + | $(am__list_recheck_tests)` || exit 1; \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + log_list=`echo $$log_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ + am__force_recheck=am--force-recheck \ + TEST_LOGS="$$log_list"; \ + exit $$? +test-ibft.log: test-ibft$(EXEEXT) + @p='test-ibft$(EXEEXT)'; \ + b='test-ibft'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +.test.log: + @p='$<'; \ + $(am__set_b); \ + $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +@am__EXEEXT_TRUE@.test$(EXEEXT).log: +@am__EXEEXT_TRUE@ @p='$<'; \ +@am__EXEEXT_TRUE@ $(am__set_b); \ +@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ +@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ +@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ +@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ @@ -804,6 +1058,9 @@ install-strip: "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: + -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) + -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) + -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) clean-generic: @@ -899,7 +1156,7 @@ uninstall-am: installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ - tags tags-am uninstall uninstall-am + recheck tags tags-am uninstall uninstall-am .PRECIOUS: Makefile diff --git a/src/settings/plugins/ifcfg-rh/Makefile.in b/src/settings/plugins/ifcfg-rh/Makefile.in index 89511983..0cd26960 100644 --- a/src/settings/plugins/ifcfg-rh/Makefile.in +++ b/src/settings/plugins/ifcfg-rh/Makefile.in @@ -269,6 +269,7 @@ ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -382,6 +383,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ +LOG_DRIVER = @LOG_DRIVER@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ diff --git a/src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.c b/src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.c index 94d22258..a18920c6 100644 --- a/src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.c +++ b/src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.c @@ -96,7 +96,7 @@ devtimeout_ready (gpointer user_data) } static void -link_changed (NMPlatform *platform, int ifindex, NMPlatformLink *link, +link_changed (NMPlatform *platform, NMPObjectType *obj_type, int ifindex, const NMPlatformLink *link, NMPlatformSignalChangeType change_type, NMPlatformReason reason, NMConnection *self) { @@ -164,7 +164,7 @@ nm_ifcfg_connection_check_devtimeout (NMIfcfgConnection *self) if (!devtimeout) return; - if (nm_platform_link_get_ifindex (ifname) != 0) + if (nm_platform_link_get_ifindex (NM_PLATFORM_GET, ifname) != 0) return; /* ONBOOT=yes, DEVICE and DEVTIMEOUT are set, but device is not present */ @@ -201,7 +201,8 @@ files_changed_cb (NMInotifyHelper *ih, NMIfcfgConnection * nm_ifcfg_connection_new (NMConnection *source, const char *full_path, - GError **error) + GError **error, + gboolean *out_ignore_error) { GObject *object; NMConnection *tmp; @@ -211,13 +212,17 @@ nm_ifcfg_connection_new (NMConnection *source, 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); + error, + out_ignore_error); if (!tmp) return NULL; @@ -376,7 +381,7 @@ commit_changes (NMSettingsConnection *connection, */ filename = nm_settings_connection_get_filename (connection); if (filename) { - reread = connection_from_file (filename, NULL, NULL); + reread = connection_from_file (filename, NULL, NULL, NULL); if (reread) { same = nm_connection_compare (NM_CONNECTION (connection), reread, diff --git a/src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.h b/src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.h index 328e58f5..44e02987 100644 --- a/src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.h +++ b/src/settings/plugins/ifcfg-rh/nm-ifcfg-connection.h @@ -48,7 +48,8 @@ GType nm_ifcfg_connection_get_type (void); NMIfcfgConnection *nm_ifcfg_connection_new (NMConnection *source, const char *full_path, - GError **error); + 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); diff --git a/src/settings/plugins/ifcfg-rh/plugin.c b/src/settings/plugins/ifcfg-rh/plugin.c index b7efa864..1a3f791a 100644 --- a/src/settings/plugins/ifcfg-rh/plugin.c +++ b/src/settings/plugins/ifcfg-rh/plugin.c @@ -169,7 +169,8 @@ remove_connection (SCPluginIfcfg *self, NMIfcfgConnection *connection) g_object_ref (connection); g_hash_table_remove (priv->connections, nm_connection_get_uuid (NM_CONNECTION (connection))); - nm_settings_connection_signal_remove (NM_SETTINGS_CONNECTION (connection)); + if (!unmanaged && !unrecognized) + nm_settings_connection_signal_remove (NM_SETTINGS_CONNECTION (connection)); g_object_unref (connection); /* Emit changes _after_ removing the connection */ @@ -213,6 +214,7 @@ update_connection (SCPluginIfcfg *self, 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); @@ -222,13 +224,16 @@ update_connection (SCPluginIfcfg *self, /* Create a NMIfcfgConnection instance, either by reading from @full_path or * based on @source. */ - connection_new = nm_ifcfg_connection_new (source, full_path, error); + 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 && !ignore_error) + _LOGW ("loading \"%s\" fails: %s", full_path, local ? local->message : "(unknown reason)"); + g_propagate_error (error, local); return NULL; } @@ -421,25 +426,13 @@ ifcfg_dir_changed (GFileMonitor *monitor, gpointer user_data) { SCPluginIfcfg *plugin = SC_PLUGIN_IFCFG (user_data); - char *path, *base, *ifcfg_path; + char *path, *ifcfg_path; NMIfcfgConnection *connection; path = g_file_get_path (file); - if (utils_should_ignore_file (path, FALSE)) { - g_free (path); - return; - } - - _LOGD ("ifcfg_dir_changed(%s) = %d", path, event_type); - base = g_file_get_basename (file); - if (utils_is_ifcfg_alias_file (base, NULL)) { - /* Alias file changed. Get the base ifcfg file from it */ - ifcfg_path = utils_get_ifcfg_from_alias (path); - } else { - /* Given any ifcfg, keys, or routes file, get the ifcfg file path */ - ifcfg_path = utils_get_ifcfg_path (path); - } + ifcfg_path = utils_detect_ifcfg_path (path, FALSE); + _LOGD ("ifcfg_dir_changed(%s) = %d // %s", path, event_type, ifcfg_path ? ifcfg_path : "(none)"); if (ifcfg_path) { connection = find_by_path (plugin, ifcfg_path); switch (event_type) { @@ -458,7 +451,6 @@ ifcfg_dir_changed (GFileMonitor *monitor, g_free (ifcfg_path); } g_free (path); - g_free (base); } static void @@ -542,18 +534,14 @@ read_connections (SCPluginIfcfg *plugin) filenames = g_ptr_array_new_with_free_func (g_free); while ((item = g_dir_read_name (dir))) { - char *full_path; - - if (utils_should_ignore_file (item, TRUE)) - continue; - if (utils_is_ifcfg_alias_file (item, NULL)) - continue; + char *full_path, *real_path; full_path = g_build_filename (IFCFG_DIR, item, NULL); - if (!utils_get_ifcfg_name (full_path, TRUE)) - g_free (full_path); - else - g_ptr_array_add (filenames, full_path); + real_path = utils_detect_ifcfg_path (full_path, TRUE); + + if (real_path) + g_ptr_array_add (filenames, real_path); + g_free (full_path); } g_dir_close (dir); @@ -625,20 +613,25 @@ load_connection (NMSystemConfigInterface *config, SCPluginIfcfg *plugin = SC_PLUGIN_IFCFG (config); NMIfcfgConnection *connection; int dir_len = strlen (IFCFG_DIR); + char *ifcfg_path; if ( strncmp (filename, IFCFG_DIR, dir_len) != 0 || filename[dir_len] != '/' || strchr (filename + dir_len + 1, '/') != NULL) return FALSE; - if (utils_should_ignore_file (filename + dir_len + 1, TRUE)) + /* 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; - connection = find_by_path (plugin, filename); - update_connection (plugin, NULL, filename, connection, TRUE, NULL, NULL); + connection = find_by_path (plugin, ifcfg_path); + update_connection (plugin, NULL, ifcfg_path, connection, TRUE, NULL, NULL); if (!connection) - connection = find_by_path (plugin, filename); + connection = find_by_path (plugin, ifcfg_path); + g_free (ifcfg_path); return (connection != NULL); } @@ -1139,7 +1132,7 @@ nm_system_config_factory (void) dbus_g_connection_register_g_object (priv->bus, DBUS_OBJECT_PATH, G_OBJECT (singleton)); - _LOGI ("Acquired D-Bus service %s", DBUS_SERVICE_NAME); + _LOGD ("Acquired D-Bus service %s", DBUS_SERVICE_NAME); } else g_object_ref (singleton); diff --git a/src/settings/plugins/ifcfg-rh/reader.c b/src/settings/plugins/ifcfg-rh/reader.c index 41439f61..ec874167 100644 --- a/src/settings/plugins/ifcfg-rh/reader.c +++ b/src/settings/plugins/ifcfg-rh/reader.c @@ -53,7 +53,6 @@ #include <nm-utils.h> #include "nm-platform.h" -#include "nm-posix-signals.h" #include "NetworkManagerUtils.h" #include "nm-logging.h" #include "gsystem-local-alloc.h" @@ -178,6 +177,8 @@ make_connection_setting (const char *file, NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_MIN, NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_MAX, NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_DEFAULT), + NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES, + svTrueValue (ifcfg, "AUTOCONNECT_SLAVES", NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_DEFAULT), NULL); value = svGetValue (ifcfg, "USERS", FALSE); @@ -507,7 +508,7 @@ read_one_ip4_route (shvarFile *ifcfg, /* Metric */ value = svGetValue (ifcfg, metric_tag, FALSE); if (value) { - metric = nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXUINT32, -1); + metric = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXUINT32, -1); if (metric < 0) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "Invalid IP4 route metric '%s'", value); @@ -1153,16 +1154,14 @@ read_aliases (NMSettingIPConfig *s_ip4, const char *filename, const char *networ GDir *dir; char *dirname, *base; shvarFile *parsed; - NMIPAddress *base_addr; + NMIPAddress *base_addr = NULL; GError *err = NULL; g_return_if_fail (s_ip4 != NULL); g_return_if_fail (filename != NULL); - if (nm_setting_ip_config_get_num_addresses (s_ip4) == 0) - return; - - base_addr = nm_setting_ip_config_get_address (s_ip4, 0); + if (nm_setting_ip_config_get_num_addresses (s_ip4) > 0) + base_addr = nm_setting_ip_config_get_address (s_ip4, 0); dirname = g_path_get_dirname (filename); g_return_if_fail (dirname != NULL); @@ -3351,7 +3350,7 @@ make_wireless_setting (shvarFile *ifcfg, value = svGetValue (ifcfg, "CHANNEL", FALSE); if (value) { errno = 0; - chan = nm_utils_ascii_str_to_int64 (value, 10, 1, 196, 0); + chan = _nm_utils_ascii_str_to_int64 (value, 10, 1, 196, 0); if (errno || (chan == 0)) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "Invalid wireless channel '%s'", value); @@ -4335,11 +4334,11 @@ is_wifi_device (const char *name, shvarFile *parsed) g_return_val_if_fail (name != NULL, FALSE); g_return_val_if_fail (parsed != NULL, FALSE); - ifindex = nm_platform_link_get_ifindex (name); + ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, name); if (ifindex == 0) return FALSE; - return nm_platform_link_get_type (ifindex) == NM_LINK_TYPE_WIFI; + return nm_platform_link_get_type (NM_PLATFORM_GET, ifindex) == NM_LINK_TYPE_WIFI; } static void @@ -4433,13 +4432,10 @@ make_vlan_setting (shvarFile *ifcfg, /* Grab VLAN ID from interface name; this takes precedence over the * separate VLAN_ID property for backwards compat. */ - vlan_id = (gint) g_ascii_strtoll (p, &end, 10); - if (vlan_id < 0 || vlan_id > 4095 || end == p || *end) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Failed to determine VLAN ID from DEVICE '%s'", - iface_name); - goto error; - } + + gint device_vlan_id = (gint) g_ascii_strtoll (p, &end, 10); + if (device_vlan_id >= 0 && device_vlan_id <= 4095 && end != p && !*end) + vlan_id = device_vlan_id; } } @@ -4679,7 +4675,8 @@ connection_from_file_full (const char *filename, connection = create_unhandled_connection (filename, parsed, "unmanaged", out_unhandled); if (!connection) - PARSE_WARNING ("NM_CONTROLLED was false but device was not uniquely identified; device will be managed"); + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, + "NM_CONTROLLED was false but device was not uniquely identified; device will be managed"); goto done; } @@ -4841,18 +4838,13 @@ done: NMConnection * connection_from_file (const char *filename, char **out_unhandled, - GError **error) + GError **error, + gboolean *out_ignore_error) { - gboolean ignore_error = FALSE; - NMConnection *conn; - - conn = connection_from_file_full (filename, NULL, NULL, + return connection_from_file_full (filename, NULL, NULL, out_unhandled, error, - &ignore_error); - if (error && *error && !ignore_error) - PARSE_WARNING ("%s", (*error)->message); - return conn; + out_ignore_error); } NMConnection * @@ -4885,7 +4877,7 @@ devtimeout_from_file (const char *filename) devtimeout_str = svGetValue (ifcfg, "DEVTIMEOUT", FALSE); if (devtimeout_str) { - devtimeout = nm_utils_ascii_str_to_int64 (devtimeout_str, 10, 0, G_MAXUINT, 0); + devtimeout = _nm_utils_ascii_str_to_int64 (devtimeout_str, 10, 0, G_MAXUINT, 0); g_free (devtimeout_str); } else devtimeout = 0; diff --git a/src/settings/plugins/ifcfg-rh/reader.h b/src/settings/plugins/ifcfg-rh/reader.h index 70e9ce4e..2096ffc4 100644 --- a/src/settings/plugins/ifcfg-rh/reader.h +++ b/src/settings/plugins/ifcfg-rh/reader.h @@ -28,7 +28,8 @@ NMConnection *connection_from_file (const char *filename, char **out_unhandled, - GError **error); + GError **error, + gboolean *out_ignore_error); char *uuid_from_file (const char *filename); diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c index 5f3d173b..4a5ca1d1 100644 --- a/src/settings/plugins/ifcfg-rh/shvar.c +++ b/src/settings/plugins/ifcfg-rh/shvar.c @@ -36,7 +36,8 @@ #include "shvar.h" -#include "NetworkManagerUtils.h" +#include "gsystem-local-alloc.h" +#include "nm-core-internal.h" #include "nm-logging.h" #define PARSE_WARNING(msg...) nm_log_warn (LOGD_SETTINGS, " " msg) @@ -206,8 +207,8 @@ static const char escapees[] = "\"'\\$~`"; /* must be escaped */ static const char spaces[] = " \t|&;()<>"; /* only require "" */ static const char newlines[] = "\n\r"; /* will be removed */ -char * -svEscape (const char *s) +const char * +svEscape (const char *s, char **to_free) { char *new; int i, j, mangle = 0, space = 0, newline = 0; @@ -223,8 +224,10 @@ svEscape (const char *s) if (strchr (newlines, s[i])) newline++; } - if (!mangle && !space && !newline) - return strdup (s); + if (!mangle && !space && !newline) { + *to_free = NULL; + return s; + } newlen = slen + mangle - newline + 3; /* 3 is extra ""\0 */ new = g_malloc (newlen); @@ -243,6 +246,7 @@ svEscape (const char *s) new[j++] = '\0'; g_assert (j == slen + mangle - newline + 3); + *to_free = new; return new; } @@ -253,6 +257,22 @@ svEscape (const char *s) char * svGetValue (shvarFile *s, const char *key, gboolean verbatim) { + char *value; + + value = svGetValueFull (s, key, verbatim); + if (value && !*value) { + g_free (value); + return NULL; + } + return value; +} + +/* svGetValueFull() is identical to svGetValue() except that + * svGetValue() will never return an empty value (but %NULL instead). + * svGetValueFull() will return empty values if that is the value for the @key. */ +char * +svGetValueFull (shvarFile *s, const char *key, gboolean verbatim) +{ char *value = NULL; char *line; char *keyString; @@ -276,12 +296,7 @@ svGetValue (shvarFile *s, const char *key, gboolean verbatim) } g_free (keyString); - if (value && value[0]) { - return value; - } else { - g_free (value); - return NULL; - } + return value; } /* return TRUE if <key> resolves to any truth value (e.g. "yes", "y", "true") @@ -321,7 +336,7 @@ svTrueValue (shvarFile *s, const char *key, gboolean def) * @max: the maximum for range-check * @fallback: the fallback value in any error case * - * Reads a value @key and converts it to an integer using nm_utils_ascii_str_to_int64(). + * Reads a value @key and converts it to an integer using _nm_utils_ascii_str_to_int64(). * In case of error, @errno will be set and @fallback returned. */ gint64 svGetValueInt64 (shvarFile *s, const char *key, guint base, gint64 min, gint64 max, gint64 fallback) @@ -330,13 +345,13 @@ svGetValueInt64 (shvarFile *s, const char *key, guint base, gint64 min, gint64 m gint64 result; int errsv; - tmp = svGetValue (s, key, FALSE); + tmp = svGetValueFull (s, key, FALSE); if (!tmp) { errno = 0; return fallback; } - result = nm_utils_ascii_str_to_int64 (tmp, base, min, max, fallback); + result = _nm_utils_ascii_str_to_int64 (tmp, base, min, max, fallback); errsv = errno; if (errsv != 0) PARSE_WARNING ("Error reading '%s' value '%s' as integer (%d)", key, tmp, errsv); @@ -354,20 +369,30 @@ svGetValueInt64 (shvarFile *s, const char *key, guint base, gint64 min, gint64 m void svSetValue (shvarFile *s, const char *key, const char *value, gboolean verbatim) { - char *newval = NULL, *oldval = NULL; + svSetValueFull (s, key, value && value[0] ? value : NULL, verbatim); +} + +/* Same as svSetValue() but it preserves empty @value -- contrary to + * svSetValue() for which "" effectively means to remove the value. */ +void +svSetValueFull (shvarFile *s, const char *key, const char *value, gboolean verbatim) +{ + gs_free char *newval_free = NULL; + gs_free char *oldval = NULL; + const char *newval; char *keyValue; g_return_if_fail (s != NULL); g_return_if_fail (key != NULL); /* value may be NULL */ - if (value) - newval = verbatim ? g_strdup (value) : svEscape (value); - keyValue = g_strdup_printf ("%s=%s", key, newval ? newval : ""); - - oldval = svGetValue (s, key, FALSE); + if (!value || verbatim) + newval = value; + else + newval = svEscape (value, &newval_free); + oldval = svGetValueFull (s, key, FALSE); - if (!newval || !newval[0]) { + if (!newval) { /* delete value */ if (oldval) { /* delete line */ @@ -376,15 +401,15 @@ svSetValue (shvarFile *s, const char *key, const char *value, gboolean verbatim) g_list_free_1 (s->current); s->modified = TRUE; } - g_free (keyValue); - goto end; + return; } + keyValue = g_strdup_printf ("%s=%s", key, newval); if (!oldval) { /* append line */ s->lineList = g_list_append (s->lineList, keyValue); s->modified = TRUE; - goto end; + return; } if (strcmp (oldval, newval) != 0) { @@ -397,11 +422,6 @@ svSetValue (shvarFile *s, const char *key, const char *value, gboolean verbatim) s->modified = TRUE; } else g_free (keyValue); - - end: - g_free (newval); - g_free (oldval); - return; } /* Write the current contents iff modified. Returns FALSE on error diff --git a/src/settings/plugins/ifcfg-rh/shvar.h b/src/settings/plugins/ifcfg-rh/shvar.h index b2a2f263..4902541b 100644 --- a/src/settings/plugins/ifcfg-rh/shvar.h +++ b/src/settings/plugins/ifcfg-rh/shvar.h @@ -56,6 +56,7 @@ shvarFile *svOpenFile (const char *name, GError **error); * be freed by the caller. */ char *svGetValue (shvarFile *s, const char *key, gboolean verbatim); +char *svGetValueFull (shvarFile *s, const char *key, gboolean verbatim); /* return TRUE if <key> resolves to any truth value (e.g. "yes", "y", "true") * return FALSE if <key> resolves to any non-truth value (e.g. "no", "n", "false") @@ -71,6 +72,7 @@ gint64 svGetValueInt64 (shvarFile *s, const char *key, guint base, gint64 min, g * to the top of the file. */ void svSetValue (shvarFile *s, const char *key, const char *value, gboolean verbatim); +void svSetValueFull (shvarFile *s, const char *key, const char *value, gboolean verbatim); /* Write the current contents iff modified. Returns FALSE on error @@ -84,8 +86,8 @@ gboolean svWriteFile (shvarFile *s, int mode, GError **error); /* Close the file descriptor (if open) and free the shvarFile. */ void svCloseFile (shvarFile *s); -/* Return a new escaped string */ -char *svEscape (const char *s); +/* Return @s unmodified or an escaped string */ +const char *svEscape (const char *s, char **to_free); /* Unescape a string in-place */ void svUnescape (char *s); diff --git a/src/settings/plugins/ifcfg-rh/tests/Makefile.in b/src/settings/plugins/ifcfg-rh/tests/Makefile.in index d3c838a7..d0832e47 100644 --- a/src/settings/plugins/ifcfg-rh/tests/Makefile.in +++ b/src/settings/plugins/ifcfg-rh/tests/Makefile.in @@ -192,7 +192,7 @@ am__recursive_targets = \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ - distdir + check recheck distdir am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is @@ -234,9 +234,190 @@ am__tty_colors = { \ std='[m'; \ fi; \ } +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__recheck_rx = ^[ ]*:recheck:[ ]* +am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* +am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* +# A command that, given a newline-separated list of test names on the +# standard input, print the name of the tests that are to be re-run +# upon "make recheck". +am__list_recheck_tests = $(AWK) '{ \ + recheck = 1; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + { \ + if ((getline line2 < ($$0 ".log")) < 0) \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ + { \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ + { \ + break; \ + } \ + }; \ + if (recheck) \ + print $$0; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# A command that, given a newline-separated list of test names on the +# standard input, create the global log from their .trs and .log files. +am__create_global_log = $(AWK) ' \ +function fatal(msg) \ +{ \ + print "fatal: making $@: " msg | "cat >&2"; \ + exit 1; \ +} \ +function rst_section(header) \ +{ \ + print header; \ + len = length(header); \ + for (i = 1; i <= len; i = i + 1) \ + printf "="; \ + printf "\n\n"; \ +} \ +{ \ + copy_in_global_log = 1; \ + global_test_result = "RUN"; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".trs"); \ + if (line ~ /$(am__global_test_result_rx)/) \ + { \ + sub("$(am__global_test_result_rx)", "", line); \ + sub("[ ]*$$", "", line); \ + global_test_result = line; \ + } \ + else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ + copy_in_global_log = 0; \ + }; \ + if (copy_in_global_log) \ + { \ + rst_section(global_test_result ": " $$0); \ + while ((rc = (getline line < ($$0 ".log"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".log"); \ + print line; \ + }; \ + printf "\n"; \ + }; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# Restructured Text title. +am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } +# Solaris 10 'make', and several other traditional 'make' implementations, +# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it +# by disabling -e (using the XSI extension "set +e") if it's set. +am__sh_e_setup = case $$- in *e*) set +e;; esac +# Default flags passed to test drivers. +am__common_driver_flags = \ + --color-tests "$$am__color_tests" \ + --enable-hard-errors "$$am__enable_hard_errors" \ + --expect-failure "$$am__expect_failure" +# To be inserted before the command running the test. Creates the +# directory for the log if needed. Stores in $dir the directory +# containing $f, in $tst the test, in $log the log. Executes the +# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and +# passes TESTS_ENVIRONMENT. Set up options for the wrapper that +# will run the test scripts (or their associated LOG_COMPILER, if +# thy have one). +am__check_pre = \ +$(am__sh_e_setup); \ +$(am__vpath_adj_setup) $(am__vpath_adj) \ +$(am__tty_colors); \ +srcdir=$(srcdir); export srcdir; \ +case "$@" in \ + */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ + *) am__odir=.;; \ +esac; \ +test "x$$am__odir" = x"." || test -d "$$am__odir" \ + || $(MKDIR_P) "$$am__odir" || exit $$?; \ +if test -f "./$$f"; then dir=./; \ +elif test -f "$$f"; then dir=; \ +else dir="$(srcdir)/"; fi; \ +tst=$$dir$$f; log='$@'; \ +if test -n '$(DISABLE_HARD_ERRORS)'; then \ + am__enable_hard_errors=no; \ +else \ + am__enable_hard_errors=yes; \ +fi; \ +case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ + am__expect_failure=yes;; \ + *) \ + am__expect_failure=no;; \ +esac; \ +$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) +# A shell command to get the names of the tests scripts with any registered +# extension removed (i.e., equivalently, the names of the test logs, with +# the '.log' extension removed). The result is saved in the shell variable +# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, +# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", +# since that might cause problem with VPATH rewrites for suffix-less tests. +# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. +am__set_TESTS_bases = \ + bases='$(TEST_LOGS)'; \ + bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ + bases=`echo $$bases` +RECHECK_LOGS = $(TEST_LOGS) +TEST_SUITE_LOG = test-suite.log +TEST_EXTENSIONS = @EXEEXT@ .test +LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) +am__set_b = \ + case '$@' in \ + */*) \ + case '$*' in \ + */*) b='$*';; \ + *) b=`echo '$@' | sed 's/\.log$$//'`; \ + esac;; \ + *) \ + b='$*';; \ + esac +am__test_logs1 = $(TESTS:=.log) +am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) +TEST_LOGS = $(am__test_logs2:.test.log=.log) +TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/build-aux/test-driver +TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ + $(TEST_LOG_FLAGS) DIST_SUBDIRS = network-scripts am__DIST_COMMON = $(srcdir)/Makefile.in \ - $(top_srcdir)/build-aux/depcomp + $(top_srcdir)/build-aux/depcomp \ + $(top_srcdir)/build-aux/test-driver DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ @@ -267,6 +448,7 @@ ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -380,6 +562,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ +LOG_DRIVER = @LOG_DRIVER@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ @@ -568,7 +751,7 @@ with_valgrind = @with_valgrind@ all: all-recursive .SUFFIXES: -.SUFFIXES: .c .lo .o .obj +.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ @@ -811,98 +994,175 @@ cscopelist-am: $(am__tagged_files) distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -check-TESTS: $(TESTS) - @failed=0; all=0; xfail=0; xpass=0; skip=0; \ - srcdir=$(srcdir); export srcdir; \ - list=' $(TESTS) '; \ - $(am__tty_colors); \ - if test -n "$$list"; then \ - for tst in $$list; do \ - if test -f ./$$tst; then dir=./; \ - elif test -f $$tst; then dir=; \ - else dir="$(srcdir)/"; fi; \ - if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \ - all=`expr $$all + 1`; \ - case " $(XFAIL_TESTS) " in \ - *[\ \ ]$$tst[\ \ ]*) \ - xpass=`expr $$xpass + 1`; \ - failed=`expr $$failed + 1`; \ - col=$$red; res=XPASS; \ - ;; \ - *) \ - col=$$grn; res=PASS; \ - ;; \ - esac; \ - elif test $$? -ne 77; then \ - all=`expr $$all + 1`; \ - case " $(XFAIL_TESTS) " in \ - *[\ \ ]$$tst[\ \ ]*) \ - xfail=`expr $$xfail + 1`; \ - col=$$lgn; res=XFAIL; \ - ;; \ - *) \ - failed=`expr $$failed + 1`; \ - col=$$red; res=FAIL; \ - ;; \ - esac; \ - else \ - skip=`expr $$skip + 1`; \ - col=$$blu; res=SKIP; \ - fi; \ - echo "$${col}$$res$${std}: $$tst"; \ - done; \ - if test "$$all" -eq 1; then \ - tests="test"; \ - All=""; \ - else \ - tests="tests"; \ - All="All "; \ +# Recover from deleted '.trs' file; this should ensure that +# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create +# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells +# to avoid problems with "make -n". +.log.trs: + rm -f $< $@ + $(MAKE) $(AM_MAKEFLAGS) $< + +# Leading 'am--fnord' is there to ensure the list of targets does not +# expand to empty, as could happen e.g. with make check TESTS=''. +am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) +am--force-recheck: + @: + +$(TEST_SUITE_LOG): $(TEST_LOGS) + @$(am__set_TESTS_bases); \ + am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ + redo_bases=`for i in $$bases; do \ + am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ + done`; \ + if test -n "$$redo_bases"; then \ + redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ + redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ + if $(am__make_dryrun); then :; else \ + rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ fi; \ - if test "$$failed" -eq 0; then \ - if test "$$xfail" -eq 0; then \ - banner="$$All$$all $$tests passed"; \ - else \ - if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ - banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ - fi; \ - else \ - if test "$$xpass" -eq 0; then \ - banner="$$failed of $$all $$tests failed"; \ + fi; \ + if test -n "$$am__remaking_logs"; then \ + echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ + "recursion detected" >&2; \ + elif test -n "$$redo_logs"; then \ + am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ + fi; \ + if $(am__make_dryrun); then :; else \ + st=0; \ + errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ + for i in $$redo_bases; do \ + test -f $$i.trs && test -r $$i.trs \ + || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ + test -f $$i.log && test -r $$i.log \ + || { echo "$$errmsg $$i.log" >&2; st=1; }; \ + done; \ + test $$st -eq 0 || exit 1; \ + fi + @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ + ws='[ ]'; \ + results=`for b in $$bases; do echo $$b.trs; done`; \ + test -n "$$results" || results=/dev/null; \ + all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ + pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ + fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ + skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ + xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ + xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ + error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ + if test `expr $$fail + $$xpass + $$error` -eq 0; then \ + success=true; \ + else \ + success=false; \ + fi; \ + br='==================='; br=$$br$$br$$br$$br; \ + result_count () \ + { \ + if test x"$$1" = x"--maybe-color"; then \ + maybe_colorize=yes; \ + elif test x"$$1" = x"--no-color"; then \ + maybe_colorize=no; \ else \ - if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ - banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ + echo "$@: invalid 'result_count' usage" >&2; exit 4; \ fi; \ - fi; \ - dashes="$$banner"; \ - skipped=""; \ - if test "$$skip" -ne 0; then \ - if test "$$skip" -eq 1; then \ - skipped="($$skip test was not run)"; \ + shift; \ + desc=$$1 count=$$2; \ + if test $$maybe_colorize = yes && test $$count -gt 0; then \ + color_start=$$3 color_end=$$std; \ else \ - skipped="($$skip tests were not run)"; \ + color_start= color_end=; \ fi; \ - test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ - dashes="$$skipped"; \ - fi; \ - report=""; \ - if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ - report="Please report to $(PACKAGE_BUGREPORT)"; \ - test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ - dashes="$$report"; \ - fi; \ - dashes=`echo "$$dashes" | sed s/./=/g`; \ - if test "$$failed" -eq 0; then \ - col="$$grn"; \ - else \ - col="$$red"; \ - fi; \ - echo "$${col}$$dashes$${std}"; \ - echo "$${col}$$banner$${std}"; \ - test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \ - test -z "$$report" || echo "$${col}$$report$${std}"; \ - echo "$${col}$$dashes$${std}"; \ - test "$$failed" -eq 0; \ - else :; fi + echo "$${color_start}# $$desc $$count$${color_end}"; \ + }; \ + create_testsuite_report () \ + { \ + result_count $$1 "TOTAL:" $$all "$$brg"; \ + result_count $$1 "PASS: " $$pass "$$grn"; \ + result_count $$1 "SKIP: " $$skip "$$blu"; \ + result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ + result_count $$1 "FAIL: " $$fail "$$red"; \ + result_count $$1 "XPASS:" $$xpass "$$red"; \ + result_count $$1 "ERROR:" $$error "$$mgn"; \ + }; \ + { \ + echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ + $(am__rst_title); \ + create_testsuite_report --no-color; \ + echo; \ + echo ".. contents:: :depth: 2"; \ + echo; \ + for b in $$bases; do echo $$b; done \ + | $(am__create_global_log); \ + } >$(TEST_SUITE_LOG).tmp || exit 1; \ + mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ + if $$success; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ + fi; \ + echo "$${col}$$br$${std}"; \ + echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \ + echo "$${col}$$br$${std}"; \ + create_testsuite_report --maybe-color; \ + echo "$$col$$br$$std"; \ + if $$success; then :; else \ + echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ + if test -n "$(PACKAGE_BUGREPORT)"; then \ + echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ + fi; \ + echo "$$col$$br$$std"; \ + fi; \ + $$success || exit 1 + +check-TESTS: + @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list + @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + trs_list=`for i in $$bases; do echo $$i.trs; done`; \ + log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ + exit $$?; +recheck: all + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + bases=`for i in $$bases; do echo $$i; done \ + | $(am__list_recheck_tests)` || exit 1; \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + log_list=`echo $$log_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ + am__force_recheck=am--force-recheck \ + TEST_LOGS="$$log_list"; \ + exit $$? +test-ifcfg-rh-utils.log: test-ifcfg-rh-utils$(EXEEXT) + @p='test-ifcfg-rh-utils$(EXEEXT)'; \ + b='test-ifcfg-rh-utils'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +test-ifcfg-rh.log: test-ifcfg-rh$(EXEEXT) + @p='test-ifcfg-rh$(EXEEXT)'; \ + b='test-ifcfg-rh'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +.test.log: + @p='$<'; \ + $(am__set_b); \ + $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +@am__EXEEXT_TRUE@.test$(EXEEXT).log: +@am__EXEEXT_TRUE@ @p='$<'; \ +@am__EXEEXT_TRUE@ $(am__set_b); \ +@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ +@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ +@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ +@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ @@ -985,6 +1245,9 @@ install-strip: "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: + -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) + -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) + -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) clean-generic: @@ -1080,8 +1343,8 @@ uninstall-am: install-strip installcheck installcheck-am installdirs \ installdirs-am maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ - mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ - uninstall-am + mostlyclean-libtool pdf pdf-am ps ps-am recheck tags tags-am \ + uninstall uninstall-am .PRECIOUS: Makefile diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.in b/src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.in index 58798f77..ef06c5ef 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.in +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.in @@ -135,6 +135,7 @@ ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -248,6 +249,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ +LOG_DRIVER = @LOG_DRIVER@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh-utils.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh-utils.c index 13f1126e..68f236af 100644 --- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh-utils.c +++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh-utils.c @@ -115,15 +115,9 @@ test_ignored (const char *desc, const char *path, gboolean expected_ignored) ASSERT (result == expected_ignored, desc, "unexpected ignore result for path '%s'", path); } -NMTST_DEFINE (); - -int main (int argc, char **argv) +static void +test_name (void) { - char *base; - - nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT"); - - /* The tests */ test_get_ifcfg_name ("get-ifcfg-name-bad", "/foo/bar/adfasdfadf", FALSE, NULL); test_get_ifcfg_name ("get-ifcfg-name-good", "/foo/bar/ifcfg-FooBar", FALSE, "FooBar"); test_get_ifcfg_name ("get-ifcfg-name-keys", "/foo/bar/keys-BlahLbah", FALSE, "BlahLbah"); @@ -137,7 +131,11 @@ int main (int argc, char **argv) test_get_ifcfg_name ("get-ifcfg-name-bad2-ifcfg", "/foo/bar/asdfasifcfg-Foobar", FALSE, NULL); test_get_ifcfg_name ("get-ifcfg-name-bad2-keys", "/foo/bar/asdfaskeys-Foobar", FALSE, NULL); test_get_ifcfg_name ("get-ifcfg-name-bad2-route", "/foo/bar/asdfasroute-Foobar", FALSE, NULL); +} +static void +test_path (void) +{ test_get_ifcfg_path ("ifcfg-path-bad", "/foo/bar/adfasdfasdf", NULL); test_get_ifcfg_path ("ifcfg-path-from-keys-no-path", "keys-BlahBlah", "ifcfg-BlahBlah"); test_get_ifcfg_path ("ifcfg-path-from-keys", "/foo/bar/keys-BlahBlah", "/foo/bar/ifcfg-BlahBlah"); @@ -152,7 +150,11 @@ int main (int argc, char **argv) test_get_route_path ("route-path-from-ifcfg-no-path", "ifcfg-FooBar", "route-FooBar"); test_get_route_path ("route-path-from-ifcfg", "/foo/bar/ifcfg-FooBar", "/foo/bar/route-FooBar"); test_get_route_path ("route-path-from-keys", "/foo/bar/keys-FooBar", "/foo/bar/route-FooBar"); +} +static void +test_ignore (void) +{ test_ignored ("ignored-ifcfg", "ifcfg-FooBar", FALSE); test_ignored ("ignored-keys", "keys-FooBar", FALSE); test_ignored ("ignored-route", "route-FooBar", FALSE); @@ -163,10 +165,19 @@ int main (int argc, char **argv) test_ignored ("ignored-rpmnew", "ifcfg-FooBar" RPMNEW_TAG, TRUE); test_ignored ("ignored-augnew", "ifcfg-FooBar" AUGNEW_TAG, TRUE); test_ignored ("ignored-augtmp", "ifcfg-FooBar" AUGTMP_TAG, TRUE); +} + +NMTST_DEFINE (); + +int main (int argc, char **argv) +{ + nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT"); + + /* The tests */ + g_test_add_func ("/settings/plugins/ifcfg-rh/name", test_name); + g_test_add_func ("/settings/plugins/ifcfg-rh/path", test_path); + g_test_add_func ("/settings/plugins/ifcfg-rh/ignore", test_ignore); - base = g_path_get_basename (argv[0]); - fprintf (stdout, "%s: SUCCESS\n", base); - g_free (base); - return 0; + return g_test_run (); } diff --git a/src/settings/plugins/ifcfg-rh/utils.c b/src/settings/plugins/ifcfg-rh/utils.c index 4b7cb43d..a793288d 100644 --- a/src/settings/plugins/ifcfg-rh/utils.c +++ b/src/settings/plugins/ifcfg-rh/utils.c @@ -25,6 +25,9 @@ #include <string.h> #include "nm-core-internal.h" +#include "nm-macros-internal.h" +#include "NetworkManagerUtils.h" +#include "gsystem-local-alloc.h" #include "utils.h" #include "shvar.h" @@ -149,41 +152,34 @@ check_suffix (const char *base, const char *tag) gboolean utils_should_ignore_file (const char *filename, gboolean only_ifcfg) { - char *base; - gboolean ignore = TRUE; - gboolean is_ifcfg = FALSE; - gboolean is_other = FALSE; + gs_free char *base = NULL; g_return_val_if_fail (filename != NULL, TRUE); base = g_path_get_basename (filename); - g_return_val_if_fail (base != NULL, TRUE); /* Only handle ifcfg, keys, and routes files */ - if (!strncmp (base, IFCFG_TAG, strlen (IFCFG_TAG))) - is_ifcfg = TRUE; - - if (only_ifcfg == FALSE) { - if ( !strncmp (base, KEYS_TAG, strlen (KEYS_TAG)) - || !strncmp (base, ROUTE_TAG, strlen (ROUTE_TAG)) - || !strncmp (base, ROUTE6_TAG, strlen (ROUTE6_TAG))) - is_other = TRUE; + if (strncmp (base, IFCFG_TAG, strlen (IFCFG_TAG)) != 0) { + if (only_ifcfg) + return TRUE; + else if ( strncmp (base, KEYS_TAG, strlen (KEYS_TAG)) != 0 + && strncmp (base, ROUTE_TAG, strlen (ROUTE_TAG)) != 0 + && strncmp (base, ROUTE6_TAG, strlen (ROUTE6_TAG)) != 0) + return TRUE; } /* But not those that have certain suffixes */ - if ( (is_ifcfg || is_other) - && !check_suffix (base, BAK_TAG) - && !check_suffix (base, TILDE_TAG) - && !check_suffix (base, ORIG_TAG) - && !check_suffix (base, REJ_TAG) - && !check_suffix (base, RPMNEW_TAG) - && !check_suffix (base, AUGNEW_TAG) - && !check_suffix (base, AUGTMP_TAG) - && !check_rpm_temp_suffix (base)) - ignore = FALSE; - - g_free (base); - return ignore; + if ( check_suffix (base, BAK_TAG) + || check_suffix (base, TILDE_TAG) + || check_suffix (base, ORIG_TAG) + || check_suffix (base, REJ_TAG) + || check_suffix (base, RPMNEW_TAG) + || check_suffix (base, AUGNEW_TAG) + || check_suffix (base, AUGTMP_TAG) + || check_rpm_temp_suffix (base)) + return TRUE; + + return FALSE; } char * @@ -205,34 +201,43 @@ utils_cert_path (const char *parent, const char *suffix) const char * utils_get_ifcfg_name (const char *file, gboolean only_ifcfg) { - const char *name = NULL, *start = NULL; - char *base; + const char *name; g_return_val_if_fail (file != NULL, NULL); - base = g_path_get_basename (file); - if (!base) + name = strrchr (file, '/'); + if (!name) + name = file; + else + name++; + if (!*name) return NULL; - /* Find the point in 'file' where 'base' starts. We use 'file' since it's - * const and thus will survive after we free 'base'. - */ - start = file + strlen (file) - strlen (base); - g_assert (strcmp (start, base) == 0); - g_free (base); - - if (!strncmp (start, IFCFG_TAG, strlen (IFCFG_TAG))) - name = start + strlen (IFCFG_TAG); - else if (only_ifcfg == FALSE) { - if (!strncmp (start, KEYS_TAG, strlen (KEYS_TAG))) - name = start + strlen (KEYS_TAG); - else if (!strncmp (start, ROUTE_TAG, strlen (ROUTE_TAG))) - name = start + strlen (ROUTE_TAG); - else if (!strncmp (start, ROUTE6_TAG, strlen (ROUTE6_TAG))) - name = start + strlen (ROUTE6_TAG); +#define MATCH_TAG_AND_RETURN(name, TAG) \ + G_STMT_START { \ + if (strncmp (name, TAG, STRLEN (TAG)) == 0) { \ + name += STRLEN (TAG); \ + if (name[0] == '\0') \ + return NULL; \ + else \ + return name; \ + } \ + } G_STMT_END + + /* Do not detect alias files and return 'eth0:0' instead of 'eth0'. + * Unfortunately, we cannot be sure that our files don't contain colons, + * so we cannot reject files with colons. + * + * Instead, you must not call utils_get_ifcfg_name() with an alias file + * or files that are ignored. */ + MATCH_TAG_AND_RETURN (name, IFCFG_TAG); + if (!only_ifcfg) { + MATCH_TAG_AND_RETURN (name, KEYS_TAG); + MATCH_TAG_AND_RETURN (name, ROUTE_TAG); + MATCH_TAG_AND_RETURN (name, ROUTE6_TAG); } - return name; + return NULL; } /* Used to get any ifcfg/extra file path from any other ifcfg/extra path @@ -420,26 +425,43 @@ utils_is_ifcfg_alias_file (const char *alias, const char *ifcfg) } char * -utils_get_ifcfg_from_alias (const char *alias) +utils_detect_ifcfg_path (const char *path, gboolean only_ifcfg) { - char *base, *ptr, *ifcfg = NULL; + gs_free char *base = NULL; + char *ptr, *ifcfg = NULL; - g_return_val_if_fail (alias != NULL, NULL); + g_return_val_if_fail (path != NULL, NULL); - base = g_path_get_basename (alias); - g_return_val_if_fail (base != NULL, NULL); + if (utils_should_ignore_file (path, only_ifcfg)) + return NULL; - if (utils_is_ifcfg_alias_file (base, NULL)) { - ifcfg = g_strdup (alias); - ptr = strrchr (ifcfg, ':'); - if (ptr) - *ptr = '\0'; - else { + base = g_path_get_basename (path); + + if (strncmp (base, IFCFG_TAG, STRLEN (IFCFG_TAG)) == 0) { + if (base[STRLEN (IFCFG_TAG)] == '\0') + return NULL; + if (utils_is_ifcfg_alias_file (base, NULL)) { + ifcfg = g_strdup (path); + ptr = strrchr (ifcfg, ':'); + if (ptr && ptr > ifcfg) { + *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); + return NULL; + } + return ifcfg; + } + } g_free (ifcfg); - ifcfg = NULL; } + return g_strdup (path); } - g_free (base); - return ifcfg; + if (only_ifcfg) + return NULL; + return utils_get_ifcfg_path (path); } diff --git a/src/settings/plugins/ifcfg-rh/utils.h b/src/settings/plugins/ifcfg-rh/utils.h index 445437c4..547bfcb2 100644 --- a/src/settings/plugins/ifcfg-rh/utils.h +++ b/src/settings/plugins/ifcfg-rh/utils.h @@ -59,7 +59,8 @@ gboolean utils_has_complex_routes (const char *filename); gboolean utils_ignore_ip_config (NMConnection *connection); gboolean utils_is_ifcfg_alias_file (const char *alias, const char *ifcfg); -char *utils_get_ifcfg_from_alias (const char *alias); + +char *utils_detect_ifcfg_path (const char *path, gboolean only_ifcfg); #endif /* _UTILS_H_ */ diff --git a/src/settings/plugins/ifcfg-rh/writer.c b/src/settings/plugins/ifcfg-rh/writer.c index d2b0c970..52bf51d1 100644 --- a/src/settings/plugins/ifcfg-rh/writer.c +++ b/src/settings/plugins/ifcfg-rh/writer.c @@ -888,20 +888,22 @@ write_wireless_setting (NMConnection *connection, svSetValue (ifcfg, "ESSID", str->str, TRUE); g_string_free (str, TRUE); } else { + const char *tmp_escaped; + /* Printable SSIDs always get quoted */ memset (buf, 0, sizeof (buf)); memcpy (buf, ssid_data, ssid_len); - tmp = svEscape (buf); + tmp_escaped = svEscape (buf, &tmp); /* svEscape will usually quote the string, but just for consistency, * if svEscape doesn't quote the ESSID, we quote it ourselves. */ - if (tmp[0] != '"' && tmp[strlen (tmp) - 1] != '"') { - tmp2 = g_strdup_printf ("\"%s\"", tmp); + if (tmp_escaped[0] != '"' && tmp_escaped[strlen (tmp_escaped) - 1] != '"') { + tmp2 = g_strdup_printf ("\"%s\"", tmp_escaped); svSetValue (ifcfg, "ESSID", tmp2, TRUE); g_free (tmp2); } else - svSetValue (ifcfg, "ESSID", tmp, TRUE); + svSetValue (ifcfg, "ESSID", tmp_escaped, TRUE); g_free (tmp); } @@ -1665,7 +1667,7 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg) { guint32 n, i; GString *str; - const char *master; + const char *master, *type; char *tmp; gint i_int; @@ -1682,6 +1684,20 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg) svSetValue (ifcfg, "AUTOCONNECT_PRIORITY", tmp, FALSE); g_free (tmp); + /* Only save the value for master connections */ + svSetValue (ifcfg, "AUTOCONNECT_SLAVES", NULL, FALSE); + type = nm_setting_connection_get_connection_type (s_con); + if ( !g_strcmp0 (type, NM_SETTING_BOND_SETTING_NAME) + || !g_strcmp0 (type, NM_SETTING_TEAM_SETTING_NAME) + || !g_strcmp0 (type, NM_SETTING_BRIDGE_SETTING_NAME)) { + NMSettingConnectionAutoconnectSlaves autoconnect_slaves; + autoconnect_slaves = nm_setting_connection_get_autoconnect_slaves (s_con); + svSetValue (ifcfg, "AUTOCONNECT_SLAVES", + autoconnect_slaves == NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_YES ? "yes" : + autoconnect_slaves == NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_NO ? "no" : NULL, + FALSE); + } + /* Permissions */ svSetValue (ifcfg, "USERS", NULL, FALSE); n = nm_setting_connection_get_num_permissions (s_con); @@ -2448,7 +2464,7 @@ escape_id (const char *id) while (*p) { if (*p == ' ') *p = '_'; - else if (strchr ("\\][|/=()!", *p)) + else if (strchr ("\\][|/=()!:", *p)) *p = '-'; p++; } diff --git a/src/settings/plugins/ifcfg-suse/Makefile.in b/src/settings/plugins/ifcfg-suse/Makefile.in index f7df30b4..3d7df19e 100644 --- a/src/settings/plugins/ifcfg-suse/Makefile.in +++ b/src/settings/plugins/ifcfg-suse/Makefile.in @@ -219,6 +219,7 @@ ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -332,6 +333,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ +LOG_DRIVER = @LOG_DRIVER@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ diff --git a/src/settings/plugins/ifnet/Makefile.in b/src/settings/plugins/ifnet/Makefile.in index cbedcd79..4832249a 100644 --- a/src/settings/plugins/ifnet/Makefile.in +++ b/src/settings/plugins/ifnet/Makefile.in @@ -267,6 +267,7 @@ ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -380,6 +381,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ +LOG_DRIVER = @LOG_DRIVER@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ diff --git a/src/settings/plugins/ifnet/connection_parser.c b/src/settings/plugins/ifnet/connection_parser.c index 41baf315..888ee589 100644 --- a/src/settings/plugins/ifnet/connection_parser.c +++ b/src/settings/plugins/ifnet/connection_parser.c @@ -706,13 +706,13 @@ make_ip4_setting (NMConnection *connection, GError *local = NULL; if ((metric_str = ifnet_get_data (conn_name, "metric")) != NULL) { - metric = nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1); + metric = _nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1); } else { metric_str = ifnet_get_global_data ("metric"); if (metric_str) { stripped = g_strdup (metric_str); strip_string (stripped, '"'); - metric = nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1); + metric = _nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1); g_free (stripped); } else metric = -1; @@ -846,13 +846,13 @@ make_ip6_setting (NMConnection *connection, /* metric is not per routes configuration right now * global metric is also supported (metric="x") */ if ((metric_str = ifnet_get_data (conn_name, "metric")) != NULL) - metric = nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1); + metric = _nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1); else { metric_str = ifnet_get_global_data ("metric"); if (metric_str) { stripped = g_strdup (metric_str); strip_string (stripped, '"'); - metric = nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1); + metric = _nm_utils_ascii_str_to_int64 (metric_str, 10, 0, G_MAXUINT32, -1); g_free (stripped); } else metric = 1; diff --git a/src/settings/plugins/ifnet/net_parser.c b/src/settings/plugins/ifnet/net_parser.c index 755767e2..8f528d0f 100644 --- a/src/settings/plugins/ifnet/net_parser.c +++ b/src/settings/plugins/ifnet/net_parser.c @@ -174,9 +174,9 @@ init_block_by_line (gchar * buf) /* ignored connection */ conn = add_new_connection_config ("ignore", pos); } else { - int ifindex = nm_platform_link_get_ifindex (pos); + int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, pos); - if (ifindex && nm_platform_link_get_type (ifindex) != NM_LINK_TYPE_WIFI) + if (ifindex && nm_platform_link_get_type (NM_PLATFORM_GET, ifindex) != NM_LINK_TYPE_WIFI) /* wired connection */ conn = add_new_connection_config ("wired", pos); else diff --git a/src/settings/plugins/ifnet/net_parser.h b/src/settings/plugins/ifnet/net_parser.h index 005207ad..d10979cc 100644 --- a/src/settings/plugins/ifnet/net_parser.h +++ b/src/settings/plugins/ifnet/net_parser.h @@ -25,7 +25,6 @@ #include <glib.h> #define CONF_NET_FILE SYSCONFDIR "/conf.d/net" -#define IFNET_KEY_FILE_GROUP "ifnet" gboolean ifnet_init (gchar * config_file); void ifnet_destroy (void); diff --git a/src/settings/plugins/ifnet/plugin.c b/src/settings/plugins/ifnet/plugin.c index 33711639..5eab31ab 100644 --- a/src/settings/plugins/ifnet/plugin.c +++ b/src/settings/plugins/ifnet/plugin.c @@ -47,7 +47,6 @@ #define IFNET_PLUGIN_INFO "(C) 1999-2010 Gentoo Foundation, Inc. To report bugs please use bugs.gentoo.org with [networkmanager] or [qiaomuf] prefix." #define IFNET_SYSTEM_HOSTNAME_FILE "/etc/conf.d/hostname" #define IFNET_MANAGE_WELL_KNOWN_DEFAULT TRUE -#define IFNET_KEY_FILE_KEY_MANAGED "managed" typedef struct { GHashTable *connections; /* uuid::connection */ @@ -121,17 +120,9 @@ write_system_hostname (NMSystemConfigInterface * config, static gboolean is_managed_plugin (void) { - char *result = NULL; - - result = nm_config_get_value (nm_config_get (), - IFNET_KEY_FILE_GROUP, IFNET_KEY_FILE_KEY_MANAGED, - NULL); - if (result) { - gboolean ret = is_true (result); - g_free (result); - return ret; - } - return IFNET_MANAGE_WELL_KNOWN_DEFAULT; + return nm_config_data_get_value_boolean (NM_CONFIG_GET_DATA_ORIG, + NM_CONFIG_KEYFILE_GROUP_IFNET, NM_CONFIG_KEYFILE_KEY_IFNET_MANAGED, + IFNET_MANAGE_WELL_KNOWN_DEFAULT); } static void @@ -246,8 +237,7 @@ reload_connections (NMSystemConfigInterface *config) SCPluginIfnet *self = SC_PLUGIN_IFNET (config); SCPluginIfnetPrivate *priv = SC_PLUGIN_IFNET_GET_PRIVATE (self); GList *conn_names = NULL, *n_iter = NULL; - gboolean auto_refresh = FALSE; - char *str_auto_refresh; + gboolean auto_refresh; GError *error = NULL; /* save names for removing unused connections */ @@ -264,12 +254,9 @@ reload_connections (NMSystemConfigInterface *config) nm_log_info (LOGD_SETTINGS, "Loading connections"); - str_auto_refresh = nm_config_get_value (nm_config_get (), - IFNET_KEY_FILE_GROUP, "auto_refresh", - NULL); - if (str_auto_refresh && is_true (str_auto_refresh)) - auto_refresh = TRUE; - g_free (str_auto_refresh); + auto_refresh = nm_config_data_get_value_boolean (NM_CONFIG_GET_DATA_ORIG, + NM_CONFIG_KEYFILE_GROUP_IFNET, NM_CONFIG_KEYFILE_KEY_IFNET_AUTO_REFRESH, + FALSE); new_connections = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref); diff --git a/src/settings/plugins/ifnet/tests/Makefile.in b/src/settings/plugins/ifnet/tests/Makefile.in index 5b0dcc8d..b94a46fc 100644 --- a/src/settings/plugins/ifnet/tests/Makefile.in +++ b/src/settings/plugins/ifnet/tests/Makefile.in @@ -195,6 +195,7 @@ ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -308,6 +309,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ +LOG_DRIVER = @LOG_DRIVER@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ diff --git a/src/settings/plugins/ifnet/tests/test_all.c b/src/settings/plugins/ifnet/tests/test_all.c index 76ed0c7d..55e7e283 100644 --- a/src/settings/plugins/ifnet/tests/test_all.c +++ b/src/settings/plugins/ifnet/tests/test_all.c @@ -111,7 +111,7 @@ test_is_static (void) "a dhcp interface is recognized as static"); ASSERT (is_static_ip4 ("eth0") == TRUE, "is static", "a static interface is recognized as dhcp"); - ASSERT (!is_static_ip6 ("eth0") == TRUE, "is static", + ASSERT (is_static_ip6 ("eth0") == FALSE, "is static", "a dhcp interface is recognized as static"); } diff --git a/src/settings/plugins/ifupdown/Makefile.in b/src/settings/plugins/ifupdown/Makefile.in index 6d0da08c..7cd1ef4e 100644 --- a/src/settings/plugins/ifupdown/Makefile.in +++ b/src/settings/plugins/ifupdown/Makefile.in @@ -266,6 +266,7 @@ ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -379,6 +380,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ +LOG_DRIVER = @LOG_DRIVER@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ diff --git a/src/settings/plugins/ifupdown/plugin.c b/src/settings/plugins/ifupdown/plugin.c index 1b5642af..8652dd8d 100644 --- a/src/settings/plugins/ifupdown/plugin.c +++ b/src/settings/plugins/ifupdown/plugin.c @@ -63,8 +63,6 @@ #define IFUPDOWN_PLUGIN_INFO "(C) 2008 Canonical Ltd. To report bugs please use the NetworkManager mailing list." #define IFUPDOWN_SYSTEM_HOSTNAME_FILE "/etc/hostname" -#define IFUPDOWN_KEY_FILE_GROUP "ifupdown" -#define IFUPDOWN_KEY_FILE_KEY_MANAGED "managed" #define IFUPDOWN_UNMANAGE_WELL_KNOWN_DEFAULT TRUE /* #define ALWAYS_UNMANAGE TRUE */ @@ -327,8 +325,6 @@ SCPluginIfupdown_init (NMSystemConfigInterface *config) GHashTable *auto_ifaces; if_block *block = NULL; NMInotifyHelper *inotify_helper; - char *value; - GError *error = NULL; GList *keys, *iter; GHashTableIter con_iter; const char *block_name; @@ -354,8 +350,6 @@ SCPluginIfupdown_init (NMSystemConfigInterface *config) } else g_signal_connect (priv->client, "uevent", G_CALLBACK (handle_uevent), self); - priv->unmanage_well_known = IFUPDOWN_UNMANAGE_WELL_KNOWN_DEFAULT; - inotify_helper = nm_inotify_helper_get (); priv->inotify_event_id = g_signal_connect (inotify_helper, "event", @@ -456,21 +450,10 @@ SCPluginIfupdown_init (NMSystemConfigInterface *config) g_hash_table_destroy (auto_ifaces); /* Check the config file to find out whether to manage interfaces */ - value = nm_config_get_value (nm_config_get (), - IFUPDOWN_KEY_FILE_GROUP, IFUPDOWN_KEY_FILE_KEY_MANAGED, - &error); - if (error) { - nm_log_info (LOGD_SETTINGS, "loading system config file (%s) caused error: %s", - nm_config_get_path (nm_config_get ()), - error->message); - } else { - gboolean manage_well_known; - error = NULL; - - manage_well_known = !g_strcmp0 (value, "true") || !g_strcmp0 (value, "1"); - priv->unmanage_well_known = !manage_well_known; - g_free (value); - } + priv->unmanage_well_known = !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); nm_log_info (LOGD_SETTINGS, "management mode: %s", priv->unmanage_well_known ? "unmanaged" : "managed"); /* Add well-known interfaces */ @@ -630,7 +613,7 @@ sc_plugin_ifupdown_init (SCPluginIfupdown *plugin) static void GObject__get_property (GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) + GValue *value, GParamSpec *pspec) { NMSystemConfigInterface *self = NM_SYSTEM_CONFIG_INTERFACE (object); diff --git a/src/settings/plugins/ifupdown/tests/Makefile.in b/src/settings/plugins/ifupdown/tests/Makefile.in index d8c5b39e..a1c029c3 100644 --- a/src/settings/plugins/ifupdown/tests/Makefile.in +++ b/src/settings/plugins/ifupdown/tests/Makefile.in @@ -206,13 +206,196 @@ am__tty_colors = { \ std='[m'; \ fi; \ } +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__recheck_rx = ^[ ]*:recheck:[ ]* +am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* +am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* +# A command that, given a newline-separated list of test names on the +# standard input, print the name of the tests that are to be re-run +# upon "make recheck". +am__list_recheck_tests = $(AWK) '{ \ + recheck = 1; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + { \ + if ((getline line2 < ($$0 ".log")) < 0) \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ + { \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ + { \ + break; \ + } \ + }; \ + if (recheck) \ + print $$0; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# A command that, given a newline-separated list of test names on the +# standard input, create the global log from their .trs and .log files. +am__create_global_log = $(AWK) ' \ +function fatal(msg) \ +{ \ + print "fatal: making $@: " msg | "cat >&2"; \ + exit 1; \ +} \ +function rst_section(header) \ +{ \ + print header; \ + len = length(header); \ + for (i = 1; i <= len; i = i + 1) \ + printf "="; \ + printf "\n\n"; \ +} \ +{ \ + copy_in_global_log = 1; \ + global_test_result = "RUN"; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".trs"); \ + if (line ~ /$(am__global_test_result_rx)/) \ + { \ + sub("$(am__global_test_result_rx)", "", line); \ + sub("[ ]*$$", "", line); \ + global_test_result = line; \ + } \ + else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ + copy_in_global_log = 0; \ + }; \ + if (copy_in_global_log) \ + { \ + rst_section(global_test_result ": " $$0); \ + while ((rc = (getline line < ($$0 ".log"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".log"); \ + print line; \ + }; \ + printf "\n"; \ + }; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# Restructured Text title. +am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } +# Solaris 10 'make', and several other traditional 'make' implementations, +# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it +# by disabling -e (using the XSI extension "set +e") if it's set. +am__sh_e_setup = case $$- in *e*) set +e;; esac +# Default flags passed to test drivers. +am__common_driver_flags = \ + --color-tests "$$am__color_tests" \ + --enable-hard-errors "$$am__enable_hard_errors" \ + --expect-failure "$$am__expect_failure" +# To be inserted before the command running the test. Creates the +# directory for the log if needed. Stores in $dir the directory +# containing $f, in $tst the test, in $log the log. Executes the +# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and +# passes TESTS_ENVIRONMENT. Set up options for the wrapper that +# will run the test scripts (or their associated LOG_COMPILER, if +# thy have one). +am__check_pre = \ +$(am__sh_e_setup); \ +$(am__vpath_adj_setup) $(am__vpath_adj) \ +$(am__tty_colors); \ +srcdir=$(srcdir); export srcdir; \ +case "$@" in \ + */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ + *) am__odir=.;; \ +esac; \ +test "x$$am__odir" = x"." || test -d "$$am__odir" \ + || $(MKDIR_P) "$$am__odir" || exit $$?; \ +if test -f "./$$f"; then dir=./; \ +elif test -f "$$f"; then dir=; \ +else dir="$(srcdir)/"; fi; \ +tst=$$dir$$f; log='$@'; \ +if test -n '$(DISABLE_HARD_ERRORS)'; then \ + am__enable_hard_errors=no; \ +else \ + am__enable_hard_errors=yes; \ +fi; \ +case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ + am__expect_failure=yes;; \ + *) \ + am__expect_failure=no;; \ +esac; \ +$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) +# A shell command to get the names of the tests scripts with any registered +# extension removed (i.e., equivalently, the names of the test logs, with +# the '.log' extension removed). The result is saved in the shell variable +# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, +# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", +# since that might cause problem with VPATH rewrites for suffix-less tests. +# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. +am__set_TESTS_bases = \ + bases='$(TEST_LOGS)'; \ + bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ + bases=`echo $$bases` +RECHECK_LOGS = $(TEST_LOGS) +AM_RECURSIVE_TARGETS = check recheck +TEST_SUITE_LOG = test-suite.log +TEST_EXTENSIONS = @EXEEXT@ .test +LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) +am__set_b = \ + case '$@' in \ + */*) \ + case '$*' in \ + */*) b='$*';; \ + *) b=`echo '$@' | sed 's/\.log$$//'`; \ + esac;; \ + *) \ + b='$*';; \ + esac +am__test_logs1 = $(TESTS:=.log) +am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) +TEST_LOGS = $(am__test_logs2:.test.log=.log) +TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/build-aux/test-driver +TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ + $(TEST_LOG_FLAGS) am__DIST_COMMON = $(srcdir)/Makefile.in \ - $(top_srcdir)/build-aux/depcomp + $(top_srcdir)/build-aux/depcomp \ + $(top_srcdir)/build-aux/test-driver DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -326,6 +509,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ +LOG_DRIVER = @LOG_DRIVER@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ @@ -499,7 +683,7 @@ EXTRA_DIST = \ all: all-am .SUFFIXES: -.SUFFIXES: .c .lo .o .obj +.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ @@ -660,98 +844,168 @@ cscopelist-am: $(am__tagged_files) distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -check-TESTS: $(TESTS) - @failed=0; all=0; xfail=0; xpass=0; skip=0; \ - srcdir=$(srcdir); export srcdir; \ - list=' $(TESTS) '; \ - $(am__tty_colors); \ - if test -n "$$list"; then \ - for tst in $$list; do \ - if test -f ./$$tst; then dir=./; \ - elif test -f $$tst; then dir=; \ - else dir="$(srcdir)/"; fi; \ - if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \ - all=`expr $$all + 1`; \ - case " $(XFAIL_TESTS) " in \ - *[\ \ ]$$tst[\ \ ]*) \ - xpass=`expr $$xpass + 1`; \ - failed=`expr $$failed + 1`; \ - col=$$red; res=XPASS; \ - ;; \ - *) \ - col=$$grn; res=PASS; \ - ;; \ - esac; \ - elif test $$? -ne 77; then \ - all=`expr $$all + 1`; \ - case " $(XFAIL_TESTS) " in \ - *[\ \ ]$$tst[\ \ ]*) \ - xfail=`expr $$xfail + 1`; \ - col=$$lgn; res=XFAIL; \ - ;; \ - *) \ - failed=`expr $$failed + 1`; \ - col=$$red; res=FAIL; \ - ;; \ - esac; \ - else \ - skip=`expr $$skip + 1`; \ - col=$$blu; res=SKIP; \ - fi; \ - echo "$${col}$$res$${std}: $$tst"; \ - done; \ - if test "$$all" -eq 1; then \ - tests="test"; \ - All=""; \ - else \ - tests="tests"; \ - All="All "; \ +# Recover from deleted '.trs' file; this should ensure that +# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create +# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells +# to avoid problems with "make -n". +.log.trs: + rm -f $< $@ + $(MAKE) $(AM_MAKEFLAGS) $< + +# Leading 'am--fnord' is there to ensure the list of targets does not +# expand to empty, as could happen e.g. with make check TESTS=''. +am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) +am--force-recheck: + @: + +$(TEST_SUITE_LOG): $(TEST_LOGS) + @$(am__set_TESTS_bases); \ + am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ + redo_bases=`for i in $$bases; do \ + am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ + done`; \ + if test -n "$$redo_bases"; then \ + redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ + redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ + if $(am__make_dryrun); then :; else \ + rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ fi; \ - if test "$$failed" -eq 0; then \ - if test "$$xfail" -eq 0; then \ - banner="$$All$$all $$tests passed"; \ - else \ - if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ - banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ - fi; \ - else \ - if test "$$xpass" -eq 0; then \ - banner="$$failed of $$all $$tests failed"; \ + fi; \ + if test -n "$$am__remaking_logs"; then \ + echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ + "recursion detected" >&2; \ + elif test -n "$$redo_logs"; then \ + am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ + fi; \ + if $(am__make_dryrun); then :; else \ + st=0; \ + errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ + for i in $$redo_bases; do \ + test -f $$i.trs && test -r $$i.trs \ + || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ + test -f $$i.log && test -r $$i.log \ + || { echo "$$errmsg $$i.log" >&2; st=1; }; \ + done; \ + test $$st -eq 0 || exit 1; \ + fi + @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ + ws='[ ]'; \ + results=`for b in $$bases; do echo $$b.trs; done`; \ + test -n "$$results" || results=/dev/null; \ + all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ + pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ + fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ + skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ + xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ + xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ + error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ + if test `expr $$fail + $$xpass + $$error` -eq 0; then \ + success=true; \ + else \ + success=false; \ + fi; \ + br='==================='; br=$$br$$br$$br$$br; \ + result_count () \ + { \ + if test x"$$1" = x"--maybe-color"; then \ + maybe_colorize=yes; \ + elif test x"$$1" = x"--no-color"; then \ + maybe_colorize=no; \ else \ - if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ - banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ + echo "$@: invalid 'result_count' usage" >&2; exit 4; \ fi; \ - fi; \ - dashes="$$banner"; \ - skipped=""; \ - if test "$$skip" -ne 0; then \ - if test "$$skip" -eq 1; then \ - skipped="($$skip test was not run)"; \ + shift; \ + desc=$$1 count=$$2; \ + if test $$maybe_colorize = yes && test $$count -gt 0; then \ + color_start=$$3 color_end=$$std; \ else \ - skipped="($$skip tests were not run)"; \ + color_start= color_end=; \ fi; \ - test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ - dashes="$$skipped"; \ - fi; \ - report=""; \ - if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ - report="Please report to $(PACKAGE_BUGREPORT)"; \ - test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ - dashes="$$report"; \ - fi; \ - dashes=`echo "$$dashes" | sed s/./=/g`; \ - if test "$$failed" -eq 0; then \ - col="$$grn"; \ - else \ - col="$$red"; \ - fi; \ - echo "$${col}$$dashes$${std}"; \ - echo "$${col}$$banner$${std}"; \ - test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \ - test -z "$$report" || echo "$${col}$$report$${std}"; \ - echo "$${col}$$dashes$${std}"; \ - test "$$failed" -eq 0; \ - else :; fi + echo "$${color_start}# $$desc $$count$${color_end}"; \ + }; \ + create_testsuite_report () \ + { \ + result_count $$1 "TOTAL:" $$all "$$brg"; \ + result_count $$1 "PASS: " $$pass "$$grn"; \ + result_count $$1 "SKIP: " $$skip "$$blu"; \ + result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ + result_count $$1 "FAIL: " $$fail "$$red"; \ + result_count $$1 "XPASS:" $$xpass "$$red"; \ + result_count $$1 "ERROR:" $$error "$$mgn"; \ + }; \ + { \ + echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ + $(am__rst_title); \ + create_testsuite_report --no-color; \ + echo; \ + echo ".. contents:: :depth: 2"; \ + echo; \ + for b in $$bases; do echo $$b; done \ + | $(am__create_global_log); \ + } >$(TEST_SUITE_LOG).tmp || exit 1; \ + mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ + if $$success; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ + fi; \ + echo "$${col}$$br$${std}"; \ + echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \ + echo "$${col}$$br$${std}"; \ + create_testsuite_report --maybe-color; \ + echo "$$col$$br$$std"; \ + if $$success; then :; else \ + echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ + if test -n "$(PACKAGE_BUGREPORT)"; then \ + echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ + fi; \ + echo "$$col$$br$$std"; \ + fi; \ + $$success || exit 1 + +check-TESTS: + @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list + @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + trs_list=`for i in $$bases; do echo $$i.trs; done`; \ + log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ + exit $$?; +recheck: all + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + bases=`for i in $$bases; do echo $$i; done \ + | $(am__list_recheck_tests)` || exit 1; \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + log_list=`echo $$log_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ + am__force_recheck=am--force-recheck \ + TEST_LOGS="$$log_list"; \ + exit $$? +test-ifupdown.log: test-ifupdown$(EXEEXT) + @p='test-ifupdown$(EXEEXT)'; \ + b='test-ifupdown'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +.test.log: + @p='$<'; \ + $(am__set_b); \ + $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +@am__EXEEXT_TRUE@.test$(EXEEXT).log: +@am__EXEEXT_TRUE@ @p='$<'; \ +@am__EXEEXT_TRUE@ $(am__set_b); \ +@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ +@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ +@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ +@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ @@ -808,6 +1062,9 @@ install-strip: "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: + -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) + -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) + -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) clean-generic: @@ -903,7 +1160,7 @@ uninstall-am: installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ - tags tags-am uninstall uninstall-am + recheck tags tags-am uninstall uninstall-am .PRECIOUS: Makefile diff --git a/src/settings/plugins/keyfile/Makefile.in b/src/settings/plugins/keyfile/Makefile.in index 21c5577c..c409d481 100644 --- a/src/settings/plugins/keyfile/Makefile.in +++ b/src/settings/plugins/keyfile/Makefile.in @@ -235,6 +235,7 @@ ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -348,6 +349,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ +LOG_DRIVER = @LOG_DRIVER@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ diff --git a/src/settings/plugins/keyfile/common.h b/src/settings/plugins/keyfile/common.h index 7bde4bf3..86fe0028 100644 --- a/src/settings/plugins/keyfile/common.h +++ b/src/settings/plugins/keyfile/common.h @@ -28,7 +28,5 @@ #define KEYFILE_DIR NMCONFDIR "/system-connections" -#define VPN_SECRETS_GROUP "vpn-secrets" - #endif /* __COMMON_H__ */ diff --git a/src/settings/plugins/keyfile/plugin.c b/src/settings/plugins/keyfile/plugin.c index fcdb3c3c..7f21e71e 100644 --- a/src/settings/plugins/keyfile/plugin.c +++ b/src/settings/plugins/keyfile/plugin.c @@ -585,45 +585,19 @@ get_unmanaged_specs (NMSystemConfigInterface *config) GKeyFile *key_file; GSList *specs = NULL; GError *error = NULL; - char *str; if (!priv->conf_file) return NULL; - key_file = g_key_file_new (); - if (!parse_key_file_allow_none (priv, key_file, &error)) - goto out; - - str = g_key_file_get_value (key_file, "keyfile", "unmanaged-devices", NULL); - if (str) { - char **udis; - int i; - - udis = g_strsplit_set (str, ";,", -1); - g_free (str); - - for (i = 0; udis[i] != NULL; i++) { - /* Verify unmanaged specification and add it to the list */ - if (!strncmp (udis[i], "mac:", 4) && nm_utils_hwaddr_valid (udis[i] + 4, -1)) { - specs = g_slist_append (specs, udis[i]); - } else if (!strncmp (udis[i], "interface-name:", 15) && nm_utils_iface_valid_name (udis[i] + 15)) { - specs = g_slist_append (specs, udis[i]); - } else { - nm_log_warn (LOGD_SETTINGS, "keyfile: error in file '%s': invalid unmanaged-devices entry: '%s'", priv->conf_file, udis[i]); - g_free (udis[i]); - } - } + key_file = nm_config_create_keyfile (); + if (parse_key_file_allow_none (priv, key_file, &error)) + specs = nm_config_get_device_match_spec (key_file, NM_CONFIG_KEYFILE_GROUP_KEYFILE, "unmanaged-devices", NULL); - g_free (udis); /* Yes, g_free, not g_strfreev because we need the strings in the list */ - } - - out: if (error) { nm_log_warn (LOGD_SETTINGS, "keyfile: error getting unmanaged specs: %s", error->message); g_error_free (error); } - if (key_file) - g_key_file_free (key_file); + g_key_file_free (key_file); return specs; } @@ -643,7 +617,7 @@ plugin_get_hostname (SCPluginKeyfile *plugin) if (!parse_key_file_allow_none (priv, key_file, &error)) goto out; - hostname = g_key_file_get_value (key_file, "keyfile", "hostname", NULL); + hostname = g_key_file_get_value (key_file, NM_CONFIG_KEYFILE_GROUP_KEYFILE, "hostname", NULL); out: if (error) { @@ -679,7 +653,7 @@ plugin_set_hostname (SCPluginKeyfile *plugin, const char *hostname) if (!parse_key_file_allow_none (priv, key_file, &error)) goto out; - g_key_file_set_string (key_file, "keyfile", "hostname", hostname); + g_key_file_set_string (key_file, NM_CONFIG_KEYFILE_GROUP_KEYFILE, "hostname", hostname); data = g_key_file_to_data (key_file, &len, &error); if (!data) @@ -843,7 +817,7 @@ nm_settings_keyfile_plugin_new (void) singleton = SC_PLUGIN_KEYFILE (g_object_new (SC_TYPE_PLUGIN_KEYFILE, NULL)); priv = SC_PLUGIN_KEYFILE_GET_PRIVATE (singleton); - priv->conf_file = nm_config_get_path (nm_config_get ()); + priv->conf_file = nm_config_data_get_config_main_file (nm_config_get_data (nm_config_get ())); /* plugin_set_hostname() has to be called *after* priv->conf_file is set */ priv->hostname = plugin_get_hostname (singleton); diff --git a/src/settings/plugins/keyfile/reader.c b/src/settings/plugins/keyfile/reader.c index 1f0953e0..bd08c17d 100644 --- a/src/settings/plugins/keyfile/reader.c +++ b/src/settings/plugins/keyfile/reader.c @@ -15,1253 +15,83 @@ * 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 - 2009 Novell, Inc. - * Copyright (C) 2008 - 2011 Red Hat, Inc. + * Copyright (C) 2015 Red Hat, Inc. */ #include "config.h" -#include <errno.h> -#include <stdlib.h> #include <sys/stat.h> -#include <unistd.h> -#include <sys/types.h> -#include <arpa/inet.h> #include <string.h> -#include "nm-core-internal.h" -#include "nm-dbus-glib-types.h" -#include "nm-glib-compat.h" -#include "nm-system-config-interface.h" -#include "nm-logging.h" #include "reader.h" -#include "common.h" -#include "utils.h" -#include "nm-core-internal.h" -#include "NetworkManagerUtils.h" - -/* Some setting properties also contain setting names, such as - * NMSettingConnection's 'type' property (which specifies the base type of the - * connection, e.g. ethernet or wifi) or 'slave-type' (specifies type of slave - * connection, e.g. bond or bridge). This function handles translating those - * properties' values to the real setting name if they are an alias. - */ -static void -setting_alias_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path) -{ - const char *setting_name = nm_setting_get_name (setting); - char *s; - const char *key_setting_name; - - s = nm_keyfile_plugin_kf_get_string (keyfile, setting_name, key, NULL); - if (s) { - key_setting_name = nm_keyfile_plugin_get_setting_name_for_alias (s); - g_object_set (G_OBJECT (setting), - key, key_setting_name ? key_setting_name : s, - NULL); - g_free (s); - } -} - -static gboolean -read_array_of_uint (GKeyFile *file, - NMSetting *setting, - const char *key) -{ - GArray *array = NULL; - gsize length; - int i; - gint *tmp; - - tmp = nm_keyfile_plugin_kf_get_integer_list (file, nm_setting_get_name (setting), key, &length, NULL); - array = g_array_sized_new (FALSE, FALSE, sizeof (guint32), length); - g_return_val_if_fail (array != NULL, FALSE); - - for (i = 0; i < length; i++) - g_array_append_val (array, tmp[i]); - - g_object_set (setting, key, array, NULL); - g_array_unref (array); - - return TRUE; -} - -static gboolean -get_one_int (const char *str, guint32 max_val, const char *key_name, guint32 *out) -{ - long tmp; - char *endptr; - - if (!str || !str[0]) { - if (key_name) - nm_log_warn (LOGD_SETTINGS, "%s: ignoring missing number %s", __func__, key_name); - return FALSE; - } - - errno = 0; - tmp = strtol (str, &endptr, 10); - if (errno || (tmp < 0) || (tmp > max_val) || *endptr != 0) { - if (key_name) - nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid number %s '%s'", __func__, key_name, str); - return FALSE; - } - - *out = (guint32) tmp; - return TRUE; -} - -static gpointer -build_address (int family, const char *address_str, guint32 plen) -{ - NMIPAddress *addr; - GError *error = NULL; - - g_return_val_if_fail (address_str, NULL); - - addr = nm_ip_address_new (family, address_str, plen, &error); - if (!addr) { - nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid %s address: %s", __func__, - family == AF_INET ? "IPv4" : "IPv6", - error->message); - g_error_free (error); - } - - return addr; -} - -static gpointer -build_route (int family, - const char *dest_str, guint32 plen, - const char *gateway_str, const char *metric_str, - const char *key_name) -{ - NMIPRoute *route; - guint32 metric = 0; - GError *error = NULL; - - g_return_val_if_fail (plen, NULL); - g_return_val_if_fail (dest_str, NULL); - - /* Next hop */ - if (gateway_str && gateway_str[0]) { - if (!nm_utils_ipaddr_valid (family, gateway_str)) { - /* Try workaround for routes written by broken keyfile writer. - * Due to bug bgo#719851, an older version of writer would have - * written "a:b:c:d::/plen,metric" if the gateway was ::, instead - * of "a:b:c:d::/plen,,metric" or "a:b:c:d::/plen,::,metric" - * Try workaround by interpreting gateway_str as metric to accept such - * invalid routes. This broken syntax should not be not officially - * supported. - **/ - if ( family == AF_INET6 - && !metric_str - && get_one_int (gateway_str, G_MAXUINT32, NULL, &metric)) - gateway_str = NULL; - else { - nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid gateway '%s'", __func__, gateway_str); - return NULL; - } - } - } else - gateway_str = NULL; - - /* parse metric, default to 0 */ - if (metric_str) { - if (!get_one_int (metric_str, G_MAXUINT32, key_name, &metric)) - return NULL; - } - - route = nm_ip_route_new (family, dest_str, plen, gateway_str, - metric ? (gint64) metric : -1, - &error); - if (!route) { - nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid %s route: %s", __func__, - family == AF_INET ? "IPv4" : "IPv6", - error->message); - g_error_free (error); - } - - return route; -} - -/* On success, returns pointer to the zero-terminated field (original @current). - * The @current * pointer target is set to point to the rest of the input - * or %NULL if there is no more input. Sets error to %NULL for convenience. - * - * On failure, returns %NULL (unspecified). The @current pointer target is - * resets to its original value to allow skipping fields. The @error target - * is set to the character that breaks the parsing or %NULL if @current was %NULL. - * - * When @current target is %NULL, gracefully fail returning %NULL while - * leaving the @current target %NULL end setting @error to %NULL; - */ -static char * -read_field (char **current, char **error, const char *characters, const char *delimiters) -{ - char *start; - - g_return_val_if_fail (current, NULL); - g_return_val_if_fail (error, NULL); - g_return_val_if_fail (characters, NULL); - g_return_val_if_fail (delimiters, NULL); - - if (!*current) { - /* graceful failure, leave '*current' NULL */ - *error = NULL; - return NULL; - } - - /* fail on empty input */ - if (!**current) - return NULL; - - /* remember beginning of input */ - start = *current; - - while (**current && strchr (characters, **current)) - (*current)++; - if (**current) - if (strchr (delimiters, **current)) { - /* success, more data available */ - *error = NULL; - *(*current)++ = '\0'; - return start; - } else { - /* error, bad character */ - *error = *current; - *current = start; - return NULL; - } - else { - /* success, end of input */ - *error = NULL; - *current = NULL; - return start; - } -} - -#define IP_ADDRESS_CHARS "0123456789abcdefABCDEF:.%" -#define DIGITS "0123456789" -#define DELIMITERS "/;," - - -/* The following IPv4 and IPv6 address formats are supported: - * - * address (DEPRECATED) - * address/plen - * address/gateway (DEPRECATED) - * address/plen,gateway - * - * The following IPv4 and IPv6 route formats are supported: - * - * address/plen (NETWORK dev DEVICE) - * address/plen,gateway (NETWORK via GATEWAY dev DEVICE) - * address/plen,,metric (NETWORK dev DEVICE metric METRIC) - * address/plen,gateway,metric (NETWORK via GATEWAY dev DEVICE metric METRIC) - * - * For backward, forward and sideward compatibility, slash (/), - * semicolon (;) and comma (,) are interchangable. The choice of - * separator in the above examples is therefore not significant. - * - * Leaving out the prefix length is discouraged and DEPRECATED. The - * default value of IPv6 prefix length was 64 and has not been - * changed. The default for IPv4 is now 24, which is the closest - * IPv4 equivalent. These defaults may just as well be changed to - * match the iproute2 defaults (32 for IPv4 and 128 for IPv6). - */ -static gpointer -read_one_ip_address_or_route (GKeyFile *file, - const char *setting_name, - const char *key_name, - gboolean ipv6, - gboolean route, - char **out_gateway) -{ - guint32 plen = G_MAXUINT32; - gpointer result; - char *address_str, *plen_str, *gateway_str, *metric_str, *value, *current, *error; - - current = value = nm_keyfile_plugin_kf_get_string (file, setting_name, key_name, NULL); - if (!value) - return NULL; - - /* get address field */ - address_str = read_field (¤t, &error, IP_ADDRESS_CHARS, DELIMITERS); - if (error) { - nm_log_warn (LOGD_SETTINGS, "keyfile: Unexpected character '%c' in '%s.%s' address (position %td of '%s').", - *error, setting_name, key_name, error - current, current); - goto error; - } - /* get prefix length field (skippable) */ - plen_str = read_field (¤t, &error, DIGITS, DELIMITERS); - /* get gateway field */ - gateway_str = read_field (¤t, &error, IP_ADDRESS_CHARS, DELIMITERS); - if (error) { - nm_log_warn (LOGD_SETTINGS, "keyfile: Unexpected character '%c' in '%s.%s' %s (position %td of '%s').", - *error, setting_name, key_name, - plen_str ? "gateway" : "gateway or prefix length", - error - current, current); - goto error; - } - /* for routes, get metric */ - if (route) { - metric_str = read_field (¤t, &error, DIGITS, DELIMITERS); - if (error) { - nm_log_warn (LOGD_SETTINGS, "keyfile: Unexpected character '%c' in '%s.%s' prefix length (position %td of '%s').", - *error, setting_name, key_name, error - current, current); - goto error; - } - } else - metric_str = NULL; - if (current) { - /* there is still some data */ - if (*current) { - /* another field follows */ - nm_log_warn (LOGD_SETTINGS, "keyfile: %s.%s: Garbage at the and of the line: %s", - setting_name, key_name, current); - goto error; - } else { - /* semicolon at the end of input */ - nm_log_info (LOGD_SETTINGS, "keyfile: %s.%s: Deprecated semicolon at the end of value.", - setting_name, key_name); - } - } - -#define DEFAULT_PREFIX(for_route, for_ipv6) ( (for_route) ? ( (for_ipv6) ? 128 : 24 ) : ( (for_ipv6) ? 64 : 24 ) ) - - /* parse plen, fallback to defaults */ - if (plen_str) { - if (!get_one_int (plen_str, ipv6 ? 128 : 32, key_name, &plen) - || (route && plen == 0)) { - plen = DEFAULT_PREFIX (route, ipv6); - nm_log_warn (LOGD_SETTINGS, "keyfile: invalid prefix length '%s' in '%s.%s', defaulting to %d", - plen_str, setting_name, key_name, plen); - } - } else { - plen = DEFAULT_PREFIX (route, ipv6); - nm_log_warn (LOGD_SETTINGS, "keyfile: Missing prefix length in '%s.%s', defaulting to %d", - setting_name, key_name, plen); - } - - /* build the appropriate data structure for NetworkManager settings */ - if (route) { - result = build_route (ipv6 ? AF_INET6 : AF_INET, - address_str, plen, gateway_str, metric_str, - key_name); - } else { - result = build_address (ipv6 ? AF_INET6 : AF_INET, - address_str, plen); - if (out_gateway && gateway_str) - *out_gateway = g_strdup (gateway_str); - } - g_free (value); - return result; -error: - g_free (value); - return NULL; -} +#include "nm-logging.h" +#include "nm-keyfile-internal.h" +#include "NetworkManagerUtils.h" -static void -ip_address_or_route_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path) +static const char * +_fmt_warn (const char *group, NMSetting *setting, const char *property_name, const char *message, char **out_message) { - const char *setting_name = nm_setting_get_name (setting); - gboolean ipv6 = !strcmp (setting_name, "ipv6"); - gboolean routes = !strcmp (key, "routes"); - static const char *key_names_routes[] = { "route", "routes", NULL }; - static const char *key_names_addresses[] = { "address", "addresses", NULL }; - const char **key_names = routes ? key_names_routes : key_names_addresses; - char *gateway = NULL; - GPtrArray *list; - GDestroyNotify free_func; - int i; - - if (routes) - free_func = (GDestroyNotify) nm_ip_route_unref; - else - free_func = (GDestroyNotify) nm_ip_address_unref; - list = g_ptr_array_new_with_free_func (free_func); + const char *setting_name = setting ? nm_setting_get_name (setting) : NULL; - for (i = -1; i < 1000; i++) { - const char **key_basename; - - for (key_basename = key_names; *key_basename; key_basename++) { - char *key_name; - gpointer item; + if (group) { + char *res; - /* -1 means no suffix */ - if (i >= 0) - key_name = g_strdup_printf ("%s%d", *key_basename, i); + if (setting_name) { + if (property_name && !strcmp (group, setting_name)) + res = g_strdup_printf ("%s.%s: %s", group, property_name, message); + else if (property_name) + res = g_strdup_printf ("%s/%s.%s: %s", group, setting_name, property_name, message); + else if (!strcmp (group, setting_name)) + res = g_strdup_printf ("%s: %s", group, message); else - key_name = g_strdup (*key_basename); - - item = read_one_ip_address_or_route (keyfile, setting_name, key_name, ipv6, routes, - gateway ? NULL : &gateway); - if (item) - g_ptr_array_add (list, item); - - g_free (key_name); - } - } - - if (list->len >= 1) - g_object_set (setting, key, list, NULL); - - if (gateway) { - g_object_set (setting, "gateway", gateway, NULL); - g_free (gateway); - } - - g_ptr_array_unref (list); -} - -static void -ip4_dns_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path) -{ - const char *setting_name = nm_setting_get_name (setting); - GPtrArray *array; - gsize length; - char **list, **iter; - int ret; - - list = nm_keyfile_plugin_kf_get_string_list (keyfile, setting_name, key, &length, NULL); - if (!list || !g_strv_length (list)) - return; - - array = g_ptr_array_sized_new (length + 1); - for (iter = list; *iter; iter++) { - guint32 addr; - - ret = inet_pton (AF_INET, *iter, &addr); - if (ret <= 0) { - nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid DNS server address '%s'", __func__, *iter); - continue; - } - - g_ptr_array_add (array, *iter); - } - g_ptr_array_add (array, NULL); - - g_object_set (setting, key, array->pdata, NULL); - g_ptr_array_unref (array); - g_strfreev (list); -} - -static void -ip6_dns_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path) -{ - const char *setting_name = nm_setting_get_name (setting); - GPtrArray *array = NULL; - gsize length; - char **list, **iter; - int ret; - - list = nm_keyfile_plugin_kf_get_string_list (keyfile, setting_name, key, &length, NULL); - if (!list || !g_strv_length (list)) - return; - - array = g_ptr_array_sized_new (length + 1); - - for (iter = list; *iter; iter++) { - struct in6_addr addr; - - ret = inet_pton (AF_INET6, *iter, &addr); - if (ret <= 0) { - nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid DNS server IPv6 address '%s'", __func__, *iter); - continue; - } - - g_ptr_array_add (array, *iter); - } - g_ptr_array_add (array, NULL); - - g_object_set (setting, key, array->pdata, NULL); - g_ptr_array_unref (array); - g_strfreev (list); -} - -static void -mac_address_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path, gsize enforce_length) -{ - const char *setting_name = nm_setting_get_name (setting); - char *tmp_string = NULL, *p, *mac_str; - gint *tmp_list; - GByteArray *array = NULL; - gsize length; - - p = tmp_string = nm_keyfile_plugin_kf_get_string (keyfile, setting_name, key, NULL); - if (tmp_string && tmp_string[0]) { - /* Look for enough ':' characters to signify a MAC address */ - guint i = 0; - - while (*p) { - if (*p == ':') - i++; - p++; - } - - if (enforce_length == 0 || enforce_length == i+1) { - /* If we found enough it's probably a string-format MAC address */ - array = g_byte_array_sized_new (i+1); - g_byte_array_set_size (array, i+1); - if (!nm_utils_hwaddr_aton (tmp_string, array->data, array->len)) { - g_byte_array_unref (array); - array = NULL; - } - } - } - g_free (tmp_string); - - if (array == NULL) { - /* Old format; list of ints */ - tmp_list = nm_keyfile_plugin_kf_get_integer_list (keyfile, setting_name, key, &length, NULL); - if (length > 0 && (enforce_length == 0 || enforce_length == length)) { - gsize i; - - array = g_byte_array_sized_new (length); - for (i = 0; i < length; i++) { - int val = tmp_list[i]; - const guint8 v = (guint8) (val & 0xFF); - - if (val < 0 || val > 255) { - nm_log_warn (LOGD_SETTINGS, "%s: %s / %s ignoring invalid byte element '%d' (not " - " between 0 and 255 inclusive)", __func__, setting_name, - key, val); - g_byte_array_free (array, TRUE); - array = NULL; - break; - } - g_byte_array_append (array, &v, 1); - } - } - g_free (tmp_list); - } - - if (!array) { - nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid MAC address for %s / %s", - __func__, setting_name, key); - return; - } - - mac_str = nm_utils_hwaddr_ntoa (array->data, array->len); - g_object_set (setting, key, mac_str, NULL); - g_free (mac_str); - g_byte_array_free (array, TRUE); -} - -static void -mac_address_parser_ETHER (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path) -{ - mac_address_parser (setting, key, keyfile, keyfile_path, ETH_ALEN); -} - -static void -mac_address_parser_INFINIBAND (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path) -{ - mac_address_parser (setting, key, keyfile, keyfile_path, INFINIBAND_ALEN); -} - -static void -read_hash_of_string (GKeyFile *file, NMSetting *setting, const char *key) -{ - char **keys, **iter; - char *value; - const char *setting_name = nm_setting_get_name (setting); - - keys = nm_keyfile_plugin_kf_get_keys (file, setting_name, NULL, NULL); - if (!keys || !*keys) - return; - - for (iter = keys; *iter; iter++) { - value = nm_keyfile_plugin_kf_get_string (file, setting_name, *iter, NULL); - if (!value) - continue; - - if (NM_IS_SETTING_VPN (setting)) { - /* Add any item that's not a class property to the data hash */ - if (!g_object_class_find_property (G_OBJECT_GET_CLASS (setting), *iter)) - nm_setting_vpn_add_data_item (NM_SETTING_VPN (setting), *iter, value); - } - if (NM_IS_SETTING_BOND (setting)) { - if (strcmp (*iter, "interface-name")) - nm_setting_bond_add_option (NM_SETTING_BOND (setting), *iter, value); - } - g_free (value); - } - g_strfreev (keys); -} - -static void -unescape_semicolons (char *str) -{ - int i; - gsize len = strlen (str); - - for (i = 0; i < len; i++) { - if (str[i] == '\\' && str[i+1] == ';') { - memmove(str + i, str + i + 1, len - (i + 1)); - len--; - } - str[len] = '\0'; - } -} - -static GBytes * -get_bytes (GKeyFile *keyfile, - const char *setting_name, - const char *key, - gboolean zero_terminate, - gboolean unescape_semicolon) -{ - GByteArray *array = NULL; - char *tmp_string; - gint *tmp_list; - gsize length; - int i; - - /* New format: just a string - * Old format: integer list; e.g. 11;25;38; - */ - tmp_string = nm_keyfile_plugin_kf_get_string (keyfile, setting_name, key, NULL); - if (tmp_string) { - GRegex *regex; - GMatchInfo *match_info; - const char *pattern = "^[[:space:]]*[[:digit:]]{1,3}[[:space:]]*;([[:space:]]*[[:digit:]]{1,3}[[:space:]]*;)*([[:space:]]*)?$"; - - regex = g_regex_new (pattern, 0, 0, NULL); - g_regex_match (regex, tmp_string, 0, &match_info); - if (!g_match_info_matches (match_info)) { - /* Handle as a simple string (ie, new format) */ - if (unescape_semicolon) - unescape_semicolons (tmp_string); - length = strlen (tmp_string); - if (zero_terminate) - length++; - array = g_byte_array_sized_new (length); - g_byte_array_append (array, (guint8 *) tmp_string, length); - } - g_match_info_free (match_info); - g_regex_unref (regex); - g_free (tmp_string); - } - - if (!array) { - /* Old format; list of ints */ - tmp_list = nm_keyfile_plugin_kf_get_integer_list (keyfile, setting_name, key, &length, NULL); - array = g_byte_array_sized_new (length); - for (i = 0; i < length; i++) { - int val = tmp_list[i]; - unsigned char v = (unsigned char) (val & 0xFF); - - if (val < 0 || val > 255) { - nm_log_warn (LOGD_SETTINGS, "%s: %s / %s ignoring invalid byte element '%d' (not " - " between 0 and 255 inclusive)", __func__, setting_name, - key, val); - } else - g_byte_array_append (array, (const unsigned char *) &v, sizeof (v)); - } - g_free (tmp_list); - } - - if (array->len == 0) { - g_byte_array_free (array, TRUE); - return NULL; + res = g_strdup_printf ("%s/%s: %s", group, setting_name, message); + } else + res = g_strdup_printf ("%s: %s", group, message); + *out_message = res; + return res; } else - return g_byte_array_free_to_bytes (array); -} - -static void -ssid_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path) -{ - const char *setting_name = nm_setting_get_name (setting); - GBytes *bytes; - - bytes = get_bytes (keyfile, setting_name, key, FALSE, TRUE); - if (bytes) { - g_object_set (setting, key, bytes, NULL); - g_bytes_unref (bytes); - } else { - nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid SSID for %s / %s", - __func__, setting_name, key); - } -} - -static void -password_raw_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path) -{ - const char *setting_name = nm_setting_get_name (setting); - GBytes *bytes; - - bytes = get_bytes (keyfile, setting_name, key, FALSE, TRUE); - if (bytes) { - g_object_set (setting, key, bytes, NULL); - g_bytes_unref (bytes); - } else { - nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid raw password for %s / %s", - __func__, setting_name, key); - } -} - -static char * -get_cert_path (const char *keyfile_path, const guint8 *cert_path, gsize cert_path_len) -{ - const char *base; - char *p = NULL, *path, *dirname, *tmp; - - g_return_val_if_fail (keyfile_path != NULL, NULL); - g_return_val_if_fail (cert_path != NULL, NULL); - - base = path = g_malloc0 (cert_path_len + 1); - memcpy (path, cert_path, cert_path_len); - - if (path[0] == '/') - return path; - - p = strrchr (path, '/'); - if (p) - base = p + 1; - - dirname = g_path_get_dirname (keyfile_path); - tmp = g_build_path ("/", dirname, base, NULL); - g_free (dirname); - g_free (path); - return tmp; -} - -#define SCHEME_PATH "file://" - -static const char *certext[] = { ".pem", ".cert", ".crt", ".cer", ".p12", ".der", ".key" }; - -static gboolean -has_cert_ext (const char *path) -{ - int i; - - for (i = 0; i < G_N_ELEMENTS (certext); i++) { - if (g_str_has_suffix (path, certext[i])) - return TRUE; - } - return FALSE; + return message; } static gboolean -handle_as_scheme (GBytes *bytes, NMSetting *setting, const char *key) -{ - const guint8 *data; - gsize data_len; - - data = g_bytes_get_data (bytes, &data_len); +_handler_read (GKeyFile *keyfile, + NMConnection *connection, + NMKeyfileReadType type, + void *type_data, + void *user_data, + GError **error) +{ + if (type == NM_KEYFILE_READ_TYPE_WARN) { + NMKeyfileReadTypeDataWarn *warn_data = type_data; + NMLogLevel level; + char *message_free = NULL; + + if (warn_data->severity > NM_KEYFILE_WARN_SEVERITY_WARN) + level = LOGL_ERR; + else if (warn_data->severity >= NM_KEYFILE_WARN_SEVERITY_WARN) + level = LOGL_WARN; + else if (warn_data->severity == NM_KEYFILE_WARN_SEVERITY_INFO_MISSING_FILE) + level = LOGL_WARN; + else + level = LOGL_INFO; - /* It's the PATH scheme, can just set plain data */ - if ( (data_len > strlen (SCHEME_PATH)) - && g_str_has_prefix ((const char *) data, SCHEME_PATH) - && (data[data_len - 1] == '\0')) { - g_object_set (setting, key, bytes, NULL); + nm_log (level, LOGD_SETTINGS, "keyfile: %s", + _fmt_warn (warn_data->group, warn_data->setting, + warn_data->property_name, warn_data->message, + &message_free)); + g_free (message_free); return TRUE; } return FALSE; } -static gboolean -handle_as_path (GBytes *bytes, - NMSetting *setting, - const char *key, - const char *keyfile_path) -{ - const guint8 *data; - gsize data_len; - gsize validate_len; - char *path; - gboolean exists, success = FALSE; - - data = g_bytes_get_data (bytes, &data_len); - if (data_len > 500 || data_len < 1) - return FALSE; - - /* If there's a trailing NULL tell g_utf8_validate() to to until the NULL */ - if (data[data_len - 1] == '\0') - validate_len = -1; - else - validate_len = data_len; - - if (g_utf8_validate ((const char *) data, validate_len, NULL) == FALSE) - return FALSE; - - /* Might be a bare path without the file:// prefix; in that case - * if it's an absolute path, use that, otherwise treat it as a - * relative path to the current directory. - */ - - path = get_cert_path (keyfile_path, data, data_len); - exists = g_file_test (path, G_FILE_TEST_EXISTS); - if ( exists - || memchr (data, '/', data_len) - || has_cert_ext (path)) { - GByteArray *tmp; - GBytes *val; - - /* Construct the proper value as required for the PATH scheme */ - tmp = g_byte_array_sized_new (strlen (SCHEME_PATH) + strlen (path) + 1); - g_byte_array_append (tmp, (const guint8 *) SCHEME_PATH, strlen (SCHEME_PATH)); - g_byte_array_append (tmp, (const guint8 *) path, strlen (path)); - g_byte_array_append (tmp, (const guint8 *) "\0", 1); - val = g_byte_array_free_to_bytes (tmp); - g_object_set (setting, key, val, NULL); - g_bytes_unref (val); - success = TRUE; - - /* Warn if the certificate didn't exist */ - if (exists == FALSE) - nm_log_warn (LOGD_SETTINGS, "certificate or key %s does not exist", path); - } - g_free (path); - - return success; -} - -static void -cert_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path) -{ - const char *setting_name = nm_setting_get_name (setting); - GBytes *bytes; - gboolean success = FALSE; - - bytes = get_bytes (keyfile, setting_name, key, TRUE, FALSE); - if (bytes) { - /* Try as a path + scheme (ie, starts with "file://") */ - success = handle_as_scheme (bytes, setting, key); - - /* If not, it might be a plain path */ - if (success == FALSE) - success = handle_as_path (bytes, setting, key, keyfile_path); - - /* If neither of those two, assume blob with certificate data */ - if (success == FALSE) - g_object_set (setting, key, bytes, NULL); - } else { - nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid key/cert value for %s / %s", - __func__, setting_name, key); - } - - if (bytes) - g_bytes_unref (bytes); -} - -static void -parity_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path) -{ - const char *setting_name = nm_setting_get_name (setting); - NMSettingSerialParity parity; - int int_val; - char *str_val; - - /* Keyfile traditionally stored this as the ASCII value for 'E', 'o', or 'n'. - * We now accept either that or the (case-insensitive) character itself (but - * still always write it the old way, for backward compatibility). - */ - int_val = nm_keyfile_plugin_kf_get_integer (keyfile, setting_name, key, NULL); - if (!int_val) { - str_val = nm_keyfile_plugin_kf_get_string (keyfile, setting_name, key, NULL); - if (str_val) { - if (str_val[0] && !str_val[1]) - int_val = str_val[0]; - else { - /* This will hit the warning below */ - int_val = 'X'; - } - } - g_free (str_val); - } - - if (!int_val) - return; - - switch (int_val) { - case 'E': - case 'e': - parity = NM_SETTING_SERIAL_PARITY_EVEN; - break; - case 'O': - case 'o': - parity = NM_SETTING_SERIAL_PARITY_ODD; - break; - case 'N': - case 'n': - parity = NM_SETTING_SERIAL_PARITY_NONE; - break; - default: - nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid value for %s / %s", - __func__, setting_name, key); - return; - } - - g_object_set (setting, key, parity, NULL); -} - -typedef struct { - const char *setting_name; - const char *key; - gboolean check_for_key; - void (*parser) (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path); -} KeyParser; - -/* A table of keys that require further parsing/conversion because they are - * stored in a format that can't be automatically read using the key's type. - * i.e. IPv4 addresses, which are stored in NetworkManager as guint32, but are - * stored in keyfiles as strings, eg "10.1.1.2" or IPv6 addresses stored - * in struct in6_addr internally, but as string in keyfiles. - */ -static KeyParser key_parsers[] = { - { NM_SETTING_CONNECTION_SETTING_NAME, - NM_SETTING_CONNECTION_TYPE, - TRUE, - setting_alias_parser }, - { NM_SETTING_BRIDGE_SETTING_NAME, - NM_SETTING_BRIDGE_MAC_ADDRESS, - TRUE, - mac_address_parser_ETHER }, - { NM_SETTING_IP4_CONFIG_SETTING_NAME, - NM_SETTING_IP_CONFIG_ADDRESSES, - FALSE, - ip_address_or_route_parser }, - { NM_SETTING_IP6_CONFIG_SETTING_NAME, - NM_SETTING_IP_CONFIG_ADDRESSES, - FALSE, - ip_address_or_route_parser }, - { NM_SETTING_IP4_CONFIG_SETTING_NAME, - NM_SETTING_IP_CONFIG_ROUTES, - FALSE, - ip_address_or_route_parser }, - { NM_SETTING_IP6_CONFIG_SETTING_NAME, - NM_SETTING_IP_CONFIG_ROUTES, - FALSE, - ip_address_or_route_parser }, - { NM_SETTING_IP4_CONFIG_SETTING_NAME, - NM_SETTING_IP_CONFIG_DNS, - FALSE, - ip4_dns_parser }, - { NM_SETTING_IP6_CONFIG_SETTING_NAME, - NM_SETTING_IP_CONFIG_DNS, - FALSE, - ip6_dns_parser }, - { NM_SETTING_WIRED_SETTING_NAME, - NM_SETTING_WIRED_MAC_ADDRESS, - TRUE, - mac_address_parser_ETHER }, - { NM_SETTING_WIRED_SETTING_NAME, - NM_SETTING_WIRED_CLONED_MAC_ADDRESS, - TRUE, - mac_address_parser_ETHER }, - { NM_SETTING_WIRELESS_SETTING_NAME, - NM_SETTING_WIRELESS_MAC_ADDRESS, - TRUE, - mac_address_parser_ETHER }, - { NM_SETTING_WIRELESS_SETTING_NAME, - NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, - TRUE, - mac_address_parser_ETHER }, - { NM_SETTING_WIRELESS_SETTING_NAME, - NM_SETTING_WIRELESS_BSSID, - TRUE, - mac_address_parser_ETHER }, - { NM_SETTING_BLUETOOTH_SETTING_NAME, - NM_SETTING_BLUETOOTH_BDADDR, - TRUE, - mac_address_parser_ETHER }, - { NM_SETTING_INFINIBAND_SETTING_NAME, - NM_SETTING_INFINIBAND_MAC_ADDRESS, - TRUE, - mac_address_parser_INFINIBAND }, - { NM_SETTING_WIMAX_SETTING_NAME, - NM_SETTING_WIMAX_MAC_ADDRESS, - TRUE, - mac_address_parser_ETHER }, - { NM_SETTING_WIRELESS_SETTING_NAME, - NM_SETTING_WIRELESS_SSID, - TRUE, - ssid_parser }, - { NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_PASSWORD_RAW, - TRUE, - password_raw_parser }, - { NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_CA_CERT, - TRUE, - cert_parser }, - { NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_CLIENT_CERT, - TRUE, - cert_parser }, - { NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_PRIVATE_KEY, - TRUE, - cert_parser }, - { NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_PHASE2_CA_CERT, - TRUE, - cert_parser }, - { NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_PHASE2_CLIENT_CERT, - TRUE, - cert_parser }, - { NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, - TRUE, - cert_parser }, - { NM_SETTING_SERIAL_SETTING_NAME, - NM_SETTING_SERIAL_PARITY, - TRUE, - parity_parser }, - { NULL, NULL, FALSE } -}; - -typedef struct { - GKeyFile *keyfile; - const char *keyfile_path; -} ReadInfo; - -static void -read_one_setting_value (NMSetting *setting, - const char *key, - const GValue *value, - GParamFlags flags, - gpointer user_data) -{ - ReadInfo *info = user_data; - const char *setting_name; - int errsv; - GType type; - GError *err = NULL; - gboolean check_for_key = TRUE; - KeyParser *parser = &key_parsers[0]; - - /* Property is not writable */ - if (!(flags & G_PARAM_WRITABLE)) - return; - - /* Setting name gets picked up from the keyfile's section name instead */ - if (!strcmp (key, NM_SETTING_NAME)) - return; - - /* Don't read the NMSettingConnection object's 'read-only' property */ - if ( NM_IS_SETTING_CONNECTION (setting) - && !strcmp (key, NM_SETTING_CONNECTION_READ_ONLY)) - return; - - setting_name = nm_setting_get_name (setting); - - /* Look through the list of handlers for non-standard format key values */ - while (parser->setting_name) { - if (!strcmp (parser->setting_name, setting_name) && !strcmp (parser->key, key)) { - check_for_key = parser->check_for_key; - break; - } - parser++; - } - - /* VPN properties don't have the exact key name */ - if (NM_IS_SETTING_VPN (setting)) - check_for_key = FALSE; - - /* Bonding 'options' don't have the exact key name. The options are right under [bond] group. */ - if (NM_IS_SETTING_BOND (setting)) - check_for_key = FALSE; - - /* Check for the exact key in the GKeyFile if required. Most setting - * properties map 1:1 to a key in the GKeyFile, but for those properties - * like IP addresses and routes where more than one value is actually - * encoded by the setting property, this won't be true. - */ - if (check_for_key && !nm_keyfile_plugin_kf_has_key (info->keyfile, setting_name, key, &err)) { - /* Key doesn't exist or an error ocurred, thus nothing to do. */ - if (err) { - nm_log_warn (LOGD_SETTINGS, "Error loading setting '%s' value: %s", setting_name, err->message); - g_error_free (err); - } - return; - } - - /* If there's a custom parser for this key, handle that before the generic - * parsers below. - */ - if (parser->setting_name) { - (*parser->parser) (setting, key, info->keyfile, info->keyfile_path); - return; - } - - type = G_VALUE_TYPE (value); - - if (type == G_TYPE_STRING) { - char *str_val; - - str_val = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key, NULL); - g_object_set (setting, key, str_val, NULL); - g_free (str_val); - } else if (type == G_TYPE_UINT) { - int int_val; - - int_val = nm_keyfile_plugin_kf_get_integer (info->keyfile, setting_name, key, NULL); - if (int_val < 0) - nm_log_warn (LOGD_SETTINGS, "Casting negative value (%i) to uint", int_val); - g_object_set (setting, key, int_val, NULL); - } else if (type == G_TYPE_INT) { - int int_val; - - int_val = nm_keyfile_plugin_kf_get_integer (info->keyfile, setting_name, key, NULL); - g_object_set (setting, key, int_val, NULL); - } else if (type == G_TYPE_BOOLEAN) { - gboolean bool_val; - - bool_val = nm_keyfile_plugin_kf_get_boolean (info->keyfile, setting_name, key, NULL); - g_object_set (setting, key, bool_val, NULL); - } else if (type == G_TYPE_CHAR) { - int int_val; - - int_val = nm_keyfile_plugin_kf_get_integer (info->keyfile, setting_name, key, NULL); - if (int_val < G_MININT8 || int_val > G_MAXINT8) - nm_log_warn (LOGD_SETTINGS, "Casting value (%i) to char", int_val); - - g_object_set (setting, key, int_val, NULL); - } else if (type == G_TYPE_UINT64) { - char *tmp_str; - guint64 uint_val; - - tmp_str = nm_keyfile_plugin_kf_get_value (info->keyfile, setting_name, key, NULL); - uint_val = g_ascii_strtoull (tmp_str, NULL, 10); - g_free (tmp_str); - g_object_set (setting, key, uint_val, NULL); - } else if (type == G_TYPE_INT64) { - char *tmp_str; - gint64 int_val; - - tmp_str = nm_keyfile_plugin_kf_get_value (info->keyfile, setting_name, key, NULL); - int_val = nm_utils_ascii_str_to_int64 (tmp_str, 10, G_MININT64, G_MAXINT64, 0); - errsv = errno; - if (errsv) - nm_log_warn (LOGD_SETTINGS, "Invalid int64 value (%s)", tmp_str); - else - g_object_set (setting, key, int_val, NULL); - g_free (tmp_str); - } else if (type == G_TYPE_BYTES) { - gint *tmp; - GByteArray *array; - GBytes *bytes; - gsize length; - int i; - - tmp = nm_keyfile_plugin_kf_get_integer_list (info->keyfile, setting_name, key, &length, NULL); - - array = g_byte_array_sized_new (length); - for (i = 0; i < length; i++) { - int val = tmp[i]; - unsigned char v = (unsigned char) (val & 0xFF); - - if (val < 0 || val > 255) { - nm_log_warn (LOGD_SETTINGS, "%s: %s / %s ignoring invalid byte element '%d' (not " - " between 0 and 255 inclusive)", __func__, setting_name, - key, val); - } else - g_byte_array_append (array, (const unsigned char *) &v, sizeof (v)); - } - - bytes = g_byte_array_free_to_bytes (array); - g_object_set (setting, key, bytes, NULL); - g_bytes_unref (bytes); - g_free (tmp); - } else if (type == G_TYPE_STRV) { - gchar **sa; - gsize length; - - sa = nm_keyfile_plugin_kf_get_string_list (info->keyfile, setting_name, key, &length, NULL); - g_object_set (setting, key, sa, NULL); - g_strfreev (sa); - } else if (type == G_TYPE_HASH_TABLE) { - read_hash_of_string (info->keyfile, setting, key); - } else if (type == G_TYPE_ARRAY) { - if (!read_array_of_uint (info->keyfile, setting, key)) { - nm_log_warn (LOGD_SETTINGS, "Unhandled setting property type (read): '%s/%s' : '%s'", - setting_name, key, G_VALUE_TYPE_NAME (value)); - } - } else if (G_VALUE_HOLDS_FLAGS (value)) { - guint64 uint_val; - - /* Flags are guint but GKeyFile has no uint reader, just uint64 */ - uint_val = nm_keyfile_plugin_kf_get_uint64 (info->keyfile, setting_name, key, &err); - if (!err) { - if (uint_val <= G_MAXUINT) - g_object_set (setting, key, (guint) uint_val, NULL); - else { - nm_log_warn (LOGD_SETTINGS, "Too large FLAGS property (read): '%s/%s' : '%s'", - setting_name, key, G_VALUE_TYPE_NAME (value)); - } - } - g_clear_error (&err); - } else if (G_VALUE_HOLDS_ENUM (value)) { - gint int_val; - - int_val = nm_keyfile_plugin_kf_get_integer (info->keyfile, setting_name, key, &err); - if (!err) - g_object_set (setting, key, (gint) int_val, NULL); - g_clear_error (&err); - } else { - nm_log_warn (LOGD_SETTINGS, "Unhandled setting property type (read): '%s/%s' : '%s'", - setting_name, key, G_VALUE_TYPE_NAME (value)); - } -} - -static NMSetting * -read_setting (GKeyFile *file, const char *keyfile_path, const char *group) -{ - NMSetting *setting = NULL; - ReadInfo info = { file, keyfile_path }; - const char *alias; - GType type; - - alias = nm_keyfile_plugin_get_setting_name_for_alias (group); - if (alias) - group = alias; - - type = nm_setting_lookup_type (group); - if (type) { - setting = g_object_new (type, NULL); - nm_setting_enumerate_values (setting, read_one_setting_value, &info); - } else - nm_log_warn (LOGD_SETTINGS, "Invalid setting name '%s'", group); - - return setting; -} - -static void -read_vpn_secrets (GKeyFile *file, NMSettingVpn *s_vpn) -{ - char **keys, **iter; - - keys = nm_keyfile_plugin_kf_get_keys (file, VPN_SECRETS_GROUP, NULL, NULL); - for (iter = keys; *iter; iter++) { - char *secret; - - secret = nm_keyfile_plugin_kf_get_string (file, VPN_SECRETS_GROUP, *iter, NULL); - if (secret) { - nm_setting_vpn_add_secret (s_vpn, *iter, secret); - g_free (secret); - } - } - g_strfreev (keys); -} - NMConnection * nm_keyfile_plugin_connection_from_file (const char *filename, GError **error) { GKeyFile *key_file; struct stat statbuf; - gboolean bad_permissions; NMConnection *connection = NULL; - NMSettingConnection *s_con; - NMSetting *setting; - gchar **groups; - gsize length; - int i; - gboolean vpn_secrets = FALSE; GError *verify_error = NULL; if (stat (filename, &statbuf) != 0 || !S_ISREG (statbuf.st_mode)) { @@ -1270,90 +100,34 @@ nm_keyfile_plugin_connection_from_file (const char *filename, GError **error) return NULL; } - bad_permissions = statbuf.st_mode & 0077; - - if (bad_permissions) { + if (statbuf.st_mode & 0077) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "File permissions (%o) were insecure", statbuf.st_mode); return NULL; } - key_file = g_key_file_new (); - if (!g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, error)) - goto out; - - connection = nm_simple_connection_new (); - - groups = g_key_file_get_groups (key_file, &length); - for (i = 0; i < length; i++) { - /* Only read out secrets when needed */ - if (!strcmp (groups[i], VPN_SECRETS_GROUP)) { - vpn_secrets = TRUE; - continue; - } - - setting = read_setting (key_file, filename, groups[i]); - if (setting) - nm_connection_add_setting (connection, setting); - } - - 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)); - } - - /* Make sure that we have 'id' even if not explictly specified in the keyfile */ - if (!nm_setting_connection_get_id (s_con)) { - char *base_name; - - base_name = g_path_get_basename (filename); - g_object_set (s_con, NM_SETTING_CONNECTION_ID, base_name, NULL); - g_free (base_name); - } - - /* Make sure that we have 'uuid' even if not explictly specified in the keyfile */ - if (!nm_setting_connection_get_uuid (s_con)) { - char *hashed_uuid; - - hashed_uuid = nm_utils_uuid_generate_from_strings ("keyfile", filename, NULL); - g_object_set (s_con, NM_SETTING_CONNECTION_UUID, hashed_uuid, NULL); - g_free (hashed_uuid); - } - - /* Make sure that we have 'interface-name' even if it was specified in the - * "wrong" (ie, deprecated) group. - */ - if ( !nm_setting_connection_get_interface_name (s_con) - && nm_setting_connection_get_connection_type (s_con)) { - char *interface_name; - - interface_name = g_key_file_get_string (key_file, - nm_setting_connection_get_connection_type (s_con), - "interface-name", - NULL); - if (interface_name) { - g_object_set (s_con, NM_SETTING_CONNECTION_INTERFACE_NAME, interface_name, NULL); - g_free (interface_name); + if (!NM_FLAGS_HAS (nm_utils_get_testing (), NM_UTILS_TEST_NO_KEYFILE_OWNER_CHECK)) { + if (statbuf.st_uid != 0) { + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "File owner (%o) is insecure", + statbuf.st_mode); + return NULL; } } - /* Handle vpn secrets after the 'vpn' setting was read */ - if (vpn_secrets) { - NMSettingVpn *s_vpn; - - s_vpn = nm_connection_get_setting_vpn (connection); - if (s_vpn) - read_vpn_secrets (key_file, s_vpn); - } + key_file = g_key_file_new (); + if (!g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, error)) + goto out; - g_strfreev (groups); + connection = nm_keyfile_read (key_file, filename, NULL, _handler_read, NULL, error); + if (!connection) + goto out; /* Normalize and verify the connection */ if (!nm_connection_normalize (connection, NULL, NULL, &verify_error)) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "invalid connection: %s", + "invalid connection: %s", verify_error->message); g_clear_error (&verify_error); g_object_unref (connection); @@ -1364,3 +138,4 @@ out: g_key_file_free (key_file); return connection; } + diff --git a/src/settings/plugins/keyfile/tests/Makefile.in b/src/settings/plugins/keyfile/tests/Makefile.in index c4d12459..a65110f6 100644 --- a/src/settings/plugins/keyfile/tests/Makefile.in +++ b/src/settings/plugins/keyfile/tests/Makefile.in @@ -183,7 +183,7 @@ am__recursive_targets = \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ - distdir + check recheck distdir am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is @@ -225,9 +225,190 @@ am__tty_colors = { \ std='[m'; \ fi; \ } +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__recheck_rx = ^[ ]*:recheck:[ ]* +am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* +am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* +# A command that, given a newline-separated list of test names on the +# standard input, print the name of the tests that are to be re-run +# upon "make recheck". +am__list_recheck_tests = $(AWK) '{ \ + recheck = 1; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + { \ + if ((getline line2 < ($$0 ".log")) < 0) \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ + { \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ + { \ + break; \ + } \ + }; \ + if (recheck) \ + print $$0; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# A command that, given a newline-separated list of test names on the +# standard input, create the global log from their .trs and .log files. +am__create_global_log = $(AWK) ' \ +function fatal(msg) \ +{ \ + print "fatal: making $@: " msg | "cat >&2"; \ + exit 1; \ +} \ +function rst_section(header) \ +{ \ + print header; \ + len = length(header); \ + for (i = 1; i <= len; i = i + 1) \ + printf "="; \ + printf "\n\n"; \ +} \ +{ \ + copy_in_global_log = 1; \ + global_test_result = "RUN"; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".trs"); \ + if (line ~ /$(am__global_test_result_rx)/) \ + { \ + sub("$(am__global_test_result_rx)", "", line); \ + sub("[ ]*$$", "", line); \ + global_test_result = line; \ + } \ + else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ + copy_in_global_log = 0; \ + }; \ + if (copy_in_global_log) \ + { \ + rst_section(global_test_result ": " $$0); \ + while ((rc = (getline line < ($$0 ".log"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".log"); \ + print line; \ + }; \ + printf "\n"; \ + }; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# Restructured Text title. +am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } +# Solaris 10 'make', and several other traditional 'make' implementations, +# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it +# by disabling -e (using the XSI extension "set +e") if it's set. +am__sh_e_setup = case $$- in *e*) set +e;; esac +# Default flags passed to test drivers. +am__common_driver_flags = \ + --color-tests "$$am__color_tests" \ + --enable-hard-errors "$$am__enable_hard_errors" \ + --expect-failure "$$am__expect_failure" +# To be inserted before the command running the test. Creates the +# directory for the log if needed. Stores in $dir the directory +# containing $f, in $tst the test, in $log the log. Executes the +# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and +# passes TESTS_ENVIRONMENT. Set up options for the wrapper that +# will run the test scripts (or their associated LOG_COMPILER, if +# thy have one). +am__check_pre = \ +$(am__sh_e_setup); \ +$(am__vpath_adj_setup) $(am__vpath_adj) \ +$(am__tty_colors); \ +srcdir=$(srcdir); export srcdir; \ +case "$@" in \ + */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ + *) am__odir=.;; \ +esac; \ +test "x$$am__odir" = x"." || test -d "$$am__odir" \ + || $(MKDIR_P) "$$am__odir" || exit $$?; \ +if test -f "./$$f"; then dir=./; \ +elif test -f "$$f"; then dir=; \ +else dir="$(srcdir)/"; fi; \ +tst=$$dir$$f; log='$@'; \ +if test -n '$(DISABLE_HARD_ERRORS)'; then \ + am__enable_hard_errors=no; \ +else \ + am__enable_hard_errors=yes; \ +fi; \ +case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ + am__expect_failure=yes;; \ + *) \ + am__expect_failure=no;; \ +esac; \ +$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) +# A shell command to get the names of the tests scripts with any registered +# extension removed (i.e., equivalently, the names of the test logs, with +# the '.log' extension removed). The result is saved in the shell variable +# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, +# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", +# since that might cause problem with VPATH rewrites for suffix-less tests. +# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. +am__set_TESTS_bases = \ + bases='$(TEST_LOGS)'; \ + bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ + bases=`echo $$bases` +RECHECK_LOGS = $(TEST_LOGS) +TEST_SUITE_LOG = test-suite.log +TEST_EXTENSIONS = @EXEEXT@ .test +LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) +am__set_b = \ + case '$@' in \ + */*) \ + case '$*' in \ + */*) b='$*';; \ + *) b=`echo '$@' | sed 's/\.log$$//'`; \ + esac;; \ + *) \ + b='$*';; \ + esac +am__test_logs1 = $(TESTS:=.log) +am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) +TEST_LOGS = $(am__test_logs2:.test.log=.log) +TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/build-aux/test-driver +TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ + $(TEST_LOG_FLAGS) DIST_SUBDIRS = keyfiles am__DIST_COMMON = $(srcdir)/Makefile.in \ - $(top_srcdir)/build-aux/depcomp + $(top_srcdir)/build-aux/depcomp \ + $(top_srcdir)/build-aux/test-driver DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ @@ -258,6 +439,7 @@ ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -371,6 +553,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ +LOG_DRIVER = @LOG_DRIVER@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ @@ -546,7 +729,7 @@ with_valgrind = @with_valgrind@ all: all-recursive .SUFFIXES: -.SUFFIXES: .c .lo .o .obj +.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ @@ -769,98 +952,168 @@ cscopelist-am: $(am__tagged_files) distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -check-TESTS: $(TESTS) - @failed=0; all=0; xfail=0; xpass=0; skip=0; \ - srcdir=$(srcdir); export srcdir; \ - list=' $(TESTS) '; \ - $(am__tty_colors); \ - if test -n "$$list"; then \ - for tst in $$list; do \ - if test -f ./$$tst; then dir=./; \ - elif test -f $$tst; then dir=; \ - else dir="$(srcdir)/"; fi; \ - if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \ - all=`expr $$all + 1`; \ - case " $(XFAIL_TESTS) " in \ - *[\ \ ]$$tst[\ \ ]*) \ - xpass=`expr $$xpass + 1`; \ - failed=`expr $$failed + 1`; \ - col=$$red; res=XPASS; \ - ;; \ - *) \ - col=$$grn; res=PASS; \ - ;; \ - esac; \ - elif test $$? -ne 77; then \ - all=`expr $$all + 1`; \ - case " $(XFAIL_TESTS) " in \ - *[\ \ ]$$tst[\ \ ]*) \ - xfail=`expr $$xfail + 1`; \ - col=$$lgn; res=XFAIL; \ - ;; \ - *) \ - failed=`expr $$failed + 1`; \ - col=$$red; res=FAIL; \ - ;; \ - esac; \ - else \ - skip=`expr $$skip + 1`; \ - col=$$blu; res=SKIP; \ - fi; \ - echo "$${col}$$res$${std}: $$tst"; \ - done; \ - if test "$$all" -eq 1; then \ - tests="test"; \ - All=""; \ - else \ - tests="tests"; \ - All="All "; \ +# Recover from deleted '.trs' file; this should ensure that +# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create +# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells +# to avoid problems with "make -n". +.log.trs: + rm -f $< $@ + $(MAKE) $(AM_MAKEFLAGS) $< + +# Leading 'am--fnord' is there to ensure the list of targets does not +# expand to empty, as could happen e.g. with make check TESTS=''. +am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) +am--force-recheck: + @: + +$(TEST_SUITE_LOG): $(TEST_LOGS) + @$(am__set_TESTS_bases); \ + am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ + redo_bases=`for i in $$bases; do \ + am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ + done`; \ + if test -n "$$redo_bases"; then \ + redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ + redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ + if $(am__make_dryrun); then :; else \ + rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ fi; \ - if test "$$failed" -eq 0; then \ - if test "$$xfail" -eq 0; then \ - banner="$$All$$all $$tests passed"; \ - else \ - if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ - banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ - fi; \ - else \ - if test "$$xpass" -eq 0; then \ - banner="$$failed of $$all $$tests failed"; \ + fi; \ + if test -n "$$am__remaking_logs"; then \ + echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ + "recursion detected" >&2; \ + elif test -n "$$redo_logs"; then \ + am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ + fi; \ + if $(am__make_dryrun); then :; else \ + st=0; \ + errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ + for i in $$redo_bases; do \ + test -f $$i.trs && test -r $$i.trs \ + || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ + test -f $$i.log && test -r $$i.log \ + || { echo "$$errmsg $$i.log" >&2; st=1; }; \ + done; \ + test $$st -eq 0 || exit 1; \ + fi + @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ + ws='[ ]'; \ + results=`for b in $$bases; do echo $$b.trs; done`; \ + test -n "$$results" || results=/dev/null; \ + all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ + pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ + fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ + skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ + xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ + xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ + error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ + if test `expr $$fail + $$xpass + $$error` -eq 0; then \ + success=true; \ + else \ + success=false; \ + fi; \ + br='==================='; br=$$br$$br$$br$$br; \ + result_count () \ + { \ + if test x"$$1" = x"--maybe-color"; then \ + maybe_colorize=yes; \ + elif test x"$$1" = x"--no-color"; then \ + maybe_colorize=no; \ else \ - if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ - banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ + echo "$@: invalid 'result_count' usage" >&2; exit 4; \ fi; \ - fi; \ - dashes="$$banner"; \ - skipped=""; \ - if test "$$skip" -ne 0; then \ - if test "$$skip" -eq 1; then \ - skipped="($$skip test was not run)"; \ + shift; \ + desc=$$1 count=$$2; \ + if test $$maybe_colorize = yes && test $$count -gt 0; then \ + color_start=$$3 color_end=$$std; \ else \ - skipped="($$skip tests were not run)"; \ + color_start= color_end=; \ fi; \ - test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ - dashes="$$skipped"; \ - fi; \ - report=""; \ - if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ - report="Please report to $(PACKAGE_BUGREPORT)"; \ - test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ - dashes="$$report"; \ - fi; \ - dashes=`echo "$$dashes" | sed s/./=/g`; \ - if test "$$failed" -eq 0; then \ - col="$$grn"; \ - else \ - col="$$red"; \ - fi; \ - echo "$${col}$$dashes$${std}"; \ - echo "$${col}$$banner$${std}"; \ - test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \ - test -z "$$report" || echo "$${col}$$report$${std}"; \ - echo "$${col}$$dashes$${std}"; \ - test "$$failed" -eq 0; \ - else :; fi + echo "$${color_start}# $$desc $$count$${color_end}"; \ + }; \ + create_testsuite_report () \ + { \ + result_count $$1 "TOTAL:" $$all "$$brg"; \ + result_count $$1 "PASS: " $$pass "$$grn"; \ + result_count $$1 "SKIP: " $$skip "$$blu"; \ + result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ + result_count $$1 "FAIL: " $$fail "$$red"; \ + result_count $$1 "XPASS:" $$xpass "$$red"; \ + result_count $$1 "ERROR:" $$error "$$mgn"; \ + }; \ + { \ + echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ + $(am__rst_title); \ + create_testsuite_report --no-color; \ + echo; \ + echo ".. contents:: :depth: 2"; \ + echo; \ + for b in $$bases; do echo $$b; done \ + | $(am__create_global_log); \ + } >$(TEST_SUITE_LOG).tmp || exit 1; \ + mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ + if $$success; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ + fi; \ + echo "$${col}$$br$${std}"; \ + echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \ + echo "$${col}$$br$${std}"; \ + create_testsuite_report --maybe-color; \ + echo "$$col$$br$$std"; \ + if $$success; then :; else \ + echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ + if test -n "$(PACKAGE_BUGREPORT)"; then \ + echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ + fi; \ + echo "$$col$$br$$std"; \ + fi; \ + $$success || exit 1 + +check-TESTS: + @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list + @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + trs_list=`for i in $$bases; do echo $$i.trs; done`; \ + log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ + exit $$?; +recheck: all + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + bases=`for i in $$bases; do echo $$i; done \ + | $(am__list_recheck_tests)` || exit 1; \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + log_list=`echo $$log_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ + am__force_recheck=am--force-recheck \ + TEST_LOGS="$$log_list"; \ + exit $$? +test-keyfile.log: test-keyfile$(EXEEXT) + @p='test-keyfile$(EXEEXT)'; \ + b='test-keyfile'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +.test.log: + @p='$<'; \ + $(am__set_b); \ + $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +@am__EXEEXT_TRUE@.test$(EXEEXT).log: +@am__EXEEXT_TRUE@ @p='$<'; \ +@am__EXEEXT_TRUE@ $(am__set_b); \ +@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ +@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ +@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ +@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ @@ -943,6 +1196,9 @@ install-strip: "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: + -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) + -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) + -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) clean-generic: @@ -1038,8 +1294,8 @@ uninstall-am: install-strip installcheck installcheck-am installdirs \ installdirs-am maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ - mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ - uninstall-am + mostlyclean-libtool pdf pdf-am ps ps-am recheck tags tags-am \ + uninstall uninstall-am .PRECIOUS: Makefile diff --git a/src/settings/plugins/keyfile/tests/keyfiles/Makefile.in b/src/settings/plugins/keyfile/tests/keyfiles/Makefile.in index f7a0c45e..de7af82b 100644 --- a/src/settings/plugins/keyfile/tests/keyfiles/Makefile.in +++ b/src/settings/plugins/keyfile/tests/keyfiles/Makefile.in @@ -135,6 +135,7 @@ ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -248,6 +249,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ +LOG_DRIVER = @LOG_DRIVER@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ diff --git a/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Blob b/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Blob index 9f4ef62f..62e6ae31 100644 --- a/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Blob +++ b/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Blob @@ -8,8 +8,8 @@ type=802-3-ethernet eap=tls; identity=Bill Smith ca-cert=48;130;2;52;48;130;1;161;2;16;2;173;102;126;78;69;254;94;87;111;60;152;25;94;221;192;48;13;6;9;42;134;72;134;247;13;1;1;2;5;0;48;95;49;11;48;9;6;3;85;4;6;19;2;85;83;49;32;48;30;6;3;85;4;10;19;23;82;83;65;32;68;97;116;97;32;83;101;99;117;114;105;116;121;44;32;73;110;99;46;49;46;48;44;6;3;85;4;11;19;37;83;101;99;117;114;101;32;83;101;114;118;101;114;32;67;101;114;116;105;102;105;99;97;116;105;111;110;32;65;117;116;104;111;114;105;116;121;48;30;23;13;57;52;49;49;48;57;48;48;48;48;48;48;90;23;13;49;48;48;49;48;55;50;51;53;57;53;57;90;48;95;49;11;48;9;6;3;85;4;6;19;2;85;83;49;32;48;30;6;3;85;4;10;19;23;82;83;65;32;68;97;116;97;32;83;101;99;117;114;105;116;121;44;32;73;110;99;46;49;46;48;44;6;3;85;4;11;19;37;83;101;99;117;114;101;32;83;101;114;118;101;114;32;67;101;114;116;105;102;105;99;97;116;105;111;110;32;65;117;116;104;111;114;105;116;121;48;129;155;48;13;6;9;42;134;72;134;247;13;1;1;1;5;0;3;129;137;0;48;129;133;2;126;0;146;206;122;193;174;131;62;90;170;137;131;87;172;37;1;118;12;173;174;142;44;55;206;235;53;120;100;84;3;229;132;64;81;201;191;143;8;226;138;130;8;210;22;134;55;85;233;177;33;2;173;118;104;129;154;5;162;75;201;75;37;102;34;86;108;136;7;143;247;129;89;109;132;7;101;112;19;113;118;62;155;119;76;227;80;137;86;152;72;185;29;167;41;26;19;46;74;17;89;156;30;21;213;73;84;44;115;58;105;130;177;151;57;156;109;112;103;72;229;221;45;214;200;30;123;2;3;1;0;1;48;13;6;9;42;134;72;134;247;13;1;1;2;5;0;3;126;0;101;221;126;225;178;236;176;226;58;224;236;113;70;154;25;17;184;211;199;160;180;3;64;38;2;62;9;156;225;18;179;209;90;246;55;165;183;97;3;182;91;22;105;59;198;68;8;12;136;83;12;107;151;73;199;62;53;220;108;185;187;170;223;92;187;58;47;147;96;182;169;75;77;242;32;247;205;95;127;100;123;142;220;0;92;215;250;119;202;57;22;89;111;14;234;211;181;131;127;77;77;66;86;118;180;201;95;4;248;56;248;235;210;95;117;95;205;123;252;229;142;128;124;252;80; -client-cert=102;105;108;101;58;47;47;47;104;111;109;101;47;100;99;98;119;47;68;101;115;107;116;111;112;47;99;101;114;116;105;110;102;114;97;47;99;108;105;101;110;116;46;112;101;109;0; -private-key=102;105;108;101;58;47;47;47;104;111;109;101;47;100;99;98;119;47;68;101;115;107;116;111;112;47;99;101;114;116;105;110;102;114;97;47;99;108;105;101;110;116;46;112;101;109;0; +client-cert=102;105;108;101;58;47;47;47;67;65;83;65;47;100;99;98;119;47;68;101;115;107;116;111;112;47;99;101;114;116;105;110;102;114;97;47;99;108;105;101;110;116;46;112;101;109;0; +private-key=102;105;108;101;58;47;47;47;67;65;83;65;47;100;99;98;119;47;68;101;115;107;116;111;112;47;99;101;114;116;105;110;102;114;97;47;99;108;105;101;110;116;46;112;101;109;0; private-key-password=12345testing [ipv4] diff --git a/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Old b/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Old index 61afdd91..d3da598c 100644 --- a/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Old +++ b/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_TLS_Old @@ -7,9 +7,9 @@ type=802-3-ethernet [802-1x] eap=tls; identity=Bill Smith -ca-cert=102;105;108;101;58;47;47;47;104;111;109;101;47;100;99;98;119;47;68;101;115;107;116;111;112;47;99;101;114;116;105;110;102;114;97;47;67;65;47;101;97;112;116;101;115;116;95;99;97;95;99;101;114;116;46;112;101;109;0; -client-cert=102;105;108;101;58;47;47;47;104;111;109;101;47;100;99;98;119;47;68;101;115;107;116;111;112;47;99;101;114;116;105;110;102;114;97;47;99;108;105;101;110;116;46;112;101;109;0; -private-key=102;105;108;101;58;47;47;47;104;111;109;101;47;100;99;98;119;47;68;101;115;107;116;111;112;47;99;101;114;116;105;110;102;114;97;47;99;108;105;101;110;116;46;112;101;109;0; +ca-cert=102;105;108;101;58;47;47;47;67;65;83;65;47;100;99;98;119;47;68;101;115;107;116;111;112;47;99;101;114;116;105;110;102;114;97;47;67;65;47;101;97;112;116;101;115;116;95;99;97;95;99;101;114;116;46;112;101;109;0; +client-cert=102;105;108;101;58;47;47;47;67;65;83;65;47;100;99;98;119;47;68;101;115;107;116;111;112;47;99;101;114;116;105;110;102;114;97;47;99;108;105;101;110;116;46;112;101;109;0; +private-key=102;105;108;101;58;47;47;47;67;65;83;65;47;100;99;98;119;47;68;101;115;107;116;111;112;47;99;101;114;116;105;110;102;114;97;47;99;108;105;101;110;116;46;112;101;109;0; private-key-password=12345testing [ipv4] diff --git a/src/settings/plugins/keyfile/tests/test-keyfile.c b/src/settings/plugins/keyfile/tests/test-keyfile.c index edece1b2..4fc44c7b 100644 --- a/src/settings/plugins/keyfile/tests/test-keyfile.c +++ b/src/settings/plugins/keyfile/tests/test-keyfile.c @@ -33,6 +33,7 @@ #include "reader.h" #include "writer.h" +#include "utils.h" #include "nm-test-utils.h" @@ -99,33 +100,33 @@ test_read_valid_wired_connection (void) const char *expected6_dnssearch3 = "gnu.org"; g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*ipv4.addresses1*semicolon at the end*"); + "*ipv4.addresses:*semicolon at the end*addresses1*"); g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*ipv4.addresses2*semicolon at the end*"); + "*ipv4.addresses:*semicolon at the end*addresses2*"); g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, - "*Missing prefix length*ipv4.address4*"); + "*missing prefix length*address4*"); g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, - "*Missing prefix length*ipv4.address5*"); + "*missing prefix length*address5*"); g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*ipv4.routes2*semicolon at the end*"); + "*ipv4.routes*semicolon at the end*routes2*"); g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*ipv4.routes3*semicolon at the end*"); + "*ipv4.routes*semicolon at the end*routes3*"); g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*ipv4.routes5*semicolon at the end*"); + "*ipv4.routes*semicolon at the end*routes5*"); g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*ipv4.routes8*semicolon at the end*"); + "*ipv4.routes*semicolon at the end*routes8*"); g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, - "*Missing prefix length*ipv6.address4*"); + "*missing prefix length*address4*"); g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*ipv6.address5*semicolon at the end*"); + "*ipv6.address*semicolon at the end*address5*"); g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, - "*Missing prefix length*ipv6.address5*"); + "*missing prefix length*address5*"); g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*ipv6.address7*semicolon at the end*"); + "*ipv6.address*semicolon at the end*address7*"); g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*ipv6.routes1*semicolon at the end*"); + "*ipv6.routes*semicolon at the end*routes1*"); g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*ipv6.route6*semicolon at the end*"); + "*ipv6.route*semicolon at the end*route6*"); connection = nm_keyfile_plugin_connection_from_file (TEST_WIRED_FILE, NULL); g_test_assert_expected_messages (); ASSERT (connection != NULL, @@ -772,11 +773,11 @@ test_read_wired_mac_case (void) const char *expected_uuid = "4e80a56d-c99f-4aad-a6dd-b449bc398c57"; g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*ipv4.addresses1*semicolon at the end*"); + "*ipv4.addresses*semicolon at the end*addresses1*"); g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*ipv4.addresses2*semicolon at the end*"); + "*ipv4.addresses*semicolon at the end*addresses2*"); g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, - "*ipv6.routes1*semicolon at the end*"); + "*ipv6.routes*semicolon at the end*routes1*"); connection = nm_keyfile_plugin_connection_from_file (TEST_WIRED_MAC_CASE_FILE, NULL); g_test_assert_expected_messages (); ASSERT (connection != NULL, @@ -2126,6 +2127,10 @@ test_read_wired_8021x_tls_blob_connection (void) gboolean success; GBytes *blob; + g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, + "*<warn> keyfile: 802-1x.client-cert: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*"); + g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, + "*<warn> keyfile: 802-1x.private-key: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*"); connection = nm_keyfile_plugin_connection_from_file (TEST_WIRED_TLS_BLOB_FILE, &error); if (connection == NULL) { g_assert (error); @@ -2173,10 +2178,10 @@ test_read_wired_8021x_tls_blob_connection (void) g_assert_cmpint (g_bytes_get_size (blob), ==, 568); tmp = nm_setting_802_1x_get_client_cert_path (s_8021x); - g_assert_cmpstr (tmp, ==, "/home/dcbw/Desktop/certinfra/client.pem"); + g_assert_cmpstr (tmp, ==, "/CASA/dcbw/Desktop/certinfra/client.pem"); tmp = nm_setting_802_1x_get_private_key_path (s_8021x); - g_assert_cmpstr (tmp, ==, "/home/dcbw/Desktop/certinfra/client.pem"); + g_assert_cmpstr (tmp, ==, "/CASA/dcbw/Desktop/certinfra/client.pem"); g_object_unref (connection); } @@ -2258,6 +2263,12 @@ test_read_wired_8021x_tls_old_connection (void) const char *tmp; gboolean success; + g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, + "*<warn> keyfile: 802-1x.ca-cert: certificate or key file '/CASA/dcbw/Desktop/certinfra/CA/eaptest_ca_cert.pem' does not exist*"); + g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, + "*<warn> keyfile: 802-1x.client-cert: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*"); + g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, + "*<warn> keyfile: 802-1x.private-key: certificate or key file '/CASA/dcbw/Desktop/certinfra/client.pem' does not exist*"); connection = nm_keyfile_plugin_connection_from_file (TEST_WIRED_TLS_OLD_FILE, &error); if (connection == NULL) { g_assert (error); @@ -2291,13 +2302,13 @@ test_read_wired_8021x_tls_old_connection (void) g_assert (g_strcmp0 (tmp, "12345testing") == 0); tmp = nm_setting_802_1x_get_ca_cert_path (s_8021x); - g_assert (g_strcmp0 (tmp, "/home/dcbw/Desktop/certinfra/CA/eaptest_ca_cert.pem") == 0); + g_assert (g_strcmp0 (tmp, "/CASA/dcbw/Desktop/certinfra/CA/eaptest_ca_cert.pem") == 0); tmp = nm_setting_802_1x_get_client_cert_path (s_8021x); - g_assert (g_strcmp0 (tmp, "/home/dcbw/Desktop/certinfra/client.pem") == 0); + g_assert (g_strcmp0 (tmp, "/CASA/dcbw/Desktop/certinfra/client.pem") == 0); tmp = nm_setting_802_1x_get_private_key_path (s_8021x); - g_assert (g_strcmp0 (tmp, "/home/dcbw/Desktop/certinfra/client.pem") == 0); + g_assert (g_strcmp0 (tmp, "/CASA/dcbw/Desktop/certinfra/client.pem") == 0); g_object_unref (connection); } @@ -3566,10 +3577,61 @@ test_write_flags_property (void) g_object_unref (connection); } +/*****************************************************************************/ + +static void +_escape_filename (const char *filename, gboolean would_be_ignored) +{ + gs_free char *esc = NULL; + + g_assert (filename && filename[0]); + + if (!!would_be_ignored != !!nm_keyfile_plugin_utils_should_ignore_file (filename)) { + if (would_be_ignored) + g_error ("We expect filename \"%s\" to be ignored, but it isn't", filename); + else + g_error ("We expect filename \"%s\" not to be ignored, but it is", filename); + } + + esc = nm_keyfile_plugin_utils_escape_filename (filename); + g_assert (esc && esc[0]); + g_assert (!strchr (esc, '/')); + + if (nm_keyfile_plugin_utils_should_ignore_file (esc)) + g_error ("Escaping filename \"%s\" yielded \"%s\", but this is ignored", filename, esc); +} + +static void +test_nm_keyfile_plugin_utils_escape_filename (void) +{ + _escape_filename ("ab", FALSE); + _escape_filename (".vim-file.swp", TRUE); + _escape_filename (".vim-file.Swp", TRUE); + _escape_filename (".vim-file.SWP", TRUE); + _escape_filename (".vim-file.swpx", TRUE); + _escape_filename (".vim-file.Swpx", TRUE); + _escape_filename (".vim-file.SWPX", TRUE); + _escape_filename (".pem-file.pem", TRUE); + _escape_filename (".pem-file.Pem", TRUE); + _escape_filename (".pem-file.PEM", TRUE); + _escape_filename (".pem-file.der", TRUE); + _escape_filename (".pem-file.Der", TRUE); + _escape_filename (".mkstemp.ABCEDF", TRUE); + _escape_filename (".mkstemp.abcdef", TRUE); + _escape_filename (".mkstemp.123456", TRUE); + _escape_filename (".mkstemp.A23456", TRUE); + _escape_filename (".#emacs-locking", TRUE); + _escape_filename ("file-with-tilde~", TRUE); + _escape_filename (".file-with-dot", TRUE); +} + +/*****************************************************************************/ + NMTST_DEFINE (); int main (int argc, char **argv) { + _nm_utils_set_testing (NM_UTILS_TEST_NO_KEYFILE_OWNER_CHECK); nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT"); /* The tests */ @@ -3636,6 +3698,8 @@ int main (int argc, char **argv) g_test_add_func ("/keyfile/test_read_flags_property ", test_read_flags_property); g_test_add_func ("/keyfile/test_write_flags_property ", test_write_flags_property); + g_test_add_func ("/keyfile/test_nm_keyfile_plugin_utils_escape_filename ", test_nm_keyfile_plugin_utils_escape_filename); + return g_test_run (); } diff --git a/src/settings/plugins/keyfile/utils.c b/src/settings/plugins/keyfile/utils.c index 3361d554..4809c642 100644 --- a/src/settings/plugins/keyfile/utils.c +++ b/src/settings/plugins/keyfile/utils.c @@ -23,6 +23,7 @@ #include <glib.h> #include <stdlib.h> #include <string.h> +#include "gsystem-local-alloc.h" #include "utils.h" #include <nm-setting-wired.h> #include <nm-setting-wireless.h> @@ -59,7 +60,7 @@ check_prefix (const char *base, const char *tag) len = strlen (base); tag_len = strlen (tag); - if ((len > tag_len) && !strncasecmp (base, tag, tag_len)) + if ((len > tag_len) && !g_ascii_strncasecmp (base, tag, tag_len)) return TRUE; return FALSE; } @@ -74,7 +75,7 @@ check_suffix (const char *base, const char *tag) len = strlen (base); tag_len = strlen (tag); - if ((len > tag_len) && !strcasecmp (base + len - tag_len, tag)) + if ((len > tag_len) && !g_ascii_strcasecmp (base + len - tag_len, tag)) return TRUE; return FALSE; } @@ -87,199 +88,61 @@ check_suffix (const char *base, const char *tag) gboolean nm_keyfile_plugin_utils_should_ignore_file (const char *filename) { - char *base; - gboolean ignore = FALSE; + gs_free char *base = NULL; g_return_val_if_fail (filename != NULL, TRUE); base = g_path_get_basename (filename); g_return_val_if_fail (base != NULL, TRUE); - /* Ignore files with certain patterns */ - if ( (check_prefix (base, ".") && check_suffix (base, SWP_TAG)) /* vim temporary files: .filename.swp */ - || (check_prefix (base, ".") && check_suffix (base, SWPX_TAG)) /* vim temporary files: .filename.swpx */ - || check_suffix (base, PEM_TAG) /* 802.1x certificates and keys */ - || check_suffix (base, DER_TAG) /* 802.1x certificates and keys */ - || check_mkstemp_suffix (base) /* temporary files created by mkstemp() */ - || check_prefix (base, ".#") /* Emacs locking file (link) */ - || base[strlen (base) - 1] == '~') - ignore = TRUE; + /* Ignore hidden and backup files */ + /* should_ignore_file() must mirror escape_filename() */ + if (check_prefix (base, ".") || check_suffix (base, "~")) + return TRUE; + /* Ignore temporary files */ + if (check_mkstemp_suffix (base)) + return TRUE; + /* Ignore 802.1x certificates and keys */ + if (check_suffix (base, PEM_TAG) || check_suffix (base, DER_TAG)) + return TRUE; - g_free (base); - return ignore; + return FALSE; } -typedef struct { - const char *setting; - const char *alias; -} SettingAlias; - -static const SettingAlias alias_list[] = { - { NM_SETTING_WIRED_SETTING_NAME, "ethernet" }, - { NM_SETTING_WIRELESS_SETTING_NAME, "wifi" }, - { NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, "wifi-security" }, -}; - -const char * -nm_keyfile_plugin_get_alias_for_setting_name (const char *setting_name) +char * +nm_keyfile_plugin_utils_escape_filename (const char *filename) { - guint i; + GString *str; + const char *f = filename; + const char ESCAPE_CHAR = '*'; - g_return_val_if_fail (setting_name != NULL, NULL); + /* keyfile used to escape with '*', do not change that behavior. + * But for newly added escapings, use '_' instead. */ + const char ESCAPE_CHAR2 = '_'; - for (i = 0; i < G_N_ELEMENTS (alias_list); i++) { - if (strcmp (setting_name, alias_list[i].setting) == 0) - return alias_list[i].alias; - } - return NULL; -} + g_return_val_if_fail (filename && filename[0], NULL); -const char * -nm_keyfile_plugin_get_setting_name_for_alias (const char *alias) -{ - guint i; - - g_return_val_if_fail (alias != NULL, NULL); + str = g_string_sized_new (60); - for (i = 0; i < G_N_ELEMENTS (alias_list); i++) { - if (strcmp (alias, alias_list[i].alias) == 0) - return alias_list[i].setting; + /* Convert '/' to ESCAPE_CHAR */ + for (f = filename; f[0]; f++) { + if (f[0] == '/') + g_string_append_c (str, ESCAPE_CHAR); + else + g_string_append_c (str, f[0]); } - return NULL; -} - -/**********************************************************************/ - -/* List helpers */ -#define DEFINE_KF_LIST_WRAPPER(stype, get_ctype, set_ctype) \ -get_ctype \ -nm_keyfile_plugin_kf_get_##stype##_list (GKeyFile *kf, \ - const char *group, \ - const char *key, \ - gsize *out_length, \ - GError **error) \ -{ \ - get_ctype list; \ - const char *alias; \ - GError *local = NULL; \ - \ - list = g_key_file_get_##stype##_list (kf, group, key, out_length, &local); \ - if (g_error_matches (local, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) { \ - alias = nm_keyfile_plugin_get_alias_for_setting_name (group); \ - if (alias) { \ - g_clear_error (&local); \ - list = g_key_file_get_##stype##_list (kf, alias, key, out_length, &local); \ - } \ - } \ - if (local) \ - g_propagate_error (error, local); \ - return list; \ -} \ - \ -void \ -nm_keyfile_plugin_kf_set_##stype##_list (GKeyFile *kf, \ - const char *group, \ - const char *key, \ - set_ctype list[], \ - gsize length) \ -{ \ - const char *alias; \ - \ - alias = nm_keyfile_plugin_get_alias_for_setting_name (group); \ - g_key_file_set_##stype##_list (kf, alias ? alias : group, key, list, length); \ -} - -DEFINE_KF_LIST_WRAPPER(integer, gint*, gint); -DEFINE_KF_LIST_WRAPPER(string, gchar **, const gchar* const); -/* Single value helpers */ -#define DEFINE_KF_WRAPPER(stype, get_ctype, set_ctype) \ -get_ctype \ -nm_keyfile_plugin_kf_get_##stype (GKeyFile *kf, \ - const char *group, \ - const char *key, \ - GError **error) \ -{ \ - get_ctype val; \ - const char *alias; \ - GError *local = NULL; \ - \ - val = g_key_file_get_##stype (kf, group, key, &local); \ - if (g_error_matches (local, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) { \ - alias = nm_keyfile_plugin_get_alias_for_setting_name (group); \ - if (alias) { \ - g_clear_error (&local); \ - val = g_key_file_get_##stype (kf, alias, key, &local); \ - } \ - } \ - if (local) \ - g_propagate_error (error, local); \ - return val; \ -} \ - \ -void \ -nm_keyfile_plugin_kf_set_##stype (GKeyFile *kf, \ - const char *group, \ - const char *key, \ - set_ctype value) \ -{ \ - const char *alias; \ - \ - alias = nm_keyfile_plugin_get_alias_for_setting_name (group); \ - g_key_file_set_##stype (kf, alias ? alias : group, key, value); \ + /* escape_filename() must avoid anything that should_ignore_file() would reject. + * We can escape here more aggressivly then what we would read back. */ + if (check_prefix (str->str, ".")) + str->str[0] = ESCAPE_CHAR2; + if (check_suffix (str->str, "~")) + str->str[str->len - 1] = ESCAPE_CHAR2; + if ( check_mkstemp_suffix (str->str) + || check_suffix (str->str, PEM_TAG) + || check_suffix (str->str, DER_TAG)) + g_string_append_c (str, ESCAPE_CHAR2); + + return g_string_free (str, FALSE);; } -DEFINE_KF_WRAPPER(string, gchar*, const gchar*); -DEFINE_KF_WRAPPER(integer, gint, gint); -DEFINE_KF_WRAPPER(uint64, guint64, guint64); -DEFINE_KF_WRAPPER(boolean, gboolean, gboolean); -DEFINE_KF_WRAPPER(value, gchar*, const gchar*); - - -gchar ** -nm_keyfile_plugin_kf_get_keys (GKeyFile *kf, - const char *group, - gsize *out_length, - GError **error) -{ - gchar **keys; - const char *alias; - GError *local = NULL; - - keys = g_key_file_get_keys (kf, group, out_length, &local); - if (g_error_matches (local, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) { - alias = nm_keyfile_plugin_get_alias_for_setting_name (group); - if (alias) { - g_clear_error (&local); - keys = g_key_file_get_keys (kf, alias, out_length, &local); - } - } - if (local) - g_propagate_error (error, local); - return keys; -} - -gboolean -nm_keyfile_plugin_kf_has_key (GKeyFile *kf, - const char *group, - const char *key, - GError **error) -{ - gboolean has; - const char *alias; - GError *local = NULL; - - has = g_key_file_has_key (kf, group, key, &local); - if (g_error_matches (local, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) { - alias = nm_keyfile_plugin_get_alias_for_setting_name (group); - if (alias) { - g_clear_error (&local); - has = g_key_file_has_key (kf, alias, key, &local); - } - } - if (local) - g_propagate_error (error, local); - return has; -} - - diff --git a/src/settings/plugins/keyfile/utils.h b/src/settings/plugins/keyfile/utils.h index d1533671..d0862284 100644 --- a/src/settings/plugins/keyfile/utils.h +++ b/src/settings/plugins/keyfile/utils.h @@ -22,7 +22,6 @@ #define _UTILS_H_ #include <glib.h> -#include "common.h" #include "NetworkManagerUtils.h" #define NM_KEYFILE_CONNECTION_LOG_PATH(path) str_if_set (path,"in-memory") @@ -33,55 +32,7 @@ gboolean nm_keyfile_plugin_utils_should_ignore_file (const char *filename); -const char *nm_keyfile_plugin_get_alias_for_setting_name (const char *setting_name); - -const char *nm_keyfile_plugin_get_setting_name_for_alias (const char *alias); - -/*********************************************************/ - -/* List helpers */ -#define DEFINE_KF_LIST_WRAPPER_PROTO(stype, get_ctype, set_ctype) \ -get_ctype nm_keyfile_plugin_kf_get_##stype##_list (GKeyFile *kf, \ - const char *group, \ - const char *key, \ - gsize *out_length, \ - GError **error); \ -\ -void nm_keyfile_plugin_kf_set_##stype##_list (GKeyFile *kf, \ - const char *group, \ - const char *key, \ - set_ctype list[], \ - gsize length); -DEFINE_KF_LIST_WRAPPER_PROTO(integer, gint*, gint) -DEFINE_KF_LIST_WRAPPER_PROTO(string, gchar**, const gchar* const) - -/* Single-value helpers */ -#define DEFINE_KF_WRAPPER_PROTO(stype, get_ctype, set_ctype) \ -get_ctype nm_keyfile_plugin_kf_get_##stype (GKeyFile *kf, \ - const char *group, \ - const char *key, \ - GError **error); \ -\ -void nm_keyfile_plugin_kf_set_##stype (GKeyFile *kf, \ - const char *group, \ - const char *key, \ - set_ctype value); -DEFINE_KF_WRAPPER_PROTO(string, gchar*, const gchar*) -DEFINE_KF_WRAPPER_PROTO(integer, gint, gint) -DEFINE_KF_WRAPPER_PROTO(uint64, guint64, guint64) -DEFINE_KF_WRAPPER_PROTO(boolean, gboolean, gboolean) -DEFINE_KF_WRAPPER_PROTO(value, gchar*, const gchar*) - -/* Misc */ -gchar ** nm_keyfile_plugin_kf_get_keys (GKeyFile *kf, - const char *group, - gsize *out_length, - GError **error); - -gboolean nm_keyfile_plugin_kf_has_key (GKeyFile *kf, - const char *group, - const char *key, - GError **error); +char *nm_keyfile_plugin_utils_escape_filename (const char *filename); #endif /* _UTILS_H_ */ diff --git a/src/settings/plugins/keyfile/writer.c b/src/settings/plugins/keyfile/writer.c index 7aa5bfb0..b6a8786d 100644 --- a/src/settings/plugins/keyfile/writer.c +++ b/src/settings/plugins/keyfile/writer.c @@ -16,7 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Copyright (C) 2008 Novell, Inc. - * Copyright (C) 2008 - 2012 Red Hat, Inc. + * Copyright (C) 2008 - 2015 Red Hat, Inc. */ #include "config.h" @@ -24,407 +24,22 @@ #include <stdlib.h> #include <sys/stat.h> #include <unistd.h> -#include <stdio.h> #include <errno.h> - -#include <nm-setting.h> -#include <nm-setting-connection.h> -#include <nm-setting-ip4-config.h> -#include <nm-setting-ip6-config.h> -#include <nm-setting-vpn.h> -#include <nm-setting-wired.h> -#include <nm-setting-wireless.h> -#include <nm-setting-ip4-config.h> -#include <nm-setting-bluetooth.h> -#include <nm-setting-8021x.h> -#include <nm-utils.h> #include <string.h> -#include <arpa/inet.h> #include "nm-glib-compat.h" + #include "nm-logging.h" #include "writer.h" #include "common.h" #include "utils.h" +#include "nm-keyfile-internal.h" -/* Some setting properties also contain setting names, such as - * NMSettingConnection's 'type' property (which specifies the base type of the - * connection, eg ethernet or wifi) or the 802-11-wireless setting's - * 'security' property which specifies whether or not the AP requires - * encrpytion. This function handles translating those properties' values - * from the real setting name to the more-readable alias. - */ -static void -setting_alias_writer (GKeyFile *file, - const char *keyfile_dir, - const char *uuid, - NMSetting *setting, - const char *key, - const GValue *value) -{ - const char *str, *alias; - - str = g_value_get_string (value); - alias = nm_keyfile_plugin_get_alias_for_setting_name (str); - nm_keyfile_plugin_kf_set_string (file, - nm_setting_get_name (setting), - key, - alias ? alias : str); -} - -static gboolean -write_array_of_uint (GKeyFile *file, - NMSetting *setting, - const char *key, - const GValue *value) -{ - GArray *array; - int i; - int *tmp_array; - - array = (GArray *) g_value_get_boxed (value); - if (!array || !array->len) - return TRUE; - - tmp_array = g_new (gint, array->len); - for (i = 0; i < array->len; i++) - tmp_array[i] = g_array_index (array, int, i); - - nm_keyfile_plugin_kf_set_integer_list (file, nm_setting_get_name (setting), key, tmp_array, array->len); - g_free (tmp_array); - return TRUE; -} - -static void -dns_writer (GKeyFile *file, - const char *keyfile_dir, - const char *uuid, - NMSetting *setting, - const char *key, - const GValue *value) -{ - char **list; - - list = g_value_get_boxed (value); - if (list && list[0]) { - nm_keyfile_plugin_kf_set_string_list (file, nm_setting_get_name (setting), key, - (const char **) list, g_strv_length (list)); - } -} - -static void -write_ip_values (GKeyFile *file, - const char *setting_name, - GPtrArray *array, - const char *gateway, - gboolean is_route) -{ - GString *output; - int family, i; - const char *addr, *gw; - guint32 plen, metric; - char key_name[30], *key_name_idx; - - if (!array->len) - return; - - family = !strcmp (setting_name, NM_SETTING_IP4_CONFIG_SETTING_NAME) ? AF_INET : AF_INET6; - - strcpy (key_name, is_route ? "route" : "address"); - key_name_idx = key_name + strlen (key_name); - - output = g_string_sized_new (2*INET_ADDRSTRLEN + 10); - for (i = 0; i < array->len; i++) { - if (is_route) { - NMIPRoute *route = array->pdata[i]; - - addr = nm_ip_route_get_dest (route); - plen = nm_ip_route_get_prefix (route); - gw = nm_ip_route_get_next_hop (route); - metric = MAX (0, nm_ip_route_get_metric (route)); - } else { - NMIPAddress *address = array->pdata[i]; - - addr = nm_ip_address_get_address (address); - plen = nm_ip_address_get_prefix (address); - gw = i == 0 ? gateway : NULL; - metric = 0; - } - - g_string_set_size (output, 0); - g_string_append_printf (output, "%s/%u", addr, plen); - if (metric || gw) { - /* Older versions of the plugin do not support the form - * "a.b.c.d/plen,,metric", so, we always have to write the - * gateway, even if there isn't one. - * The current version supports reading of the above form. - */ - if (!gw) { - if (family == AF_INET) - gw = "0.0.0.0"; - else - gw = "::"; - } - - g_string_append_printf (output, ",%s", gw); - if (metric) - g_string_append_printf (output, ",%lu", (unsigned long) metric); - } - - sprintf (key_name_idx, "%d", i + 1); - nm_keyfile_plugin_kf_set_string (file, setting_name, key_name, output->str); - } - g_string_free (output, TRUE); -} - -static void -addr_writer (GKeyFile *file, - const char *keyfile_dir, - const char *uuid, - NMSetting *setting, - const char *key, - const GValue *value) -{ - GPtrArray *array; - const char *setting_name = nm_setting_get_name (setting); - const char *gateway = nm_setting_ip_config_get_gateway (NM_SETTING_IP_CONFIG (setting)); - - array = (GPtrArray *) g_value_get_boxed (value); - if (array && array->len) - write_ip_values (file, setting_name, array, gateway, FALSE); -} - -static void -ip4_addr_label_writer (GKeyFile *file, - const char *keyfile_dir, - const char *uuid, - NMSetting *setting, - const char *key, - const GValue *value) -{ - /* skip */ -} - -static void -gateway_writer (GKeyFile *file, - const char *keyfile_dir, - const char *uuid, - NMSetting *setting, - const char *key, - const GValue *value) -{ - /* skip */ -} - -static void -route_writer (GKeyFile *file, - const char *keyfile_dir, - const char *uuid, - NMSetting *setting, - const char *key, - const GValue *value) -{ - GPtrArray *array; - const char *setting_name = nm_setting_get_name (setting); - - array = (GPtrArray *) g_value_get_boxed (value); - if (array && array->len) - write_ip_values (file, setting_name, array, NULL, TRUE); -} - -static void -write_hash_of_string (GKeyFile *file, - NMSetting *setting, - const char *key, - const GValue *value) -{ - GHashTableIter iter; - const char *property = NULL, *data = NULL; - const char *group_name = nm_setting_get_name (setting); - gboolean vpn_secrets = FALSE; - - /* Write VPN secrets out to a different group to keep them separate */ - if (NM_IS_SETTING_VPN (setting) && !strcmp (key, NM_SETTING_VPN_SECRETS)) { - group_name = VPN_SECRETS_GROUP; - vpn_secrets = TRUE; - } - - g_hash_table_iter_init (&iter, (GHashTable *) g_value_get_boxed (value)); - while (g_hash_table_iter_next (&iter, (gpointer *) &property, (gpointer *) &data)) { - gboolean write_item = TRUE; - - /* Handle VPN secrets specially; they are nested in the property's hash; - * we don't want to write them if the secret is not saved, not required, - * or owned by a user's secret agent. - */ - if (vpn_secrets) { - NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE; - - nm_setting_get_secret_flags (setting, property, &secret_flags, NULL); - if (secret_flags != NM_SETTING_SECRET_FLAG_NONE) - write_item = FALSE; - } - - if (write_item) - nm_keyfile_plugin_kf_set_string (file, group_name, property, data); - } -} -static void -ssid_writer (GKeyFile *file, - const char *keyfile_dir, - const char *uuid, - NMSetting *setting, - const char *key, - const GValue *value) -{ - GBytes *bytes; - const guint8 *ssid_data; - gsize ssid_len; - const char *setting_name = nm_setting_get_name (setting); - gboolean new_format = TRUE; - unsigned int semicolons = 0; - int i, *tmp_array; - char *ssid; - - g_return_if_fail (G_VALUE_HOLDS (value, G_TYPE_BYTES)); - - bytes = g_value_get_boxed (value); - if (!bytes) - return; - ssid_data = g_bytes_get_data (bytes, &ssid_len); - if (ssid_len == 0) - return; - - /* Check whether each byte is printable. If not, we have to use an - * integer list, otherwise we can just use a string. - */ - for (i = 0; i < ssid_len; i++) { - char c = ssid_data[i] & 0xFF; - if (!g_ascii_isprint (c)) { - new_format = FALSE; - break; - } - if (c == ';') - semicolons++; - } - - if (new_format) { - ssid = g_malloc0 (ssid_len + semicolons + 1); - if (semicolons == 0) - memcpy (ssid, ssid_data, ssid_len); - else { - /* Escape semicolons with backslashes to make strings - * containing ';', such as '16;17;' unambiguous */ - int j = 0; - for (i = 0; i < ssid_len; i++) { - if (ssid_data[i] == ';') - ssid[j++] = '\\'; - ssid[j++] = ssid_data[i]; - } - } - nm_keyfile_plugin_kf_set_string (file, setting_name, key, ssid); - g_free (ssid); - } else { - tmp_array = g_new (gint, ssid_len); - for (i = 0; i < ssid_len; i++) - tmp_array[i] = (int) ssid_data[i]; - nm_keyfile_plugin_kf_set_integer_list (file, setting_name, key, tmp_array, ssid_len); - g_free (tmp_array); - } -} - -static void -password_raw_writer (GKeyFile *file, - const char *keyfile_dir, - const char *uuid, - NMSetting *setting, - const char *key, - const GValue *value) -{ - const char *setting_name = nm_setting_get_name (setting); - GBytes *array; - int *tmp_array; - gsize i, len; - const char *data; - - g_return_if_fail (G_VALUE_HOLDS (value, G_TYPE_BYTES)); - - array = (GBytes *) g_value_get_boxed (value); - if (!array) - return; - data = g_bytes_get_data (array, &len); - if (!data || !len) - return; - - tmp_array = g_new (gint, len); - for (i = 0; i < len; i++) - tmp_array[i] = (int) data[i]; - nm_keyfile_plugin_kf_set_integer_list (file, setting_name, key, tmp_array, len); - g_free (tmp_array); -} +typedef struct { + const char *keyfile_dir; +} WriteInfo; -typedef struct ObjectType { - const char *key; - const char *suffix; - const char *privkey_pw_prop; - NMSetting8021xCKScheme (*scheme_func) (NMSetting8021x *setting); - NMSetting8021xCKFormat (*format_func) (NMSetting8021x *setting); - const char * (*path_func) (NMSetting8021x *setting); - GBytes * (*blob_func) (NMSetting8021x *setting); -} ObjectType; - -static const ObjectType objtypes[10] = { - { NM_SETTING_802_1X_CA_CERT, - "ca-cert", - NULL, - nm_setting_802_1x_get_ca_cert_scheme, - NULL, - nm_setting_802_1x_get_ca_cert_path, - nm_setting_802_1x_get_ca_cert_blob }, - - { NM_SETTING_802_1X_PHASE2_CA_CERT, - "inner-ca-cert", - NULL, - nm_setting_802_1x_get_phase2_ca_cert_scheme, - NULL, - nm_setting_802_1x_get_phase2_ca_cert_path, - nm_setting_802_1x_get_phase2_ca_cert_blob }, - - { NM_SETTING_802_1X_CLIENT_CERT, - "client-cert", - NULL, - nm_setting_802_1x_get_client_cert_scheme, - NULL, - nm_setting_802_1x_get_client_cert_path, - nm_setting_802_1x_get_client_cert_blob }, - - { NM_SETTING_802_1X_PHASE2_CLIENT_CERT, - "inner-client-cert", - NULL, - nm_setting_802_1x_get_phase2_client_cert_scheme, - NULL, - nm_setting_802_1x_get_phase2_client_cert_path, - nm_setting_802_1x_get_phase2_client_cert_blob }, - - { NM_SETTING_802_1X_PRIVATE_KEY, - "private-key", - NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD, - nm_setting_802_1x_get_private_key_scheme, - nm_setting_802_1x_get_private_key_format, - nm_setting_802_1x_get_private_key_path, - nm_setting_802_1x_get_private_key_blob }, - - { NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, - "inner-private-key", - NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD, - nm_setting_802_1x_get_phase2_private_key_scheme, - nm_setting_802_1x_get_phase2_private_key_format, - nm_setting_802_1x_get_phase2_private_key_path, - nm_setting_802_1x_get_phase2_private_key_blob }, - - { NULL }, -}; static gboolean write_cert_key_file (const char *path, @@ -490,61 +105,75 @@ out: } static void -cert_writer (GKeyFile *file, - const char *keyfile_dir, - const char *uuid, - NMSetting *setting, - const char *key, - const GValue *value) +cert_writer (NMConnection *connection, + GKeyFile *file, + NMKeyfileWriteTypeDataCert *cert_data, + WriteInfo *info, + GError **error) { - const char *setting_name = nm_setting_get_name (setting); + const char *setting_name = nm_setting_get_name (NM_SETTING (cert_data->setting)); NMSetting8021xCKScheme scheme; NMSetting8021xCKFormat format; const char *path = NULL, *ext = "pem"; - const ObjectType *objtype = NULL; - int i; - for (i = 0; i < G_N_ELEMENTS (objtypes) && objtypes[i].key; i++) { - if (g_strcmp0 (objtypes[i].key, key) == 0) { - objtype = &objtypes[i]; - break; - } - } - if (!objtype) { - g_return_if_fail (objtype); - return; - } - - scheme = objtype->scheme_func (NM_SETTING_802_1X (setting)); + scheme = cert_data->scheme_func (cert_data->setting); if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) { - path = objtype->path_func (NM_SETTING_802_1X (setting)); + char *tmp = NULL; + const char *accepted_path = NULL; + + path = cert_data->path_func (cert_data->setting); g_assert (path); - /* If the path is rooted in the keyfile directory, just use a - * relative path instead of an absolute one. - */ - if (g_str_has_prefix (path, keyfile_dir)) { - path += strlen (keyfile_dir); - while (*path == '/') - path++; + if (g_str_has_prefix (path, info->keyfile_dir)) { + const char *p = path + strlen (info->keyfile_dir); + + /* If the path is rooted in the keyfile directory, just use a + * relative path instead of an absolute one. + */ + if (*p == '/') { + while (*p == '/') + p++; + if (p[0]) { + /* If @p looks like an integer list, the following detection will fail too and + * we will file:// qualify the path below. We thus avoid writing a path string + * that would be interpreted as legacy binary format by reader. */ + tmp = nm_keyfile_detect_unqualified_path_scheme (info->keyfile_dir, p, -1, FALSE, NULL); + if (tmp) { + g_clear_pointer (&tmp, g_free); + accepted_path = p; + } + } + } + } + if (!accepted_path) { + /* What we are about to write, must also be understood by the reader. + * Otherwise, add a file:// prefix */ + tmp = nm_keyfile_detect_unqualified_path_scheme (info->keyfile_dir, path, -1, FALSE, NULL); + if (tmp) { + g_clear_pointer (&tmp, g_free); + accepted_path = path; + } } - nm_keyfile_plugin_kf_set_string (file, setting_name, key, path); + if (!accepted_path) + accepted_path = tmp = g_strconcat (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH, path, NULL); + nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->property_name, accepted_path); + g_free (tmp); } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { GBytes *blob; const guint8 *blob_data; gsize blob_len; gboolean success; - GError *error = NULL; + GError *local = NULL; char *new_path; - blob = objtype->blob_func (NM_SETTING_802_1X (setting)); + blob = cert_data->blob_func (cert_data->setting); g_assert (blob); blob_data = g_bytes_get_data (blob, &blob_len); - if (objtype->format_func) { + if (cert_data->format_func) { /* Get the extension for a private key */ - format = objtype->format_func (NM_SETTING_802_1X (setting)); + format = cert_data->format_func (cert_data->setting); if (format == NM_SETTING_802_1X_CK_FORMAT_PKCS12) ext = "p12"; } else { @@ -556,246 +185,46 @@ cert_writer (GKeyFile *file, /* Write the raw data out to the standard file so that we can use paths * from now on instead of pushing around the certificate data. */ - new_path = g_strdup_printf ("%s/%s-%s.%s", keyfile_dir, uuid, objtype->suffix, ext); - g_assert (new_path); + new_path = g_strdup_printf ("%s/%s-%s.%s", info->keyfile_dir, nm_connection_get_uuid (connection), + cert_data->suffix, ext); - success = write_cert_key_file (new_path, blob_data, blob_len, &error); + success = write_cert_key_file (new_path, blob_data, blob_len, &local); if (success) { - /* Write the path value to the keyfile */ - nm_keyfile_plugin_kf_set_string (file, setting_name, key, new_path); + /* Write the path value to the keyfile. + * We know, that basename(new_path) starts with a UUID, hence no conflict with "data:;base64," */ + nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->property_name, strrchr (new_path, '/') + 1); } else { - nm_log_warn (LOGD_SETTINGS, "Failed to write certificate/key %s: %s", - new_path, error->message); - g_error_free (error); + nm_log_warn (LOGD_SETTINGS, "keyfile: %s.%s: failed to write certificate to file %s: %s", + setting_name, cert_data->property_name, new_path, local->message); + g_error_free (local); } g_free (new_path); - } else - g_assert_not_reached (); -} - -typedef struct { - const char *setting_name; - const char *key; - void (*writer) (GKeyFile *keyfile, - const char *keyfile_dir, - const char *uuid, - NMSetting *setting, - const char *key, - const GValue *value); -} KeyWriter; - -/* A table of keys that require further parsing/conversion because they are - * stored in a format that can't be automatically read using the key's type. - * i.e. IPv4 addresses, which are stored in NetworkManager as guint32, but are - * stored in keyfiles as strings, eg "10.1.1.2" or IPv6 addresses stored - * in struct in6_addr internally, but as string in keyfiles. - */ -static KeyWriter key_writers[] = { - { NM_SETTING_CONNECTION_SETTING_NAME, - NM_SETTING_CONNECTION_TYPE, - setting_alias_writer }, - { NM_SETTING_IP4_CONFIG_SETTING_NAME, - NM_SETTING_IP_CONFIG_ADDRESSES, - addr_writer }, - { NM_SETTING_IP4_CONFIG_SETTING_NAME, - "address-labels", - ip4_addr_label_writer }, - { NM_SETTING_IP6_CONFIG_SETTING_NAME, - NM_SETTING_IP_CONFIG_ADDRESSES, - addr_writer }, - { NM_SETTING_IP4_CONFIG_SETTING_NAME, - NM_SETTING_IP_CONFIG_GATEWAY, - gateway_writer }, - { NM_SETTING_IP6_CONFIG_SETTING_NAME, - NM_SETTING_IP_CONFIG_GATEWAY, - gateway_writer }, - { NM_SETTING_IP4_CONFIG_SETTING_NAME, - NM_SETTING_IP_CONFIG_ROUTES, - route_writer }, - { NM_SETTING_IP6_CONFIG_SETTING_NAME, - NM_SETTING_IP_CONFIG_ROUTES, - route_writer }, - { NM_SETTING_IP4_CONFIG_SETTING_NAME, - NM_SETTING_IP_CONFIG_DNS, - dns_writer }, - { NM_SETTING_IP6_CONFIG_SETTING_NAME, - NM_SETTING_IP_CONFIG_DNS, - dns_writer }, - { NM_SETTING_WIRELESS_SETTING_NAME, - NM_SETTING_WIRELESS_SSID, - ssid_writer }, - { NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_PASSWORD_RAW, - password_raw_writer }, - { NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_CA_CERT, - cert_writer }, - { NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_CLIENT_CERT, - cert_writer }, - { NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_PRIVATE_KEY, - cert_writer }, - { NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_PHASE2_CA_CERT, - cert_writer }, - { NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_PHASE2_CLIENT_CERT, - cert_writer }, - { NM_SETTING_802_1X_SETTING_NAME, - NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, - cert_writer }, - { NULL, NULL, NULL } -}; - -typedef struct { - GKeyFile *keyfile; - const char *keyfile_dir; - const char *uuid; -} WriteInfo; - -static void -write_setting_value (NMSetting *setting, - const char *key, - const GValue *value, - GParamFlags flag, - gpointer user_data) -{ - WriteInfo *info = user_data; - const char *setting_name; - GType type = G_VALUE_TYPE (value); - KeyWriter *writer = &key_writers[0]; - GParamSpec *pspec; - - /* Setting name gets picked up from the keyfile's section name instead */ - if (!strcmp (key, NM_SETTING_NAME)) - return; - - /* Don't write the NMSettingConnection object's 'read-only' property */ - if ( NM_IS_SETTING_CONNECTION (setting) - && !strcmp (key, NM_SETTING_CONNECTION_READ_ONLY)) - return; - - setting_name = nm_setting_get_name (setting); - - /* If the value is the default value, remove the item from the keyfile */ - pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), key); - if (pspec) { - if (g_param_value_defaults (pspec, (GValue *) value)) { - g_key_file_remove_key (info->keyfile, setting_name, key, NULL); - return; - } - } - - /* Don't write secrets that are owned by user secret agents or aren't - * supposed to be saved. VPN secrets are handled specially though since - * the secret flags there are in a third-level hash in the 'secrets' - * property. - */ - if (pspec && (pspec->flags & NM_SETTING_PARAM_SECRET) && !NM_IS_SETTING_VPN (setting)) { - NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE; - - if (!nm_setting_get_secret_flags (setting, key, &secret_flags, NULL)) - g_assert_not_reached (); - if (secret_flags != NM_SETTING_SECRET_FLAG_NONE) - return; - } - - /* Look through the list of handlers for non-standard format key values */ - while (writer->setting_name) { - if (!strcmp (writer->setting_name, setting_name) && !strcmp (writer->key, key)) { - (*writer->writer) (info->keyfile, info->keyfile_dir, info->uuid, setting, key, value); - return; - } - writer++; - } - - if (type == G_TYPE_STRING) { - const char *str; - - str = g_value_get_string (value); - if (str) - nm_keyfile_plugin_kf_set_string (info->keyfile, setting_name, key, str); - } else if (type == G_TYPE_UINT) - nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, (int) g_value_get_uint (value)); - else if (type == G_TYPE_INT) - nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, g_value_get_int (value)); - else if (type == G_TYPE_UINT64) { - char *numstr; - - numstr = g_strdup_printf ("%" G_GUINT64_FORMAT, g_value_get_uint64 (value)); - nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key, numstr); - g_free (numstr); - } else if (type == G_TYPE_INT64) { - char *numstr; - - numstr = g_strdup_printf ("%" G_GINT64_FORMAT, g_value_get_int64 (value)); - nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key, numstr); - g_free (numstr); - } else if (type == G_TYPE_BOOLEAN) { - nm_keyfile_plugin_kf_set_boolean (info->keyfile, setting_name, key, g_value_get_boolean (value)); - } else if (type == G_TYPE_CHAR) { - nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, (int) g_value_get_schar (value)); - } else if (type == G_TYPE_BYTES) { - GBytes *bytes; - const guint8 *data; - gsize len = 0; - - bytes = g_value_get_boxed (value); - data = bytes ? g_bytes_get_data (bytes, &len) : NULL; - - if (data != NULL && len > 0) { - int *tmp_array; - int i; - - tmp_array = g_new (gint, len); - for (i = 0; i < len; i++) - tmp_array[i] = (int) data[i]; - - nm_keyfile_plugin_kf_set_integer_list (info->keyfile, setting_name, key, tmp_array, len); - g_free (tmp_array); - } - } else if (type == G_TYPE_STRV) { - char **array; - - array = (char **) g_value_get_boxed (value); - nm_keyfile_plugin_kf_set_string_list (info->keyfile, setting_name, key, (const gchar **const) array, g_strv_length (array)); - } else if (type == G_TYPE_HASH_TABLE) { - write_hash_of_string (info->keyfile, setting, key, value); - } else if (type == G_TYPE_ARRAY) { - if (!write_array_of_uint (info->keyfile, setting, key, value)) { - nm_log_warn (LOGD_SETTINGS, "Unhandled setting property type (write) '%s/%s' : '%s'", - setting_name, key, g_type_name (type)); - } - } else if (G_VALUE_HOLDS_FLAGS (value)) { - /* Flags are guint but GKeyFile has no uint reader, just uint64 */ - nm_keyfile_plugin_kf_set_uint64 (info->keyfile, setting_name, key, (guint64) g_value_get_flags (value)); - } else if (G_VALUE_HOLDS_ENUM (value)) - nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, (gint) g_value_get_enum (value)); - else { - nm_log_warn (LOGD_SETTINGS, "Unhandled setting property type (write) '%s/%s' : '%s'", - setting_name, key, g_type_name (type)); + } else { + /* scheme_func() returns UNKNOWN in all other cases. The only valid case + * where a scheme is allowed to be UNKNOWN, is unsetting the value. In this + * case, we don't expect the writer to be called, because the default value + * will not be serialized. + * The only other reason for the scheme to be UNKNOWN is an invalid cert. + * But our connection verifies, so that cannot happen either. */ + g_return_if_reached (); } } -static char * -_writer_id_to_filename (const char *id) +static gboolean +_handler_write (NMConnection *connection, + GKeyFile *keyfile, + NMKeyfileWriteType type, + void *type_data, + void *user_data, + GError **error) { - char *filename, *f; - const char *i = id; - - f = filename = g_malloc0 (strlen (id) + 1); - - /* Convert '/' to '*' */ - while (*i) { - if (*i == '/') - *f++ = '*'; - else - *f++ = *i; - i++; + if (type == NM_KEYFILE_WRITE_TYPE_CERT) { + cert_writer (connection, keyfile, + (NMKeyfileWriteTypeDataCert *) type_data, + (WriteInfo *) user_data, error); + return TRUE; } - - return filename; + return FALSE; } static gboolean @@ -811,12 +240,13 @@ _internal_write_connection (NMConnection *connection, char *data; gsize len; gboolean success = FALSE; - char *filename = NULL, *path; + char *path; const char *id; - WriteInfo info; + WriteInfo info = { 0 }; GError *local_err = NULL; g_return_val_if_fail (!out_path || !*out_path, FALSE); + g_return_val_if_fail (keyfile_dir && keyfile_dir[0] == '/', FALSE); if (!nm_connection_verify (connection, error)) g_return_val_if_reached (FALSE); @@ -824,14 +254,15 @@ _internal_write_connection (NMConnection *connection, id = nm_connection_get_id (connection); g_assert (id && *id); - info.keyfile = key_file = g_key_file_new (); info.keyfile_dir = keyfile_dir; - info.uuid = nm_connection_get_uuid (connection); - g_assert (info.uuid); - nm_connection_for_each_setting_value (connection, write_setting_value, &info); + + key_file = nm_keyfile_write (connection, _handler_write, &info, error); + if (!key_file) + return FALSE; data = g_key_file_to_data (key_file, &len, error); + g_key_file_unref (key_file); if (!data) - goto out; + return FALSE; /* If we have existing file path, use it. Else generate one from * connection's ID. @@ -839,8 +270,10 @@ _internal_write_connection (NMConnection *connection, if (existing_path != NULL) { path = g_strdup (existing_path); } else { - filename = _writer_id_to_filename (id); - path = g_build_filename (keyfile_dir, filename, NULL); + char *filename_escaped = nm_keyfile_plugin_utils_escape_filename (id); + + path = g_build_filename (keyfile_dir, filename_escaped, NULL); + g_free (filename_escaped); } /* If a file with this path already exists (but isn't the existing path @@ -850,24 +283,42 @@ _internal_write_connection (NMConnection *connection, * 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 (g_file_test (path, G_FILE_TEST_EXISTS) && (g_strcmp0 (path, existing_path) != 0)) { - /* A keyfile with this connection's ID already exists. Pick another name. */ - g_free (path); + if (g_strcmp0 (path, existing_path) != 0 && g_file_test (path, G_FILE_TEST_EXISTS)) { + guint i; + gboolean name_found = FALSE; - path = g_strdup_printf ("%s/%s-%s", keyfile_dir, filename, nm_connection_get_uuid (connection)); - if (g_file_test (path, G_FILE_TEST_EXISTS)) { - if (existing_path == NULL || g_strcmp0 (path, existing_path) != 0) { - /* This should not happen. But, it actually occurs when - * two connections have the same UUID, and one of the connections - * is edited to contain the same ID as the other one. - * Give up. - */ + /* A keyfile with this connection's ID already exists. Pick another name. */ + for (i = 0; i < 100; i++) { + char *filename, *filename_escaped; + + if (i == 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_escaped = nm_keyfile_plugin_utils_escape_filename (filename); + + g_free (path); + path = g_strdup_printf ("%s/%s", keyfile_dir, filename_escaped); + g_free (filename); + g_free (filename_escaped); + if (g_strcmp0 (path, existing_path) == 0 || !g_file_test (path, G_FILE_TEST_EXISTS)) { + name_found = TRUE; + break; + } + } + if (!name_found) { + if (existing_path == NULL) { + /* 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, - "%s.%d: could not find suitable keyfile file name (%s already used)", - __FILE__, __LINE__, path); + "could not find suitable keyfile file name (%s already used)", path); g_free (path); goto out; } + /* 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); } } @@ -909,9 +360,7 @@ _internal_write_connection (NMConnection *connection, g_free (path); out: - g_free (filename); g_free (data); - g_key_file_free (key_file); return success; } diff --git a/src/settings/plugins/keyfile/writer.h b/src/settings/plugins/keyfile/writer.h index a602f2f4..8b08812d 100644 --- a/src/settings/plugins/keyfile/writer.h +++ b/src/settings/plugins/keyfile/writer.h @@ -22,7 +22,6 @@ #ifndef _KEYFILE_PLUGIN_WRITER_H #define _KEYFILE_PLUGIN_WRITER_H -#include <sys/types.h> #include <glib.h> #include <nm-connection.h> diff --git a/src/supplicant-manager/nm-call-store.c b/src/supplicant-manager/nm-call-store.c deleted file mode 100644 index 6e910a96..00000000 --- a/src/supplicant-manager/nm-call-store.c +++ /dev/null @@ -1,119 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 2007 Novell, Inc. - * Copyright (C) 2010 Red Hat, Inc. - */ - -#include "config.h" - -#include "nm-call-store.h" -#include "nm-logging.h" - -NMCallStore * -nm_call_store_new (void) -{ - /* Maps { DBusGProxy :: GHashTable { DBusGProxyCall :: NULL } } */ - return g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) g_hash_table_destroy); -} - -static void -proxy_destroyed_cb (gpointer data, GObject *proxy) -{ - g_hash_table_remove ((NMCallStore *) data, proxy); -} - -void -nm_call_store_add (NMCallStore *store, - DBusGProxy *proxy, - DBusGProxyCall *call) -{ - GHashTable *calls; - - g_return_if_fail (store != NULL); - g_return_if_fail (proxy != NULL); - - if (!call) { - /* Allow calling nm_call_store_add() with NULL @call for convenience. - * This way you can pass the result of dbus_g_proxy_begin_call() directly - * to nm_call_store_add() without checking for NULL. */ - return; - } - - calls = g_hash_table_lookup (store, proxy); - if (!calls) { - calls = g_hash_table_new (NULL, NULL); - g_hash_table_insert (store, proxy, calls); - g_object_weak_ref (G_OBJECT (proxy), proxy_destroyed_cb, store); - } - - g_hash_table_add (calls, call); -} - -void -nm_call_store_remove (NMCallStore *store, - DBusGProxy *proxy, - DBusGProxyCall *call) -{ - GHashTable *calls; - - g_return_if_fail (store != NULL); - g_return_if_fail (proxy != NULL); - g_return_if_fail (call != NULL); - - calls = g_hash_table_lookup (store, proxy); - if (!calls) - return; - - g_hash_table_remove (calls, call); - if (g_hash_table_size (calls) == 0) { - g_hash_table_remove (store, proxy); - g_object_weak_unref (G_OBJECT (proxy), proxy_destroyed_cb, store); - } -} - -void -nm_call_store_clear (NMCallStore *store) -{ - DBusGProxy *proxy; - GHashTable *calls; - GHashTableIter proxies_iter; - - g_return_if_fail (store != NULL); - - g_hash_table_iter_init (&proxies_iter, store); - while (g_hash_table_iter_next (&proxies_iter, (gpointer) &proxy, (gpointer) &calls)) { - GHashTableIter calls_iter; - DBusGProxyCall *call; - - g_hash_table_iter_init (&calls_iter, calls); - while (g_hash_table_iter_next (&calls_iter, (gpointer) &call, NULL)) { - dbus_g_proxy_cancel_call (proxy, call); - g_hash_table_iter_remove (&calls_iter); - } - g_object_weak_unref (G_OBJECT (proxy), proxy_destroyed_cb, store); - g_hash_table_iter_remove (&proxies_iter); - } - g_assert_cmpint (g_hash_table_size (store), ==, 0); -} - -void -nm_call_store_destroy (NMCallStore *store) -{ - g_return_if_fail (store); - g_hash_table_destroy (store); -} diff --git a/src/supplicant-manager/nm-call-store.h b/src/supplicant-manager/nm-call-store.h deleted file mode 100644 index bcee54cf..00000000 --- a/src/supplicant-manager/nm-call-store.h +++ /dev/null @@ -1,41 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * (C) Copyright 2007 Novell, Inc. - */ - -#ifndef __NETWORKMANAGER_CALLBACK_STORE_H__ -#define __NETWORKMANAGER_CALLBACK_STORE_H__ - -#include <glib-object.h> -#include <dbus/dbus-glib.h> - -typedef GHashTable NMCallStore; - -NMCallStore *nm_call_store_new (void); -void nm_call_store_add (NMCallStore *store, - DBusGProxy *proxy, - DBusGProxyCall *call); - -void nm_call_store_remove (NMCallStore *store, - DBusGProxy *proxy, - DBusGProxyCall *call); - -void nm_call_store_clear (NMCallStore *store); -void nm_call_store_destroy (NMCallStore *store); - -#endif /* __NETWORKMANAGER_CALLBACK_STORE_H__ */ diff --git a/src/supplicant-manager/nm-supplicant-config.c b/src/supplicant-manager/nm-supplicant-config.c index c60027ad..b78a24d0 100644 --- a/src/supplicant-manager/nm-supplicant-config.c +++ b/src/supplicant-manager/nm-supplicant-config.c @@ -260,65 +260,44 @@ nm_supplicant_config_fast_required (NMSupplicantConfig *self) return NM_SUPPLICANT_CONFIG_GET_PRIVATE (self)->fast_required; } -static void -get_hash_cb (gpointer key, gpointer value, gpointer user_data) -{ - ConfigOption *opt = (ConfigOption *) value; - GValue *variant; - GByteArray *array; - - variant = g_slice_new0 (GValue); - - switch (opt->type) { - case TYPE_INT: - g_value_init (variant, G_TYPE_INT); - g_value_set_int (variant, atoi (opt->value)); - break; - case TYPE_BYTES: - case TYPE_UTF8: - array = g_byte_array_sized_new (opt->len); - g_byte_array_append (array, (const guint8 *) opt->value, opt->len); - g_value_init (variant, DBUS_TYPE_G_UCHAR_ARRAY); - g_value_set_boxed (variant, array); - g_byte_array_free (array, TRUE); - break; - case TYPE_KEYWORD: - case TYPE_STRING: - g_value_init (variant, G_TYPE_STRING); - g_value_set_string (variant, opt->value); - break; - default: - g_slice_free (GValue, variant); - return; - } - - g_hash_table_insert ((GHashTable *) user_data, g_strdup (key), variant); -} - -static void -destroy_hash_value (gpointer data) -{ - GValue *value = (GValue *) data; - - g_value_unset (value); - g_slice_free (GValue, value); -} - -GHashTable * -nm_supplicant_config_get_hash (NMSupplicantConfig * self) +GVariant * +nm_supplicant_config_to_variant (NMSupplicantConfig *self) { NMSupplicantConfigPrivate *priv; - GHashTable *hash; + GVariantBuilder builder; + GHashTableIter iter; + ConfigOption *option; + const char *key; g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), NULL); - hash = g_hash_table_new_full (g_str_hash, g_str_equal, - (GDestroyNotify) g_free, - destroy_hash_value); - priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE (self); - g_hash_table_foreach (priv->config, get_hash_cb, hash); - return hash; + + g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); + + g_hash_table_iter_init (&iter, priv->config); + while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &option)) { + switch (option->type) { + case TYPE_INT: + g_variant_builder_add (&builder, "{sv}", key, g_variant_new_int32 (atoi (option->value))); + break; + case TYPE_BYTES: + case TYPE_UTF8: + g_variant_builder_add (&builder, "{sv}", + key, + g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, + option->value, option->len, 1)); + break; + case TYPE_KEYWORD: + case TYPE_STRING: + g_variant_builder_add (&builder, "{sv}", key, g_variant_new_string (option->value)); + break; + default: + break; + } + } + + return g_variant_builder_end (&builder); } GHashTable * @@ -754,6 +733,7 @@ nm_supplicant_config_add_setting_8021x (NMSupplicantConfig *self, gboolean fast = FALSE; guint32 i, num_eap; gboolean fast_provisoning_allowed = FALSE; + const char *ca_path_override = NULL, *ca_cert_override = NULL; g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), FALSE); g_return_val_if_fail (setting != NULL, FALSE); @@ -891,10 +871,18 @@ nm_supplicant_config_add_setting_8021x (NMSupplicantConfig *self, } } + /* If user wants to use system CA certs, either populate ca_path (if the path + * is a directory) or ca_cert (the path is a file name) */ + if (nm_setting_802_1x_get_system_ca_certs (setting)) { + if (g_file_test (SYSTEM_CA_PATH, G_FILE_TEST_IS_DIR)) + ca_path_override = SYSTEM_CA_PATH; + else + ca_cert_override = SYSTEM_CA_PATH; + } + /* CA path */ path = nm_setting_802_1x_get_ca_path (setting); - if (nm_setting_802_1x_get_system_ca_certs (setting)) - path = SYSTEM_CA_PATH; + path = ca_path_override ? ca_path_override : path; if (path) { if (!add_string_val (self, path, "ca_path", FALSE, FALSE)) return FALSE; @@ -902,41 +890,50 @@ nm_supplicant_config_add_setting_8021x (NMSupplicantConfig *self, /* Phase2 CA path */ path = nm_setting_802_1x_get_phase2_ca_path (setting); - if (nm_setting_802_1x_get_system_ca_certs (setting)) - path = SYSTEM_CA_PATH; + path = ca_path_override ? ca_path_override : path; if (path) { if (!add_string_val (self, path, "ca_path2", FALSE, FALSE)) return FALSE; } /* CA certificate */ - switch (nm_setting_802_1x_get_ca_cert_scheme (setting)) { - case NM_SETTING_802_1X_CK_SCHEME_BLOB: - bytes = nm_setting_802_1x_get_ca_cert_blob (setting); - ADD_BLOB_VAL (bytes, "ca_cert", con_uuid); - break; - case NM_SETTING_802_1X_CK_SCHEME_PATH: - path = nm_setting_802_1x_get_ca_cert_path (setting); - if (!add_string_val (self, path, "ca_cert", FALSE, FALSE)) + if (ca_cert_override) { + if (!add_string_val (self, ca_cert_override, "ca_cert", FALSE, FALSE)) return FALSE; - break; - default: - break; + } else { + switch (nm_setting_802_1x_get_ca_cert_scheme (setting)) { + case NM_SETTING_802_1X_CK_SCHEME_BLOB: + bytes = nm_setting_802_1x_get_ca_cert_blob (setting); + ADD_BLOB_VAL (bytes, "ca_cert", con_uuid); + break; + case NM_SETTING_802_1X_CK_SCHEME_PATH: + path = nm_setting_802_1x_get_ca_cert_path (setting); + if (!add_string_val (self, path, "ca_cert", FALSE, FALSE)) + return FALSE; + break; + default: + break; + } } /* Phase 2 CA certificate */ - switch (nm_setting_802_1x_get_phase2_ca_cert_scheme (setting)) { - case NM_SETTING_802_1X_CK_SCHEME_BLOB: - bytes = nm_setting_802_1x_get_phase2_ca_cert_blob (setting); - ADD_BLOB_VAL (bytes, "ca_cert2", con_uuid); - break; - case NM_SETTING_802_1X_CK_SCHEME_PATH: - path = nm_setting_802_1x_get_phase2_ca_cert_path (setting); - if (!add_string_val (self, path, "ca_cert2", FALSE, FALSE)) + if (ca_cert_override) { + if (!add_string_val (self, ca_cert_override, "ca_cert2", FALSE, FALSE)) return FALSE; - break; - default: - break; + } else { + switch (nm_setting_802_1x_get_phase2_ca_cert_scheme (setting)) { + case NM_SETTING_802_1X_CK_SCHEME_BLOB: + bytes = nm_setting_802_1x_get_phase2_ca_cert_blob (setting); + ADD_BLOB_VAL (bytes, "ca_cert2", con_uuid); + break; + case NM_SETTING_802_1X_CK_SCHEME_PATH: + path = nm_setting_802_1x_get_phase2_ca_cert_path (setting); + if (!add_string_val (self, path, "ca_cert2", FALSE, FALSE)) + return FALSE; + break; + default: + break; + } } /* Subject match */ diff --git a/src/supplicant-manager/nm-supplicant-config.h b/src/supplicant-manager/nm-supplicant-config.h index 482e3545..3324f637 100644 --- a/src/supplicant-manager/nm-supplicant-config.h +++ b/src/supplicant-manager/nm-supplicant-config.h @@ -59,7 +59,7 @@ void nm_supplicant_config_set_ap_scan (NMSupplicantConfig *self, gboolean nm_supplicant_config_fast_required (NMSupplicantConfig *self); -GHashTable *nm_supplicant_config_get_hash (NMSupplicantConfig *self); +GVariant *nm_supplicant_config_to_variant (NMSupplicantConfig *self); GHashTable *nm_supplicant_config_get_blobs (NMSupplicantConfig *self); diff --git a/src/supplicant-manager/nm-supplicant-interface.c b/src/supplicant-manager/nm-supplicant-interface.c index 02b82b7e..e9775a19 100644 --- a/src/supplicant-manager/nm-supplicant-interface.c +++ b/src/supplicant-manager/nm-supplicant-interface.c @@ -27,13 +27,11 @@ #include "NetworkManagerUtils.h" #include "nm-supplicant-interface.h" -#include "nm-supplicant-manager.h" #include "nm-logging.h" #include "nm-supplicant-config.h" -#include "nm-dbus-manager.h" -#include "nm-call-store.h" -#include "nm-dbus-glib-types.h" #include "nm-glib-compat.h" +#include "gsystem-local-alloc.h" +#include "nm-core-internal.h" #define WPAS_DBUS_IFACE_INTERFACE WPAS_DBUS_INTERFACE ".Interface" #define WPAS_DBUS_IFACE_BSS WPAS_DBUS_INTERFACE ".BSS" @@ -43,16 +41,6 @@ G_DEFINE_TYPE (NMSupplicantInterface, nm_supplicant_interface, G_TYPE_OBJECT) -static void wpas_iface_properties_changed (DBusGProxy *proxy, - GHashTable *props, - gpointer user_data); - -static void wpas_iface_scan_done (DBusGProxy *proxy, - gboolean success, - gpointer user_data); - -static void wpas_iface_get_props (NMSupplicantInterface *self); - #define NM_SUPPLICANT_INTERFACE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \ NM_TYPE_SUPPLICANT_INTERFACE, \ NMSupplicantInterfacePrivate)) @@ -81,89 +69,53 @@ enum { typedef struct { - NMSupplicantManager * smgr; - gulong smgr_avail_id; - NMDBusManager * dbus_mgr; - char * dev; - gboolean is_wireless; - gboolean has_credreq; /* Whether querying 802.1x credentials is supported */ - ApSupport ap_support; /* Lightweight AP mode support */ - gboolean fast_supported; - guint32 max_scan_ssids; - guint32 ready_count; - - char * object_path; - guint32 state; - int disconnect_reason; - NMCallStore * assoc_pcalls; - NMCallStore * other_pcalls; - - gboolean scanning; - - DBusGProxy * wpas_proxy; - DBusGProxy * introspect_proxy; - DBusGProxy * iface_proxy; - DBusGProxy * props_proxy; - char * net_path; - guint32 blobs_left; - GHashTable * bss_proxies; - - gint32 last_scan; /* timestamp as returned by nm_utils_get_monotonic_timestamp_s() */ - - NMSupplicantConfig * cfg; - - gboolean disposed; + char * dev; + gboolean is_wireless; + gboolean has_credreq; /* Whether querying 802.1x credentials is supported */ + ApSupport ap_support; /* Lightweight AP mode support */ + gboolean fast_supported; + guint32 max_scan_ssids; + guint32 ready_count; + + char * object_path; + guint32 state; + int disconnect_reason; + + gboolean scanning; + + GDBusProxy * wpas_proxy; + GCancellable * init_cancellable; + GDBusProxy * iface_proxy; + GCancellable * other_cancellable; + GCancellable * assoc_cancellable; + char * net_path; + guint32 blobs_left; + GHashTable * bss_proxies; + + gint32 last_scan; /* timestamp as returned by nm_utils_get_monotonic_timestamp_s() */ + + NMSupplicantConfig *cfg; } NMSupplicantInterfacePrivate; -static void -emit_error_helper (NMSupplicantInterface *self, - GError *err) -{ - const char *name = NULL; - - if (err->domain == DBUS_GERROR && err->code == DBUS_GERROR_REMOTE_EXCEPTION) - name = dbus_g_error_get_name (err); - - g_signal_emit (self, signals[CONNECTION_ERROR], 0, name, err->message); -} +/***************************************************************/ static void -signal_new_bss (NMSupplicantInterface *self, - const char *object_path, - GHashTable *props) +emit_error_helper (NMSupplicantInterface *self, GError *error) { - g_signal_emit (self, signals[NEW_BSS], 0, object_path, props); -} + char *name = NULL; -static void -bssid_properties_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) -{ - NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); - NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - GError *error = NULL; - GHashTable *props = NULL; - - nm_call_store_remove (priv->other_pcalls, proxy, call_id); - if (dbus_g_proxy_end_call (proxy, call_id, &error, - DBUS_TYPE_G_MAP_OF_VARIANT, &props, - G_TYPE_INVALID)) { - signal_new_bss (self, dbus_g_proxy_get_path (proxy), props); - g_hash_table_destroy (props); - } else { - if (!strstr (error->message, "The BSSID requested was invalid")) { - nm_log_warn (LOGD_SUPPLICANT, "Couldn't retrieve BSSID properties: %s.", - error->message); - } - g_error_free (error); - } + if (g_dbus_error_is_remote_error (error)) + name = g_dbus_error_get_remote_error (error); + + g_signal_emit (self, signals[CONNECTION_ERROR], 0, name, error->message); + g_free (name); } static void -bss_properties_changed (DBusGProxy *proxy, - const char *interface, - GHashTable *props, - const char **unused, - gpointer user_data) +bss_props_changed_cb (GDBusProxy *proxy, + GVariant *changed_properties, + char **invalidated_properties, + gpointer user_data) { NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); @@ -171,111 +123,77 @@ bss_properties_changed (DBusGProxy *proxy, if (priv->scanning) priv->last_scan = nm_utils_get_monotonic_timestamp_s (); - if (g_strcmp0 (interface, WPAS_DBUS_IFACE_BSS) == 0) - g_signal_emit (self, signals[BSS_UPDATED], 0, dbus_g_proxy_get_path (proxy), props); + g_signal_emit (self, signals[BSS_UPDATED], 0, + g_dbus_proxy_get_object_path (proxy), + changed_properties); } static void -handle_new_bss (NMSupplicantInterface *self, - const char *object_path, - GHashTable *props) +on_bss_proxy_acquired (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) { - NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - DBusGProxy *bss_proxy; - DBusGProxyCall *call; - - g_return_if_fail (object_path != NULL); - - if (g_hash_table_lookup (priv->bss_proxies, object_path)) + NMSupplicantInterface *self; + NMSupplicantInterfacePrivate *priv; + gs_free_error GError *error = NULL; + gs_strfreev char **properties = NULL; + gs_unref_variant GVariant *props = NULL; + GVariantBuilder builder; + char **iter; + + if (!g_async_initable_init_finish (G_ASYNC_INITABLE (proxy), result, &error)) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + nm_log_dbg (LOGD_SUPPLICANT, "Failed to acquire BSS proxy: (%s)", error->message); + g_hash_table_remove (NM_SUPPLICANT_INTERFACE_GET_PRIVATE (user_data)->bss_proxies, + g_dbus_proxy_get_object_path (proxy)); + } return; + } - bss_proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (priv->dbus_mgr), - WPAS_DBUS_SERVICE, - object_path, - DBUS_INTERFACE_PROPERTIES); - g_hash_table_insert (priv->bss_proxies, - (gpointer) dbus_g_proxy_get_path (bss_proxy), - bss_proxy); + self = NM_SUPPLICANT_INTERFACE (user_data); + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - /* Standard D-Bus PropertiesChanged signal */ - dbus_g_object_register_marshaller (g_cclosure_marshal_generic, - G_TYPE_NONE, - G_TYPE_STRING, DBUS_TYPE_G_MAP_OF_VARIANT, G_TYPE_STRV, - G_TYPE_INVALID); - dbus_g_proxy_add_signal (bss_proxy, "PropertiesChanged", - G_TYPE_STRING, DBUS_TYPE_G_MAP_OF_VARIANT, G_TYPE_STRV, - G_TYPE_INVALID); - dbus_g_proxy_connect_signal (bss_proxy, "PropertiesChanged", - G_CALLBACK (bss_properties_changed), - self, NULL); - - if (props) { - signal_new_bss (self, object_path, props); - } else { - call = dbus_g_proxy_begin_call (bss_proxy, "GetAll", - bssid_properties_cb, - self, - NULL, - G_TYPE_STRING, WPAS_DBUS_IFACE_BSS, - G_TYPE_INVALID); - nm_call_store_add (priv->other_pcalls, bss_proxy, call); - } -} + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); -static void -wpas_iface_bss_added (DBusGProxy *proxy, - const char *object_path, - GHashTable *props, - gpointer user_data) -{ - NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); - NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + iter = properties = g_dbus_proxy_get_cached_property_names (proxy); + while (*iter) { + GVariant *copy = g_dbus_proxy_get_cached_property (proxy, *iter); - if (priv->scanning) - priv->last_scan = nm_utils_get_monotonic_timestamp_s (); + g_variant_builder_add (&builder, "{sv}", *iter++, copy); + g_variant_unref (copy); + } - handle_new_bss (self, object_path, props); + props = g_variant_builder_end (&builder); + g_signal_emit (self, signals[NEW_BSS], 0, + g_dbus_proxy_get_object_path (proxy), + g_variant_ref_sink (props)); } static void -wpas_iface_bss_removed (DBusGProxy *proxy, - const char *object_path, - gpointer user_data) +handle_new_bss (NMSupplicantInterface *self, const char *object_path) { - NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + GDBusProxy *bss_proxy; - g_signal_emit (self, signals[BSS_REMOVED], 0, object_path); - - g_hash_table_remove (priv->bss_proxies, object_path); -} + g_return_if_fail (object_path != NULL); -static int -wpas_state_string_to_enum (const char *str_state) -{ - if (!strcmp (str_state, "interface_disabled")) - return NM_SUPPLICANT_INTERFACE_STATE_DISABLED; - else if (!strcmp (str_state, "disconnected")) - return NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED; - else if (!strcmp (str_state, "inactive")) - return NM_SUPPLICANT_INTERFACE_STATE_INACTIVE; - else if (!strcmp (str_state, "scanning")) - return NM_SUPPLICANT_INTERFACE_STATE_SCANNING; - else if (!strcmp (str_state, "authenticating")) - return NM_SUPPLICANT_INTERFACE_STATE_AUTHENTICATING; - else if (!strcmp (str_state, "associating")) - return NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATING; - else if (!strcmp (str_state, "associated")) - return NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATED; - else if (!strcmp (str_state, "4way_handshake")) - return NM_SUPPLICANT_INTERFACE_STATE_4WAY_HANDSHAKE; - else if (!strcmp (str_state, "group_handshake")) - return NM_SUPPLICANT_INTERFACE_STATE_GROUP_HANDSHAKE; - else if (!strcmp (str_state, "completed")) - return NM_SUPPLICANT_INTERFACE_STATE_COMPLETED; + if (g_hash_table_lookup (priv->bss_proxies, object_path)) + return; - nm_log_warn (LOGD_SUPPLICANT, "Unknown supplicant state '%s'", str_state); - return -1; + bss_proxy = g_object_new (G_TYPE_DBUS_PROXY, + "g-bus-type", G_BUS_TYPE_SYSTEM, + "g-flags", G_DBUS_PROXY_FLAGS_NONE, + "g-name", WPAS_DBUS_SERVICE, + "g-object-path", object_path, + "g-interface-name", WPAS_DBUS_IFACE_BSS, + NULL); + g_hash_table_insert (priv->bss_proxies, + (char *) g_dbus_proxy_get_object_path (bss_proxy), + bss_proxy); + g_signal_connect (bss_proxy, "g-properties-changed", G_CALLBACK (bss_props_changed_cb), self); + g_async_initable_init_async (G_ASYNC_INITABLE (bss_proxy), + G_PRIORITY_DEFAULT, + priv->other_cancellable, + (GAsyncReadyCallback) on_bss_proxy_acquired, + self); } static void @@ -297,39 +215,23 @@ set_state (NMSupplicantInterface *self, guint32 new_state) g_return_if_fail (new_state > NM_SUPPLICANT_INTERFACE_STATE_READY); if (new_state == NM_SUPPLICANT_INTERFACE_STATE_READY) { - /* Get properties again to update to the actual wpa_supplicant - * interface state. - */ - wpas_iface_get_props (self); - } else if (new_state == NM_SUPPLICANT_INTERFACE_STATE_DOWN) { - /* Cancel all pending calls when going down */ - nm_call_store_clear (priv->other_pcalls); - nm_call_store_clear (priv->assoc_pcalls); - - /* Disconnect supplicant manager state listeners since we're done */ - if (priv->smgr_avail_id) { - g_signal_handler_disconnect (priv->smgr, priv->smgr_avail_id); - priv->smgr_avail_id = 0; + if (priv->other_cancellable) { + g_warn_if_fail (priv->other_cancellable == NULL); + g_cancellable_cancel (priv->other_cancellable); + g_clear_object (&priv->other_cancellable); } + priv->other_cancellable = g_cancellable_new (); + } else if (new_state == NM_SUPPLICANT_INTERFACE_STATE_DOWN) { + if (priv->init_cancellable) + g_cancellable_cancel (priv->init_cancellable); + g_clear_object (&priv->init_cancellable); - if (priv->iface_proxy) { - dbus_g_proxy_disconnect_signal (priv->iface_proxy, - "PropertiesChanged", - G_CALLBACK (wpas_iface_properties_changed), - self); - dbus_g_proxy_disconnect_signal (priv->iface_proxy, - "ScanDone", - G_CALLBACK (wpas_iface_scan_done), - self); - dbus_g_proxy_disconnect_signal (priv->iface_proxy, - "BSSAdded", - G_CALLBACK (wpas_iface_bss_added), - self); - dbus_g_proxy_disconnect_signal (priv->iface_proxy, - "BSSRemoved", - G_CALLBACK (wpas_iface_bss_removed), - self); - } + if (priv->other_cancellable) + g_cancellable_cancel (priv->other_cancellable); + g_clear_object (&priv->other_cancellable); + + if (priv->iface_proxy) + g_signal_handlers_disconnect_by_data (priv->iface_proxy, self); } priv->state = new_state; @@ -348,6 +250,34 @@ set_state (NMSupplicantInterface *self, guint32 new_state) priv->disconnect_reason); } +static int +wpas_state_string_to_enum (const char *str_state) +{ + if (!strcmp (str_state, "interface_disabled")) + return NM_SUPPLICANT_INTERFACE_STATE_DISABLED; + else if (!strcmp (str_state, "disconnected")) + return NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED; + else if (!strcmp (str_state, "inactive")) + return NM_SUPPLICANT_INTERFACE_STATE_INACTIVE; + else if (!strcmp (str_state, "scanning")) + return NM_SUPPLICANT_INTERFACE_STATE_SCANNING; + else if (!strcmp (str_state, "authenticating")) + return NM_SUPPLICANT_INTERFACE_STATE_AUTHENTICATING; + else if (!strcmp (str_state, "associating")) + return NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATING; + else if (!strcmp (str_state, "associated")) + return NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATED; + else if (!strcmp (str_state, "4way_handshake")) + return NM_SUPPLICANT_INTERFACE_STATE_4WAY_HANDSHAKE; + else if (!strcmp (str_state, "group_handshake")) + return NM_SUPPLICANT_INTERFACE_STATE_GROUP_HANDSHAKE; + else if (!strcmp (str_state, "completed")) + return NM_SUPPLICANT_INTERFACE_STATE_COMPLETED; + + nm_log_warn (LOGD_SUPPLICANT, "Unknown supplicant state '%s'", str_state); + return -1; +} + static void set_state_from_string (NMSupplicantInterface *self, const char *new_state) { @@ -396,107 +326,36 @@ nm_supplicant_interface_get_last_scan_time (NMSupplicantInterface *self) return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->last_scan; } -static void -wpas_iface_scan_done (DBusGProxy *proxy, - gboolean success, - gpointer user_data) -{ - NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); - NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - - /* Cache last scan completed time */ - priv->last_scan = nm_utils_get_monotonic_timestamp_s (); - g_signal_emit (self, signals[SCAN_DONE], 0, success); -} +#define MATCH_PROPERTY(p, n, v, t) (!strcmp (p, n) && g_variant_is_of_type (v, t)) static void -parse_capabilities (NMSupplicantInterface *self, GHashTable *props) +parse_capabilities (NMSupplicantInterface *self, GVariant *capabilities) { NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - GValue *value; gboolean have_active = FALSE, have_ssid = FALSE; + gint32 max_scan_ssids = -1; + const char **array; - g_return_if_fail (props != NULL); - - value = g_hash_table_lookup (props, "Scan"); - if (value && G_VALUE_HOLDS (value, G_TYPE_STRV)) { - const char **vals = g_value_get_boxed (value); - const char **iter = vals; + g_return_if_fail (capabilities && g_variant_is_of_type (capabilities, G_VARIANT_TYPE_VARDICT)); - while (iter && *iter && (!have_active || !have_ssid)) { - if (g_strcmp0 (*iter, "active") == 0) - have_active = TRUE; - else if (g_strcmp0 (*iter, "ssid") == 0) - have_ssid = TRUE; - iter++; - } + if (g_variant_lookup (capabilities, "Scan", "^a&s", &array)) { + if (_nm_utils_string_in_list ("active", array)) + have_active = TRUE; + if (_nm_utils_string_in_list ("ssid", array)) + have_ssid = TRUE; + g_free (array); } - value = g_hash_table_lookup (props, "MaxScanSSID"); - if (value && G_VALUE_HOLDS (value, G_TYPE_INT)) { + if (g_variant_lookup (capabilities, "MaxScanSSID", "i", &max_scan_ssids)) { /* We need active scan and SSID probe capabilities to care about MaxScanSSIDs */ - if (have_active && have_ssid) { + if (max_scan_ssids > 0 && have_active && have_ssid) { /* wpa_supplicant's WPAS_MAX_SCAN_SSIDS value is 16, but for speed * and to ensure we don't disclose too many SSIDs from the hidden * list, we'll limit to 5. */ - priv->max_scan_ssids = CLAMP (g_value_get_int (value), 0, 5); + priv->max_scan_ssids = CLAMP (max_scan_ssids, 0, 5); nm_log_info (LOGD_SUPPLICANT, "(%s) supports %d scan SSIDs", - priv->dev, priv->max_scan_ssids); - } - } -} - -static void -wpas_iface_properties_changed (DBusGProxy *proxy, - GHashTable *props, - gpointer user_data) -{ - NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); - NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - GValue *value; - - value = g_hash_table_lookup (props, "Scanning"); - if (value && G_VALUE_HOLDS_BOOLEAN (value)) - set_scanning (self, g_value_get_boolean (value)); - - value = g_hash_table_lookup (props, "State"); - if (value && G_VALUE_HOLDS_STRING (value)) { - if (priv->state >= NM_SUPPLICANT_INTERFACE_STATE_READY) { - /* Only transition to actual wpa_supplicant interface states (ie, - * anything > READY) after the NMSupplicantInterface has had a - * chance to initialize, which is signalled by entering the READY - * state. - */ - set_state_from_string (self, g_value_get_string (value)); - } - } - - value = g_hash_table_lookup (props, "BSSs"); - if (value && G_VALUE_HOLDS (value, DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH)) { - GPtrArray *paths = g_value_get_boxed (value); - int i; - - for (i = 0; paths && (i < paths->len); i++) - handle_new_bss (self, g_ptr_array_index (paths, i), NULL); - } - - value = g_hash_table_lookup (props, "Capabilities"); - if (value && G_VALUE_HOLDS (value, DBUS_TYPE_G_MAP_OF_VARIANT)) - parse_capabilities (self, g_value_get_boxed (value)); - - /* Disconnect reason is currently only given for deauthentication events, - * not disassociation; currently they are IEEE 802.11 "reason codes", - * defined by (IEEE 802.11-2007, 7.3.1.7, Table 7-22). Any locally caused - * deauthentication will be negative, while authentications caused by the - * AP will be positive. - */ - value = g_hash_table_lookup (props, "DisconnectReason"); - if (value && G_VALUE_HOLDS (value, G_TYPE_INT)) { - priv->disconnect_reason = g_value_get_int (value); - if (priv->disconnect_reason != 0) { - nm_log_warn (LOGD_SUPPLICANT, "Connection disconnected (reason %d)", - priv->disconnect_reason); + priv->dev, priv->max_scan_ssids); } } } @@ -513,43 +372,6 @@ iface_check_ready (NMSupplicantInterface *self) } } -static void -iface_get_props_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) -{ - NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); - NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - GHashTable *props = NULL; - GError *error = NULL; - - nm_call_store_remove (priv->other_pcalls, proxy, call_id); - if (dbus_g_proxy_end_call (proxy, call_id, &error, - DBUS_TYPE_G_MAP_OF_VARIANT, &props, - G_TYPE_INVALID)) { - wpas_iface_properties_changed (NULL, props, self); - g_hash_table_destroy (props); - } else { - nm_log_warn (LOGD_SUPPLICANT, "could not get interface properties: %s.", - error && error->message ? error->message : "(unknown)"); - g_clear_error (&error); - } - iface_check_ready (self); -} - -static void -wpas_iface_get_props (NMSupplicantInterface *self) -{ - NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - DBusGProxyCall *call; - - call = dbus_g_proxy_begin_call (priv->props_proxy, "GetAll", - iface_get_props_cb, - self, - NULL, - G_TYPE_STRING, WPAS_DBUS_IFACE_INTERFACE, - G_TYPE_INVALID); - nm_call_store_add (priv->other_pcalls, priv->props_proxy, call); -} - gboolean nm_supplicant_interface_credentials_reply (NMSupplicantInterface *self, const char *field, @@ -557,6 +379,7 @@ nm_supplicant_interface_credentials_reply (NMSupplicantInterface *self, GError **error) { NMSupplicantInterfacePrivate *priv; + gs_unref_variant GVariant *reply = NULL; g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), FALSE); g_return_val_if_fail (field != NULL, FALSE); @@ -567,74 +390,62 @@ nm_supplicant_interface_credentials_reply (NMSupplicantInterface *self, /* Need a network block object path */ g_return_val_if_fail (priv->net_path, FALSE); - return dbus_g_proxy_call_with_timeout (priv->iface_proxy, "NetworkReply", - 5000, - error, - DBUS_TYPE_G_OBJECT_PATH, priv->net_path, - G_TYPE_STRING, field, - G_TYPE_STRING, value, - G_TYPE_INVALID); + reply = g_dbus_proxy_call_sync (priv->iface_proxy, + "NetworkReply", + g_variant_new ("(oss)", + priv->net_path, + field, + value), + G_DBUS_CALL_FLAGS_NONE, + 5000, + NULL, + error); + /* reply will be unrefed when function exits */ + return !!reply; } -static void -wpas_iface_network_request (DBusGProxy *proxy, - const char *object_path, - const char *field, - const char *message, - gpointer user_data) +static gboolean +_dbus_error_has_name (GError *error, const char *dbus_error_name) { - NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); - NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - - g_return_if_fail (priv->has_credreq == TRUE); - g_return_if_fail (priv->net_path != NULL); - g_return_if_fail (g_strcmp0 (object_path, priv->net_path) == 0); + gs_free char *error_name = NULL; + gboolean is_error = FALSE; - g_signal_emit (self, signals[CREDENTIALS_REQUEST], 0, field, message); + if (error && g_dbus_error_is_remote_error (error)) { + error_name = g_dbus_error_get_remote_error (error); + is_error = !g_strcmp0 (error_name, dbus_error_name); + } + return is_error; } static void -iface_check_netreply_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +iface_check_netreply_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) { - NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); - NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - GError *error = NULL; - - nm_call_store_remove (priv->other_pcalls, proxy, call_id); - if ( dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID) - || dbus_g_error_has_name (error, "fi.w1.wpa_supplicant1.InvalidArgs")) { - /* We know NetworkReply is supported if the NetworkReply method returned - * successfully (which is unexpected since we sent a bogus network - * object path) or if we got an "InvalidArgs" (which indicates NetworkReply - * is supported). We know it's not supported if we get an - * "UnknownMethod" error. - */ - priv->has_credreq = TRUE; + NMSupplicantInterface *self; + NMSupplicantInterfacePrivate *priv; + gs_unref_variant GVariant *variant = NULL; + gs_free_error GError *error = NULL; + + /* We know NetworkReply is supported if the NetworkReply method returned + * successfully (which is unexpected since we sent a bogus network + * object path) or if we got an "InvalidArgs" (which indicates NetworkReply + * is supported). We know it's not supported if we get an + * "UnknownMethod" error. + */ - nm_log_dbg (LOGD_SUPPLICANT, "Supplicant %s network credentials requests", - priv->has_credreq ? "supports" : "does not support"); - } - g_clear_error (&error); + variant = g_dbus_proxy_call_finish (proxy, result, &error); + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; - iface_check_ready (self); -} + self = NM_SUPPLICANT_INTERFACE (user_data); + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); -static void -wpas_iface_check_network_reply (NMSupplicantInterface *self) -{ - NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - DBusGProxyCall *call; + if (variant || _dbus_error_has_name (error, "fi.w1.wpa_supplicant1.InvalidArgs")) + priv->has_credreq = TRUE; - priv->ready_count++; - call = dbus_g_proxy_begin_call (priv->iface_proxy, "NetworkReply", - iface_check_netreply_cb, - self, - NULL, - DBUS_TYPE_G_OBJECT_PATH, "/foobaraasdfasdf", - G_TYPE_STRING, "foobar", - G_TYPE_STRING, "foobar", - G_TYPE_INVALID); - nm_call_store_add (priv->other_pcalls, priv->iface_proxy, call); + nm_log_dbg (LOGD_SUPPLICANT, "Supplicant %s network credentials requests", + priv->has_credreq ? "supports" : "does not support"); + + iface_check_ready (self); } ApSupport @@ -657,207 +468,271 @@ nm_supplicant_interface_set_ap_support (NMSupplicantInterface *self, } static void -iface_check_ap_mode_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +iface_check_ap_mode_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) { - NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); - NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - char *data; + NMSupplicantInterface *self; + NMSupplicantInterfacePrivate *priv; + gs_unref_variant GVariant *variant = NULL; + gs_free_error GError *error = NULL; + const char *data; /* The ProbeRequest method only exists if AP mode has been enabled */ - nm_call_store_remove (priv->other_pcalls, proxy, call_id); - if (dbus_g_proxy_end_call (proxy, call_id, NULL, - G_TYPE_STRING, - &data, - G_TYPE_INVALID)) { - if (data && strstr (data, "ProbeRequest")) + variant = g_dbus_proxy_call_finish (proxy, result, &error); + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = NM_SUPPLICANT_INTERFACE (user_data); + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + if (variant && g_variant_is_of_type (variant, G_VARIANT_TYPE ("(s)"))) { + g_variant_get (variant, "(&s)", &data); + if (strstr (data, "ProbeRequest")) priv->ap_support = AP_SUPPORT_YES; - g_free (data); } iface_check_ready (self); } +#define MATCH_SIGNAL(s, n, v, t) (!strcmp (s, n) && g_variant_is_of_type (v, t)) + static void -wpas_iface_check_ap_mode (NMSupplicantInterface *self) +signal_cb (GDBusProxy *proxy, + const gchar *sender, + const gchar *signal, + GVariant *args, + gpointer user_data) { + NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - DBusGProxyCall *call; + const char *path, *field, *message; + gboolean success; - priv->ready_count++; + if (MATCH_SIGNAL (signal, "ScanDone", args, G_VARIANT_TYPE ("(b)"))) { + /* Cache last scan completed time */ + priv->last_scan = nm_utils_get_monotonic_timestamp_s (); - /* If the global supplicant capabilities property is not present, we can - * fall back to checking whether the ProbeRequest method is supported. If - * neither of these works we have no way of determining if AP mode is - * supported or not. hostap 1.0 and earlier don't support either of these. - */ - call = dbus_g_proxy_begin_call (priv->introspect_proxy, "Introspect", - iface_check_ap_mode_cb, - self, - NULL, - G_TYPE_INVALID); - nm_call_store_add (priv->other_pcalls, priv->introspect_proxy, call); + g_variant_get (args, "(b)", &success); + g_signal_emit (self, signals[SCAN_DONE], 0, success); + } else if (MATCH_SIGNAL (signal, "BSSAdded", args, G_VARIANT_TYPE ("(oa{sv})"))) { + if (priv->scanning) + priv->last_scan = nm_utils_get_monotonic_timestamp_s (); + + g_variant_get (args, "(&oa{sv})", &path, NULL); + handle_new_bss (self, path); + } else if (MATCH_SIGNAL (signal, "BSSRemoved", args, G_VARIANT_TYPE ("(o)"))) { + g_variant_get (args, "(&o)", &path); + g_signal_emit (self, signals[BSS_REMOVED], 0, path); + g_hash_table_remove (priv->bss_proxies, path); + } else if (MATCH_SIGNAL (signal, "NetworkRequest", args, G_VARIANT_TYPE ("(oss)"))) { + g_variant_get (args, "(&o&s&s)", &path, &field, &message); + if (priv->has_credreq && priv->net_path && !g_strcmp0 (path, priv->net_path)) + g_signal_emit (self, signals[CREDENTIALS_REQUEST], 0, field, message); + } } static void -interface_add_done (NMSupplicantInterface *self, char *path) +props_changed_cb (GDBusProxy *proxy, + GVariant *changed_properties, + GStrv invalidated_properties, + gpointer user_data) { + NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + const char *s, **array, **iter; + gboolean b = FALSE; + gint32 i32; + GVariant *v; + + if (g_variant_lookup (changed_properties, "Scanning", "b", &b)) + set_scanning (self, b); + + if ( g_variant_lookup (changed_properties, "State", "&s", &s) + && priv->state >= NM_SUPPLICANT_INTERFACE_STATE_READY) { + /* Only transition to actual wpa_supplicant interface states (ie, + * anything > READY) after the NMSupplicantInterface has had a + * chance to initialize, which is signalled by entering the READY + * state. + */ + set_state_from_string (self, s); + } - nm_log_dbg (LOGD_SUPPLICANT, "(%s): interface added to supplicant", priv->dev); + if (g_variant_lookup (changed_properties, "BSSs", "^a&s", &array)) { + iter = array; + while (*iter) + handle_new_bss (self, *iter++); + g_free (array); + } - priv->object_path = path; - - priv->iface_proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (priv->dbus_mgr), - WPAS_DBUS_SERVICE, - path, - WPAS_DBUS_IFACE_INTERFACE); - - dbus_g_object_register_marshaller (g_cclosure_marshal_VOID__BOXED, - G_TYPE_NONE, - DBUS_TYPE_G_MAP_OF_VARIANT, - G_TYPE_INVALID); - dbus_g_proxy_add_signal (priv->iface_proxy, "PropertiesChanged", - DBUS_TYPE_G_MAP_OF_VARIANT, G_TYPE_INVALID); - dbus_g_proxy_connect_signal (priv->iface_proxy, "PropertiesChanged", - G_CALLBACK (wpas_iface_properties_changed), - self, NULL); - - dbus_g_proxy_add_signal (priv->iface_proxy, "ScanDone", - G_TYPE_BOOLEAN, G_TYPE_INVALID); - dbus_g_proxy_connect_signal (priv->iface_proxy, "ScanDone", - G_CALLBACK (wpas_iface_scan_done), - self, - NULL); - - dbus_g_object_register_marshaller (g_cclosure_marshal_generic, - G_TYPE_NONE, - DBUS_TYPE_G_OBJECT_PATH, DBUS_TYPE_G_MAP_OF_VARIANT, - G_TYPE_INVALID); - dbus_g_proxy_add_signal (priv->iface_proxy, "BSSAdded", - DBUS_TYPE_G_OBJECT_PATH, DBUS_TYPE_G_MAP_OF_VARIANT, - G_TYPE_INVALID); - dbus_g_proxy_connect_signal (priv->iface_proxy, "BSSAdded", - G_CALLBACK (wpas_iface_bss_added), - self, - NULL); - - dbus_g_object_register_marshaller (g_cclosure_marshal_VOID__BOXED, - G_TYPE_NONE, - DBUS_TYPE_G_OBJECT_PATH, - G_TYPE_INVALID); - dbus_g_proxy_add_signal (priv->iface_proxy, "BSSRemoved", - DBUS_TYPE_G_OBJECT_PATH, - G_TYPE_INVALID); - dbus_g_proxy_connect_signal (priv->iface_proxy, "BSSRemoved", - G_CALLBACK (wpas_iface_bss_removed), - self, - NULL); - - dbus_g_object_register_marshaller (g_cclosure_marshal_generic, - G_TYPE_NONE, - DBUS_TYPE_G_OBJECT_PATH, G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_INVALID); - dbus_g_proxy_add_signal (priv->iface_proxy, "NetworkRequest", - DBUS_TYPE_G_OBJECT_PATH, G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_INVALID); - dbus_g_proxy_connect_signal (priv->iface_proxy, "NetworkRequest", - G_CALLBACK (wpas_iface_network_request), - self, - NULL); - - priv->introspect_proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (priv->dbus_mgr), - WPAS_DBUS_SERVICE, - priv->object_path, - DBUS_INTERFACE_INTROSPECTABLE); - - priv->props_proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (priv->dbus_mgr), - WPAS_DBUS_SERVICE, - path, - DBUS_INTERFACE_PROPERTIES); - /* Get initial properties and check whether NetworkReply is supported */ - priv->ready_count = 1; - wpas_iface_get_props (self); + v = g_variant_lookup_value (changed_properties, "Capabilities", G_VARIANT_TYPE_VARDICT); + if (v) { + parse_capabilities (self, v); + g_variant_unref (v); + } - /* These two increment ready_count themselves */ - wpas_iface_check_network_reply (self); - if (priv->ap_support == AP_SUPPORT_UNKNOWN) - wpas_iface_check_ap_mode (self); + if (g_variant_lookup (changed_properties, "DisconnectReason", "i", &i32)) { + /* Disconnect reason is currently only given for deauthentication events, + * not disassociation; currently they are IEEE 802.11 "reason codes", + * defined by (IEEE 802.11-2007, 7.3.1.7, Table 7-22). Any locally caused + * deauthentication will be negative, while authentications caused by the + * AP will be positive. + */ + priv->disconnect_reason = i32; + if (priv->disconnect_reason != 0) { + nm_log_warn (LOGD_SUPPLICANT, "Connection disconnected (reason %d)", + priv->disconnect_reason); + } + } } static void -interface_get_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +on_iface_proxy_acquired (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) { - NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); - NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - GError *error = NULL; - char *path = NULL; + NMSupplicantInterface *self; + NMSupplicantInterfacePrivate *priv; + gs_free_error GError *error = NULL; - nm_call_store_remove (priv->other_pcalls, proxy, call_id); - if (dbus_g_proxy_end_call (proxy, call_id, &error, - DBUS_TYPE_G_OBJECT_PATH, &path, - G_TYPE_INVALID)) { - interface_add_done (self, path); - } else { - nm_log_err (LOGD_SUPPLICANT, "(%s): error getting interface: %s", - priv->dev, error->message); - g_clear_error (&error); - set_state (self, NM_SUPPLICANT_INTERFACE_STATE_DOWN); + if (!g_async_initable_init_finish (G_ASYNC_INITABLE (proxy), result, &error)) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + nm_log_warn (LOGD_SUPPLICANT, "Failed to acquire wpa_supplicant interface proxy: (%s)", error->message); + set_state (NM_SUPPLICANT_INTERFACE (user_data), NM_SUPPLICANT_INTERFACE_STATE_DOWN); + } + return; + } + + self = NM_SUPPLICANT_INTERFACE (user_data); + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + g_signal_connect (priv->iface_proxy, "g-signal", G_CALLBACK (signal_cb), self); + + /* Check whether NetworkReply and AP mode are supported */ + priv->ready_count = 1; + g_dbus_proxy_call (priv->iface_proxy, + "NetworkReply", + g_variant_new ("(oss)", + "/fff", + "foobar", + "foobar"), + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->init_cancellable, + (GAsyncReadyCallback) iface_check_netreply_cb, + self); + + if (priv->ap_support == AP_SUPPORT_UNKNOWN) { + /* If the global supplicant capabilities property is not present, we can + * fall back to checking whether the ProbeRequest method is supported. If + * neither of these works we have no way of determining if AP mode is + * supported or not. hostap 1.0 and earlier don't support either of these. + */ + priv->ready_count++; + g_dbus_proxy_call (priv->iface_proxy, + "org.freedesktop.DBus.Introspectable.Introspect", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->init_cancellable, + (GAsyncReadyCallback) iface_check_ap_mode_cb, + self); } } static void -interface_get (NMSupplicantInterface *self) +interface_add_done (NMSupplicantInterface *self, const char *path) { NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - DBusGProxyCall *call; - call = dbus_g_proxy_begin_call (priv->wpas_proxy, "GetInterface", - interface_get_cb, - self, - NULL, - G_TYPE_STRING, priv->dev, - G_TYPE_INVALID); - nm_call_store_add (priv->other_pcalls, priv->wpas_proxy, call); + nm_log_dbg (LOGD_SUPPLICANT, "(%s): interface added to supplicant", priv->dev); + + priv->object_path = g_strdup (path); + priv->iface_proxy = g_object_new (G_TYPE_DBUS_PROXY, + "g-bus-type", G_BUS_TYPE_SYSTEM, + "g-flags", G_DBUS_PROXY_FLAGS_NONE, + "g-name", WPAS_DBUS_SERVICE, + "g-object-path", priv->object_path, + "g-interface-name", WPAS_DBUS_IFACE_INTERFACE, + NULL); + g_signal_connect (priv->iface_proxy, "g-properties-changed", G_CALLBACK (props_changed_cb), self); + g_async_initable_init_async (G_ASYNC_INITABLE (priv->iface_proxy), + G_PRIORITY_DEFAULT, + priv->init_cancellable, + (GAsyncReadyCallback) on_iface_proxy_acquired, + self); } static void -interface_add_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +interface_get_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) { - NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); - NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - GError *error = NULL; - char *path = NULL; + NMSupplicantInterface *self; + NMSupplicantInterfacePrivate *priv; + gs_unref_variant GVariant *variant = NULL; + gs_free_error GError *error = NULL; + char *path; - nm_call_store_remove (priv->other_pcalls, proxy, call_id); - if (dbus_g_proxy_end_call (proxy, call_id, &error, - DBUS_TYPE_G_OBJECT_PATH, &path, - G_TYPE_INVALID)) { + variant = g_dbus_proxy_call_finish (proxy, result, &error); + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = NM_SUPPLICANT_INTERFACE (user_data); + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + if (variant && g_variant_is_of_type (variant, G_VARIANT_TYPE ("(o)"))) { + g_variant_get (variant, "(o)", &path); interface_add_done (self, path); + g_free (path); } else { - if (dbus_g_error_has_name (error, WPAS_ERROR_EXISTS_ERROR)) { - /* Interface already added, just get its object path */ - interface_get (self); - } else if ( g_error_matches (error, DBUS_GERROR, DBUS_GERROR_SERVICE_UNKNOWN) - || g_error_matches (error, DBUS_GERROR, DBUS_GERROR_SPAWN_EXEC_FAILED) - || g_error_matches (error, DBUS_GERROR, DBUS_GERROR_SPAWN_FORK_FAILED) - || g_error_matches (error, DBUS_GERROR, DBUS_GERROR_SPAWN_FAILED) - || g_error_matches (error, DBUS_GERROR, DBUS_GERROR_TIMEOUT) - || g_error_matches (error, DBUS_GERROR, DBUS_GERROR_NO_REPLY) - || g_error_matches (error, DBUS_GERROR, DBUS_GERROR_TIMED_OUT) - || dbus_g_error_has_name (error, DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND)) { - /* Supplicant wasn't running and could not be launched via service - * activation. Wait for it to start by moving back to the INIT - * state. - */ - nm_log_dbg (LOGD_SUPPLICANT, "(%s): failed to activate supplicant: %s", - priv->dev, error->message); - set_state (self, NM_SUPPLICANT_INTERFACE_STATE_INIT); - } else { - nm_log_err (LOGD_SUPPLICANT, "(%s): error adding interface: %s", - priv->dev, error->message); - set_state (self, NM_SUPPLICANT_INTERFACE_STATE_DOWN); - } - g_clear_error (&error); + nm_log_err (LOGD_SUPPLICANT, "(%s): error getting interface: %s", priv->dev, error->message); + set_state (self, NM_SUPPLICANT_INTERFACE_STATE_DOWN); + } +} + +static void +interface_add_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) +{ + NMSupplicantInterface *self; + NMSupplicantInterfacePrivate *priv; + gs_free_error GError *error = NULL; + gs_unref_variant GVariant *variant = NULL; + char *path; + + variant = g_dbus_proxy_call_finish (proxy, result, &error); + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = NM_SUPPLICANT_INTERFACE (user_data); + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + if (variant && g_variant_is_of_type (variant, G_VARIANT_TYPE ("(o)"))) { + g_variant_get (variant, "(o)", &path); + interface_add_done (self, path); + g_free (path); + } else if (_dbus_error_has_name (error, WPAS_ERROR_EXISTS_ERROR)) { + /* Interface already added, just get its object path */ + g_dbus_proxy_call (priv->wpas_proxy, + "GetInterface", + g_variant_new ("(s)", priv->dev), + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->init_cancellable, + (GAsyncReadyCallback) interface_get_cb, + self); + } else if ( g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN) + || g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SPAWN_EXEC_FAILED) + || g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SPAWN_FORK_FAILED) + || g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SPAWN_FAILED) + || g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_TIMEOUT) + || g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_NO_REPLY) + || g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_TIMED_OUT) + || g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND)) { + /* Supplicant wasn't running and could not be launched via service + * activation. Wait for it to start by moving back to the INIT + * state. + */ + nm_log_dbg (LOGD_SUPPLICANT, "(%s): failed to activate supplicant: %s", + priv->dev, error->message); + set_state (self, NM_SUPPLICANT_INTERFACE_STATE_INIT); + } else { + nm_log_err (LOGD_SUPPLICANT, "(%s): error adding interface: %s", priv->dev, error->message); + set_state (self, NM_SUPPLICANT_INTERFACE_STATE_DOWN); } } @@ -868,59 +743,90 @@ interface_add_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data #endif static void -interface_add (NMSupplicantInterface *self, gboolean is_wireless) +on_wpas_proxy_acquired (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) { - NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - DBusGProxyCall *call; - GHashTable *hash; - GValue driver = G_VALUE_INIT; - GValue ifname = G_VALUE_INIT; - - /* Can only start the interface from INIT state */ - g_return_if_fail (priv->state == NM_SUPPLICANT_INTERFACE_STATE_INIT); + NMSupplicantInterface *self; + NMSupplicantInterfacePrivate *priv; + gs_free_error GError *error = NULL; + GDBusProxy *wpas_proxy; + GVariantBuilder props; + + wpas_proxy = g_dbus_proxy_new_for_bus_finish (result, &error); + if (!wpas_proxy) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + nm_log_warn (LOGD_SUPPLICANT, "Failed to acquire wpa_supplicant proxy: (%s)", + error ? error->message : "unknown"); + set_state (NM_SUPPLICANT_INTERFACE (user_data), NM_SUPPLICANT_INTERFACE_STATE_DOWN); + } + return; + } - nm_log_dbg (LOGD_SUPPLICANT, "(%s): adding interface to supplicant", priv->dev); + self = NM_SUPPLICANT_INTERFACE (user_data); + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - /* Move to starting to prevent double-calls of interface_add() */ - set_state (self, NM_SUPPLICANT_INTERFACE_STATE_STARTING); + priv->wpas_proxy = wpas_proxy; /* Try to add the interface to the supplicant. If the supplicant isn't * running, this will start it via D-Bus activation and return the response * when the supplicant has started. */ - hash = g_hash_table_new (g_str_hash, g_str_equal); + g_variant_builder_init (&props, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add (&props, "{sv}", + "Driver", + g_variant_new_string (priv->is_wireless ? DEFAULT_WIFI_DRIVER : "wired")); + g_variant_builder_add (&props, "{sv}", + "Ifname", + g_variant_new_string (priv->dev)); + + g_dbus_proxy_call (priv->wpas_proxy, + "CreateInterface", + g_variant_new ("(a{sv})", &props), + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->init_cancellable, + (GAsyncReadyCallback) interface_add_cb, + self); +} - g_value_init (&driver, G_TYPE_STRING); - g_value_set_string (&driver, is_wireless ? DEFAULT_WIFI_DRIVER : "wired"); - g_hash_table_insert (hash, "Driver", &driver); +static void +interface_add (NMSupplicantInterface *self, gboolean is_wireless) +{ + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - g_value_init (&ifname, G_TYPE_STRING); - g_value_set_string (&ifname, priv->dev); - g_hash_table_insert (hash, "Ifname", &ifname); + /* Can only start the interface from INIT state */ + g_return_if_fail (priv->state == NM_SUPPLICANT_INTERFACE_STATE_INIT); - call = dbus_g_proxy_begin_call (priv->wpas_proxy, "CreateInterface", - interface_add_cb, - self, - NULL, - DBUS_TYPE_G_MAP_OF_VARIANT, hash, - G_TYPE_INVALID); - nm_call_store_add (priv->other_pcalls, priv->wpas_proxy, call); + nm_log_dbg (LOGD_SUPPLICANT, "(%s): adding interface to supplicant", priv->dev); + + priv->is_wireless = is_wireless; + + /* Move to starting to prevent double-calls of interface_add() */ + set_state (self, NM_SUPPLICANT_INTERFACE_STATE_STARTING); - g_hash_table_destroy (hash); - g_value_unset (&driver); - g_value_unset (&ifname); + g_warn_if_fail (priv->init_cancellable == NULL); + g_clear_object (&priv->init_cancellable); + priv->init_cancellable = g_cancellable_new (); + + g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, + NULL, + WPAS_DBUS_SERVICE, + WPAS_DBUS_PATH, + WPAS_DBUS_INTERFACE, + priv->init_cancellable, + (GAsyncReadyCallback) on_wpas_proxy_acquired, + self); } -static void -smgr_avail_cb (NMSupplicantManager *smgr, - GParamSpec *pspec, - gpointer user_data) +void +nm_supplicant_interface_set_supplicant_available (NMSupplicantInterface *self, + gboolean available) { - NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); - NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (user_data); + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - if (nm_supplicant_manager_available (smgr)) { + if (available) { /* This can happen if the supplicant couldn't be activated but * for some reason was started after the activation failure. */ @@ -933,27 +839,14 @@ smgr_avail_cb (NMSupplicantManager *smgr, } static void -remove_network_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) -{ - GError *error = NULL; - - if (!dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID)) { - nm_log_dbg (LOGD_SUPPLICANT, "Couldn't remove network from supplicant interface: %s.", - error && error->message ? error->message : "(unknown)"); - g_clear_error (&error); - } -} - -static void -disconnect_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +log_result_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) { - GError *error = NULL; + gs_unref_variant GVariant *reply = NULL; + gs_free_error GError *error = NULL; - if (!dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID)) { - nm_log_warn (LOGD_SUPPLICANT, "Couldn't disconnect supplicant interface: %s.", - error && error->message ? error->message : "(unknown)"); - g_clear_error (&error); - } + reply = g_dbus_proxy_call_finish (proxy, result, &error); + if (!reply && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + nm_log_warn (LOGD_SUPPLICANT, "Failed to %s: %s.", error->message, (char *) user_data); } void @@ -965,10 +858,11 @@ nm_supplicant_interface_disconnect (NMSupplicantInterface * self) priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - /* Clear and cancel all pending calls related to a prior - * connection attempt. - */ - nm_call_store_clear (priv->assoc_pcalls); + /* Cancel all pending calls related to a prior connection attempt */ + if (priv->assoc_cancellable) { + g_cancellable_cancel (priv->assoc_cancellable); + g_clear_object (&priv->assoc_cancellable); + } /* Don't do anything if there is no connection to the supplicant yet. */ if (!priv->iface_proxy) @@ -977,36 +871,41 @@ nm_supplicant_interface_disconnect (NMSupplicantInterface * self) /* Disconnect from the current AP */ if ( (priv->state >= NM_SUPPLICANT_INTERFACE_STATE_SCANNING) && (priv->state <= NM_SUPPLICANT_INTERFACE_STATE_COMPLETED)) { - dbus_g_proxy_begin_call (priv->iface_proxy, "Disconnect", - disconnect_cb, - NULL, NULL, - G_TYPE_INVALID); + g_dbus_proxy_call (priv->iface_proxy, + "Disconnect", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) log_result_cb, + "disconnect"); } /* Remove any network that was added by NetworkManager */ if (priv->net_path) { - dbus_g_proxy_begin_call (priv->iface_proxy, "RemoveNetwork", - remove_network_cb, - NULL, NULL, - DBUS_TYPE_G_OBJECT_PATH, priv->net_path, - G_TYPE_INVALID); + g_dbus_proxy_call (priv->iface_proxy, + "RemoveNetwork", + g_variant_new ("(o)", priv->net_path), + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->other_cancellable, + (GAsyncReadyCallback) log_result_cb, + "remove network"); g_free (priv->net_path); priv->net_path = NULL; } } static void -select_network_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +select_network_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) { - NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); - NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - GError *err = NULL; + gs_unref_variant GVariant *reply = NULL; + gs_free_error GError *err = NULL; - nm_call_store_remove (priv->assoc_pcalls, proxy, call_id); - if (!dbus_g_proxy_end_call (proxy, call_id, &err, G_TYPE_INVALID)) { + reply = g_dbus_proxy_call_finish (proxy, result, &err); + if (!reply && !g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { nm_log_warn (LOGD_SUPPLICANT, "Couldn't select network config: %s.", err->message); - emit_error_helper (self, err); - g_error_free (err); + emit_error_helper (NM_SUPPLICANT_INTERFACE (user_data), err); } } @@ -1014,111 +913,136 @@ static void call_select_network (NMSupplicantInterface *self) { NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - DBusGProxyCall *call; /* We only select the network after all blobs (if any) have been set */ if (priv->blobs_left == 0) { - call = dbus_g_proxy_begin_call (priv->iface_proxy, "SelectNetwork", - select_network_cb, - self, - NULL, - DBUS_TYPE_G_OBJECT_PATH, priv->net_path, - G_TYPE_INVALID); - nm_call_store_add (priv->assoc_pcalls, priv->iface_proxy, call); + g_dbus_proxy_call (priv->iface_proxy, + "SelectNetwork", + g_variant_new ("(o)", priv->net_path), + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->assoc_cancellable, + (GAsyncReadyCallback) select_network_cb, + self); } } static void -add_blob_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +add_blob_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) { - NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); - NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - GError *err = NULL; + NMSupplicantInterface *self; + NMSupplicantInterfacePrivate *priv; + gs_unref_variant GVariant *reply = NULL; + gs_free_error GError *err = NULL; - priv->blobs_left--; + reply = g_dbus_proxy_call_finish (proxy, result, &err); + if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = NM_SUPPLICANT_INTERFACE (user_data); + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - nm_call_store_remove (priv->assoc_pcalls, proxy, call_id); - if (!dbus_g_proxy_end_call (proxy, call_id, &err, G_TYPE_INVALID)) { + priv->blobs_left--; + if (reply) + call_select_network (self); + else { nm_log_warn (LOGD_SUPPLICANT, "Couldn't set network certificates: %s.", err->message); emit_error_helper (self, err); - g_error_free (err); - } else - call_select_network (self); + } } static void -add_network_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +add_network_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) { - NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); - NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - GError *err = NULL; + NMSupplicantInterface *self; + NMSupplicantInterfacePrivate *priv; + gs_unref_variant GVariant *reply = NULL; + gs_free_error GError *error = NULL; GHashTable *blobs; GHashTableIter iter; - gpointer name, data; - DBusGProxyCall *call; + const char *blob_name; + GByteArray *blob_data; + + reply = g_dbus_proxy_call_finish (proxy, result, &error); + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = NM_SUPPLICANT_INTERFACE (user_data); + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + if (reply && !g_variant_is_of_type (reply, G_VARIANT_TYPE ("(o)"))) { + error = g_error_new (NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED, + "Unexpected AddNetwork reply type %s", + g_variant_get_type_string (reply)); + } g_free (priv->net_path); priv->net_path = NULL; - nm_call_store_remove (priv->assoc_pcalls, proxy, call_id); - if (!dbus_g_proxy_end_call (proxy, call_id, &err, - DBUS_TYPE_G_OBJECT_PATH, &priv->net_path, - G_TYPE_INVALID)) { - nm_log_warn (LOGD_SUPPLICANT, "Couldn't add a network to the supplicant interface: %s.", - err->message); - emit_error_helper (self, err); - g_error_free (err); + if (error) { + nm_log_warn (LOGD_SUPPLICANT, "Adding network to supplicant failed: %s.", error->message); + emit_error_helper (self, error); return; } - /* Send blobs first; otherwise jump to sending the config settings */ + g_variant_get (reply, "(o)", &priv->net_path); + g_assert (priv->net_path); + + /* Send blobs first; otherwise jump to selecting the network */ blobs = nm_supplicant_config_get_blobs (priv->cfg); priv->blobs_left = g_hash_table_size (blobs); + g_hash_table_iter_init (&iter, blobs); - while (g_hash_table_iter_next (&iter, &name, &data)) { - call = dbus_g_proxy_begin_call (priv->iface_proxy, "AddBlob", - add_blob_cb, - self, - NULL, - G_TYPE_STRING, name, - DBUS_TYPE_G_UCHAR_ARRAY, data, - G_TYPE_INVALID); - nm_call_store_add (priv->assoc_pcalls, priv->iface_proxy, call); + while (g_hash_table_iter_next (&iter, (gpointer) &blob_name, (gpointer) &blob_data)) { + g_dbus_proxy_call (priv->iface_proxy, + "AddBlob", + g_variant_new ("(s@ay)", + blob_name, + g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, + blob_data->data, blob_data->len, 1)), + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->assoc_cancellable, + (GAsyncReadyCallback) add_blob_cb, + self); } call_select_network (self); } static void -set_ap_scan_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +set_ap_scan_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) { - NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); - NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - GError *err = NULL; - DBusGProxyCall *call; - GHashTable *config_hash; + NMSupplicantInterface *self; + NMSupplicantInterfacePrivate *priv; + gs_unref_variant GVariant *reply = NULL; + gs_free_error GError *error = NULL; + + reply = g_dbus_proxy_call_finish (proxy, result, &error); + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; - nm_call_store_remove (priv->assoc_pcalls, proxy, call_id); - if (!dbus_g_proxy_end_call (proxy, call_id, &err, G_TYPE_INVALID)) { + self = NM_SUPPLICANT_INTERFACE (user_data); + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + if (!reply) { nm_log_warn (LOGD_SUPPLICANT, "Couldn't send AP scan mode to the supplicant interface: %s.", - err->message); - emit_error_helper (self, err); - g_error_free (err); + error->message); + emit_error_helper (self, error); return; } nm_log_info (LOGD_SUPPLICANT, "Config: set interface ap_scan to %d", nm_supplicant_config_get_ap_scan (priv->cfg)); - config_hash = nm_supplicant_config_get_hash (priv->cfg); - call = dbus_g_proxy_begin_call (priv->iface_proxy, "AddNetwork", - add_network_cb, - self, - NULL, - DBUS_TYPE_G_MAP_OF_VARIANT, config_hash, - G_TYPE_INVALID); - g_hash_table_destroy (config_hash); - nm_call_store_add (priv->assoc_pcalls, priv->iface_proxy, call); + g_dbus_proxy_call (priv->iface_proxy, + "AddNetwork", + g_variant_new ("(@a{sv})", nm_supplicant_config_to_variant (priv->cfg)), + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->assoc_cancellable, + (GAsyncReadyCallback) add_network_cb, + self); } gboolean @@ -1126,8 +1050,6 @@ nm_supplicant_interface_set_config (NMSupplicantInterface *self, NMSupplicantConfig *cfg) { NMSupplicantInterfacePrivate *priv; - DBusGProxyCall *call; - GValue value = G_VALUE_INIT; g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), FALSE); @@ -1143,103 +1065,75 @@ nm_supplicant_interface_set_config (NMSupplicantInterface *self, return FALSE; } - if (priv->cfg) - g_object_unref (priv->cfg); - priv->cfg = cfg; - - if (cfg == NULL) - return TRUE; - - g_object_ref (priv->cfg); - - g_value_init (&value, G_TYPE_UINT); - g_value_set_uint (&value, nm_supplicant_config_get_ap_scan (priv->cfg)); - - call = dbus_g_proxy_begin_call (priv->props_proxy, "Set", - set_ap_scan_cb, - self, - NULL, - G_TYPE_STRING, WPAS_DBUS_IFACE_INTERFACE, - G_TYPE_STRING, "ApScan", - G_TYPE_VALUE, &value, - G_TYPE_INVALID); - nm_call_store_add (priv->assoc_pcalls, priv->props_proxy, call); - - g_value_unset (&value); - return call != NULL; -} - -static void -scan_request_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) -{ - NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); - NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - GError *err = NULL; - - nm_call_store_remove (priv->other_pcalls, proxy, call_id); - if (!dbus_g_proxy_end_call (proxy, call_id, &err, G_TYPE_INVALID)) - nm_log_warn (LOGD_SUPPLICANT, "Could not get scan request result: %s", err->message); - - g_signal_emit (self, signals[SCAN_DONE], 0, err ? FALSE : TRUE); - g_clear_error (&err); + g_clear_object (&priv->cfg); + if (cfg) { + priv->cfg = g_object_ref (cfg); + g_dbus_proxy_call (priv->iface_proxy, + "org.freedesktop.DBus.Properties.Set", + g_variant_new ("(ssv)", + WPAS_DBUS_IFACE_INTERFACE, + "ApScan", + g_variant_new_uint32 (nm_supplicant_config_get_ap_scan (priv->cfg))), + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->assoc_cancellable, + (GAsyncReadyCallback) set_ap_scan_cb, + self); + } + return TRUE; } static void -destroy_gvalue (gpointer data) -{ - GValue *value = (GValue *) data; - - g_value_unset (value); - g_slice_free (GValue, value); -} - -static GValue * -string_to_gvalue (const char *str) +scan_request_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) { - GValue *val = g_slice_new0 (GValue); + gs_unref_variant GVariant *reply = NULL; + gs_free_error GError *error = NULL; - g_value_init (val, G_TYPE_STRING); - g_value_set_string (val, str); - return val; -} - -static GValue * -byte_array_array_to_gvalue (const GPtrArray *array) -{ - GValue *val = g_slice_new0 (GValue); + reply = g_dbus_proxy_call_finish (proxy, result, &error); + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; - g_value_init (val, DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UCHAR); - g_value_set_boxed (val, array); - return val; + if (error) + nm_log_warn (LOGD_SUPPLICANT, "Could not get scan request result: %s", error->message); + g_signal_emit (NM_SUPPLICANT_INTERFACE (user_data), signals[SCAN_DONE], 0, error ? FALSE : TRUE); } gboolean nm_supplicant_interface_request_scan (NMSupplicantInterface *self, const GPtrArray *ssids) { NMSupplicantInterfacePrivate *priv; - DBusGProxyCall *call; - GHashTable *hash; + GVariantBuilder builder; + guint i; g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), FALSE); priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); /* Scan parameters */ - hash = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, destroy_gvalue); - g_hash_table_insert (hash, "Type", string_to_gvalue ("active")); - if (ssids) - g_hash_table_insert (hash, "SSIDs", byte_array_array_to_gvalue (ssids)); - - call = dbus_g_proxy_begin_call (priv->iface_proxy, "Scan", - scan_request_cb, - self, - NULL, - DBUS_TYPE_G_MAP_OF_VARIANT, hash, - G_TYPE_INVALID); - g_hash_table_destroy (hash); - nm_call_store_add (priv->other_pcalls, priv->iface_proxy, call); + g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add (&builder, "{sv}", "Type", g_variant_new_string ("active")); + if (ssids) { + GVariantBuilder ssids_builder; + + g_variant_builder_init (&ssids_builder, G_VARIANT_TYPE_BYTESTRING_ARRAY); + for (i = 0; i < ssids->len; i++) { + GByteArray *ssid = g_ptr_array_index (ssids, i); + g_variant_builder_add (&ssids_builder, "@ay", + g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, + ssid->data, ssid->len, 1)); + } + g_variant_builder_add (&builder, "{sv}", "SSIDs", g_variant_builder_end (&ssids_builder)); + } - return call != NULL; + g_dbus_proxy_call (priv->iface_proxy, + "Scan", + g_variant_new ("(a{sv})", &builder), + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->other_cancellable, + (GAsyncReadyCallback) scan_request_cb, + self); + return TRUE; } guint32 @@ -1323,8 +1217,7 @@ nm_supplicant_interface_get_max_scan_ssids (NMSupplicantInterface *self) /*******************************************************************/ NMSupplicantInterface * -nm_supplicant_interface_new (NMSupplicantManager *smgr, - const char *ifname, +nm_supplicant_interface_new (const char *ifname, gboolean is_wireless, gboolean fast_supported, ApSupport ap_support, @@ -1332,21 +1225,12 @@ nm_supplicant_interface_new (NMSupplicantManager *smgr, { NMSupplicantInterface *self; NMSupplicantInterfacePrivate *priv; - guint id; - g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (smgr), NULL); g_return_val_if_fail (ifname != NULL, NULL); self = g_object_new (NM_TYPE_SUPPLICANT_INTERFACE, NULL); priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - priv->smgr = g_object_ref (smgr); - id = g_signal_connect (priv->smgr, - "notify::" NM_SUPPLICANT_MANAGER_AVAILABLE, - G_CALLBACK (smgr_avail_cb), - self); - priv->smgr_avail_id = id; - priv->dev = g_strdup (ifname); priv->is_wireless = is_wireless; priv->fast_supported = fast_supported; @@ -1362,19 +1246,8 @@ static void nm_supplicant_interface_init (NMSupplicantInterface * self) { NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); - DBusGConnection *bus; priv->state = NM_SUPPLICANT_INTERFACE_STATE_INIT; - priv->assoc_pcalls = nm_call_store_new (); - priv->other_pcalls = nm_call_store_new (); - priv->dbus_mgr = nm_dbus_manager_get (); - - bus = nm_dbus_manager_get_connection (priv->dbus_mgr); - priv->wpas_proxy = dbus_g_proxy_new_for_name (bus, - WPAS_DBUS_SERVICE, - WPAS_DBUS_PATH, - WPAS_DBUS_INTERFACE); - priv->bss_proxies = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref); } @@ -1412,49 +1285,26 @@ dispose (GObject *object) { NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (object); - if (priv->disposed) { - G_OBJECT_CLASS (nm_supplicant_interface_parent_class)->dispose (object); - return; - } - priv->disposed = TRUE; - - /* Cancel pending calls before unrefing the dbus manager */ - nm_call_store_clear (priv->other_pcalls); - nm_call_store_destroy (priv->other_pcalls); - - nm_call_store_clear (priv->assoc_pcalls); - nm_call_store_destroy (priv->assoc_pcalls); - - if (priv->props_proxy) - g_object_unref (priv->props_proxy); - if (priv->iface_proxy) - g_object_unref (priv->iface_proxy); + g_signal_handlers_disconnect_by_data (priv->iface_proxy, NM_SUPPLICANT_INTERFACE (object)); + g_clear_object (&priv->iface_proxy); - g_free (priv->net_path); + if (priv->init_cancellable) + g_cancellable_cancel (priv->init_cancellable); + g_clear_object (&priv->init_cancellable); - if (priv->introspect_proxy) - g_object_unref (priv->introspect_proxy); + if (priv->other_cancellable) + g_cancellable_cancel (priv->other_cancellable); + g_clear_object (&priv->other_cancellable); - if (priv->wpas_proxy) - g_object_unref (priv->wpas_proxy); + g_clear_object (&priv->wpas_proxy); + g_clear_pointer (&priv->bss_proxies, (GDestroyNotify) g_hash_table_destroy); - g_hash_table_destroy (priv->bss_proxies); + g_clear_pointer (&priv->net_path, g_free); + g_clear_pointer (&priv->dev, g_free); + g_clear_pointer (&priv->object_path, g_free); - if (priv->smgr) { - if (priv->smgr_avail_id) - g_signal_handler_disconnect (priv->smgr, priv->smgr_avail_id); - g_object_unref (priv->smgr); - } - - g_free (priv->dev); - - priv->dbus_mgr = NULL; - - if (priv->cfg) - g_object_unref (priv->cfg); - - g_free (priv->object_path); + g_clear_object (&priv->cfg); /* Chain up to the parent class */ G_OBJECT_CLASS (nm_supplicant_interface_parent_class)->dispose (object); @@ -1502,7 +1352,7 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass) G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (NMSupplicantInterfaceClass, new_bss), NULL, NULL, NULL, - G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_POINTER); + G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_VARIANT); signals[BSS_UPDATED] = g_signal_new (NM_SUPPLICANT_INTERFACE_BSS_UPDATED, diff --git a/src/supplicant-manager/nm-supplicant-interface.h b/src/supplicant-manager/nm-supplicant-interface.h index 0112e40e..1b1139d4 100644 --- a/src/supplicant-manager/nm-supplicant-interface.h +++ b/src/supplicant-manager/nm-supplicant-interface.h @@ -119,13 +119,15 @@ typedef struct { GType nm_supplicant_interface_get_type (void); -NMSupplicantInterface * nm_supplicant_interface_new (NMSupplicantManager * smgr, - const char *ifname, +NMSupplicantInterface * nm_supplicant_interface_new (const char *ifname, gboolean is_wireless, gboolean fast_supported, ApSupport ap_support, gboolean start_now); +void nm_supplicant_interface_set_supplicant_available (NMSupplicantInterface *self, + gboolean available); + gboolean nm_supplicant_interface_set_config (NMSupplicantInterface * iface, NMSupplicantConfig * cfg); diff --git a/src/supplicant-manager/nm-supplicant-manager.c b/src/supplicant-manager/nm-supplicant-manager.c index 01e35929..6358c180 100644 --- a/src/supplicant-manager/nm-supplicant-manager.c +++ b/src/supplicant-manager/nm-supplicant-manager.c @@ -25,11 +25,12 @@ #include <glib.h> #include <dbus/dbus.h> +#include "nm-glib-compat.h" + #include "nm-supplicant-manager.h" #include "nm-supplicant-interface.h" -#include "nm-dbus-manager.h" #include "nm-logging.h" -#include "nm-dbus-glib-types.h" +#include "nm-core-internal.h" #define NM_SUPPLICANT_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \ NM_TYPE_SUPPLICANT_MANAGER, \ @@ -37,25 +38,16 @@ G_DEFINE_TYPE (NMSupplicantManager, nm_supplicant_manager, G_TYPE_OBJECT) -/* Properties */ -enum { - PROP_0 = 0, - PROP_AVAILABLE, - LAST_PROP -}; - typedef struct { - NMDBusManager * dbus_mgr; - guint name_owner_id; - DBusGProxy * proxy; - DBusGProxy * props_proxy; - gboolean running; + GDBusProxy * proxy; + GCancellable * cancellable; + gboolean running; + GHashTable * ifaces; gboolean fast_supported; ApSupport ap_support; guint die_count_reset_id; guint die_count; - gboolean disposed; } NMSupplicantManagerPrivate; /********************************************************************/ @@ -90,14 +82,16 @@ nm_supplicant_manager_iface_get (NMSupplicantManager * self, start_now = !die_count_exceeded (priv->die_count); nm_log_dbg (LOGD_SUPPLICANT, "(%s): creating new supplicant interface", ifname); - iface = nm_supplicant_interface_new (self, - ifname, + iface = nm_supplicant_interface_new (ifname, is_wireless, priv->fast_supported, priv->ap_support, start_now); - if (iface) - g_hash_table_insert (priv->ifaces, g_strdup (ifname), iface); + if (iface) { + g_hash_table_insert (priv->ifaces, + (char *) nm_supplicant_interface_get_ifname (iface), + iface); + } } else { nm_log_dbg (LOGD_SUPPLICANT, "(%s): returning existing supplicant interface", ifname); } @@ -125,35 +119,27 @@ nm_supplicant_manager_iface_release (NMSupplicantManager *self, /* Ask wpa_supplicant to remove this interface */ op = nm_supplicant_interface_get_object_path (iface); if (priv->running && priv->proxy && op) { - dbus_g_proxy_call_no_reply (priv->proxy, "RemoveInterface", - DBUS_TYPE_G_OBJECT_PATH, op, - G_TYPE_INVALID); + g_dbus_proxy_call (priv->proxy, + "RemoveInterface", + g_variant_new ("(o)", op), + G_DBUS_CALL_FLAGS_NONE, + 3000, + NULL, + NULL, + NULL); } g_hash_table_remove (priv->ifaces, ifname); } static void -get_capabilities_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +update_capabilities (NMSupplicantManager *self) { - NMSupplicantManager *self = NM_SUPPLICANT_MANAGER (user_data); NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); NMSupplicantInterface *iface; GHashTableIter hash_iter; - GError *error = NULL; - GHashTable *props = NULL; - GValue *value; - char **iter; - - if (!dbus_g_proxy_end_call (proxy, call_id, &error, - DBUS_TYPE_G_MAP_OF_VARIANT, &props, - G_TYPE_INVALID)) { - nm_log_warn (LOGD_CORE, "Unexpected error requesting supplicant properties: (%d) %s", - error ? error->code : -1, - error && error->message ? error->message : "(unknown)"); - g_clear_error (&error); - return; - } + const char **array; + GVariant *value; /* The supplicant only advertises global capabilities if the following * commit has been applied: @@ -165,13 +151,17 @@ get_capabilities_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_ * dbus: Add global capabilities property */ priv->ap_support = AP_SUPPORT_UNKNOWN; - value = g_hash_table_lookup (props, "Capabilities"); - if (value && G_VALUE_HOLDS (value, G_TYPE_STRV)) { - priv->ap_support = AP_SUPPORT_NO; - for (iter = g_value_get_boxed (value); iter && *iter; iter++) { - if (strcasecmp (*iter, "ap") == 0) + + value = g_dbus_proxy_get_cached_property (priv->proxy, "Capabilities"); + if (value) { + if (g_variant_is_of_type (value, G_VARIANT_TYPE_STRING_ARRAY)) { + array = g_variant_get_strv (value, NULL); + priv->ap_support = AP_SUPPORT_NO; + if (_nm_utils_string_in_list ("ap", array)) priv->ap_support = AP_SUPPORT_YES; + g_free (array); } + g_variant_unref (value); } /* Tell all interfaces about results of the AP check */ @@ -185,32 +175,35 @@ get_capabilities_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_ /* EAP-FAST */ priv->fast_supported = FALSE; - value = g_hash_table_lookup (props, "EapMethods"); - if (value && G_VALUE_HOLDS (value, G_TYPE_STRV)) { - for (iter = g_value_get_boxed (value); iter && *iter; iter++) { - if (strcasecmp (*iter, "fast") == 0) + value = g_dbus_proxy_get_cached_property (priv->proxy, "EapMethods"); + if (value) { + if (g_variant_is_of_type (value, G_VARIANT_TYPE_STRING_ARRAY)) { + array = g_variant_get_strv (value, NULL); + if (_nm_utils_string_in_list ("fast", array)) priv->fast_supported = TRUE; + g_free (array); } + g_variant_unref (value); } nm_log_dbg (LOGD_SUPPLICANT, "EAP-FAST is %ssupported", priv->fast_supported ? "" : "not "); - - g_hash_table_unref (props); } static void -check_capabilities (NMSupplicantManager *self) +availability_changed (NMSupplicantManager *self, gboolean available) { NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); + GList *ifaces, *iter; - dbus_g_proxy_begin_call (priv->props_proxy, "GetAll", - get_capabilities_cb, self, NULL, - G_TYPE_STRING, WPAS_DBUS_INTERFACE, - G_TYPE_INVALID); + /* priv->ifaces may be modified if availability changes; can't use GHashTableIter */ + ifaces = g_hash_table_get_values (priv->ifaces); + for (iter = ifaces; iter; iter = iter->next) + nm_supplicant_interface_set_supplicant_available (NM_SUPPLICANT_INTERFACE (iter->data), available); + g_list_free (ifaces); } -gboolean -nm_supplicant_manager_available (NMSupplicantManager *self) +static gboolean +is_available (NMSupplicantManager *self) { g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (self), FALSE); @@ -223,22 +216,26 @@ static void set_running (NMSupplicantManager *self, gboolean now_running) { NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); - gboolean old_available = nm_supplicant_manager_available (self); + gboolean old_available = is_available (self); + gboolean new_available; priv->running = now_running; - if (old_available != nm_supplicant_manager_available (self)) - g_object_notify (G_OBJECT (self), NM_SUPPLICANT_MANAGER_AVAILABLE); + new_available = is_available (self); + if (old_available != new_available) + availability_changed (self, new_available); } static void set_die_count (NMSupplicantManager *self, guint new_die_count) { NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); - gboolean old_available = nm_supplicant_manager_available (self); + gboolean old_available = is_available (self); + gboolean new_available; priv->die_count = new_die_count; - if (old_available != nm_supplicant_manager_available (self)) - g_object_notify (G_OBJECT (self), NM_SUPPLICANT_MANAGER_AVAILABLE); + new_available = is_available (self); + if (old_available != new_available) + availability_changed (self, new_available); } static gboolean @@ -255,28 +252,21 @@ wpas_die_count_reset_cb (gpointer user_data) } static void -name_owner_changed (NMDBusManager *dbus_mgr, - const char *name, - const char *old_owner, - const char *new_owner, - gpointer user_data) +name_owner_cb (GDBusProxy *proxy, GParamSpec *pspec, gpointer user_data) { NMSupplicantManager *self = NM_SUPPLICANT_MANAGER (user_data); NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); - gboolean old_owner_good = (old_owner && strlen (old_owner)); - gboolean new_owner_good = (new_owner && strlen (new_owner)); + char *owner; - /* We only care about the supplicant here */ - if (strcmp (WPAS_DBUS_SERVICE, name) != 0) - return; + g_return_if_fail (proxy == priv->proxy); - if (!old_owner_good && new_owner_good) { - nm_log_info (LOGD_SUPPLICANT, "wpa_supplicant started"); - set_running (self, TRUE); - check_capabilities (self); - } else if (old_owner_good && !new_owner_good) { - nm_log_info (LOGD_SUPPLICANT, "wpa_supplicant stopped"); + owner = g_dbus_proxy_get_name_owner (proxy); + nm_log_info (LOGD_SUPPLICANT, "wpa_supplicant %s", owner ? "running" : "stopped"); + if (owner) { + set_running (self, TRUE); + update_capabilities (self); + } else if (priv->running) { /* Reschedule the die count reset timeout. Every time the supplicant * dies we wait 10 seconds before resetting the counter. If the * supplicant died more than twice before the timer is reset, then @@ -297,6 +287,33 @@ name_owner_changed (NMDBusManager *dbus_mgr, priv->fast_supported = FALSE; } + + g_free (owner); +} + +static void +on_proxy_acquired (GObject *object, GAsyncResult *result, gpointer user_data) +{ + NMSupplicantManager *self; + NMSupplicantManagerPrivate *priv; + GError *error = NULL; + GDBusProxy *proxy; + + proxy = g_dbus_proxy_new_for_bus_finish (result, &error); + if (!proxy) { + nm_log_warn (LOGD_SUPPLICANT, + "Failed to acquire wpa_supplicant proxy: Wi-Fi and 802.1x will not be available (%s)", + error->message); + g_clear_error (&error); + return; + } + + self = NM_SUPPLICANT_MANAGER (user_data); + priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); + + priv->proxy = proxy; + g_signal_connect (priv->proxy, "notify::g-name-owner", G_CALLBACK (name_owner_cb), self); + name_owner_cb (priv->proxy, NULL, self); } /*******************************************************************/ @@ -319,50 +336,19 @@ static void nm_supplicant_manager_init (NMSupplicantManager *self) { NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self); - DBusGConnection *bus; - - priv->dbus_mgr = nm_dbus_manager_get (); - priv->name_owner_id = g_signal_connect (priv->dbus_mgr, - NM_DBUS_MANAGER_NAME_OWNER_CHANGED, - G_CALLBACK (name_owner_changed), - self); - priv->running = nm_dbus_manager_name_has_owner (priv->dbus_mgr, WPAS_DBUS_SERVICE); - - bus = nm_dbus_manager_get_connection (priv->dbus_mgr); - priv->proxy = dbus_g_proxy_new_for_name (bus, - WPAS_DBUS_SERVICE, - WPAS_DBUS_PATH, - WPAS_DBUS_INTERFACE); - - priv->props_proxy = dbus_g_proxy_new_for_name (bus, - WPAS_DBUS_SERVICE, - WPAS_DBUS_PATH, - DBUS_INTERFACE_PROPERTIES); - - priv->ifaces = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); - - /* Check generic supplicant capabilities */ - if (priv->running) - check_capabilities (self); -} -static void -set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) -{ - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); -} - -static void -get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) -{ - switch (prop_id) { - case PROP_AVAILABLE: - g_value_set_boolean (value, nm_supplicant_manager_available (NM_SUPPLICANT_MANAGER (object))); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } + priv->ifaces = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref); + + priv->cancellable = g_cancellable_new (); + g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + WPAS_DBUS_SERVICE, + WPAS_DBUS_PATH, + WPAS_DBUS_INTERFACE, + priv->cancellable, + (GAsyncReadyCallback) on_proxy_acquired, + self); } static void @@ -370,29 +356,19 @@ dispose (GObject *object) { NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (object); - if (priv->disposed) - goto out; - priv->disposed = TRUE; - - if (priv->die_count_reset_id) + if (priv->die_count_reset_id) { g_source_remove (priv->die_count_reset_id); - - if (priv->dbus_mgr) { - if (priv->name_owner_id) - g_signal_handler_disconnect (priv->dbus_mgr, priv->name_owner_id); - priv->dbus_mgr = NULL; + priv->die_count_reset_id = 0; } - g_hash_table_destroy (priv->ifaces); - - if (priv->proxy) - g_object_unref (priv->proxy); + if (priv->cancellable) { + g_cancellable_cancel (priv->cancellable); + g_clear_object (&priv->cancellable); + } - if (priv->props_proxy) - g_object_unref (priv->props_proxy); + g_clear_pointer (&priv->ifaces, g_hash_table_unref); + g_clear_object (&priv->proxy); -out: - /* Chain up to the parent class */ G_OBJECT_CLASS (nm_supplicant_manager_parent_class)->dispose (object); } @@ -403,15 +379,6 @@ nm_supplicant_manager_class_init (NMSupplicantManagerClass *klass) g_type_class_add_private (object_class, sizeof (NMSupplicantManagerPrivate)); - object_class->get_property = get_property; - object_class->set_property = set_property; object_class->dispose = dispose; - - g_object_class_install_property - (object_class, PROP_AVAILABLE, - g_param_spec_boolean (NM_SUPPLICANT_MANAGER_AVAILABLE, "", "", - FALSE, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); } diff --git a/src/supplicant-manager/nm-supplicant-manager.h b/src/supplicant-manager/nm-supplicant-manager.h index 5e7f1eb5..3b8fddf4 100644 --- a/src/supplicant-manager/nm-supplicant-manager.h +++ b/src/supplicant-manager/nm-supplicant-manager.h @@ -26,11 +26,6 @@ #include "nm-supplicant-types.h" #include "nm-device.h" -#define WPAS_DBUS_SERVICE "fi.w1.wpa_supplicant1" -#define WPAS_DBUS_PATH "/fi/w1/wpa_supplicant1" -#define WPAS_DBUS_INTERFACE "fi.w1.wpa_supplicant1" - - G_BEGIN_DECLS #define NM_TYPE_SUPPLICANT_MANAGER (nm_supplicant_manager_get_type ()) @@ -40,8 +35,6 @@ G_BEGIN_DECLS #define NM_IS_SUPPLICANT_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SUPPLICANT_MANAGER)) #define NM_SUPPLICANT_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SUPPLICANT_MANAGER, NMSupplicantManagerClass)) -#define NM_SUPPLICANT_MANAGER_AVAILABLE "available" - struct _NMSupplicantManager { GObject parent; @@ -63,6 +56,4 @@ NMSupplicantInterface *nm_supplicant_manager_iface_get (NMSupplicantManager *mgr void nm_supplicant_manager_iface_release (NMSupplicantManager *mgr, NMSupplicantInterface *iface); -gboolean nm_supplicant_manager_available (NMSupplicantManager *mgr); - #endif /* __NETWORKMANAGER_SUPPLICANT_MANAGER_H__ */ diff --git a/src/supplicant-manager/nm-supplicant-types.h b/src/supplicant-manager/nm-supplicant-types.h index 619f0c3e..1c16e494 100644 --- a/src/supplicant-manager/nm-supplicant-types.h +++ b/src/supplicant-manager/nm-supplicant-types.h @@ -21,6 +21,10 @@ #ifndef __NETWORKMANAGER_SUPPLICANT_TYPES_H__ #define __NETWORKMANAGER_SUPPLICANT_TYPES_H__ +#define WPAS_DBUS_SERVICE "fi.w1.wpa_supplicant1" +#define WPAS_DBUS_PATH "/fi/w1/wpa_supplicant1" +#define WPAS_DBUS_INTERFACE "fi.w1.wpa_supplicant1" + typedef struct _NMSupplicantManager NMSupplicantManager; typedef struct _NMSupplicantInterface NMSupplicantInterface; typedef struct _NMSupplicantConfig NMSupplicantConfig; diff --git a/src/supplicant-manager/tests/Makefile.in b/src/supplicant-manager/tests/Makefile.in index 50981a19..ef5233b2 100644 --- a/src/supplicant-manager/tests/Makefile.in +++ b/src/supplicant-manager/tests/Makefile.in @@ -203,13 +203,196 @@ am__tty_colors = { \ std='[m'; \ fi; \ } +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__recheck_rx = ^[ ]*:recheck:[ ]* +am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* +am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* +# A command that, given a newline-separated list of test names on the +# standard input, print the name of the tests that are to be re-run +# upon "make recheck". +am__list_recheck_tests = $(AWK) '{ \ + recheck = 1; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + { \ + if ((getline line2 < ($$0 ".log")) < 0) \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ + { \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ + { \ + break; \ + } \ + }; \ + if (recheck) \ + print $$0; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# A command that, given a newline-separated list of test names on the +# standard input, create the global log from their .trs and .log files. +am__create_global_log = $(AWK) ' \ +function fatal(msg) \ +{ \ + print "fatal: making $@: " msg | "cat >&2"; \ + exit 1; \ +} \ +function rst_section(header) \ +{ \ + print header; \ + len = length(header); \ + for (i = 1; i <= len; i = i + 1) \ + printf "="; \ + printf "\n\n"; \ +} \ +{ \ + copy_in_global_log = 1; \ + global_test_result = "RUN"; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".trs"); \ + if (line ~ /$(am__global_test_result_rx)/) \ + { \ + sub("$(am__global_test_result_rx)", "", line); \ + sub("[ ]*$$", "", line); \ + global_test_result = line; \ + } \ + else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ + copy_in_global_log = 0; \ + }; \ + if (copy_in_global_log) \ + { \ + rst_section(global_test_result ": " $$0); \ + while ((rc = (getline line < ($$0 ".log"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".log"); \ + print line; \ + }; \ + printf "\n"; \ + }; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# Restructured Text title. +am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } +# Solaris 10 'make', and several other traditional 'make' implementations, +# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it +# by disabling -e (using the XSI extension "set +e") if it's set. +am__sh_e_setup = case $$- in *e*) set +e;; esac +# Default flags passed to test drivers. +am__common_driver_flags = \ + --color-tests "$$am__color_tests" \ + --enable-hard-errors "$$am__enable_hard_errors" \ + --expect-failure "$$am__expect_failure" +# To be inserted before the command running the test. Creates the +# directory for the log if needed. Stores in $dir the directory +# containing $f, in $tst the test, in $log the log. Executes the +# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and +# passes TESTS_ENVIRONMENT. Set up options for the wrapper that +# will run the test scripts (or their associated LOG_COMPILER, if +# thy have one). +am__check_pre = \ +$(am__sh_e_setup); \ +$(am__vpath_adj_setup) $(am__vpath_adj) \ +$(am__tty_colors); \ +srcdir=$(srcdir); export srcdir; \ +case "$@" in \ + */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ + *) am__odir=.;; \ +esac; \ +test "x$$am__odir" = x"." || test -d "$$am__odir" \ + || $(MKDIR_P) "$$am__odir" || exit $$?; \ +if test -f "./$$f"; then dir=./; \ +elif test -f "$$f"; then dir=; \ +else dir="$(srcdir)/"; fi; \ +tst=$$dir$$f; log='$@'; \ +if test -n '$(DISABLE_HARD_ERRORS)'; then \ + am__enable_hard_errors=no; \ +else \ + am__enable_hard_errors=yes; \ +fi; \ +case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ + am__expect_failure=yes;; \ + *) \ + am__expect_failure=no;; \ +esac; \ +$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) +# A shell command to get the names of the tests scripts with any registered +# extension removed (i.e., equivalently, the names of the test logs, with +# the '.log' extension removed). The result is saved in the shell variable +# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, +# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", +# since that might cause problem with VPATH rewrites for suffix-less tests. +# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. +am__set_TESTS_bases = \ + bases='$(TEST_LOGS)'; \ + bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ + bases=`echo $$bases` +RECHECK_LOGS = $(TEST_LOGS) +AM_RECURSIVE_TARGETS = check recheck +TEST_SUITE_LOG = test-suite.log +TEST_EXTENSIONS = @EXEEXT@ .test +LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) +am__set_b = \ + case '$@' in \ + */*) \ + case '$*' in \ + */*) b='$*';; \ + *) b=`echo '$@' | sed 's/\.log$$//'`; \ + esac;; \ + *) \ + b='$*';; \ + esac +am__test_logs1 = $(TESTS:=.log) +am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) +TEST_LOGS = $(am__test_logs2:.test.log=.log) +TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/build-aux/test-driver +TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ + $(TEST_LOG_FLAGS) am__DIST_COMMON = $(srcdir)/Makefile.in \ - $(top_srcdir)/build-aux/depcomp + $(top_srcdir)/build-aux/depcomp \ + $(top_srcdir)/build-aux/test-driver DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -323,6 +506,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ +LOG_DRIVER = @LOG_DRIVER@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ @@ -487,7 +671,7 @@ test_supplicant_config_LDADD = \ all: all-am .SUFFIXES: -.SUFFIXES: .c .lo .o .obj +.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ @@ -618,98 +802,168 @@ cscopelist-am: $(am__tagged_files) distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -check-TESTS: $(TESTS) - @failed=0; all=0; xfail=0; xpass=0; skip=0; \ - srcdir=$(srcdir); export srcdir; \ - list=' $(TESTS) '; \ - $(am__tty_colors); \ - if test -n "$$list"; then \ - for tst in $$list; do \ - if test -f ./$$tst; then dir=./; \ - elif test -f $$tst; then dir=; \ - else dir="$(srcdir)/"; fi; \ - if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \ - all=`expr $$all + 1`; \ - case " $(XFAIL_TESTS) " in \ - *[\ \ ]$$tst[\ \ ]*) \ - xpass=`expr $$xpass + 1`; \ - failed=`expr $$failed + 1`; \ - col=$$red; res=XPASS; \ - ;; \ - *) \ - col=$$grn; res=PASS; \ - ;; \ - esac; \ - elif test $$? -ne 77; then \ - all=`expr $$all + 1`; \ - case " $(XFAIL_TESTS) " in \ - *[\ \ ]$$tst[\ \ ]*) \ - xfail=`expr $$xfail + 1`; \ - col=$$lgn; res=XFAIL; \ - ;; \ - *) \ - failed=`expr $$failed + 1`; \ - col=$$red; res=FAIL; \ - ;; \ - esac; \ - else \ - skip=`expr $$skip + 1`; \ - col=$$blu; res=SKIP; \ - fi; \ - echo "$${col}$$res$${std}: $$tst"; \ - done; \ - if test "$$all" -eq 1; then \ - tests="test"; \ - All=""; \ - else \ - tests="tests"; \ - All="All "; \ +# Recover from deleted '.trs' file; this should ensure that +# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create +# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells +# to avoid problems with "make -n". +.log.trs: + rm -f $< $@ + $(MAKE) $(AM_MAKEFLAGS) $< + +# Leading 'am--fnord' is there to ensure the list of targets does not +# expand to empty, as could happen e.g. with make check TESTS=''. +am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) +am--force-recheck: + @: + +$(TEST_SUITE_LOG): $(TEST_LOGS) + @$(am__set_TESTS_bases); \ + am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ + redo_bases=`for i in $$bases; do \ + am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ + done`; \ + if test -n "$$redo_bases"; then \ + redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ + redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ + if $(am__make_dryrun); then :; else \ + rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ fi; \ - if test "$$failed" -eq 0; then \ - if test "$$xfail" -eq 0; then \ - banner="$$All$$all $$tests passed"; \ - else \ - if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ - banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ - fi; \ - else \ - if test "$$xpass" -eq 0; then \ - banner="$$failed of $$all $$tests failed"; \ + fi; \ + if test -n "$$am__remaking_logs"; then \ + echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ + "recursion detected" >&2; \ + elif test -n "$$redo_logs"; then \ + am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ + fi; \ + if $(am__make_dryrun); then :; else \ + st=0; \ + errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ + for i in $$redo_bases; do \ + test -f $$i.trs && test -r $$i.trs \ + || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ + test -f $$i.log && test -r $$i.log \ + || { echo "$$errmsg $$i.log" >&2; st=1; }; \ + done; \ + test $$st -eq 0 || exit 1; \ + fi + @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ + ws='[ ]'; \ + results=`for b in $$bases; do echo $$b.trs; done`; \ + test -n "$$results" || results=/dev/null; \ + all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ + pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ + fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ + skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ + xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ + xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ + error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ + if test `expr $$fail + $$xpass + $$error` -eq 0; then \ + success=true; \ + else \ + success=false; \ + fi; \ + br='==================='; br=$$br$$br$$br$$br; \ + result_count () \ + { \ + if test x"$$1" = x"--maybe-color"; then \ + maybe_colorize=yes; \ + elif test x"$$1" = x"--no-color"; then \ + maybe_colorize=no; \ else \ - if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ - banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ + echo "$@: invalid 'result_count' usage" >&2; exit 4; \ fi; \ - fi; \ - dashes="$$banner"; \ - skipped=""; \ - if test "$$skip" -ne 0; then \ - if test "$$skip" -eq 1; then \ - skipped="($$skip test was not run)"; \ + shift; \ + desc=$$1 count=$$2; \ + if test $$maybe_colorize = yes && test $$count -gt 0; then \ + color_start=$$3 color_end=$$std; \ else \ - skipped="($$skip tests were not run)"; \ + color_start= color_end=; \ fi; \ - test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ - dashes="$$skipped"; \ - fi; \ - report=""; \ - if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ - report="Please report to $(PACKAGE_BUGREPORT)"; \ - test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ - dashes="$$report"; \ - fi; \ - dashes=`echo "$$dashes" | sed s/./=/g`; \ - if test "$$failed" -eq 0; then \ - col="$$grn"; \ - else \ - col="$$red"; \ - fi; \ - echo "$${col}$$dashes$${std}"; \ - echo "$${col}$$banner$${std}"; \ - test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \ - test -z "$$report" || echo "$${col}$$report$${std}"; \ - echo "$${col}$$dashes$${std}"; \ - test "$$failed" -eq 0; \ - else :; fi + echo "$${color_start}# $$desc $$count$${color_end}"; \ + }; \ + create_testsuite_report () \ + { \ + result_count $$1 "TOTAL:" $$all "$$brg"; \ + result_count $$1 "PASS: " $$pass "$$grn"; \ + result_count $$1 "SKIP: " $$skip "$$blu"; \ + result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ + result_count $$1 "FAIL: " $$fail "$$red"; \ + result_count $$1 "XPASS:" $$xpass "$$red"; \ + result_count $$1 "ERROR:" $$error "$$mgn"; \ + }; \ + { \ + echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ + $(am__rst_title); \ + create_testsuite_report --no-color; \ + echo; \ + echo ".. contents:: :depth: 2"; \ + echo; \ + for b in $$bases; do echo $$b; done \ + | $(am__create_global_log); \ + } >$(TEST_SUITE_LOG).tmp || exit 1; \ + mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ + if $$success; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ + fi; \ + echo "$${col}$$br$${std}"; \ + echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \ + echo "$${col}$$br$${std}"; \ + create_testsuite_report --maybe-color; \ + echo "$$col$$br$$std"; \ + if $$success; then :; else \ + echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ + if test -n "$(PACKAGE_BUGREPORT)"; then \ + echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ + fi; \ + echo "$$col$$br$$std"; \ + fi; \ + $$success || exit 1 + +check-TESTS: + @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list + @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + trs_list=`for i in $$bases; do echo $$i.trs; done`; \ + log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ + exit $$?; +recheck: all + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + bases=`for i in $$bases; do echo $$i; done \ + | $(am__list_recheck_tests)` || exit 1; \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + log_list=`echo $$log_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ + am__force_recheck=am--force-recheck \ + TEST_LOGS="$$log_list"; \ + exit $$? +test-supplicant-config.log: test-supplicant-config$(EXEEXT) + @p='test-supplicant-config$(EXEEXT)'; \ + b='test-supplicant-config'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +.test.log: + @p='$<'; \ + $(am__set_b); \ + $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +@am__EXEEXT_TRUE@.test$(EXEEXT).log: +@am__EXEEXT_TRUE@ @p='$<'; \ +@am__EXEEXT_TRUE@ $(am__set_b); \ +@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ +@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ +@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ +@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ @@ -766,6 +1020,9 @@ install-strip: "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: + -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) + -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) + -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) clean-generic: @@ -861,7 +1118,7 @@ uninstall-am: installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ - tags tags-am uninstall uninstall-am + recheck tags tags-am uninstall uninstall-am .PRECIOUS: Makefile diff --git a/src/supplicant-manager/tests/test-supplicant-config.c b/src/supplicant-manager/tests/test-supplicant-config.c index 5ffead40..937757b2 100644 --- a/src/supplicant-manager/tests/test-supplicant-config.c +++ b/src/supplicant-manager/tests/test-supplicant-config.c @@ -41,78 +41,67 @@ static gboolean validate_opt (const char *detail, - GHashTable *hash, + GVariant *config, const char *key, OptType val_type, gconstpointer expected, size_t expected_len) { - GValue *value; - gint int_val; - GByteArray *array; + char *config_key; + GVariant *config_value; + gboolean found = FALSE; + const guint8 *bytes; + gsize len; const char *s; const unsigned char *expected_array = expected; - int result; - - ASSERT (hash != NULL, detail, "hash was NULL"); - - value = g_hash_table_lookup (hash, key); - ASSERT (value != NULL, - detail, "option '%s' expected but not found in config hash."); - - switch (val_type) { - case TYPE_INT: - ASSERT (G_VALUE_HOLDS_INT (value), - detail, "config hash item '%s' was not TYPE_INT.", key); - int_val = g_value_get_int (value); - ASSERT (int_val == GPOINTER_TO_INT (expected), - detail, "unexpected config hash item '%s' value %d (expected %d)", - key, int_val, GPOINTER_TO_INT (expected)); - break; - case TYPE_BYTES: - ASSERT (G_VALUE_HOLDS (value, DBUS_TYPE_G_UCHAR_ARRAY), - detail, "config hash item '%s' was not TYPE_BYTES.", key); - array = g_value_get_boxed (value); - ASSERT (array->len == expected_len, - detail, "unexpected config hash item '%s' length %d (expected %d)", - key, array->len, expected_len); - result = memcmp (array->data, expected_array, expected_len); - ASSERT (result == 0, detail, "unexpected config hash item '%s' value", key); - break; - case TYPE_KEYWORD: - case TYPE_STRING: - ASSERT (G_VALUE_HOLDS_STRING (value), - detail, "config hash item '%s' was not TYPE_STRING or TYPE_KEYWORD.", key); - if (expected_len == -1) - expected_len = strlen ((const char *) expected); - s = g_value_get_string (value); - ASSERT (s != NULL, detail, "unexpected NULL config hash string item '%s'.", key); - ASSERT (strlen (s) == expected_len, - detail, "unexpected config hash string item '%s' length %d (expected %d)", - key, strlen (s), expected_len); - result = strcmp (s, (const char *) expected); - ASSERT (result == 0, - detail, "unexpected config hash string item '%s' value '%s' (expected '%s')", - key, s, (const char *) expected); - break; - default: - g_warning ("unknown supplicant config hash item '%s' option type %d", - key, val_type); - return FALSE; + GVariantIter iter; + + g_assert (g_variant_is_of_type (config, G_VARIANT_TYPE_VARDICT)); + + g_variant_iter_init (&iter, config); + while (g_variant_iter_next (&iter, "{&sv}", (gpointer) &config_key, (gpointer) &config_value)) { + if (!strcmp (key, config_key)) { + found = TRUE; + switch (val_type) { + case TYPE_INT: + g_assert (g_variant_is_of_type (config_value, G_VARIANT_TYPE_INT32)); + g_assert_cmpint (g_variant_get_int32 (config_value), ==, GPOINTER_TO_INT (expected)); + break; + case TYPE_BYTES: + g_assert (g_variant_is_of_type (config_value, G_VARIANT_TYPE_BYTESTRING)); + bytes = g_variant_get_fixed_array (config_value, &len, 1); + g_assert_cmpint (len, ==, expected_len); + g_assert (memcmp (bytes, expected_array, expected_len) == 0); + break; + case TYPE_KEYWORD: + case TYPE_STRING: + g_assert (g_variant_is_of_type (config_value, G_VARIANT_TYPE_STRING)); + if (expected_len == -1) + expected_len = strlen ((const char *) expected); + s = g_variant_get_string (config_value, NULL); + g_assert_cmpint (strlen (s), ==, expected_len); + g_assert_cmpstr (s, ==, expected); + break; + default: + g_assert_not_reached (); + break; + } + } + g_variant_unref (config_value); } - return TRUE; + return found; } static void test_wifi_open (void) { - NMConnection *connection; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMSupplicantConfig *config = NULL; + gs_unref_variant GVariant *config_dict = NULL; NMSettingConnection *s_con; NMSettingWireless *s_wifi; NMSettingIPConfig *s_ip4; - gs_unref_object NMSupplicantConfig *config = NULL; - GHashTable *hash; char *uuid; gboolean success; GError *error = NULL; @@ -156,9 +145,9 @@ test_wifi_open (void) g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); - ASSERT (nm_connection_verify (connection, &error) == TRUE, - "wifi-open", "failed to verify connection: %s", - (error && error->message) ? error->message : "(unknown)"); + success = nm_connection_verify (connection, &error); + g_assert_no_error (error); + g_assert (success); config = nm_supplicant_config_new (); @@ -170,29 +159,21 @@ test_wifi_open (void) "*added 'bssid' value '11:22:33:44:55:66'*"); g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*added 'freq_list' value *"); - success = nm_supplicant_config_add_setting_wireless (config, s_wifi, 0); - ASSERT (success == TRUE, - "wifi-open", "failed to add wireless setting to supplicant config."); + g_assert (nm_supplicant_config_add_setting_wireless (config, s_wifi, 0)); g_test_assert_expected_messages (); g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*added 'key_mgmt' value 'NONE'"); - success = nm_supplicant_config_add_no_security (config); - ASSERT (success == TRUE, - "wifi-open", "failed to add wireless security to supplicant config."); + g_assert (nm_supplicant_config_add_no_security (config)); g_test_assert_expected_messages (); - hash = nm_supplicant_config_get_hash (config); - ASSERT (hash != NULL, - "wifi-open", "failed to hash supplicant config options."); - - validate_opt ("wifi-open", hash, "scan_ssid", TYPE_INT, GINT_TO_POINTER (1), -1); - validate_opt ("wifi-open", hash, "ssid", TYPE_BYTES, ssid_data, sizeof (ssid_data)); - validate_opt ("wifi-open", hash, "bssid", TYPE_KEYWORD, bssid_str, -1); - validate_opt ("wifi-open", hash, "key_mgmt", TYPE_KEYWORD, "NONE", -1); + config_dict = nm_supplicant_config_to_variant (config); + g_assert (config_dict); - g_hash_table_unref (hash); - g_object_unref (connection); + validate_opt ("wifi-open", config_dict, "scan_ssid", TYPE_INT, GINT_TO_POINTER (1), -1); + validate_opt ("wifi-open", config_dict, "ssid", TYPE_BYTES, ssid_data, sizeof (ssid_data)); + validate_opt ("wifi-open", config_dict, "bssid", TYPE_KEYWORD, bssid_str, -1); + validate_opt ("wifi-open", config_dict, "key_mgmt", TYPE_KEYWORD, "NONE", -1); } static void @@ -202,13 +183,13 @@ test_wifi_wep_key (const char *detail, const unsigned char *expected, size_t expected_size) { - NMConnection *connection; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMSupplicantConfig *config = NULL; + gs_unref_variant GVariant *config_dict = NULL; NMSettingConnection *s_con; NMSettingWireless *s_wifi; NMSettingWirelessSecurity *s_wsec; NMSettingIPConfig *s_ip4; - gs_unref_object NMSupplicantConfig *config = NULL; - GHashTable *hash; char *uuid; gboolean success; GError *error = NULL; @@ -262,9 +243,9 @@ test_wifi_wep_key (const char *detail, g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); - ASSERT (nm_connection_verify (connection, &error) == TRUE, - detail, "failed to verify connection: %s", - (error && error->message) ? error->message : "(unknown)"); + success = nm_connection_verify (connection, &error); + g_assert_no_error (error); + g_assert (success); config = nm_supplicant_config_new (); @@ -276,9 +257,7 @@ test_wifi_wep_key (const char *detail, "*added 'bssid' value '11:22:33:44:55:66'*"); g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*added 'freq_list' value *"); - success = nm_supplicant_config_add_setting_wireless (config, s_wifi, 0); - ASSERT (success == TRUE, - detail, "failed to add wireless setting to supplicant config."); + g_assert (nm_supplicant_config_add_setting_wireless (config, s_wifi, 0)); g_test_assert_expected_messages (); g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, @@ -287,27 +266,21 @@ test_wifi_wep_key (const char *detail, "*added 'wep_key0' value *"); g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*added 'wep_tx_keyidx' value '0'"); - success = nm_supplicant_config_add_setting_wireless_security (config, + g_assert (nm_supplicant_config_add_setting_wireless_security (config, s_wsec, NULL, - "376aced7-b28c-46be-9a62-fcdf072571da"); - ASSERT (success == TRUE, - detail, "failed to add wireless security to supplicant config."); + "376aced7-b28c-46be-9a62-fcdf072571da")); g_test_assert_expected_messages (); - hash = nm_supplicant_config_get_hash (config); - ASSERT (hash != NULL, - detail, "failed to hash supplicant config options."); + config_dict = nm_supplicant_config_to_variant (config); + g_assert (config_dict); - validate_opt (detail, hash, "scan_ssid", TYPE_INT, GINT_TO_POINTER (1), -1); - validate_opt (detail, hash, "ssid", TYPE_BYTES, ssid_data, sizeof (ssid_data)); - validate_opt (detail, hash, "bssid", TYPE_KEYWORD, bssid_str, -1); - validate_opt (detail, hash, "key_mgmt", TYPE_KEYWORD, "NONE", -1); - validate_opt (detail, hash, "wep_tx_keyidx", TYPE_INT, GINT_TO_POINTER (0), -1); - validate_opt (detail, hash, "wep_key0", TYPE_BYTES, expected, expected_size); - - g_hash_table_unref (hash); - g_object_unref (connection); + validate_opt (detail, config_dict, "scan_ssid", TYPE_INT, GINT_TO_POINTER (1), -1); + validate_opt (detail, config_dict, "ssid", TYPE_BYTES, ssid_data, sizeof (ssid_data)); + validate_opt (detail, config_dict, "bssid", TYPE_KEYWORD, bssid_str, -1); + validate_opt (detail, config_dict, "key_mgmt", TYPE_KEYWORD, "NONE", -1); + validate_opt (detail, config_dict, "wep_tx_keyidx", TYPE_INT, GINT_TO_POINTER (0), -1); + validate_opt (detail, config_dict, "wep_key0", TYPE_BYTES, expected, expected_size); } static void @@ -340,13 +313,13 @@ test_wifi_wpa_psk (const char *detail, const unsigned char *expected, size_t expected_size) { - NMConnection *connection; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMSupplicantConfig *config = NULL; + gs_unref_variant GVariant *config_dict = NULL; NMSettingConnection *s_con; NMSettingWireless *s_wifi; NMSettingWirelessSecurity *s_wsec; NMSettingIPConfig *s_ip4; - gs_unref_object NMSupplicantConfig *config = NULL; - GHashTable *hash; char *uuid; gboolean success; GError *error = NULL; @@ -406,9 +379,9 @@ test_wifi_wpa_psk (const char *detail, g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); - ASSERT (nm_connection_verify (connection, &error) == TRUE, - detail, "failed to verify connection: %s", - (error && error->message) ? error->message : "(unknown)"); + success = nm_connection_verify (connection, &error); + g_assert_no_error (error); + g_assert (success); config = nm_supplicant_config_new (); @@ -420,9 +393,7 @@ test_wifi_wpa_psk (const char *detail, "*added 'bssid' value '11:22:33:44:55:66'*"); g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*added 'freq_list' value *"); - success = nm_supplicant_config_add_setting_wireless (config, s_wifi, 0); - ASSERT (success == TRUE, - detail, "failed to add wireless setting to supplicant config."); + g_assert (nm_supplicant_config_add_setting_wireless (config, s_wifi, 0)); g_test_assert_expected_messages (); g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, @@ -435,29 +406,23 @@ test_wifi_wpa_psk (const char *detail, "*added 'pairwise' value 'TKIP CCMP'"); g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*added 'group' value 'TKIP CCMP'"); - success = nm_supplicant_config_add_setting_wireless_security (config, + g_assert (nm_supplicant_config_add_setting_wireless_security (config, s_wsec, NULL, - "376aced7-b28c-46be-9a62-fcdf072571da"); - ASSERT (success == TRUE, - detail, "failed to add wireless security to supplicant config."); + "376aced7-b28c-46be-9a62-fcdf072571da")); g_test_assert_expected_messages (); - hash = nm_supplicant_config_get_hash (config); - ASSERT (hash != NULL, - detail, "failed to hash supplicant config options."); - - validate_opt (detail, hash, "scan_ssid", TYPE_INT, GINT_TO_POINTER (1), -1); - validate_opt (detail, hash, "ssid", TYPE_BYTES, ssid_data, sizeof (ssid_data)); - validate_opt (detail, hash, "bssid", TYPE_KEYWORD, bssid_str, -1); - validate_opt (detail, hash, "key_mgmt", TYPE_KEYWORD, "WPA-PSK", -1); - validate_opt (detail, hash, "proto", TYPE_KEYWORD, "WPA RSN", -1); - validate_opt (detail, hash, "pairwise", TYPE_KEYWORD, "TKIP CCMP", -1); - validate_opt (detail, hash, "group", TYPE_KEYWORD, "TKIP CCMP", -1); - validate_opt (detail, hash, "psk", key_type, expected, expected_size); - - g_hash_table_unref (hash); - g_object_unref (connection); + config_dict = nm_supplicant_config_to_variant (config); + g_assert (config_dict); + + validate_opt (detail, config_dict, "scan_ssid", TYPE_INT, GINT_TO_POINTER (1), -1); + validate_opt (detail, config_dict, "ssid", TYPE_BYTES, ssid_data, sizeof (ssid_data)); + validate_opt (detail, config_dict, "bssid", TYPE_KEYWORD, bssid_str, -1); + validate_opt (detail, config_dict, "key_mgmt", TYPE_KEYWORD, "WPA-PSK", -1); + validate_opt (detail, config_dict, "proto", TYPE_KEYWORD, "WPA RSN", -1); + validate_opt (detail, config_dict, "pairwise", TYPE_KEYWORD, "TKIP CCMP", -1); + validate_opt (detail, config_dict, "group", TYPE_KEYWORD, "TKIP CCMP", -1); + validate_opt (detail, config_dict, "psk", key_type, expected, expected_size); } static void @@ -478,18 +443,12 @@ NMTST_DEFINE (); int main (int argc, char **argv) { - char *base; - nmtst_init (&argc, &argv, TRUE); - /* The tests */ - test_wifi_open (); - test_wifi_wep (); - test_wifi_wpa_psk_types (); + g_test_add_func ("/supplicant-config/wifi-open", test_wifi_open); + g_test_add_func ("/supplicant-config/wifi-wep", test_wifi_wep); + g_test_add_func ("/supplicant-config/wifi-wpa-psk-types", test_wifi_wpa_psk_types); - base = g_path_get_basename (argv[0]); - fprintf (stdout, "%s: SUCCESS\n", base); - g_free (base); - return 0; + return g_test_run (); } diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am index a96d1ce9..83632bd1 100644 --- a/src/tests/Makefile.am +++ b/src/tests/Makefile.am @@ -20,6 +20,8 @@ noinst_PROGRAMS = \ test-general-with-expect \ test-ip4-config \ test-ip6-config \ + test-route-manager-linux \ + test-route-manager-fake \ test-dcb \ test-resolvconf-capture \ test-wired-defname @@ -40,6 +42,34 @@ test_ip6_config_SOURCES = \ test_ip6_config_LDADD = \ $(top_builddir)/src/libNetworkManager.la +####### route manager test ####### + +test_route_manager_fake_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + -I$(top_srcdir)/src/platform/tests \ + -DSETUP=nm_fake_platform_setup \ + -DKERNEL_HACKS=0 + +test_route_manager_fake_SOURCES = \ + $(top_srcdir)/src/platform/tests/test-common.c \ + test-route-manager.c + +test_route_manager_fake_LDADD = \ + $(top_builddir)/src/libNetworkManager.la + +test_route_manager_linux_SOURCES = \ + $(top_srcdir)/src/platform/tests/test-common.c \ + test-route-manager.c + +test_route_manager_linux_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + -I$(top_srcdir)/src/platform/tests \ + -DSETUP=nm_linux_platform_setup \ + -DKERNEL_HACKS=1 + +test_route_manager_linux_LDADD = \ + $(top_builddir)/src/libNetworkManager.la + ####### DCB test ####### test_dcb_SOURCES = \ @@ -90,6 +120,8 @@ EXTRA_DIST = test-secret-agent.py TESTS = \ test-ip4-config \ test-ip6-config \ + test-route-manager-fake \ + test-route-manager-linux \ test-dcb \ test-resolvconf-capture \ test-general \ diff --git a/src/tests/Makefile.in b/src/tests/Makefile.in index 61e757e7..f84c5536 100644 --- a/src/tests/Makefile.in +++ b/src/tests/Makefile.in @@ -90,12 +90,14 @@ build_triplet = @build@ host_triplet = @host@ noinst_PROGRAMS = test-general$(EXEEXT) \ test-general-with-expect$(EXEEXT) test-ip4-config$(EXEEXT) \ - test-ip6-config$(EXEEXT) test-dcb$(EXEEXT) \ + test-ip6-config$(EXEEXT) test-route-manager-linux$(EXEEXT) \ + test-route-manager-fake$(EXEEXT) test-dcb$(EXEEXT) \ test-resolvconf-capture$(EXEEXT) test-wired-defname$(EXEEXT) TESTS = test-ip4-config$(EXEEXT) test-ip6-config$(EXEEXT) \ - test-dcb$(EXEEXT) test-resolvconf-capture$(EXEEXT) \ - test-general$(EXEEXT) test-general-with-expect$(EXEEXT) \ - test-wired-defname$(EXEEXT) + test-route-manager-fake$(EXEEXT) \ + test-route-manager-linux$(EXEEXT) test-dcb$(EXEEXT) \ + test-resolvconf-capture$(EXEEXT) test-general$(EXEEXT) \ + test-general-with-expect$(EXEEXT) test-wired-defname$(EXEEXT) subdir = src/tests ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \ @@ -149,6 +151,20 @@ test_resolvconf_capture_OBJECTS = \ $(am_test_resolvconf_capture_OBJECTS) test_resolvconf_capture_DEPENDENCIES = \ $(top_builddir)/src/libNetworkManager.la +am_test_route_manager_fake_OBJECTS = \ + test_route_manager_fake-test-common.$(OBJEXT) \ + test_route_manager_fake-test-route-manager.$(OBJEXT) +test_route_manager_fake_OBJECTS = \ + $(am_test_route_manager_fake_OBJECTS) +test_route_manager_fake_DEPENDENCIES = \ + $(top_builddir)/src/libNetworkManager.la +am_test_route_manager_linux_OBJECTS = \ + test_route_manager_linux-test-common.$(OBJEXT) \ + test_route_manager_linux-test-route-manager.$(OBJEXT) +test_route_manager_linux_OBJECTS = \ + $(am_test_route_manager_linux_OBJECTS) +test_route_manager_linux_DEPENDENCIES = \ + $(top_builddir)/src/libNetworkManager.la am_test_wired_defname_OBJECTS = test-wired-defname.$(OBJEXT) test_wired_defname_OBJECTS = $(am_test_wired_defname_OBJECTS) test_wired_defname_DEPENDENCIES = \ @@ -190,10 +206,14 @@ am__v_CCLD_1 = SOURCES = $(test_dcb_SOURCES) $(test_general_SOURCES) \ $(test_general_with_expect_SOURCES) $(test_ip4_config_SOURCES) \ $(test_ip6_config_SOURCES) $(test_resolvconf_capture_SOURCES) \ + $(test_route_manager_fake_SOURCES) \ + $(test_route_manager_linux_SOURCES) \ $(test_wired_defname_SOURCES) DIST_SOURCES = $(test_dcb_SOURCES) $(test_general_SOURCES) \ $(test_general_with_expect_SOURCES) $(test_ip4_config_SOURCES) \ $(test_ip6_config_SOURCES) $(test_resolvconf_capture_SOURCES) \ + $(test_route_manager_fake_SOURCES) \ + $(test_route_manager_linux_SOURCES) \ $(test_wired_defname_SOURCES) RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ @@ -215,7 +235,7 @@ am__recursive_targets = \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ - distdir + check recheck distdir am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is @@ -257,9 +277,190 @@ am__tty_colors = { \ std='[m'; \ fi; \ } +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__recheck_rx = ^[ ]*:recheck:[ ]* +am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* +am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* +# A command that, given a newline-separated list of test names on the +# standard input, print the name of the tests that are to be re-run +# upon "make recheck". +am__list_recheck_tests = $(AWK) '{ \ + recheck = 1; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + { \ + if ((getline line2 < ($$0 ".log")) < 0) \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ + { \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ + { \ + break; \ + } \ + }; \ + if (recheck) \ + print $$0; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# A command that, given a newline-separated list of test names on the +# standard input, create the global log from their .trs and .log files. +am__create_global_log = $(AWK) ' \ +function fatal(msg) \ +{ \ + print "fatal: making $@: " msg | "cat >&2"; \ + exit 1; \ +} \ +function rst_section(header) \ +{ \ + print header; \ + len = length(header); \ + for (i = 1; i <= len; i = i + 1) \ + printf "="; \ + printf "\n\n"; \ +} \ +{ \ + copy_in_global_log = 1; \ + global_test_result = "RUN"; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".trs"); \ + if (line ~ /$(am__global_test_result_rx)/) \ + { \ + sub("$(am__global_test_result_rx)", "", line); \ + sub("[ ]*$$", "", line); \ + global_test_result = line; \ + } \ + else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ + copy_in_global_log = 0; \ + }; \ + if (copy_in_global_log) \ + { \ + rst_section(global_test_result ": " $$0); \ + while ((rc = (getline line < ($$0 ".log"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".log"); \ + print line; \ + }; \ + printf "\n"; \ + }; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# Restructured Text title. +am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } +# Solaris 10 'make', and several other traditional 'make' implementations, +# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it +# by disabling -e (using the XSI extension "set +e") if it's set. +am__sh_e_setup = case $$- in *e*) set +e;; esac +# Default flags passed to test drivers. +am__common_driver_flags = \ + --color-tests "$$am__color_tests" \ + --enable-hard-errors "$$am__enable_hard_errors" \ + --expect-failure "$$am__expect_failure" +# To be inserted before the command running the test. Creates the +# directory for the log if needed. Stores in $dir the directory +# containing $f, in $tst the test, in $log the log. Executes the +# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and +# passes TESTS_ENVIRONMENT. Set up options for the wrapper that +# will run the test scripts (or their associated LOG_COMPILER, if +# thy have one). +am__check_pre = \ +$(am__sh_e_setup); \ +$(am__vpath_adj_setup) $(am__vpath_adj) \ +$(am__tty_colors); \ +srcdir=$(srcdir); export srcdir; \ +case "$@" in \ + */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ + *) am__odir=.;; \ +esac; \ +test "x$$am__odir" = x"." || test -d "$$am__odir" \ + || $(MKDIR_P) "$$am__odir" || exit $$?; \ +if test -f "./$$f"; then dir=./; \ +elif test -f "$$f"; then dir=; \ +else dir="$(srcdir)/"; fi; \ +tst=$$dir$$f; log='$@'; \ +if test -n '$(DISABLE_HARD_ERRORS)'; then \ + am__enable_hard_errors=no; \ +else \ + am__enable_hard_errors=yes; \ +fi; \ +case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ + am__expect_failure=yes;; \ + *) \ + am__expect_failure=no;; \ +esac; \ +$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) +# A shell command to get the names of the tests scripts with any registered +# extension removed (i.e., equivalently, the names of the test logs, with +# the '.log' extension removed). The result is saved in the shell variable +# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, +# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", +# since that might cause problem with VPATH rewrites for suffix-less tests. +# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. +am__set_TESTS_bases = \ + bases='$(TEST_LOGS)'; \ + bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ + bases=`echo $$bases` +RECHECK_LOGS = $(TEST_LOGS) +TEST_SUITE_LOG = test-suite.log +TEST_EXTENSIONS = @EXEEXT@ .test +LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) +am__set_b = \ + case '$@' in \ + */*) \ + case '$*' in \ + */*) b='$*';; \ + *) b=`echo '$@' | sed 's/\.log$$//'`; \ + esac;; \ + *) \ + b='$*';; \ + esac +am__test_logs1 = $(TESTS:=.log) +am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) +TEST_LOGS = $(am__test_logs2:.test.log=.log) +TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/build-aux/test-driver +TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ + $(TEST_LOG_FLAGS) DIST_SUBDIRS = $(SUBDIRS) am__DIST_COMMON = $(srcdir)/Makefile.in \ - $(top_srcdir)/build-aux/depcomp + $(top_srcdir)/build-aux/depcomp \ + $(top_srcdir)/build-aux/test-driver DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ @@ -290,6 +491,7 @@ ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -403,6 +605,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ +LOG_DRIVER = @LOG_DRIVER@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ @@ -579,6 +782,34 @@ test_ip6_config_LDADD = \ $(top_builddir)/src/libNetworkManager.la +####### route manager test ####### +test_route_manager_fake_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + -I$(top_srcdir)/src/platform/tests \ + -DSETUP=nm_fake_platform_setup \ + -DKERNEL_HACKS=0 + +test_route_manager_fake_SOURCES = \ + $(top_srcdir)/src/platform/tests/test-common.c \ + test-route-manager.c + +test_route_manager_fake_LDADD = \ + $(top_builddir)/src/libNetworkManager.la + +test_route_manager_linux_SOURCES = \ + $(top_srcdir)/src/platform/tests/test-common.c \ + test-route-manager.c + +test_route_manager_linux_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + -I$(top_srcdir)/src/platform/tests \ + -DSETUP=nm_linux_platform_setup \ + -DKERNEL_HACKS=1 + +test_route_manager_linux_LDADD = \ + $(top_builddir)/src/libNetworkManager.la + + ####### DCB test ####### test_dcb_SOURCES = \ test-dcb.c @@ -624,7 +855,7 @@ EXTRA_DIST = test-secret-agent.py all: all-recursive .SUFFIXES: -.SUFFIXES: .c .lo .o .obj +.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ @@ -688,6 +919,14 @@ test-resolvconf-capture$(EXEEXT): $(test_resolvconf_capture_OBJECTS) $(test_reso @rm -f test-resolvconf-capture$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_resolvconf_capture_OBJECTS) $(test_resolvconf_capture_LDADD) $(LIBS) +test-route-manager-fake$(EXEEXT): $(test_route_manager_fake_OBJECTS) $(test_route_manager_fake_DEPENDENCIES) $(EXTRA_test_route_manager_fake_DEPENDENCIES) + @rm -f test-route-manager-fake$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_route_manager_fake_OBJECTS) $(test_route_manager_fake_LDADD) $(LIBS) + +test-route-manager-linux$(EXEEXT): $(test_route_manager_linux_OBJECTS) $(test_route_manager_linux_DEPENDENCIES) $(EXTRA_test_route_manager_linux_DEPENDENCIES) + @rm -f test-route-manager-linux$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_route_manager_linux_OBJECTS) $(test_route_manager_linux_LDADD) $(LIBS) + test-wired-defname$(EXEEXT): $(test_wired_defname_OBJECTS) $(test_wired_defname_DEPENDENCIES) $(EXTRA_test_wired_defname_DEPENDENCIES) @rm -f test-wired-defname$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_wired_defname_OBJECTS) $(test_wired_defname_LDADD) $(LIBS) @@ -705,6 +944,10 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-ip6-config.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-resolvconf-capture.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-wired-defname.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_route_manager_fake-test-common.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_route_manager_fake-test-route-manager.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_route_manager_linux-test-common.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_route_manager_linux-test-route-manager.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @@ -727,6 +970,62 @@ distclean-compile: @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< +test_route_manager_fake-test-common.o: $(top_srcdir)/src/platform/tests/test-common.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_manager_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_route_manager_fake-test-common.o -MD -MP -MF $(DEPDIR)/test_route_manager_fake-test-common.Tpo -c -o test_route_manager_fake-test-common.o `test -f '$(top_srcdir)/src/platform/tests/test-common.c' || echo '$(srcdir)/'`$(top_srcdir)/src/platform/tests/test-common.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_route_manager_fake-test-common.Tpo $(DEPDIR)/test_route_manager_fake-test-common.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(top_srcdir)/src/platform/tests/test-common.c' object='test_route_manager_fake-test-common.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_manager_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_route_manager_fake-test-common.o `test -f '$(top_srcdir)/src/platform/tests/test-common.c' || echo '$(srcdir)/'`$(top_srcdir)/src/platform/tests/test-common.c + +test_route_manager_fake-test-common.obj: $(top_srcdir)/src/platform/tests/test-common.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_manager_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_route_manager_fake-test-common.obj -MD -MP -MF $(DEPDIR)/test_route_manager_fake-test-common.Tpo -c -o test_route_manager_fake-test-common.obj `if test -f '$(top_srcdir)/src/platform/tests/test-common.c'; then $(CYGPATH_W) '$(top_srcdir)/src/platform/tests/test-common.c'; else $(CYGPATH_W) '$(srcdir)/$(top_srcdir)/src/platform/tests/test-common.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_route_manager_fake-test-common.Tpo $(DEPDIR)/test_route_manager_fake-test-common.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(top_srcdir)/src/platform/tests/test-common.c' object='test_route_manager_fake-test-common.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_manager_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_route_manager_fake-test-common.obj `if test -f '$(top_srcdir)/src/platform/tests/test-common.c'; then $(CYGPATH_W) '$(top_srcdir)/src/platform/tests/test-common.c'; else $(CYGPATH_W) '$(srcdir)/$(top_srcdir)/src/platform/tests/test-common.c'; fi` + +test_route_manager_fake-test-route-manager.o: test-route-manager.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_manager_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_route_manager_fake-test-route-manager.o -MD -MP -MF $(DEPDIR)/test_route_manager_fake-test-route-manager.Tpo -c -o test_route_manager_fake-test-route-manager.o `test -f 'test-route-manager.c' || echo '$(srcdir)/'`test-route-manager.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_route_manager_fake-test-route-manager.Tpo $(DEPDIR)/test_route_manager_fake-test-route-manager.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-route-manager.c' object='test_route_manager_fake-test-route-manager.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_manager_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_route_manager_fake-test-route-manager.o `test -f 'test-route-manager.c' || echo '$(srcdir)/'`test-route-manager.c + +test_route_manager_fake-test-route-manager.obj: test-route-manager.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_manager_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_route_manager_fake-test-route-manager.obj -MD -MP -MF $(DEPDIR)/test_route_manager_fake-test-route-manager.Tpo -c -o test_route_manager_fake-test-route-manager.obj `if test -f 'test-route-manager.c'; then $(CYGPATH_W) 'test-route-manager.c'; else $(CYGPATH_W) '$(srcdir)/test-route-manager.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_route_manager_fake-test-route-manager.Tpo $(DEPDIR)/test_route_manager_fake-test-route-manager.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-route-manager.c' object='test_route_manager_fake-test-route-manager.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_manager_fake_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_route_manager_fake-test-route-manager.obj `if test -f 'test-route-manager.c'; then $(CYGPATH_W) 'test-route-manager.c'; else $(CYGPATH_W) '$(srcdir)/test-route-manager.c'; fi` + +test_route_manager_linux-test-common.o: $(top_srcdir)/src/platform/tests/test-common.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_manager_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_route_manager_linux-test-common.o -MD -MP -MF $(DEPDIR)/test_route_manager_linux-test-common.Tpo -c -o test_route_manager_linux-test-common.o `test -f '$(top_srcdir)/src/platform/tests/test-common.c' || echo '$(srcdir)/'`$(top_srcdir)/src/platform/tests/test-common.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_route_manager_linux-test-common.Tpo $(DEPDIR)/test_route_manager_linux-test-common.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(top_srcdir)/src/platform/tests/test-common.c' object='test_route_manager_linux-test-common.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_manager_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_route_manager_linux-test-common.o `test -f '$(top_srcdir)/src/platform/tests/test-common.c' || echo '$(srcdir)/'`$(top_srcdir)/src/platform/tests/test-common.c + +test_route_manager_linux-test-common.obj: $(top_srcdir)/src/platform/tests/test-common.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_manager_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_route_manager_linux-test-common.obj -MD -MP -MF $(DEPDIR)/test_route_manager_linux-test-common.Tpo -c -o test_route_manager_linux-test-common.obj `if test -f '$(top_srcdir)/src/platform/tests/test-common.c'; then $(CYGPATH_W) '$(top_srcdir)/src/platform/tests/test-common.c'; else $(CYGPATH_W) '$(srcdir)/$(top_srcdir)/src/platform/tests/test-common.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_route_manager_linux-test-common.Tpo $(DEPDIR)/test_route_manager_linux-test-common.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(top_srcdir)/src/platform/tests/test-common.c' object='test_route_manager_linux-test-common.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_manager_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_route_manager_linux-test-common.obj `if test -f '$(top_srcdir)/src/platform/tests/test-common.c'; then $(CYGPATH_W) '$(top_srcdir)/src/platform/tests/test-common.c'; else $(CYGPATH_W) '$(srcdir)/$(top_srcdir)/src/platform/tests/test-common.c'; fi` + +test_route_manager_linux-test-route-manager.o: test-route-manager.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_manager_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_route_manager_linux-test-route-manager.o -MD -MP -MF $(DEPDIR)/test_route_manager_linux-test-route-manager.Tpo -c -o test_route_manager_linux-test-route-manager.o `test -f 'test-route-manager.c' || echo '$(srcdir)/'`test-route-manager.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_route_manager_linux-test-route-manager.Tpo $(DEPDIR)/test_route_manager_linux-test-route-manager.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-route-manager.c' object='test_route_manager_linux-test-route-manager.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_manager_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_route_manager_linux-test-route-manager.o `test -f 'test-route-manager.c' || echo '$(srcdir)/'`test-route-manager.c + +test_route_manager_linux-test-route-manager.obj: test-route-manager.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_manager_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_route_manager_linux-test-route-manager.obj -MD -MP -MF $(DEPDIR)/test_route_manager_linux-test-route-manager.Tpo -c -o test_route_manager_linux-test-route-manager.obj `if test -f 'test-route-manager.c'; then $(CYGPATH_W) 'test-route-manager.c'; else $(CYGPATH_W) '$(srcdir)/test-route-manager.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_route_manager_linux-test-route-manager.Tpo $(DEPDIR)/test_route_manager_linux-test-route-manager.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-route-manager.c' object='test_route_manager_linux-test-route-manager.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_route_manager_linux_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_route_manager_linux-test-route-manager.obj `if test -f 'test-route-manager.c'; then $(CYGPATH_W) 'test-route-manager.c'; else $(CYGPATH_W) '$(srcdir)/test-route-manager.c'; fi` + mostlyclean-libtool: -rm -f *.lo @@ -832,98 +1131,224 @@ cscopelist-am: $(am__tagged_files) distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -check-TESTS: $(TESTS) - @failed=0; all=0; xfail=0; xpass=0; skip=0; \ - srcdir=$(srcdir); export srcdir; \ - list=' $(TESTS) '; \ - $(am__tty_colors); \ - if test -n "$$list"; then \ - for tst in $$list; do \ - if test -f ./$$tst; then dir=./; \ - elif test -f $$tst; then dir=; \ - else dir="$(srcdir)/"; fi; \ - if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \ - all=`expr $$all + 1`; \ - case " $(XFAIL_TESTS) " in \ - *[\ \ ]$$tst[\ \ ]*) \ - xpass=`expr $$xpass + 1`; \ - failed=`expr $$failed + 1`; \ - col=$$red; res=XPASS; \ - ;; \ - *) \ - col=$$grn; res=PASS; \ - ;; \ - esac; \ - elif test $$? -ne 77; then \ - all=`expr $$all + 1`; \ - case " $(XFAIL_TESTS) " in \ - *[\ \ ]$$tst[\ \ ]*) \ - xfail=`expr $$xfail + 1`; \ - col=$$lgn; res=XFAIL; \ - ;; \ - *) \ - failed=`expr $$failed + 1`; \ - col=$$red; res=FAIL; \ - ;; \ - esac; \ - else \ - skip=`expr $$skip + 1`; \ - col=$$blu; res=SKIP; \ - fi; \ - echo "$${col}$$res$${std}: $$tst"; \ - done; \ - if test "$$all" -eq 1; then \ - tests="test"; \ - All=""; \ - else \ - tests="tests"; \ - All="All "; \ +# Recover from deleted '.trs' file; this should ensure that +# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create +# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells +# to avoid problems with "make -n". +.log.trs: + rm -f $< $@ + $(MAKE) $(AM_MAKEFLAGS) $< + +# Leading 'am--fnord' is there to ensure the list of targets does not +# expand to empty, as could happen e.g. with make check TESTS=''. +am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) +am--force-recheck: + @: + +$(TEST_SUITE_LOG): $(TEST_LOGS) + @$(am__set_TESTS_bases); \ + am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ + redo_bases=`for i in $$bases; do \ + am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ + done`; \ + if test -n "$$redo_bases"; then \ + redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ + redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ + if $(am__make_dryrun); then :; else \ + rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ fi; \ - if test "$$failed" -eq 0; then \ - if test "$$xfail" -eq 0; then \ - banner="$$All$$all $$tests passed"; \ - else \ - if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ - banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ - fi; \ - else \ - if test "$$xpass" -eq 0; then \ - banner="$$failed of $$all $$tests failed"; \ + fi; \ + if test -n "$$am__remaking_logs"; then \ + echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ + "recursion detected" >&2; \ + elif test -n "$$redo_logs"; then \ + am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ + fi; \ + if $(am__make_dryrun); then :; else \ + st=0; \ + errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ + for i in $$redo_bases; do \ + test -f $$i.trs && test -r $$i.trs \ + || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ + test -f $$i.log && test -r $$i.log \ + || { echo "$$errmsg $$i.log" >&2; st=1; }; \ + done; \ + test $$st -eq 0 || exit 1; \ + fi + @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ + ws='[ ]'; \ + results=`for b in $$bases; do echo $$b.trs; done`; \ + test -n "$$results" || results=/dev/null; \ + all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ + pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ + fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ + skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ + xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ + xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ + error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ + if test `expr $$fail + $$xpass + $$error` -eq 0; then \ + success=true; \ + else \ + success=false; \ + fi; \ + br='==================='; br=$$br$$br$$br$$br; \ + result_count () \ + { \ + if test x"$$1" = x"--maybe-color"; then \ + maybe_colorize=yes; \ + elif test x"$$1" = x"--no-color"; then \ + maybe_colorize=no; \ else \ - if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ - banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ + echo "$@: invalid 'result_count' usage" >&2; exit 4; \ fi; \ - fi; \ - dashes="$$banner"; \ - skipped=""; \ - if test "$$skip" -ne 0; then \ - if test "$$skip" -eq 1; then \ - skipped="($$skip test was not run)"; \ + shift; \ + desc=$$1 count=$$2; \ + if test $$maybe_colorize = yes && test $$count -gt 0; then \ + color_start=$$3 color_end=$$std; \ else \ - skipped="($$skip tests were not run)"; \ + color_start= color_end=; \ fi; \ - test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ - dashes="$$skipped"; \ - fi; \ - report=""; \ - if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ - report="Please report to $(PACKAGE_BUGREPORT)"; \ - test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ - dashes="$$report"; \ - fi; \ - dashes=`echo "$$dashes" | sed s/./=/g`; \ - if test "$$failed" -eq 0; then \ - col="$$grn"; \ - else \ - col="$$red"; \ - fi; \ - echo "$${col}$$dashes$${std}"; \ - echo "$${col}$$banner$${std}"; \ - test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \ - test -z "$$report" || echo "$${col}$$report$${std}"; \ - echo "$${col}$$dashes$${std}"; \ - test "$$failed" -eq 0; \ - else :; fi + echo "$${color_start}# $$desc $$count$${color_end}"; \ + }; \ + create_testsuite_report () \ + { \ + result_count $$1 "TOTAL:" $$all "$$brg"; \ + result_count $$1 "PASS: " $$pass "$$grn"; \ + result_count $$1 "SKIP: " $$skip "$$blu"; \ + result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ + result_count $$1 "FAIL: " $$fail "$$red"; \ + result_count $$1 "XPASS:" $$xpass "$$red"; \ + result_count $$1 "ERROR:" $$error "$$mgn"; \ + }; \ + { \ + echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ + $(am__rst_title); \ + create_testsuite_report --no-color; \ + echo; \ + echo ".. contents:: :depth: 2"; \ + echo; \ + for b in $$bases; do echo $$b; done \ + | $(am__create_global_log); \ + } >$(TEST_SUITE_LOG).tmp || exit 1; \ + mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ + if $$success; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ + fi; \ + echo "$${col}$$br$${std}"; \ + echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \ + echo "$${col}$$br$${std}"; \ + create_testsuite_report --maybe-color; \ + echo "$$col$$br$$std"; \ + if $$success; then :; else \ + echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ + if test -n "$(PACKAGE_BUGREPORT)"; then \ + echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ + fi; \ + echo "$$col$$br$$std"; \ + fi; \ + $$success || exit 1 + +check-TESTS: + @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list + @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + trs_list=`for i in $$bases; do echo $$i.trs; done`; \ + log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ + exit $$?; +recheck: all + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + bases=`for i in $$bases; do echo $$i; done \ + | $(am__list_recheck_tests)` || exit 1; \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + log_list=`echo $$log_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ + am__force_recheck=am--force-recheck \ + TEST_LOGS="$$log_list"; \ + exit $$? +test-ip4-config.log: test-ip4-config$(EXEEXT) + @p='test-ip4-config$(EXEEXT)'; \ + b='test-ip4-config'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +test-ip6-config.log: test-ip6-config$(EXEEXT) + @p='test-ip6-config$(EXEEXT)'; \ + b='test-ip6-config'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +test-route-manager-fake.log: test-route-manager-fake$(EXEEXT) + @p='test-route-manager-fake$(EXEEXT)'; \ + b='test-route-manager-fake'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +test-route-manager-linux.log: test-route-manager-linux$(EXEEXT) + @p='test-route-manager-linux$(EXEEXT)'; \ + b='test-route-manager-linux'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +test-dcb.log: test-dcb$(EXEEXT) + @p='test-dcb$(EXEEXT)'; \ + b='test-dcb'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +test-resolvconf-capture.log: test-resolvconf-capture$(EXEEXT) + @p='test-resolvconf-capture$(EXEEXT)'; \ + b='test-resolvconf-capture'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +test-general.log: test-general$(EXEEXT) + @p='test-general$(EXEEXT)'; \ + b='test-general'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +test-general-with-expect.log: test-general-with-expect$(EXEEXT) + @p='test-general-with-expect$(EXEEXT)'; \ + b='test-general-with-expect'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +test-wired-defname.log: test-wired-defname$(EXEEXT) + @p='test-wired-defname$(EXEEXT)'; \ + b='test-wired-defname'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +.test.log: + @p='$<'; \ + $(am__set_b); \ + $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +@am__EXEEXT_TRUE@.test$(EXEEXT).log: +@am__EXEEXT_TRUE@ @p='$<'; \ +@am__EXEEXT_TRUE@ $(am__set_b); \ +@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ +@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ +@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ +@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ @@ -1007,6 +1432,9 @@ install-strip: "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: + -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) + -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) + -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) clean-generic: @@ -1103,7 +1531,7 @@ uninstall-am: installcheck-am installdirs installdirs-am maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ - tags tags-am uninstall uninstall-am + recheck tags tags-am uninstall uninstall-am .PRECIOUS: Makefile diff --git a/src/tests/config/Makefile.in b/src/tests/config/Makefile.in index 73ff9d48..6d1dd4bc 100644 --- a/src/tests/config/Makefile.in +++ b/src/tests/config/Makefile.in @@ -203,13 +203,196 @@ am__tty_colors = { \ std='[m'; \ fi; \ } +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__recheck_rx = ^[ ]*:recheck:[ ]* +am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* +am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* +# A command that, given a newline-separated list of test names on the +# standard input, print the name of the tests that are to be re-run +# upon "make recheck". +am__list_recheck_tests = $(AWK) '{ \ + recheck = 1; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + { \ + if ((getline line2 < ($$0 ".log")) < 0) \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ + { \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ + { \ + break; \ + } \ + }; \ + if (recheck) \ + print $$0; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# A command that, given a newline-separated list of test names on the +# standard input, create the global log from their .trs and .log files. +am__create_global_log = $(AWK) ' \ +function fatal(msg) \ +{ \ + print "fatal: making $@: " msg | "cat >&2"; \ + exit 1; \ +} \ +function rst_section(header) \ +{ \ + print header; \ + len = length(header); \ + for (i = 1; i <= len; i = i + 1) \ + printf "="; \ + printf "\n\n"; \ +} \ +{ \ + copy_in_global_log = 1; \ + global_test_result = "RUN"; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".trs"); \ + if (line ~ /$(am__global_test_result_rx)/) \ + { \ + sub("$(am__global_test_result_rx)", "", line); \ + sub("[ ]*$$", "", line); \ + global_test_result = line; \ + } \ + else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ + copy_in_global_log = 0; \ + }; \ + if (copy_in_global_log) \ + { \ + rst_section(global_test_result ": " $$0); \ + while ((rc = (getline line < ($$0 ".log"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".log"); \ + print line; \ + }; \ + printf "\n"; \ + }; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# Restructured Text title. +am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } +# Solaris 10 'make', and several other traditional 'make' implementations, +# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it +# by disabling -e (using the XSI extension "set +e") if it's set. +am__sh_e_setup = case $$- in *e*) set +e;; esac +# Default flags passed to test drivers. +am__common_driver_flags = \ + --color-tests "$$am__color_tests" \ + --enable-hard-errors "$$am__enable_hard_errors" \ + --expect-failure "$$am__expect_failure" +# To be inserted before the command running the test. Creates the +# directory for the log if needed. Stores in $dir the directory +# containing $f, in $tst the test, in $log the log. Executes the +# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and +# passes TESTS_ENVIRONMENT. Set up options for the wrapper that +# will run the test scripts (or their associated LOG_COMPILER, if +# thy have one). +am__check_pre = \ +$(am__sh_e_setup); \ +$(am__vpath_adj_setup) $(am__vpath_adj) \ +$(am__tty_colors); \ +srcdir=$(srcdir); export srcdir; \ +case "$@" in \ + */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ + *) am__odir=.;; \ +esac; \ +test "x$$am__odir" = x"." || test -d "$$am__odir" \ + || $(MKDIR_P) "$$am__odir" || exit $$?; \ +if test -f "./$$f"; then dir=./; \ +elif test -f "$$f"; then dir=; \ +else dir="$(srcdir)/"; fi; \ +tst=$$dir$$f; log='$@'; \ +if test -n '$(DISABLE_HARD_ERRORS)'; then \ + am__enable_hard_errors=no; \ +else \ + am__enable_hard_errors=yes; \ +fi; \ +case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ + am__expect_failure=yes;; \ + *) \ + am__expect_failure=no;; \ +esac; \ +$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) +# A shell command to get the names of the tests scripts with any registered +# extension removed (i.e., equivalently, the names of the test logs, with +# the '.log' extension removed). The result is saved in the shell variable +# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, +# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", +# since that might cause problem with VPATH rewrites for suffix-less tests. +# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. +am__set_TESTS_bases = \ + bases='$(TEST_LOGS)'; \ + bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ + bases=`echo $$bases` +RECHECK_LOGS = $(TEST_LOGS) +AM_RECURSIVE_TARGETS = check recheck +TEST_SUITE_LOG = test-suite.log +TEST_EXTENSIONS = @EXEEXT@ .test +LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) +am__set_b = \ + case '$@' in \ + */*) \ + case '$*' in \ + */*) b='$*';; \ + *) b=`echo '$@' | sed 's/\.log$$//'`; \ + esac;; \ + *) \ + b='$*';; \ + esac +am__test_logs1 = $(TESTS:=.log) +am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) +TEST_LOGS = $(am__test_logs2:.test.log=.log) +TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/build-aux/test-driver +TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ + $(TEST_LOG_FLAGS) am__DIST_COMMON = $(srcdir)/Makefile.in \ - $(top_srcdir)/build-aux/depcomp + $(top_srcdir)/build-aux/depcomp \ + $(top_srcdir)/build-aux/test-driver DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ALL_LINGUAS = @ALL_LINGUAS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AM_TESTS_FD_REDIRECT = @AM_TESTS_FD_REDIRECT@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -323,6 +506,7 @@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ +LOG_DRIVER = @LOG_DRIVER@ LTLIBICONV = @LTLIBICONV@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ @@ -498,7 +682,7 @@ EXTRA_DIST = \ all: all-am .SUFFIXES: -.SUFFIXES: .c .lo .o .obj +.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ @@ -630,98 +814,168 @@ cscopelist-am: $(am__tagged_files) distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -check-TESTS: $(TESTS) - @failed=0; all=0; xfail=0; xpass=0; skip=0; \ - srcdir=$(srcdir); export srcdir; \ - list=' $(TESTS) '; \ - $(am__tty_colors); \ - if test -n "$$list"; then \ - for tst in $$list; do \ - if test -f ./$$tst; then dir=./; \ - elif test -f $$tst; then dir=; \ - else dir="$(srcdir)/"; fi; \ - if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \ - all=`expr $$all + 1`; \ - case " $(XFAIL_TESTS) " in \ - *[\ \ ]$$tst[\ \ ]*) \ - xpass=`expr $$xpass + 1`; \ - failed=`expr $$failed + 1`; \ - col=$$red; res=XPASS; \ - ;; \ - *) \ - col=$$grn; res=PASS; \ - ;; \ - esac; \ - elif test $$? -ne 77; then \ - all=`expr $$all + 1`; \ - case " $(XFAIL_TESTS) " in \ - *[\ \ ]$$tst[\ \ ]*) \ - xfail=`expr $$xfail + 1`; \ - col=$$lgn; res=XFAIL; \ - ;; \ - *) \ - failed=`expr $$failed + 1`; \ - col=$$red; res=FAIL; \ - ;; \ - esac; \ - else \ - skip=`expr $$skip + 1`; \ - col=$$blu; res=SKIP; \ - fi; \ - echo "$${col}$$res$${std}: $$tst"; \ - done; \ - if test "$$all" -eq 1; then \ - tests="test"; \ - All=""; \ - else \ - tests="tests"; \ - All="All "; \ +# Recover from deleted '.trs' file; this should ensure that +# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create +# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells +# to avoid problems with "make -n". +.log.trs: + rm -f $< $@ + $(MAKE) $(AM_MAKEFLAGS) $< + +# Leading 'am--fnord' is there to ensure the list of targets does not +# expand to empty, as could happen e.g. with make check TESTS=''. +am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) +am--force-recheck: + @: + +$(TEST_SUITE_LOG): $(TEST_LOGS) + @$(am__set_TESTS_bases); \ + am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ + redo_bases=`for i in $$bases; do \ + am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ + done`; \ + if test -n "$$redo_bases"; then \ + redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ + redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ + if $(am__make_dryrun); then :; else \ + rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ fi; \ - if test "$$failed" -eq 0; then \ - if test "$$xfail" -eq 0; then \ - banner="$$All$$all $$tests passed"; \ - else \ - if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ - banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ - fi; \ - else \ - if test "$$xpass" -eq 0; then \ - banner="$$failed of $$all $$tests failed"; \ + fi; \ + if test -n "$$am__remaking_logs"; then \ + echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ + "recursion detected" >&2; \ + elif test -n "$$redo_logs"; then \ + am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ + fi; \ + if $(am__make_dryrun); then :; else \ + st=0; \ + errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ + for i in $$redo_bases; do \ + test -f $$i.trs && test -r $$i.trs \ + || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ + test -f $$i.log && test -r $$i.log \ + || { echo "$$errmsg $$i.log" >&2; st=1; }; \ + done; \ + test $$st -eq 0 || exit 1; \ + fi + @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ + ws='[ ]'; \ + results=`for b in $$bases; do echo $$b.trs; done`; \ + test -n "$$results" || results=/dev/null; \ + all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ + pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ + fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ + skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ + xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ + xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ + error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ + if test `expr $$fail + $$xpass + $$error` -eq 0; then \ + success=true; \ + else \ + success=false; \ + fi; \ + br='==================='; br=$$br$$br$$br$$br; \ + result_count () \ + { \ + if test x"$$1" = x"--maybe-color"; then \ + maybe_colorize=yes; \ + elif test x"$$1" = x"--no-color"; then \ + maybe_colorize=no; \ else \ - if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ - banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ + echo "$@: invalid 'result_count' usage" >&2; exit 4; \ fi; \ - fi; \ - dashes="$$banner"; \ - skipped=""; \ - if test "$$skip" -ne 0; then \ - if test "$$skip" -eq 1; then \ - skipped="($$skip test was not run)"; \ + shift; \ + desc=$$1 count=$$2; \ + if test $$maybe_colorize = yes && test $$count -gt 0; then \ + color_start=$$3 color_end=$$std; \ else \ - skipped="($$skip tests were not run)"; \ + color_start= color_end=; \ fi; \ - test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ - dashes="$$skipped"; \ - fi; \ - report=""; \ - if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ - report="Please report to $(PACKAGE_BUGREPORT)"; \ - test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ - dashes="$$report"; \ - fi; \ - dashes=`echo "$$dashes" | sed s/./=/g`; \ - if test "$$failed" -eq 0; then \ - col="$$grn"; \ - else \ - col="$$red"; \ - fi; \ - echo "$${col}$$dashes$${std}"; \ - echo "$${col}$$banner$${std}"; \ - test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \ - test -z "$$report" || echo "$${col}$$report$${std}"; \ - echo "$${col}$$dashes$${std}"; \ - test "$$failed" -eq 0; \ - else :; fi + echo "$${color_start}# $$desc $$count$${color_end}"; \ + }; \ + create_testsuite_report () \ + { \ + result_count $$1 "TOTAL:" $$all "$$brg"; \ + result_count $$1 "PASS: " $$pass "$$grn"; \ + result_count $$1 "SKIP: " $$skip "$$blu"; \ + result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ + result_count $$1 "FAIL: " $$fail "$$red"; \ + result_count $$1 "XPASS:" $$xpass "$$red"; \ + result_count $$1 "ERROR:" $$error "$$mgn"; \ + }; \ + { \ + echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ + $(am__rst_title); \ + create_testsuite_report --no-color; \ + echo; \ + echo ".. contents:: :depth: 2"; \ + echo; \ + for b in $$bases; do echo $$b; done \ + | $(am__create_global_log); \ + } >$(TEST_SUITE_LOG).tmp || exit 1; \ + mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ + if $$success; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ + fi; \ + echo "$${col}$$br$${std}"; \ + echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \ + echo "$${col}$$br$${std}"; \ + create_testsuite_report --maybe-color; \ + echo "$$col$$br$$std"; \ + if $$success; then :; else \ + echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ + if test -n "$(PACKAGE_BUGREPORT)"; then \ + echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ + fi; \ + echo "$$col$$br$$std"; \ + fi; \ + $$success || exit 1 + +check-TESTS: + @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list + @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + trs_list=`for i in $$bases; do echo $$i.trs; done`; \ + log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ + exit $$?; +recheck: all + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + bases=`for i in $$bases; do echo $$i; done \ + | $(am__list_recheck_tests)` || exit 1; \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + log_list=`echo $$log_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ + am__force_recheck=am--force-recheck \ + TEST_LOGS="$$log_list"; \ + exit $$? +test-config.log: test-config$(EXEEXT) + @p='test-config$(EXEEXT)'; \ + b='test-config'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +.test.log: + @p='$<'; \ + $(am__set_b); \ + $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +@am__EXEEXT_TRUE@.test$(EXEEXT).log: +@am__EXEEXT_TRUE@ @p='$<'; \ +@am__EXEEXT_TRUE@ $(am__set_b); \ +@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ +@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ +@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ +@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ @@ -778,6 +1032,9 @@ install-strip: "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: + -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) + -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) + -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) clean-generic: @@ -873,7 +1130,7 @@ uninstall-am: installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ - tags tags-am uninstall uninstall-am + recheck tags tags-am uninstall uninstall-am .PRECIOUS: Makefile diff --git a/src/tests/config/NetworkManager.conf b/src/tests/config/NetworkManager.conf index 401ba48a..a750c801 100644 --- a/src/tests/config/NetworkManager.conf +++ b/src/tests/config/NetworkManager.conf @@ -13,3 +13,71 @@ response=Hello [extra-section] extra-key=some value + + + +[connection] +ipv4.route-metric=50 +ipv6.ip6_privacy=0 +dummy.test1=no +dummy.test2=no + +ord.key00=A-0.0.00 +ord.key01=A-0.0.01 +ord.key02=A-0.0.02 +ord.key03=A-0.0.03 +ord.key04=A-0.0.04 +ord.key05=A-0.0.05 +ord.key06=A-0.0.06 +ord.key07=A-0.0.07 +ord.key08=A-0.0.08 +ord.key09=A-0.0.09 + +[connection.dev51] +match-device=mac:00:00:00:00:00:51 +stop-match=yes +ipv4.route-metric=51 +dummy.test1=yes + +[connection.dev52] +match-device=mac:00:00:00:00:00:52 +ipv4.route-metric=52 + +[connection.public] +match-device=interface-name:wlan1 +# match-wifi is not yet implemented. Just an idea what could be useful. +match-wifi=ssid:*[Ss]tarbucks*|*University* +ipv6.ip6_privacy=2 + + +# the following sections are tested for their order across +# multiple files. +[connection.ord.0.1] +ord.key03=A-0.1.03 +ord.key04=A-0.1.04 +ord.key05=A-0.1.05 +ord.key06=A-0.1.06 +ord.key07=A-0.1.07 +ord.key08=A-0.1.08 +ord.key09=A-0.1.09 +ord.ovw01=A-0.1.ovw01 +[connection.ord.0.2] +ord.key02=A-0.2.02 +ord.key03=A-0.2.03 +ord.key04=A-0.2.04 +ord.key05=A-0.2.05 +ord.key06=A-0.2.06 +ord.key07=A-0.2.07 +ord.key08=A-0.2.08 +ord.key09=A-0.2.09 +[connection.ord.0.3] +ord.key01=A-0.3.01 +ord.key02=A-0.3.02 +ord.key03=A-0.3.03 +ord.key04=A-0.3.04 +ord.key05=A-0.3.05 +ord.key06=A-0.3.06 +ord.key07=A-0.3.07 +ord.key08=A-0.3.08 +ord.key09=A-0.3.09 +ord.ovw01=A-0.3.ovw01 diff --git a/src/tests/config/conf.d/00-overrides.conf b/src/tests/config/conf.d/00-overrides.conf index 0aa19d48..f26ed93b 100644 --- a/src/tests/config/conf.d/00-overrides.conf +++ b/src/tests/config/conf.d/00-overrides.conf @@ -1,11 +1,57 @@ [main] dhcp=dhcpcd +no-auto-default=spec1,spec2 +ignore-carrier=\s space1 \s + [logging] domains=PLATFORM,DNS,WIFI +[appendable-test] +non-appendable-key1+=i-will-be-dropped +non-appendable-key2-=i-will-be-dropped + [order] a=0 b=0 c=0 + +# the following sections are tested for their order across +# multiple files. +[connection.ord.1.1] +ord.key06=B-1.1.06 +ord.key07=B-1.1.07 +ord.key08=B-1.1.08 +ord.key09=B-1.1.09 +[connection.ord.1.2] +ord.key05=B-1.2.05 +ord.key06=B-1.2.06 +ord.key07=B-1.2.07 +ord.key08=B-1.2.08 +ord.key09=B-1.2.09 +[connection.ord.1.3] +ord.key04=B-1.3.04 +ord.key05=B-1.3.05 +ord.key06=B-1.3.06 +ord.key07=B-1.3.07 +ord.key08=B-1.3.08 +ord.key09=B-1.3.09 + + +[.test-append-stringlist.1] +val1=a,b + +val2-=VAL2 +val2=VAL2 + +val3=VAL3 +val3-=VAL3 + +val4=VAL4 +val4+=VAL4,va,vb,va,vb +val4-=VAL4,va + +val5=VAL5 +val5-=VAL5 +val5+=VAL5 diff --git a/src/tests/config/conf.d/10-more.conf b/src/tests/config/conf.d/10-more.conf index b1424a4b..eadb7f96 100644 --- a/src/tests/config/conf.d/10-more.conf +++ b/src/tests/config/conf.d/10-more.conf @@ -1,5 +1,12 @@ [main] extra=hello + +no-auto-default-=spec1 +no-auto-default+=spec3 + +ignore-carrier+=\sspace2\t + +[.test-append-stringlist.0] new+=something [connectivity] @@ -9,3 +16,25 @@ uri=http://example.net a=10 b=10 +# the following sections are tested for their order across +# multiple files. +[connection.ord.2.1] +ord.key09=C-2.1.09 +[connection.ord.2.2] +ord.key08=C-2.2.08 +ord.key09=C-2.2.09 +[connection.ord.2.3] +ord.key07=C-2.3.07 +ord.key08=C-2.3.08 +ord.key09=C-2.3.09 + +# you can overwrite individual settings in a file loaded +# previously. But note that this does not bump the priority +# of the section, i.e. [connection.ord.0.1] still has a pretty +# low priority and is shadowed by [connection.ord.2.1]. +[connection.ord.0.1] +ord.ovw01=C-0.1.ovw01 + + +[.test-append-stringlist.1] +val1-=b diff --git a/src/tests/config/conf.d/90-last.conf b/src/tests/config/conf.d/90-last.conf index dc1de394..7d078788 100644 --- a/src/tests/config/conf.d/90-last.conf +++ b/src/tests/config/conf.d/90-last.conf @@ -3,3 +3,6 @@ plugins+=one,two [order] a=90 + +[.test-append-stringlist.1] +val1+=c,a diff --git a/src/tests/config/nm-test-device.c b/src/tests/config/nm-test-device.c index 512085e9..b5f65ded 100644 --- a/src/tests/config/nm-test-device.c +++ b/src/tests/config/nm-test-device.c @@ -61,7 +61,7 @@ dispose (GObject *object) g_object_class->dispose (object); } -static guint32 +static NMDeviceCapabilities get_generic_capabilities (NMDevice *device) { return NM_DEVICE_CAP_IS_NON_KERNEL; diff --git a/src/tests/config/test-config.c b/src/tests/config/test-config.c index e027eab8..8b8028e6 100644 --- a/src/tests/config/test-config.c +++ b/src/tests/config/test-config.c @@ -32,8 +32,8 @@ #include "nm-test-utils.h" -static void -setup_config (const char *config_file, const char *config_dir, ...) +static NMConfig * +setup_config (GError **error, const char *config_file, const char *config_dir, ...) { va_list ap; GPtrArray *args; @@ -41,6 +41,11 @@ setup_config (const char *config_file, const char *config_dir, ...) int argc; GOptionContext *context; gboolean success; + NMConfig *config; + GError *local_error = NULL; + NMConfigCmdLineOptions *cli; + + g_assert (!error || !*error); args = g_ptr_array_new (); g_ptr_array_add (args, "test-config"); @@ -57,8 +62,10 @@ setup_config (const char *config_file, const char *config_dir, ...) argv = (char **)args->pdata; argc = args->len; + cli = nm_config_cmd_line_options_new (); + context = g_option_context_new (NULL); - g_option_context_add_main_entries (context, nm_config_get_options (), NULL); + nm_config_cmd_line_options_add_to_entries (cli, context); success = g_option_context_parse (context, &argc, &argv, NULL); g_option_context_free (context); @@ -66,24 +73,36 @@ setup_config (const char *config_file, const char *config_dir, ...) g_printerr ("Invalid options.\n"); g_ptr_array_free (args, TRUE); + + config = nm_config_setup (cli, &local_error); + if (error) { + g_assert (!config); + g_assert (local_error); + g_propagate_error (error, local_error); + } else { + g_assert (config); + g_assert_no_error (local_error); + } + nm_config_cmd_line_options_free (cli); + return config; } static void test_config_simple (void) { NMConfig *config; - GError *error = NULL; const char **plugins; char *value; + gs_unref_object NMDevice *dev50 = nm_test_device_new ("00:00:00:00:00:50"); + gs_unref_object NMDevice *dev51 = nm_test_device_new ("00:00:00:00:00:51"); + gs_unref_object NMDevice *dev52 = nm_test_device_new ("00:00:00:00:00:52"); - setup_config (SRCDIR "/NetworkManager.conf", "/no/such/dir", NULL); - config = nm_config_new (&error); - g_assert_no_error (error); + config = setup_config (NULL, SRCDIR "/NetworkManager.conf", "/no/such/dir", NULL); - g_assert_cmpstr (nm_config_get_path (config), ==, SRCDIR "/NetworkManager.conf"); + g_assert_cmpstr (nm_config_data_get_config_main_file (nm_config_get_data_orig (config)), ==, SRCDIR "/NetworkManager.conf"); g_assert_cmpstr (nm_config_get_dhcp_client (config), ==, "dhclient"); g_assert_cmpstr (nm_config_get_log_level (config), ==, "INFO"); - g_assert_cmpint (nm_config_get_connectivity_interval (config), ==, 100); + g_assert_cmpint (nm_config_data_get_connectivity_interval (nm_config_get_data_orig (config)), ==, 100); plugins = nm_config_get_plugins (config); g_assert_cmpint (g_strv_length ((char **)plugins), ==, 3); @@ -91,19 +110,63 @@ test_config_simple (void) g_assert_cmpstr (plugins[1], ==, "bar"); g_assert_cmpstr (plugins[2], ==, "baz"); - value = nm_config_get_value (config, "extra-section", "extra-key", NULL); + value = nm_config_data_get_value (nm_config_get_data_orig (config), "extra-section", "extra-key", NM_CONFIG_GET_VALUE_NONE); g_assert_cmpstr (value, ==, "some value"); g_free (value); - value = nm_config_get_value (config, "extra-section", "no-key", &error); + value = nm_config_data_get_value (nm_config_get_data_orig (config), "extra-section", "no-key", NM_CONFIG_GET_VALUE_NONE); g_assert (!value); - g_assert_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND); - g_clear_error (&error); - value = nm_config_get_value (config, "no-section", "no-key", &error); + value = nm_config_data_get_value (nm_config_get_data_orig (config), "no-section", "no-key", NM_CONFIG_GET_VALUE_NONE); g_assert (!value); - g_assert_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND); - g_clear_error (&error); + + value = nm_config_data_get_value (nm_config_get_data_orig (config), "connection", "ipv6.ip6_privacy", NM_CONFIG_GET_VALUE_NONE); + g_assert_cmpstr (value, ==, "0"); + g_free (value); + + value = nm_config_data_get_value (nm_config_get_data_orig (config), "connection.dev51", "ipv4.route-metric", NM_CONFIG_GET_VALUE_NONE); + g_assert_cmpstr (value, ==, "51"); + g_free (value); + + + value = nm_config_data_get_connection_default (nm_config_get_data_orig (config), "ipv6.route-metric", NULL); + g_assert_cmpstr (value, ==, NULL); + g_free (value); + + + value = nm_config_data_get_connection_default (nm_config_get_data_orig (config), "ipv4.route-metric", NULL); + g_assert_cmpstr (value, ==, "50"); + g_free (value); + + value = nm_config_data_get_connection_default (nm_config_get_data_orig (config), "ipv4.route-metric", dev50); + g_assert_cmpstr (value, ==, "50"); + g_free (value); + + value = nm_config_data_get_connection_default (nm_config_get_data_orig (config), "ipv4.route-metric", dev51); + g_assert_cmpstr (value, ==, "51"); + g_free (value); + + value = nm_config_data_get_connection_default (nm_config_get_data_orig (config), "ipv4.route-metric", dev52); + g_assert_cmpstr (value, ==, "52"); + g_free (value); + + + value = nm_config_data_get_connection_default (nm_config_get_data_orig (config), "dummy.test1", dev51); + g_assert_cmpstr (value, ==, "yes"); + g_free (value); + + value = nm_config_data_get_connection_default (nm_config_get_data_orig (config), "dummy.test1", dev50); + g_assert_cmpstr (value, ==, "no"); + g_free (value); + + value = nm_config_data_get_connection_default (nm_config_get_data_orig (config), "dummy.test2", dev51); + g_assert_cmpstr (value, ==, NULL); + g_free (value); + + value = nm_config_data_get_connection_default (nm_config_get_data_orig (config), "dummy.test2", dev50); + g_assert_cmpstr (value, ==, "no"); + g_free (value); + g_object_unref (config); } @@ -111,45 +174,38 @@ test_config_simple (void) static void test_config_non_existent (void) { - NMConfig *config; gs_free_error GError *error = NULL; - setup_config (SRCDIR "/no-such-file", "/no/such/dir", NULL); - config = nm_config_new (&error); - g_assert (!config); + setup_config (&error, SRCDIR "/no-such-file", "/no/such/dir", NULL); g_assert_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND); + g_clear_error (&error); } static void test_config_parse_error (void) { - NMConfig *config; gs_free_error GError *error = NULL; - setup_config (SRCDIR "/bad.conf", "/no/such/dir", NULL); - config = nm_config_new (&error); - g_assert (!config); + setup_config (&error, SRCDIR "/bad.conf", "/no/such/dir", NULL); g_assert_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_PARSE); + g_clear_error (&error); } static void test_config_override (void) { NMConfig *config; - GError *error = NULL; const char **plugins; - setup_config (SRCDIR "/NetworkManager.conf", "/no/such/dir", - "--plugins", "alpha,beta,gamma,delta", - "--connectivity-interval", "12", - NULL); - config = nm_config_new (&error); - g_assert_no_error (error); + config = setup_config (NULL, SRCDIR "/NetworkManager.conf", "/no/such/dir", + "--plugins", "alpha,beta,gamma,delta", + "--connectivity-interval", "12", + NULL); - g_assert_cmpstr (nm_config_get_path (config), ==, SRCDIR "/NetworkManager.conf"); + g_assert_cmpstr (nm_config_data_get_config_main_file (nm_config_get_data_orig (config)), ==, SRCDIR "/NetworkManager.conf"); g_assert_cmpstr (nm_config_get_dhcp_client (config), ==, "dhclient"); g_assert_cmpstr (nm_config_get_log_level (config), ==, "INFO"); - g_assert_cmpint (nm_config_get_connectivity_interval (config), ==, 12); + g_assert_cmpint (nm_config_data_get_connectivity_interval (nm_config_get_data_orig (config)), ==, 12); plugins = nm_config_get_plugins (config); g_assert_cmpint (g_strv_length ((char **)plugins), ==, 4); @@ -179,37 +235,36 @@ test_config_no_auto_default (void) g_assert_cmpint (nwrote, ==, 18); close (fd); - setup_config (SRCDIR "/NetworkManager.conf", "/no/such/dir", - "--no-auto-default", state_file, - NULL); - config = nm_config_new (&error); - g_assert_no_error (error); + config = setup_config (NULL, SRCDIR "/NetworkManager.conf", "/no/such/dir", + "--no-auto-default", state_file, + NULL); dev1 = nm_test_device_new ("11:11:11:11:11:11"); dev2 = nm_test_device_new ("22:22:22:22:22:22"); dev3 = nm_test_device_new ("33:33:33:33:33:33"); dev4 = nm_test_device_new ("44:44:44:44:44:44"); - g_assert (!nm_config_get_ethernet_can_auto_default (config, dev1)); - g_assert (!nm_config_get_ethernet_can_auto_default (config, dev2)); - g_assert (nm_config_get_ethernet_can_auto_default (config, dev3)); - g_assert (!nm_config_get_ethernet_can_auto_default (config, dev4)); + g_assert (nm_config_get_no_auto_default_for_device (config, dev1)); + g_assert (nm_config_get_no_auto_default_for_device (config, dev2)); + g_assert (!nm_config_get_no_auto_default_for_device (config, dev3)); + g_assert (nm_config_get_no_auto_default_for_device (config, dev4)); + + g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE, "*config: update * (no-auto-default)*"); + nm_config_set_no_auto_default_for_device (config, dev3); + g_test_assert_expected_messages (); - nm_config_set_ethernet_no_auto_default (config, dev3); - g_assert (!nm_config_get_ethernet_can_auto_default (config, dev3)); + g_assert (nm_config_get_no_auto_default_for_device (config, dev3)); g_object_unref (config); - setup_config (SRCDIR "/NetworkManager.conf", "/no/such/dir", - "--no-auto-default", state_file, - NULL); - config = nm_config_new (&error); - g_assert_no_error (error); + config = setup_config (NULL, SRCDIR "/NetworkManager.conf", "/no/such/dir", + "--no-auto-default", state_file, + NULL); - g_assert (!nm_config_get_ethernet_can_auto_default (config, dev1)); - g_assert (!nm_config_get_ethernet_can_auto_default (config, dev2)); - g_assert (!nm_config_get_ethernet_can_auto_default (config, dev3)); - g_assert (!nm_config_get_ethernet_can_auto_default (config, dev4)); + g_assert (nm_config_get_no_auto_default_for_device (config, dev1)); + g_assert (nm_config_get_no_auto_default_for_device (config, dev2)); + g_assert (nm_config_get_no_auto_default_for_device (config, dev3)); + g_assert (nm_config_get_no_auto_default_for_device (config, dev4)); g_object_unref (config); @@ -226,20 +281,18 @@ static void test_config_confdir (void) { NMConfig *config; - GError *error = NULL; const char **plugins; char *value; + GSList *specs; - setup_config (SRCDIR "/NetworkManager.conf", SRCDIR "/conf.d", NULL); - config = nm_config_new (&error); - g_assert_no_error (error); + config = setup_config (NULL, SRCDIR "/NetworkManager.conf", SRCDIR "/conf.d", NULL); - g_assert_cmpstr (nm_config_get_path (config), ==, SRCDIR "/NetworkManager.conf"); + g_assert_cmpstr (nm_config_data_get_config_main_file (nm_config_get_data_orig (config)), ==, SRCDIR "/NetworkManager.conf"); g_assert_cmpstr (nm_config_get_dhcp_client (config), ==, "dhcpcd"); g_assert_cmpstr (nm_config_get_log_level (config), ==, "INFO"); g_assert_cmpstr (nm_config_get_log_domains (config), ==, "PLATFORM,DNS,WIFI"); - g_assert_cmpstr (nm_config_get_connectivity_uri (config), ==, "http://example.net"); - g_assert_cmpint (nm_config_get_connectivity_interval (config), ==, 100); + g_assert_cmpstr (nm_config_data_get_connectivity_uri (nm_config_get_data_orig (config)), ==, "http://example.net"); + g_assert_cmpint (nm_config_data_get_connectivity_interval (nm_config_get_data_orig (config)), ==, 100); plugins = nm_config_get_plugins (config); g_assert_cmpint (g_strv_length ((char **)plugins), ==, 5); @@ -249,38 +302,98 @@ test_config_confdir (void) g_assert_cmpstr (plugins[3], ==, "one"); g_assert_cmpstr (plugins[4], ==, "two"); - value = nm_config_get_value (config, "main", "extra", NULL); + value = nm_config_data_get_value (nm_config_get_data_orig (config), "main", "extra", NM_CONFIG_GET_VALUE_NONE); g_assert_cmpstr (value, ==, "hello"); g_free (value); - value = nm_config_get_value (config, "main", "new", NULL); + value = nm_config_data_get_value (nm_config_get_data_orig (config), "main", "no-auto-default", NM_CONFIG_GET_VALUE_TYPE_SPEC); + specs = nm_match_spec_split (value); + g_free (value); + g_assert_cmpint (g_slist_length (specs), ==, 2); + g_assert_cmpstr (g_slist_nth_data (specs, 0), ==, "spec2"); + g_assert_cmpstr (g_slist_nth_data (specs, 1), ==, "spec3"); + g_slist_free_full (specs, g_free); + + value = nm_config_data_get_value (nm_config_get_data_orig (config), "main", "ignore-carrier", NM_CONFIG_GET_VALUE_TYPE_SPEC); + specs = nm_match_spec_split (value); + g_free (value); + g_assert_cmpint (g_slist_length (specs), ==, 2); + g_assert_cmpstr (g_slist_nth_data (specs, 0), ==, " space1 "); + g_assert_cmpstr (g_slist_nth_data (specs, 1), ==, " space2\t"); + g_slist_free_full (specs, g_free); + + value = nm_config_data_get_value (nm_config_get_data_orig (config), NM_CONFIG_KEYFILE_GROUPPREFIX_TEST_APPEND_STRINGLIST".0", "new", NM_CONFIG_GET_VALUE_NONE); g_assert_cmpstr (value, ==, "something"); /* not ",something" */ g_free (value); - value = nm_config_get_value (config, "order", "a", NULL); + value = nm_config_data_get_value (nm_config_get_data_orig (config), "order", "a", NM_CONFIG_GET_VALUE_NONE); g_assert_cmpstr (value, ==, "90"); g_free (value); - value = nm_config_get_value (config, "order", "b", NULL); + value = nm_config_data_get_value (nm_config_get_data_orig (config), "order", "b", NM_CONFIG_GET_VALUE_NONE); g_assert_cmpstr (value, ==, "10"); g_free (value); - value = nm_config_get_value (config, "order", "c", NULL); + value = nm_config_data_get_value (nm_config_get_data_orig (config), "order", "c", NM_CONFIG_GET_VALUE_NONE); g_assert_cmpstr (value, ==, "0"); g_free (value); + g_assert (!nm_config_data_has_value (nm_config_get_data_orig (config), "appendable-test", "non-appendable-key1", NM_CONFIG_GET_VALUE_RAW)); + g_assert (!nm_config_data_has_value (nm_config_get_data_orig (config), "appendable-test", "non-appendable-key1+", NM_CONFIG_GET_VALUE_RAW)); + g_assert (!nm_config_data_has_value (nm_config_get_data_orig (config), "appendable-test", "non-appendable-key1-", NM_CONFIG_GET_VALUE_RAW)); + g_assert (!nm_config_data_has_value (nm_config_get_data_orig (config), "appendable-test", "non-appendable-key2", NM_CONFIG_GET_VALUE_RAW)); + g_assert (!nm_config_data_has_value (nm_config_get_data_orig (config), "appendable-test", "non-appendable-key2+", NM_CONFIG_GET_VALUE_RAW)); + g_assert (!nm_config_data_has_value (nm_config_get_data_orig (config), "appendable-test", "non-appendable-key2-", NM_CONFIG_GET_VALUE_RAW)); + +#define ASSERT_GET_CONN_DEFAULT(xconfig, xname, xvalue) \ + G_STMT_START { \ + gs_free char *_value = nm_config_data_get_connection_default (nm_config_get_data_orig (xconfig), (xname), NULL); \ + g_assert_cmpstr (_value, ==, (xvalue)); \ + } G_STMT_END + ASSERT_GET_CONN_DEFAULT (config, "ord.key00", "A-0.0.00"); + ASSERT_GET_CONN_DEFAULT (config, "ord.key01", "A-0.3.01"); + ASSERT_GET_CONN_DEFAULT (config, "ord.key02", "A-0.2.02"); + ASSERT_GET_CONN_DEFAULT (config, "ord.key03", "A-0.1.03"); + ASSERT_GET_CONN_DEFAULT (config, "ord.key04", "B-1.3.04"); + ASSERT_GET_CONN_DEFAULT (config, "ord.key05", "B-1.2.05"); + ASSERT_GET_CONN_DEFAULT (config, "ord.key06", "B-1.1.06"); + ASSERT_GET_CONN_DEFAULT (config, "ord.key07", "C-2.3.07"); + ASSERT_GET_CONN_DEFAULT (config, "ord.key08", "C-2.2.08"); + ASSERT_GET_CONN_DEFAULT (config, "ord.key09", "C-2.1.09"); + ASSERT_GET_CONN_DEFAULT (config, "ord.ovw01", "C-0.1.ovw01"); + + value = nm_config_data_get_value (nm_config_get_data_orig (config), NM_CONFIG_KEYFILE_GROUPPREFIX_TEST_APPEND_STRINGLIST".1", "val1", NM_CONFIG_GET_VALUE_NONE); + g_assert_cmpstr (value, ==, "a,c"); + g_free (value); + + value = nm_config_data_get_value (nm_config_get_data_orig (config), NM_CONFIG_KEYFILE_GROUPPREFIX_TEST_APPEND_STRINGLIST".1", "val2", NM_CONFIG_GET_VALUE_NONE); + g_assert_cmpstr (value, ==, "VAL2"); + g_free (value); + + value = nm_config_data_get_value (nm_config_get_data_orig (config), NM_CONFIG_KEYFILE_GROUPPREFIX_TEST_APPEND_STRINGLIST".1", "val3", NM_CONFIG_GET_VALUE_NONE); + g_assert_cmpstr (value, ==, NULL); + g_free (value); + + value = nm_config_data_get_value (nm_config_get_data_orig (config), NM_CONFIG_KEYFILE_GROUPPREFIX_TEST_APPEND_STRINGLIST".1", "val4", NM_CONFIG_GET_VALUE_NONE); + g_assert_cmpstr (value, ==, "vb,vb"); + g_free (value); + + value = nm_config_data_get_value (nm_config_get_data_orig (config), NM_CONFIG_KEYFILE_GROUPPREFIX_TEST_APPEND_STRINGLIST".1", "val5", NM_CONFIG_GET_VALUE_NONE); + g_assert_cmpstr (value, ==, "VAL5"); + g_free (value); + + nm_config_data_log (nm_config_get_data_orig (config), ">>> TEST: "); + g_object_unref (config); } static void test_config_confdir_parse_error (void) { - NMConfig *config; - gs_free_error GError *error = NULL; + GError *error = NULL; /* Using SRCDIR as the conf dir will pick up bad.conf */ - setup_config (SRCDIR "/NetworkManager.conf", SRCDIR, NULL); - config = nm_config_new (&error); - g_assert (!config); + setup_config (&error, SRCDIR "/NetworkManager.conf", SRCDIR, NULL); g_assert_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_PARSE); + g_clear_error (&error); } NMTST_DEFINE (); diff --git a/src/tests/test-general-with-expect.c b/src/tests/test-general-with-expect.c index 4a0627c9..bb52bde2 100644 --- a/src/tests/test-general-with-expect.c +++ b/src/tests/test-general-with-expect.c @@ -29,6 +29,7 @@ #include "NetworkManagerUtils.h" #include "nm-logging.h" +#include "nm-multi-index.h" #include "nm-test-utils.h" @@ -330,11 +331,109 @@ test_nm_utils_kill_child (void) g_test_assert_expected_messages (); } +/*******************************************/ + +static void +_remove_at_indexes_init_random_idx (GArray *idx, guint array_len, guint idx_len) +{ + GRand *rand = nmtst_get_rand (); + gs_free char *mask = NULL; + guint i, max_test_idx; + + g_assert (idx); + g_assert (array_len > 0); + g_assert (idx_len >= 1 && idx_len <= array_len); + + mask = g_new0 (char, array_len); + + max_test_idx = array_len - 1; + for (i = 0; i < idx_len; i++) { + guint itest; + + /* find a index itest that is not yet taken */ + if (max_test_idx == 0) + itest = 0; + else + itest = g_rand_int_range (rand, 0, max_test_idx); + while (itest < array_len && mask[itest]) + itest++; + g_assert (itest <= max_test_idx); + g_assert (!mask[itest]); + + mask[itest] = TRUE; + if (itest == max_test_idx) { + g_assert (max_test_idx > 0 || i == idx_len - 1); + + if (max_test_idx == 0) + g_assert_cmpint (i, ==, idx_len - 1); + else { + max_test_idx--; + while (max_test_idx > 0 && mask[max_test_idx]) + max_test_idx--; + if (mask[max_test_idx]) + g_assert_cmpint (i, ==, idx_len - 1); + } + } + } + + g_array_set_size (idx, 0); + for (i = 0; i < array_len; i++) { + if (mask[i]) + g_array_append_val (idx, i); + } + g_assert_cmpint (idx->len, ==, idx_len); +} + +static void +test_nm_utils_array_remove_at_indexes () +{ + gs_unref_array GArray *idx = NULL, *array = NULL; + gs_unref_hashtable GHashTable *unique = NULL; + guint i_len, i_idx_len, i_rnd, i; + + idx = g_array_new (FALSE, FALSE, sizeof (guint)); + array = g_array_new (FALSE, FALSE, sizeof (gssize)); + unique = g_hash_table_new (NULL, NULL); + for (i_len = 1; i_len < 20; i_len++) { + for (i_idx_len = 1; i_idx_len <= i_len; i_idx_len++) { + for (i_rnd = 0; i_rnd < 20; i_rnd++) { + + _remove_at_indexes_init_random_idx (idx, i_len, i_idx_len); + g_array_set_size (array, i_len); + for (i = 0; i < i_len; i++) + g_array_index (array, gssize, i) = i; + + nm_utils_array_remove_at_indexes (array, &g_array_index (idx, guint, 0), i_idx_len); + + g_hash_table_remove_all (unique); + /* ensure that all the indexes are still unique */ + for (i = 0; i < array->len; i++) + g_hash_table_add (unique, GUINT_TO_POINTER (g_array_index (array, gssize, i))); + g_assert_cmpint (g_hash_table_size (unique), ==, array->len); + + for (i = 0; i < idx->len; i++) + g_hash_table_add (unique, GUINT_TO_POINTER (g_array_index (idx, guint, i))); + g_assert_cmpint (g_hash_table_size (unique), ==, i_len); + + /* ensure proper sort order in array */ + for (i = 0; i < array->len; i++) { + gssize i1 = g_array_index (array, gssize, i); + + g_assert (i1 >= 0 && i1 < i_len); + if (i > 0) { + gsize i0 = g_array_index (array, gssize, i - 1); + g_assert_cmpint (i0, <, i1); + } + } + } + } + } +} /*******************************************/ static void -test_nm_ethernet_address_is_valid () +test_nm_ethernet_address_is_valid (void) { g_assert (!nm_ethernet_address_is_valid (NULL, -1)); g_assert (!nm_ethernet_address_is_valid (NULL, ETH_ALEN)); @@ -360,6 +459,362 @@ test_nm_ethernet_address_is_valid () /*******************************************/ +typedef struct { + union { + NMMultiIndexId id_base; + guint bucket; + }; +} NMMultiIndexIdTest; + +typedef struct { + guint64 buckets; + gpointer ptr_value; +} NMMultiIndexTestValue; + +static gboolean +_mi_value_bucket_has (const NMMultiIndexTestValue *value, guint bucket) +{ + g_assert (value); + g_assert (bucket < 64); + + return (value->buckets & (((guint64) 0x01) << bucket)) != 0; +} + +static gboolean +_mi_value_bucket_set (NMMultiIndexTestValue *value, guint bucket) +{ + g_assert (value); + g_assert (bucket < 64); + + if (_mi_value_bucket_has (value, bucket)) + return FALSE; + + value->buckets |= (((guint64) 0x01) << bucket); + return TRUE; +} + +static gboolean +_mi_value_bucket_unset (NMMultiIndexTestValue *value, guint bucket) +{ + g_assert (value); + g_assert (bucket < 64); + + if (!_mi_value_bucket_has (value, bucket)) + return FALSE; + + value->buckets &= ~(((guint64) 0x01) << bucket); + return TRUE; +} + +static guint +_mi_idx_hash (const NMMultiIndexIdTest *id) +{ + g_assert (id && id->bucket < 64); + return id->bucket; +} + +static gboolean +_mi_idx_equal (const NMMultiIndexIdTest *a, const NMMultiIndexIdTest *b) +{ + g_assert (a && a->bucket < 64); + g_assert (b && b->bucket < 64); + + return a->bucket == b->bucket; +} + +static NMMultiIndexIdTest * +_mi_idx_clone (const NMMultiIndexIdTest *id) +{ + NMMultiIndexIdTest *n; + + g_assert (id && id->bucket < 64); + + n = g_new0 (NMMultiIndexIdTest, 1); + n->bucket = id->bucket; + return n; +} + +static void +_mi_idx_destroy (NMMultiIndexIdTest *id) +{ + g_assert (id && id->bucket < 64); + g_free (id); +} + +static NMMultiIndexTestValue * +_mi_create_array (guint num_values) +{ + NMMultiIndexTestValue *array = g_new0 (NMMultiIndexTestValue, num_values); + guint i; + + g_assert (num_values > 0); + + for (i = 0; i < num_values; i++) { + array[i].buckets = 0; + array[i].ptr_value = GUINT_TO_POINTER (i + 1); + } + return array; +} + +typedef struct { + guint num_values; + guint num_buckets; + NMMultiIndexTestValue *array; + int test_idx; +} NMMultiIndexAssertData; + +static gboolean +_mi_assert_index_equals_array_cb (const NMMultiIndexIdTest *id, void *const* values, guint len, NMMultiIndexAssertData *data) +{ + guint i; + gboolean has_test_idx = FALSE; + + g_assert (id && id->bucket < 64); + g_assert (data); + g_assert (values); + g_assert (len > 0); + g_assert (values[len] == NULL); + g_assert (data->test_idx >= -1 || data->test_idx < data->num_buckets); + + g_assert (id->bucket < data->num_buckets); + + for (i = 0; i < data->num_values; i++) + g_assert (!_mi_value_bucket_has (&data->array[i], id->bucket)); + + for (i = 0; i < len; i++) { + guint vi = GPOINTER_TO_UINT (values[i]); + + g_assert (vi >= 1); + g_assert (vi <= data->num_values); + vi--; + if (data->test_idx == vi) + has_test_idx = TRUE; + g_assert (data->array[vi].ptr_value == values[i]); + if (!_mi_value_bucket_set (&data->array[vi], id->bucket)) + g_assert_not_reached (); + } + g_assert ((data->test_idx == -1 && !has_test_idx) || has_test_idx); + return TRUE; +} + +static void +_mi_assert_index_equals_array (guint num_values, guint num_buckets, int test_idx, const NMMultiIndexTestValue *array, const NMMultiIndex *index) +{ + NMMultiIndexAssertData data = { + .num_values = num_values, + .num_buckets = num_buckets, + .test_idx = test_idx, + }; + NMMultiIndexIter iter; + const NMMultiIndexIdTest *id; + void *const* values; + guint len; + NMMultiIndexTestValue *v; + + data.array = _mi_create_array (num_values); + v = test_idx >= 0 ? data.array[test_idx].ptr_value : NULL; + nm_multi_index_foreach (index, v, (NMMultiIndexFuncForeach) _mi_assert_index_equals_array_cb, &data); + if (test_idx >= 0) + g_assert (memcmp (&data.array[test_idx], &array[test_idx], sizeof (NMMultiIndexTestValue)) == 0); + else + g_assert (memcmp (data.array, array, sizeof (NMMultiIndexTestValue) * num_values) == 0); + g_free (data.array); + + + data.array = _mi_create_array (num_values); + v = test_idx >= 0 ? data.array[test_idx].ptr_value : NULL; + nm_multi_index_iter_init (&iter, index, v); + while (nm_multi_index_iter_next (&iter, (gpointer) &id, &values, &len)) + _mi_assert_index_equals_array_cb (id, values, len, &data); + if (test_idx >= 0) + g_assert (memcmp (&data.array[test_idx], &array[test_idx], sizeof (NMMultiIndexTestValue)) == 0); + else + g_assert (memcmp (data.array, array, sizeof (NMMultiIndexTestValue) * num_values) == 0); + g_free (data.array); +} + +typedef enum { + MI_OP_ADD, + MI_OP_REMOVE, + MI_OP_MOVE, +} NMMultiIndexOperation; + +static void +_mi_rebucket (GRand *rand, guint num_values, guint num_buckets, NMMultiIndexOperation op, guint bucket, guint bucket_old, guint array_idx, NMMultiIndexTestValue *array, NMMultiIndex *index) +{ + NMMultiIndexTestValue *v; + NMMultiIndexIdTest id, id_old; + const NMMultiIndexIdTest *id_reverse; + guint64 buckets_old; + guint i; + gboolean had_bucket, had_bucket_old; + + g_assert (array_idx < num_values); + g_assert (bucket < (int) num_buckets); + + v = &array[array_idx]; + + buckets_old = v->buckets; + if (op == MI_OP_MOVE) + had_bucket_old = _mi_value_bucket_has (v, bucket_old); + else + had_bucket_old = FALSE; + had_bucket = _mi_value_bucket_has (v, bucket); + + switch (op) { + + case MI_OP_ADD: + _mi_value_bucket_set (v, bucket); + id.bucket = bucket; + if (nm_multi_index_add (index, &id.id_base, v->ptr_value)) + g_assert (!had_bucket); + else + g_assert (had_bucket); + break; + + case MI_OP_REMOVE: + _mi_value_bucket_unset (v, bucket); + id.bucket = bucket; + if (nm_multi_index_remove (index, &id.id_base, v->ptr_value)) + g_assert (had_bucket); + else + g_assert (!had_bucket); + break; + + case MI_OP_MOVE: + + _mi_value_bucket_unset (v, bucket_old); + _mi_value_bucket_set (v, bucket); + + id.bucket = bucket; + id_old.bucket = bucket_old; + + if (nm_multi_index_move (index, &id_old.id_base, &id.id_base, v->ptr_value)) { + if (bucket == bucket_old) + g_assert (had_bucket_old && had_bucket); + else + g_assert (had_bucket_old && !had_bucket); + } else { + if (bucket == bucket_old) + g_assert (!had_bucket_old && !had_bucket); + else + g_assert (!had_bucket_old || had_bucket); + } + break; + + default: + g_assert_not_reached (); + } + +#if 0 + g_print (">>> rebucket: idx=%3u, op=%3s, bucket=%3i%c -> %3i%c, buckets=%08llx -> %08llx %s\n", array_idx, + op == MI_OP_ADD ? "ADD" : (op == MI_OP_REMOVE ? "REM" : "MOV"), + bucket_old, had_bucket_old ? '*' : ' ', + bucket, had_bucket ? '*' : ' ', + (long long unsigned) buckets_old, (long long unsigned) v->buckets, + buckets_old != v->buckets ? "(changed)" : "(unchanged)"); +#endif + + id_reverse = (const NMMultiIndexIdTest *) nm_multi_index_lookup_first_by_value (index, v->ptr_value); + if (id_reverse) + g_assert (_mi_value_bucket_has (v, id_reverse->bucket)); + else + g_assert (v->buckets == 0); + + for (i = 0; i < 64; i++) { + id.bucket = i; + if (nm_multi_index_contains (index, &id.id_base, v->ptr_value)) + g_assert (_mi_value_bucket_has (v, i)); + else + g_assert (!_mi_value_bucket_has (v, i)); + } + + _mi_assert_index_equals_array (num_values, num_buckets, -1, array, index); + _mi_assert_index_equals_array (num_values, num_buckets, array_idx, array, index); + _mi_assert_index_equals_array (num_values, num_buckets, g_rand_int_range (rand, 0, num_values), array, index); +} + +static void +_mi_test_run (guint num_values, guint num_buckets) +{ + NMMultiIndex *index = nm_multi_index_new ((NMMultiIndexFuncHash) _mi_idx_hash, + (NMMultiIndexFuncEqual) _mi_idx_equal, + (NMMultiIndexFuncClone) _mi_idx_clone, + (NMMultiIndexFuncDestroy) _mi_idx_destroy); + gs_free NMMultiIndexTestValue *array = _mi_create_array (num_values); + GRand *rand = nmtst_get_rand (); + guint i, i_rd, i_idx, i_bucket; + guint num_buckets_all = num_values * num_buckets; + + g_assert (array[0].ptr_value == GUINT_TO_POINTER (1)); + + _mi_assert_index_equals_array (num_values, num_buckets, -1, array, index); + + _mi_rebucket (rand, num_values, num_buckets, MI_OP_ADD, 0, 0, 0, array, index); + _mi_rebucket (rand, num_values, num_buckets, MI_OP_REMOVE, 0, 0, 0, array, index); + + if (num_buckets >= 3) { + _mi_rebucket (rand, num_values, num_buckets, MI_OP_ADD, 0, 0, 0, array, index); + _mi_rebucket (rand, num_values, num_buckets, MI_OP_MOVE, 2, 0, 0, array, index); + _mi_rebucket (rand, num_values, num_buckets, MI_OP_REMOVE, 2, 0, 0, array, index); + } + + g_assert (nm_multi_index_get_num_groups (index) == 0); + + /* randomly change the bucket of entries. */ + for (i = 0; i < 5 * num_values; i++) { + guint array_idx = g_rand_int_range (rand, 0, num_values); + guint bucket = g_rand_int_range (rand, 0, num_buckets); + NMMultiIndexOperation op = g_rand_int_range (rand, 0, MI_OP_MOVE + 1); + guint bucket_old = 0; + + if (op == MI_OP_MOVE) { + if ((g_rand_int (rand) % 2) && array[array_idx].buckets != 0) { + guint64 b; + + /* choose the highest (existing) bucket. */ + bucket_old = 0; + for (b = array[array_idx].buckets; b; b >>= 1) + bucket_old++; + } else { + /* choose a random bucket (even if the item is currently not in that bucket). */ + bucket_old = g_rand_int_range (rand, 0, num_buckets); + } + } + + _mi_rebucket (rand, num_values, num_buckets, op, bucket, bucket_old, array_idx, array, index); + } + + /* remove all elements from all buckets */ + i_rd = g_rand_int (rand); + for (i = 0; i < num_buckets_all; i++) { + i_rd = (i_rd + 101) % num_buckets_all; + i_idx = i_rd / num_buckets; + i_bucket = i_rd % num_buckets; + + if (_mi_value_bucket_has (&array[i_idx], i_bucket)) + _mi_rebucket (rand, num_values, num_buckets, MI_OP_REMOVE, i_bucket, 0, i_idx, array, index); + } + + g_assert (nm_multi_index_get_num_groups (index) == 0); + nm_multi_index_free (index); +} + +static void +test_nm_multi_index (void) +{ + guint i, j; + + for (i = 1; i < 7; i++) { + for (j = 1; j < 6; j++) + _mi_test_run (i, j); + } + _mi_test_run (50, 3); + _mi_test_run (50, 18); +} + +/*******************************************/ + NMTST_DEFINE (); int @@ -369,6 +824,8 @@ main (int argc, char **argv) g_test_add_func ("/general/nm_utils_kill_child", test_nm_utils_kill_child); g_test_add_func ("/general/nm_ethernet_address_is_valid", test_nm_ethernet_address_is_valid); + g_test_add_func ("/general/nm_multi_index", test_nm_multi_index); + g_test_add_func ("/general/nm_utils_array_remove_at_indexes", test_nm_utils_array_remove_at_indexes); return g_test_run (); } diff --git a/src/tests/test-general.c b/src/tests/test-general.c index f118156b..de65d50f 100644 --- a/src/tests/test-general.c +++ b/src/tests/test-general.c @@ -30,135 +30,6 @@ #include "nm-test-utils.h" -static void -test_nm_utils_ascii_str_to_int64_check (const char *str, guint base, gint64 min, - gint64 max, gint64 fallback, int exp_errno, - gint64 exp_val) -{ - gint64 v; - - errno = 1; - v = nm_utils_ascii_str_to_int64 (str, base, min, max, fallback); - g_assert_cmpint (errno, ==, exp_errno); - g_assert_cmpint (v, ==, exp_val); -} - -static void -test_nm_utils_ascii_str_to_int64_do (const char *str, guint base, gint64 min, - gint64 max, gint64 fallback, int exp_errno, - gint64 exp_val) -{ - const char *sign = ""; - const char *val; - static const char *whitespaces[] = { - "", - " ", - "\r\n\t", - " \r\n\t ", - " \r\n\t \t\r\n\t", - NULL, - }; - static const char *nulls[] = { - "", - "0", - "00", - "0000", - "0000000000000000", - "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - NULL, - }; - const char **ws_pre, **ws_post, **null; - guint i; - - if (str == NULL || exp_errno != 0) { - test_nm_utils_ascii_str_to_int64_check (str, base, min, max, fallback, exp_errno, exp_val); - return; - } - - if (strncmp (str, "-", 1) == 0) - sign = "-"; - - val = str + strlen (sign); - - for (ws_pre = whitespaces; *ws_pre; ws_pre++) { - for (ws_post = whitespaces; *ws_post; ws_post++) { - for (null = nulls; *null; null++) { - for (i = 0; ; i++) { - char *s; - const char *str_base = ""; - - if (base == 16) { - if (i == 1) - str_base = "0x"; - else if (i > 1) - break; - } else if (base == 8) { - if (i == 1) - str_base = "0"; - else if (i > 1) - break; - } else if (base == 0) { - if (i > 0) - break; - /* with base==0, a leading zero would be interpreted as octal. Only test without *null */ - if ((*null)[0]) - break; - } else { - if (i > 0) - break; - } - - s = g_strdup_printf ("%s%s%s%s%s%s", *ws_pre, sign, str_base, *null, val, *ws_post); - - test_nm_utils_ascii_str_to_int64_check (s, base, min, max, fallback, exp_errno, exp_val); - g_free (s); - } - } - } - } -} - -static void -test_nm_utils_ascii_str_to_int64 (void) -{ - test_nm_utils_ascii_str_to_int64_do (NULL, 10, 0, 10000, -1, EINVAL, -1); - test_nm_utils_ascii_str_to_int64_do ("", 10, 0, 10000, -1, EINVAL, -1); - test_nm_utils_ascii_str_to_int64_do ("1x", 10, 0, 10000, -1, EINVAL, -1); - test_nm_utils_ascii_str_to_int64_do ("4711", 10, 0, 10000, -1, 0, 4711); - test_nm_utils_ascii_str_to_int64_do ("10000", 10, 0, 10000, -1, 0, 10000); - test_nm_utils_ascii_str_to_int64_do ("10001", 10, 0, 10000, -1, ERANGE, -1); - test_nm_utils_ascii_str_to_int64_do ("FF", 16, 0, 10000, -1, 0, 255); - test_nm_utils_ascii_str_to_int64_do ("FF", 10, 0, 10000, -2, EINVAL, -2); - test_nm_utils_ascii_str_to_int64_do ("9223372036854775807", 10, 0, G_MAXINT64, -2, 0, G_MAXINT64); - test_nm_utils_ascii_str_to_int64_do ("7FFFFFFFFFFFFFFF", 16, 0, G_MAXINT64, -2, 0, G_MAXINT64); - test_nm_utils_ascii_str_to_int64_do ("9223372036854775808", 10, 0, G_MAXINT64, -2, ERANGE, -2); - test_nm_utils_ascii_str_to_int64_do ("-9223372036854775808", 10, G_MININT64, 0, -2, 0, G_MININT64); - test_nm_utils_ascii_str_to_int64_do ("-9223372036854775808", 10, G_MININT64+1, 0, -2, ERANGE, -2); - test_nm_utils_ascii_str_to_int64_do ("-9223372036854775809", 10, G_MININT64, 0, -2, ERANGE, -2); - test_nm_utils_ascii_str_to_int64_do ("1.0", 10, 1, 1, -1, EINVAL, -1); - test_nm_utils_ascii_str_to_int64_do ("1x0", 16, -10, 10, -100, EINVAL, -100); - test_nm_utils_ascii_str_to_int64_do ("0", 16, -10, 10, -100, 0, 0); - test_nm_utils_ascii_str_to_int64_do ("10001111", 2, -1000, 1000, -100000, 0, 0x8F); - test_nm_utils_ascii_str_to_int64_do ("-10001111", 2, -1000, 1000, -100000, 0, -0x8F); - test_nm_utils_ascii_str_to_int64_do ("1111111", 2, G_MININT64, G_MAXINT64, -1, 0, 0x7F); - test_nm_utils_ascii_str_to_int64_do ("111111111111111", 2, G_MININT64, G_MAXINT64, -1, 0, 0x7FFF); - test_nm_utils_ascii_str_to_int64_do ("11111111111111111111111111111111111111111111111", 2, G_MININT64, G_MAXINT64, -1, 0, 0x7FFFFFFFFFFF); - test_nm_utils_ascii_str_to_int64_do ("111111111111111111111111111111111111111111111111111111111111111", 2, G_MININT64, G_MAXINT64, -1, 0, 0x7FFFFFFFFFFFFFFF); - test_nm_utils_ascii_str_to_int64_do ("100000000000000000000000000000000000000000000000000000000000000", 2, G_MININT64, G_MAXINT64, -1, 0, 0x4000000000000000); - test_nm_utils_ascii_str_to_int64_do ("1000000000000000000000000000000000000000000000000000000000000000", 2, G_MININT64, G_MAXINT64, -1, ERANGE, -1); - test_nm_utils_ascii_str_to_int64_do ("-100000000000000000000000000000000000000000000000000000000000000", 2, G_MININT64, G_MAXINT64, -1, 0, -0x4000000000000000); - test_nm_utils_ascii_str_to_int64_do ("111111111111111111111111111111111111111111111111111111111111111", 2, G_MININT64, G_MAXINT64, -1, 0, 0x7FFFFFFFFFFFFFFF); - test_nm_utils_ascii_str_to_int64_do ("-100000000000000000000000000000000000000000000000000000000000000", 2, G_MININT64, G_MAXINT64, -1, 0, -0x4000000000000000); - test_nm_utils_ascii_str_to_int64_do ("0x70", 10, G_MININT64, G_MAXINT64, -1, EINVAL, -1); - test_nm_utils_ascii_str_to_int64_do ("4711", 0, G_MININT64, G_MAXINT64, -1, 0, 4711); - test_nm_utils_ascii_str_to_int64_do ("04711", 0, G_MININT64, G_MAXINT64, -1, 0, 04711); - test_nm_utils_ascii_str_to_int64_do ("0x4711", 0, G_MININT64, G_MAXINT64, -1, 0, 0x4711); - test_nm_utils_ascii_str_to_int64_do ("080", 0, G_MININT64, G_MAXINT64, -1, EINVAL, -1); - test_nm_utils_ascii_str_to_int64_do ("070", 0, G_MININT64, G_MAXINT64, -1, 0, 7*8); - test_nm_utils_ascii_str_to_int64_do ("0x70", 0, G_MININT64, G_MAXINT64, -1, 0, 0x70); -} - /* Reference implementation for nm_utils_ip6_address_clear_host_address. * Taken originally from set_address_masked(), src/rdisc/nm-lndp-rdisc.c **/ @@ -727,49 +598,134 @@ test_connection_sort_autoconnect_priority (void) /*******************************************/ -static void -__test_uuid (const char *expected_uuid, const char *str, gssize slen, char *uuid_test) +static const char *_test_match_spec_all[] = { + "e", + "em", + "em*", + "em\\", + "em\\*", + "em\\1", + "em\\11", + "em\\2", + "em1", + "em11", + "em2", + "=em*", + NULL +}; + +static gboolean +_test_match_spec_contains (const char **matches, const char *match) { - g_assert (uuid_test); - g_assert (nm_utils_is_uuid (uuid_test)); + guint i; - if (strcmp (uuid_test, expected_uuid)) { - g_error ("UUID test failed (1): text=%s, len=%lld, expected=%s, uuid_test=%s", - str, (long long) slen, expected_uuid, uuid_test); + for (i = 0; matches && matches[i]; i++) { + if (strcmp (match, matches[i]) == 0) + return TRUE; } - g_free (uuid_test); + return FALSE; +} + +static void +test_match_spec_ifname (const char *spec_str, const char **matches, const char **neg_matches) +{ + const char *m; + GSList *specs, *specs_reverse = NULL, *specs_resplit, *specs_i, *specs_j; + guint i; + gs_free char *specs_joined = NULL; - uuid_test = nm_utils_uuid_generate_from_string (str, slen, NM_UTILS_UUID_TYPE_VARIANT3, NM_UTILS_UUID_NS); + g_assert (spec_str); - g_assert (uuid_test); - g_assert (nm_utils_is_uuid (uuid_test)); + specs = nm_match_spec_split (spec_str); - if (strcmp (uuid_test, expected_uuid)) { - g_error ("UUID test failed (2): text=%s; len=%lld, expected=%s, uuid2=%s", - str, (long long) slen, expected_uuid, uuid_test); + /* assert that split(join(specs)) == specs */ + specs_joined = nm_match_spec_join (specs); + specs_resplit = nm_match_spec_split (specs_joined); + specs_i = specs; + specs_j = specs_resplit; + while (specs_i && specs_j && g_strcmp0 (specs_i->data, specs_j->data) == 0) { + specs_i = specs_i->next; + specs_j = specs_j->next; } - g_free (uuid_test); -} + g_assert (!specs_i); + g_assert (!specs_j); + g_slist_free_full (specs_resplit, g_free); + + /* also check the matches in the reverse order. They must yield the same result because + * matches are inclusive -- except "except:" which always wins. */ + specs_reverse = g_slist_reverse (g_slist_copy (specs)); -#define _test_uuid(expected_uuid, str, strlen, ...) __test_uuid (expected_uuid, str, strlen, nm_utils_uuid_generate_from_strings(__VA_ARGS__, NULL)) + for (i = 0; matches && matches[i]; i++) { + g_assert (nm_match_spec_interface_name (specs, matches[i]) == NM_MATCH_SPEC_MATCH); + g_assert (nm_match_spec_interface_name (specs_reverse, matches[i]) == NM_MATCH_SPEC_MATCH); + } + for (i = 0; neg_matches && neg_matches[i]; i++) { + g_assert (nm_match_spec_interface_name (specs, neg_matches[i]) == NM_MATCH_SPEC_NEG_MATCH); + g_assert (nm_match_spec_interface_name (specs_reverse, neg_matches[i]) == NM_MATCH_SPEC_NEG_MATCH); + } + for (i = 0; (m = _test_match_spec_all[i]); i++) { + if (_test_match_spec_contains (matches, m)) + continue; + if (_test_match_spec_contains (neg_matches, m)) + continue; + g_assert (nm_match_spec_interface_name (specs, m) == NM_MATCH_SPEC_NO_MATCH); + g_assert (nm_match_spec_interface_name (specs_reverse, m) == NM_MATCH_SPEC_NO_MATCH); + } + + g_slist_free (specs_reverse); + g_slist_free_full (specs, g_free); +} static void -test_nm_utils_uuid_generate_from_strings (void) +test_nm_match_spec_interface_name (void) { - _test_uuid ("b07c334a-399b-32de-8d50-58e4e08f98e3", "", 0, NULL); - _test_uuid ("b8a426cb-bcb5-30a3-bd8f-6786fea72df9", "\0", 1, ""); - _test_uuid ("12a4a982-7aae-39e1-951e-41aeb1250959", "a\0", 2, "a"); - _test_uuid ("69e22c7e-f89f-3a43-b239-1cb52ed8db69", "aa\0", 3, "aa"); - _test_uuid ("59829fd3-5ad5-3d90-a7b0-4911747e4088", "\0\0", 2, "", ""); - _test_uuid ("01ad0e06-6c50-3384-8d86-ddab81421425", "a\0\0", 3, "a", ""); - _test_uuid ("e1ed8647-9ed3-3ec8-8c6d-e8204524d71d", "aa\0\0", 4, "aa", ""); - _test_uuid ("fb1c7cd6-275c-3489-9382-83b900da8af0", "\0a\0", 3, "", "a"); - _test_uuid ("5d79494e-c4ba-31a6-80a2-d6016ccd7e17", "a\0a\0", 4, "a", "a"); - _test_uuid ("fd698d86-1b60-3ebe-855f-7aada9950a8d", "aa\0a\0", 5, "aa", "a"); - _test_uuid ("8c573b48-0f01-30ba-bb94-c5f59f4fe517", "\0aa\0", 4, "", "aa"); - _test_uuid ("2bdd3d46-eb83-3c53-a41b-a724d04b5544", "a\0aa\0", 5, "a", "aa"); - _test_uuid ("13d4b780-07c1-3ba7-b449-81c4844ef039", "aa\0aa\0", 6, "aa", "aa"); - _test_uuid ("dd265bf7-c05a-3037-9939-b9629858a477", "a\0b\0", 4, "a", "b"); +#define S(...) ((const char *[]) { __VA_ARGS__, NULL } ) + test_match_spec_ifname ("em1", + S ("em1"), + NULL); + test_match_spec_ifname ("em1,em2", + S ("em1", "em2"), + NULL); + test_match_spec_ifname ("em1,em2,interface-name:em2", + S ("em1", "em2"), + NULL); + test_match_spec_ifname ("interface-name:em1", + S ("em1"), + NULL); + test_match_spec_ifname ("interface-name:em*", + S ("em", "em*", "em\\", "em\\*", "em\\1", "em\\11", "em\\2", "em1", "em11", "em2", "em3"), + NULL); + test_match_spec_ifname ("interface-name:em\\*", + S ("em\\", "em\\*", "em\\1", "em\\11", "em\\2"), + NULL); + test_match_spec_ifname ("interface-name:~em\\*", + S ("em\\", "em\\*", "em\\1", "em\\11", "em\\2"), + NULL); + test_match_spec_ifname ("interface-name:=em*", + S ("em*"), + NULL); + test_match_spec_ifname ("interface-name:em*,except:interface-name:em1*", + S ("em", "em*", "em\\", "em\\*", "em\\1", "em\\11", "em\\2", "em2", "em3"), + S ("em1", "em11")); + test_match_spec_ifname ("interface-name:em*,except:interface-name:=em*", + S ("em", "em\\", "em\\*", "em\\1", "em\\11", "em\\2", "em1", "em11", "em2", "em3"), + S ("em*")); + test_match_spec_ifname ("aa,bb,cc\\,dd,e,,", + S ("aa", "bb", "cc,dd", "e"), + NULL); + test_match_spec_ifname ("aa;bb;cc\\;dd;e,;", + S ("aa", "bb", "cc;dd", "e"), + NULL); + test_match_spec_ifname ("interface-name:em\\;1,em\\,2,\\,,\\\\,,em\\\\x", + S ("em;1", "em,2", ",", "\\", "em\\x"), + NULL); + test_match_spec_ifname ("\\s\\s,\\sinterface-name:a,\\s,", + S (" ", " ", " interface-name:a"), + NULL); + test_match_spec_ifname (" aa ; bb ; cc\\;dd ;e , ; \t\\t , ", + S ("aa", "bb", "cc;dd", "e", "\t"), + NULL); +#undef S } /*******************************************/ @@ -781,7 +737,6 @@ main (int argc, char **argv) { nmtst_init_with_logging (&argc, &argv, NULL, "ALL"); - g_test_add_func ("/general/nm_utils_ascii_str_to_int64", test_nm_utils_ascii_str_to_int64); g_test_add_func ("/general/nm_utils_ip6_address_clear_host_address", test_nm_utils_ip6_address_clear_host_address); g_test_add_func ("/general/nm_utils_log_connection_diff", test_nm_utils_log_connection_diff); @@ -796,7 +751,7 @@ main (int argc, char **argv) g_test_add_func ("/general/connection-sort/autoconnect-priority", test_connection_sort_autoconnect_priority); - g_test_add_func ("/general/nm_utils_uuid_generate_from_strings", test_nm_utils_uuid_generate_from_strings); + g_test_add_func ("/general/nm_match_spec_interface_name", test_nm_match_spec_interface_name); return g_test_run (); } diff --git a/src/tests/test-ip4-config.c b/src/tests/test-ip4-config.c index 657f7e05..c8bdc75f 100644 --- a/src/tests/test-ip4-config.c +++ b/src/tests/test-ip4-config.c @@ -322,11 +322,11 @@ test_merge_subtract_mss_mtu (void) g_assert_cmpuint (nm_ip4_config_get_mtu (cfg1), ==, expected_mtu2); nm_ip4_config_merge (cfg1, cfg3); - /* ensure again the same MSS and MTU are in cfg1 */ - g_assert_cmpuint (nm_ip4_config_get_mss (cfg1), ==, expected_mss2); - g_assert_cmpuint (nm_ip4_config_get_mtu (cfg1), ==, expected_mtu2); + /* ensure again the MSS and MTU in cfg1 got overriden */ + g_assert_cmpuint (nm_ip4_config_get_mss (cfg1), ==, expected_mss3); + g_assert_cmpuint (nm_ip4_config_get_mtu (cfg1), ==, expected_mtu3); - nm_ip4_config_subtract (cfg1, cfg2); + nm_ip4_config_subtract (cfg1, cfg3); /* ensure MSS and MTU are zero in cfg1 */ g_assert_cmpuint (nm_ip4_config_get_mss (cfg1), ==, 0); g_assert_cmpuint (nm_ip4_config_get_mtu (cfg1), ==, 0); diff --git a/src/tests/test-route-manager.c b/src/tests/test-route-manager.c new file mode 100644 index 00000000..0164930d --- /dev/null +++ b/src/tests/test-route-manager.c @@ -0,0 +1,917 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2015 Red Hat, Inc. + * + */ + +#include "config.h" + +#include <glib.h> +#include <arpa/inet.h> +#include <linux/rtnetlink.h> + +#include "test-common.h" + +#include "nm-platform.h" +#include "nm-route-manager.h" +#include "nm-logging.h" + +#include "nm-test-utils.h" + +typedef struct { + int ifindex0, ifindex1; +} test_fixture; + +/*****************************************************************************/ + +static void +setup_dev0_ip4 (int ifindex, guint mss_of_first_route, guint32 metric_of_second_route) +{ + GArray *routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route)); + NMPlatformIP4Route route = { 0 }; + + route.ifindex = ifindex; + route.mss = 0; + + route.source = NM_IP_CONFIG_SOURCE_USER; + inet_pton (AF_INET, "6.6.6.0", &route.network); + route.plen = 24; + route.gateway = INADDR_ANY; + route.metric = 20; + route.mss = mss_of_first_route; + g_array_append_val (routes, route); + + route.source = NM_IP_CONFIG_SOURCE_USER; + inet_pton (AF_INET, "7.0.0.0", &route.network); + route.plen = 8; + inet_pton (AF_INET, "6.6.6.1", &route.gateway); + route.metric = metric_of_second_route; + route.mss = 0; + g_array_append_val (routes, route); + + nm_route_manager_ip4_route_sync (nm_route_manager_get (), ifindex, routes, TRUE, TRUE); + g_array_free (routes, TRUE); +} + +static void +setup_dev1_ip4 (int ifindex) +{ + GArray *routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route)); + NMPlatformIP4Route route = { 0 }; + + route.ifindex = ifindex; + route.mss = 0; + + /* Add some route outside of route manager. The route manager + * should get rid of it upon sync. */ + nm_platform_ip4_route_add (NM_PLATFORM_GET, + route.ifindex, + NM_IP_CONFIG_SOURCE_USER, + nmtst_inet4_from_string ("9.0.0.0"), + 8, + INADDR_ANY, + 0, + 10, + route.mss); + + route.source = NM_IP_CONFIG_SOURCE_USER; + inet_pton (AF_INET, "6.6.6.0", &route.network); + route.plen = 24; + route.gateway = INADDR_ANY; + route.metric = 20; + g_array_append_val (routes, route); + + route.source = NM_IP_CONFIG_SOURCE_USER; + inet_pton (AF_INET, "7.0.0.0", &route.network); + route.plen = 8; + route.gateway = INADDR_ANY; + route.metric = 22; + g_array_append_val (routes, route); + + route.source = NM_IP_CONFIG_SOURCE_USER; + inet_pton (AF_INET, "8.0.0.0", &route.network); + route.plen = 8; + inet_pton (AF_INET, "6.6.6.2", &route.gateway); + route.metric = 22; + g_array_append_val (routes, route); + + nm_route_manager_ip4_route_sync (nm_route_manager_get (), ifindex, routes, TRUE, TRUE); + g_array_free (routes, TRUE); +} + +static void +update_dev0_ip4 (int ifindex) +{ + GArray *routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route)); + NMPlatformIP4Route route = { 0 }; + + route.ifindex = ifindex; + route.mss = 0; + + route.source = NM_IP_CONFIG_SOURCE_USER; + inet_pton (AF_INET, "6.6.6.0", &route.network); + route.plen = 24; + route.gateway = INADDR_ANY; + route.metric = 20; + g_array_append_val (routes, route); + + route.source = NM_IP_CONFIG_SOURCE_USER; + inet_pton (AF_INET, "7.0.0.0", &route.network); + route.plen = 8; + route.gateway = INADDR_ANY; + route.metric = 21; + g_array_append_val (routes, route); + + nm_route_manager_ip4_route_sync (nm_route_manager_get (), ifindex, routes, TRUE, TRUE); + g_array_free (routes, TRUE); +} + + +static GArray * +ip4_routes (test_fixture *fixture) +{ + GArray *routes = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, + fixture->ifindex0, + NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); + GArray *routes1 = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, + fixture->ifindex1, + NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); + + g_array_append_vals (routes, routes1->data, routes1->len); + g_array_free (routes1, TRUE); + + return routes; +} + +static void +test_ip4 (test_fixture *fixture, gconstpointer user_data) +{ + GArray *routes; + + NMPlatformIP4Route state1[] = { + { + .source = NM_IP_CONFIG_SOURCE_USER, + .network = nmtst_inet4_from_string ("6.6.6.0"), + .plen = 24, + .ifindex = fixture->ifindex0, + .gateway = INADDR_ANY, + .metric = 20, + .mss = 1000, + .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), + }, + { + .source = NM_IP_CONFIG_SOURCE_USER, + .network = nmtst_inet4_from_string ("7.0.0.0"), + .plen = 8, + .ifindex = fixture->ifindex0, + .gateway = nmtst_inet4_from_string ("6.6.6.1"), + .metric = 21021, + .mss = 0, + .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_UNIVERSE), + }, + { + .source = NM_IP_CONFIG_SOURCE_USER, + .network = nmtst_inet4_from_string ("7.0.0.0"), + .plen = 8, + .ifindex = fixture->ifindex1, + .gateway = INADDR_ANY, + .metric = 22, + .mss = 0, + .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), + }, + { + .source = NM_IP_CONFIG_SOURCE_USER, + .network = nmtst_inet4_from_string ("6.6.6.0"), + .plen = 24, + .ifindex = fixture->ifindex1, + .gateway = INADDR_ANY, + .metric = 21, + .mss = 0, + .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), + }, + { + .source = NM_IP_CONFIG_SOURCE_USER, + .network = nmtst_inet4_from_string ("8.0.0.0"), + .plen = 8, + .ifindex = fixture->ifindex1, + .gateway = nmtst_inet4_from_string ("6.6.6.2"), + .metric = 22, + .mss = 0, + .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_UNIVERSE), + }, + }; + + NMPlatformIP4Route state2[] = { + { + .source = NM_IP_CONFIG_SOURCE_USER, + .network = nmtst_inet4_from_string ("6.6.6.0"), + .plen = 24, + .ifindex = fixture->ifindex0, + .gateway = INADDR_ANY, + .metric = 20, + .mss = 0, + .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), + }, + { + .source = NM_IP_CONFIG_SOURCE_USER, + .network = nmtst_inet4_from_string ("7.0.0.0"), + .plen = 8, + .ifindex = fixture->ifindex0, + .gateway = INADDR_ANY, + .metric = 21, + .mss = 0, + .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), + }, + { + .source = NM_IP_CONFIG_SOURCE_USER, + .network = nmtst_inet4_from_string ("7.0.0.0"), + .plen = 8, + .ifindex = fixture->ifindex1, + .gateway = INADDR_ANY, + .metric = 22, + .mss = 0, + .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), + }, + { + .source = NM_IP_CONFIG_SOURCE_USER, + .network = nmtst_inet4_from_string ("6.6.6.0"), + .plen = 24, + .ifindex = fixture->ifindex1, + .gateway = INADDR_ANY, + .metric = 21, + .mss = 0, + .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), + }, + { + .source = NM_IP_CONFIG_SOURCE_USER, + .network = nmtst_inet4_from_string ("8.0.0.0"), + .plen = 8, + .ifindex = fixture->ifindex1, + .gateway = nmtst_inet4_from_string ("6.6.6.2"), + .metric = 22, + .mss = 0, + .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_UNIVERSE), + }, + }; + + NMPlatformIP4Route state3[] = { + { + .source = NM_IP_CONFIG_SOURCE_USER, + .network = nmtst_inet4_from_string ("7.0.0.0"), + .plen = 8, + .ifindex = fixture->ifindex1, + .gateway = INADDR_ANY, + .metric = 22, + .mss = 0, + .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), + }, + { + .source = NM_IP_CONFIG_SOURCE_USER, + .network = nmtst_inet4_from_string ("6.6.6.0"), + .plen = 24, + .ifindex = fixture->ifindex1, + .gateway = INADDR_ANY, + .metric = 20, + .mss = 0, + .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), + }, + { + .source = NM_IP_CONFIG_SOURCE_USER, + .network = nmtst_inet4_from_string ("8.0.0.0"), + .plen = 8, + .ifindex = fixture->ifindex1, + .gateway = nmtst_inet4_from_string ("6.6.6.2"), + .metric = 22, + .mss = 0, + .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_UNIVERSE), + }, + { + .source = NM_IP_CONFIG_SOURCE_USER, + .network = nmtst_inet4_from_string ("6.6.6.0"), + .plen = 24, + .ifindex = fixture->ifindex1, + .gateway = INADDR_ANY, + /* this is a ghost entry because we synced ifindex0 and restore the route + * with metric 20 (above). But we don't remove the metric 21. */ + .metric = 21, + .mss = 0, + .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), + }, + }; + + setup_dev0_ip4 (fixture->ifindex0, 1000, 21021); + setup_dev1_ip4 (fixture->ifindex1); + g_test_assert_expected_messages (); + + /* - 6.6.6.0/24 on dev0 won over 6.6.6.0/24 on dev1 + * - 6.6.6.0/24 on dev1 has metric bumped. + * - 7.0.0.0/8 route, metric 21021 added + * - 7.0.0.0/8 route, metric 22 added + * - 8.0.0.0/8 could be added. */ + routes = ip4_routes (fixture); + g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state1)); + nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, state1, routes->len, TRUE); + g_array_free (routes, TRUE); + + setup_dev1_ip4 (fixture->ifindex1); + g_test_assert_expected_messages (); + + setup_dev0_ip4 (fixture->ifindex0, 0, 21); + + /* Ensure nothing changed. */ + routes = ip4_routes (fixture); + g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state1)); + state1[0].mss = 0; + state1[1].metric = 21; + nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, state1, routes->len, TRUE); + g_array_free (routes, TRUE); + + update_dev0_ip4 (fixture->ifindex0); + + /* minor changes in the routes. Quite similar to state1. */ + routes = ip4_routes (fixture); + g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state2)); + nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, state2, routes->len, TRUE); + g_array_free (routes, TRUE); + + nm_route_manager_route_flush (nm_route_manager_get (), fixture->ifindex0); + + /* 6.6.6.0/24 is now on dev1 + * 6.6.6.0/24 is also still on dev1 with bumped metric 21. + * 7.0.0.0/8 gone from dev0, still present on dev1 + * 8.0.0.0/8 is present on dev1 + * No dev0 routes left. */ + routes = ip4_routes (fixture); + g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state3)); + nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, state3, routes->len, TRUE); + g_array_free (routes, TRUE); + + nm_route_manager_route_flush (nm_route_manager_get (), fixture->ifindex1); + + /* No routes left. */ + routes = ip4_routes (fixture); + g_assert_cmpint (routes->len, ==, 0); + g_array_free (routes, TRUE); +} + +static void +setup_dev0_ip6 (int ifindex) +{ + GArray *routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP6Route)); + NMPlatformIP6Route *route; + + /* Add an address so that a route to the gateway below gets added. */ + nm_platform_ip6_address_add (NM_PLATFORM_GET, + ifindex, + *nmtst_inet6_from_string ("2001:db8:8086::2"), + in6addr_any, + 64, + 3600, + 3600, + 0); + + route = nmtst_platform_ip6_route_full ("2001:db8:8086::", + 48, + NULL, + ifindex, + NM_IP_CONFIG_SOURCE_USER, + 20, + 0); + g_array_append_val (routes, *route); + + route = nmtst_platform_ip6_route_full ("2001:db8:1337::", + 48, + NULL, + ifindex, + NM_IP_CONFIG_SOURCE_USER, + 0, + 0); + g_array_append_val (routes, *route); + + route = nmtst_platform_ip6_route_full ("2001:db8:abad:c0de::", + 64, + "2001:db8:8086::1", + ifindex, + NM_IP_CONFIG_SOURCE_USER, + 21, + 0); + g_array_append_val (routes, *route); + + nm_route_manager_ip6_route_sync (nm_route_manager_get (), ifindex, routes, TRUE, TRUE); + g_array_free (routes, TRUE); +} + +static void +setup_dev1_ip6 (int ifindex) +{ + GArray *routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP6Route)); + NMPlatformIP6Route *route; + + /* Add some route outside of route manager. The route manager + * should get rid of it upon sync. */ + nm_platform_ip6_route_add (NM_PLATFORM_GET, + ifindex, + NM_IP_CONFIG_SOURCE_USER, + *nmtst_inet6_from_string ("2001:db8:8088::"), + 48, + in6addr_any, + 10, + 0); + + route = nmtst_platform_ip6_route_full ("2001:db8:8086::", + 48, + NULL, + ifindex, + NM_IP_CONFIG_SOURCE_USER, + 20, + 0); + g_array_append_val (routes, *route); + + route = nmtst_platform_ip6_route_full ("2001:db8:1337::", + 48, + NULL, + ifindex, + NM_IP_CONFIG_SOURCE_USER, + 1024, + 0); + g_array_append_val (routes, *route); + + route = nmtst_platform_ip6_route_full ("2001:db8:d34d::", + 64, + "2001:db8:8086::2", + ifindex, + NM_IP_CONFIG_SOURCE_USER, + 20, + 0); + g_array_append_val (routes, *route); + + route = nmtst_platform_ip6_route_full ("2001:db8:abad:c0de::", + 64, + NULL, + ifindex, + NM_IP_CONFIG_SOURCE_USER, + 22, + 0); + g_array_append_val (routes, *route); + + nm_route_manager_ip6_route_sync (nm_route_manager_get (), ifindex, routes, TRUE, TRUE); + g_array_free (routes, TRUE); +} + +static void +update_dev0_ip6 (int ifindex) +{ + GArray *routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP6Route)); + NMPlatformIP6Route *route; + + /* Add an address so that a route to the gateway below gets added. */ + nm_platform_ip6_address_add (NM_PLATFORM_GET, + ifindex, + *nmtst_inet6_from_string ("2001:db8:8086::2"), + in6addr_any, + 64, + 3600, + 3600, + 0); + + route = nmtst_platform_ip6_route_full ("2001:db8:8086::", + 48, + NULL, + ifindex, + NM_IP_CONFIG_SOURCE_USER, + 20, + 0); + g_array_append_val (routes, *route); + + route = nmtst_platform_ip6_route_full ("2001:db8:1337::", + 48, + NULL, + ifindex, + NM_IP_CONFIG_SOURCE_USER, + 0, + 0); + g_array_append_val (routes, *route); + + route = nmtst_platform_ip6_route_full ("2001:db8:abad:c0de::", + 64, + NULL, + ifindex, + NM_IP_CONFIG_SOURCE_USER, + 21, + 0); + g_array_append_val (routes, *route); + + nm_route_manager_ip6_route_sync (nm_route_manager_get (), ifindex, routes, TRUE, TRUE); + g_array_free (routes, TRUE); +} + +static GArray * +ip6_routes (test_fixture *fixture) +{ + GArray *routes = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, + fixture->ifindex0, + NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); + GArray *routes1 = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, + fixture->ifindex1, + NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); + + g_array_append_vals (routes, routes1->data, routes1->len); + g_array_free (routes1, TRUE); + + return routes; +} + +static void +test_ip6 (test_fixture *fixture, gconstpointer user_data) +{ + GArray *routes; + + NMPlatformIP6Route state1[] = { + { + .source = NM_IP_CONFIG_SOURCE_USER, + .network = *nmtst_inet6_from_string ("2001:db8:8086::"), + .plen = 48, + .ifindex = fixture->ifindex0, + .gateway = in6addr_any, + .metric = 20, + .mss = 0, + }, + { + .source = NM_IP_CONFIG_SOURCE_USER, + .network = *nmtst_inet6_from_string ("2001:db8:1337::"), + .plen = 48, + .ifindex = fixture->ifindex0, + .gateway = in6addr_any, + .metric = 1024, + .mss = 0, + }, + { + .source = NM_IP_CONFIG_SOURCE_USER, + .network = *nmtst_inet6_from_string ("2001:db8:abad:c0de::"), + .plen = 64, + .ifindex = fixture->ifindex0, + .gateway = *nmtst_inet6_from_string ("2001:db8:8086::1"), + .metric = 21, + .mss = 0, + }, + { + .source = NM_IP_CONFIG_SOURCE_USER, + .network = *nmtst_inet6_from_string ("2001:db8:abad:c0de::"), + .plen = 64, + .ifindex = fixture->ifindex1, + .gateway = in6addr_any, + .metric = 22, + .mss = 0, + }, + { + .source = NM_IP_CONFIG_SOURCE_USER, + .network = *nmtst_inet6_from_string ("2001:db8:1337::"), + .plen = 48, + .ifindex = fixture->ifindex1, + .gateway = in6addr_any, + .metric = 1025, + .mss = 0, + }, + { + .source = NM_IP_CONFIG_SOURCE_USER, + .network = *nmtst_inet6_from_string ("2001:db8:8086::"), + .plen = 48, + .ifindex = fixture->ifindex1, + .gateway = in6addr_any, + .metric = 21, + .mss = 0, + }, + { + .source = NM_IP_CONFIG_SOURCE_USER, + .network = *nmtst_inet6_from_string ("2001:db8:d34d::"), + .plen = 64, + .ifindex = fixture->ifindex1, + .gateway = *nmtst_inet6_from_string ("2001:db8:8086::2"), + .metric = 20, + .mss = 0, + }, + }; + + NMPlatformIP6Route state2[] = { + { + .source = NM_IP_CONFIG_SOURCE_USER, + .network = *nmtst_inet6_from_string ("2001:db8:8086::"), + .plen = 48, + .ifindex = fixture->ifindex0, + .gateway = in6addr_any, + .metric = 20, + .mss = 0, + }, + { + .source = NM_IP_CONFIG_SOURCE_USER, + .network = *nmtst_inet6_from_string ("2001:db8:1337::"), + .plen = 48, + .ifindex = fixture->ifindex0, + .gateway = in6addr_any, + .metric = 1024, + .mss = 0, + }, + { + .source = NM_IP_CONFIG_SOURCE_USER, + .network = *nmtst_inet6_from_string ("2001:db8:abad:c0de::"), + .plen = 64, + .ifindex = fixture->ifindex0, + .gateway = in6addr_any, + .metric = 21, + .mss = 0, + }, + { + .source = NM_IP_CONFIG_SOURCE_USER, + .network = *nmtst_inet6_from_string ("2001:db8:abad:c0de::"), + .plen = 64, + .ifindex = fixture->ifindex1, + .gateway = in6addr_any, + .metric = 22, + .mss = 0, + }, + { + .source = NM_IP_CONFIG_SOURCE_USER, + .network = *nmtst_inet6_from_string ("2001:db8:1337::"), + .plen = 48, + .ifindex = fixture->ifindex1, + .gateway = in6addr_any, + .metric = 1025, + .mss = 0, + }, + { + .source = NM_IP_CONFIG_SOURCE_USER, + .network = *nmtst_inet6_from_string ("2001:db8:8086::"), + .plen = 48, + .ifindex = fixture->ifindex1, + .gateway = in6addr_any, + .metric = 21, + .mss = 0, + }, + { + .source = NM_IP_CONFIG_SOURCE_USER, + .network = *nmtst_inet6_from_string ("2001:db8:d34d::"), + .plen = 64, + .ifindex = fixture->ifindex1, + .gateway = *nmtst_inet6_from_string ("2001:db8:8086::2"), + .metric = 20, + .mss = 0, + }, + }; + + NMPlatformIP6Route state3[] = { + { + .source = NM_IP_CONFIG_SOURCE_USER, + .network = *nmtst_inet6_from_string ("2001:db8:abad:c0de::"), + .plen = 64, + .ifindex = fixture->ifindex1, + .gateway = in6addr_any, + .metric = 22, + .mss = 0, + }, + { + .source = NM_IP_CONFIG_SOURCE_USER, + .network = *nmtst_inet6_from_string ("2001:db8:8086::"), + .plen = 48, + .ifindex = fixture->ifindex1, + .gateway = in6addr_any, + .metric = 20, + .mss = 0, + }, + { + .source = NM_IP_CONFIG_SOURCE_USER, + .network = *nmtst_inet6_from_string ("2001:db8:1337::"), + .plen = 48, + .ifindex = fixture->ifindex1, + .gateway = in6addr_any, + .metric = 1024, + .mss = 0, + }, + { + .source = NM_IP_CONFIG_SOURCE_USER, + .network = *nmtst_inet6_from_string ("2001:db8:1337::"), + .plen = 48, + .ifindex = fixture->ifindex1, + .gateway = in6addr_any, + .metric = 1025, + .mss = 0, + }, + { + .source = NM_IP_CONFIG_SOURCE_USER, + .network = *nmtst_inet6_from_string ("2001:db8:8086::"), + .plen = 48, + .ifindex = fixture->ifindex1, + .gateway = in6addr_any, + .metric = 21, + .mss = 0, + }, + { + .source = NM_IP_CONFIG_SOURCE_USER, + .network = *nmtst_inet6_from_string ("2001:db8:d34d::"), + .plen = 64, + .ifindex = fixture->ifindex1, + .gateway = *nmtst_inet6_from_string ("2001:db8:8086::2"), + .metric = 20, + .mss = 0, + }, + }; + + setup_dev0_ip6 (fixture->ifindex0); + setup_dev1_ip6 (fixture->ifindex1); + g_test_assert_expected_messages (); + + /* 2001:db8:8086::/48 on dev0 won over 2001:db8:8086::/48 on dev1 + * 2001:db8:d34d::/64 on dev1 could not be added + * 2001:db8:1337::/48 on dev0 won over 2001:db8:1337::/48 on dev1 and has metric 1024 + * 2001:db8:abad:c0de::/64 routes did not clash */ + routes = ip6_routes (fixture); + g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state1)); + nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, state1, routes->len, TRUE); + g_array_free (routes, TRUE); + + + setup_dev1_ip6 (fixture->ifindex1); + g_test_assert_expected_messages (); + setup_dev0_ip6 (fixture->ifindex0); + + /* Ensure nothing changed. */ + routes = ip6_routes (fixture); + g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state1)); + nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, state1, routes->len, TRUE); + g_array_free (routes, TRUE); + + update_dev0_ip6 (fixture->ifindex0); + + /* 2001:db8:abad:c0de::/64 on dev0 was updated for gateway removal*/ + routes = ip6_routes (fixture); + g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state2)); + nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, state2, routes->len, TRUE); + g_array_free (routes, TRUE); + + nm_route_manager_route_flush (nm_route_manager_get (), fixture->ifindex0); + + /* 2001:db8:abad:c0de::/64 on dev1 is still there, went away from dev0 + * 2001:db8:8086::/48 is now on dev1 + * 2001:db8:1337::/48 is now on dev1, metric of 1024 still applies + * 2001:db8:d34d::/64 is present now that 2001:db8:8086::/48 is on dev1 + * No dev0 routes left. */ + routes = ip6_routes (fixture); + g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state3)); + nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, state3, routes->len, TRUE); + g_array_free (routes, TRUE); + + nm_route_manager_route_flush (nm_route_manager_get (), fixture->ifindex1); + + /* No routes left. */ + routes = ip6_routes (fixture); + g_assert_cmpint (routes->len, ==, 0); + g_array_free (routes, TRUE); +} + +/*****************************************************************************/ + +static void +_assert_route_check (const NMPlatformVTableRoute *vtable, gboolean has, const NMPlatformIPXRoute *route) +{ + const NMPlatformIPXRoute *r; + + g_assert (route); + + if (vtable->is_ip4) + r = (const NMPlatformIPXRoute *) nm_platform_ip4_route_get (NM_PLATFORM_GET, route->rx.ifindex, route->r4.network, route->rx.plen, route->rx.metric); + else + r = (const NMPlatformIPXRoute *) nm_platform_ip6_route_get (NM_PLATFORM_GET, route->rx.ifindex, route->r6.network, route->rx.plen, route->rx.metric); + + if (!has) { + g_assert (!r); + } else { + if (!r || vtable->route_cmp (route, r) != 0) + g_error ("Invalid route. Expect %s, has %s", + nmtst_static_1024_01 (vtable->route_to_string (route)), + nmtst_static_1024_02 (vtable->route_to_string (r))); + g_assert (r); + } +} + +static void +test_ip4_full_sync (test_fixture *fixture, gconstpointer user_data) +{ + const NMPlatformVTableRoute *vtable = &nm_platform_vtable_route_v4; + gs_unref_array GArray *routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route)); + NMPlatformIP4Route r01, r02, r03; + + nm_log_dbg (LOGD_CORE, "TEST start test_ip4_full_sync(): start"); + + r01 = *nmtst_platform_ip4_route_full ("12.3.4.0", 24, NULL, + fixture->ifindex0, NM_IP_CONFIG_SOURCE_USER, + 100, 0, RT_SCOPE_LINK, NULL); + r02 = *nmtst_platform_ip4_route_full ("13.4.5.6", 32, "12.3.4.1", + fixture->ifindex0, NM_IP_CONFIG_SOURCE_USER, + 100, 0, RT_SCOPE_UNIVERSE, NULL); + r03 = *nmtst_platform_ip4_route_full ("14.5.6.7", 32, "12.3.4.1", + fixture->ifindex0, NM_IP_CONFIG_SOURCE_USER, + 110, 0, RT_SCOPE_UNIVERSE, NULL); + g_array_set_size (routes, 2); + g_array_index (routes, NMPlatformIP4Route, 0) = r01; + g_array_index (routes, NMPlatformIP4Route, 1) = r02; + nm_route_manager_ip4_route_sync (nm_route_manager_get (), fixture->ifindex0, routes, TRUE, TRUE); + + _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r01); + _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r02); + _assert_route_check (vtable, FALSE, (const NMPlatformIPXRoute *) &r03); + + vtable->route_add (NM_PLATFORM_GET, 0, (const NMPlatformIPXRoute *) &r03, -1); + + _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r01); + _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r02); + _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r03); + + nm_route_manager_ip4_route_sync (nm_route_manager_get (), fixture->ifindex0, routes, TRUE, FALSE); + + _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r01); + _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r02); + _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r03); + + g_array_set_size (routes, 1); + + nm_route_manager_ip4_route_sync (nm_route_manager_get (), fixture->ifindex0, routes, TRUE, FALSE); + + _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r01); + _assert_route_check (vtable, FALSE, (const NMPlatformIPXRoute *) &r02); + _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r03); + + nm_route_manager_ip4_route_sync (nm_route_manager_get (), fixture->ifindex0, routes, TRUE, TRUE); + + _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r01); + _assert_route_check (vtable, FALSE, (const NMPlatformIPXRoute *) &r02); + _assert_route_check (vtable, FALSE, (const NMPlatformIPXRoute *) &r03); + + nm_log_dbg (LOGD_CORE, "TEST test_ip4_full_sync(): done"); +} + +/*****************************************************************************/ + +static void +fixture_setup (test_fixture *fixture, gconstpointer user_data) +{ + SignalData *link_added; + + link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, + NM_PLATFORM_SIGNAL_ADDED, + link_callback, + "nm-test-device0"); + nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, "nm-test-device0")); + g_assert (nm_platform_dummy_add (NM_PLATFORM_GET, "nm-test-device0", NULL) == NM_PLATFORM_ERROR_SUCCESS); + accept_signal (link_added); + free_signal (link_added); + fixture->ifindex0 = nm_platform_link_get_ifindex (NM_PLATFORM_GET, "nm-test-device0"); + g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, fixture->ifindex0, NULL)); + + link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, + NM_PLATFORM_SIGNAL_ADDED, + link_callback, + "nm-test-device1"); + nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, "nm-test-device1")); + g_assert (nm_platform_dummy_add (NM_PLATFORM_GET, "nm-test-device1", NULL) == NM_PLATFORM_ERROR_SUCCESS); + accept_signal (link_added); + free_signal (link_added); + fixture->ifindex1 = nm_platform_link_get_ifindex (NM_PLATFORM_GET, "nm-test-device1"); + g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, fixture->ifindex1, NULL)); +} + +static void +fixture_teardown (test_fixture *fixture, gconstpointer user_data) +{ + nm_platform_link_delete (NM_PLATFORM_GET, fixture->ifindex0); + nm_platform_link_delete (NM_PLATFORM_GET, fixture->ifindex1); +} + +/*****************************************************************************/ + +void +init_tests (int *argc, char ***argv) +{ + nmtst_init_assert_logging (argc, argv, "WARN", "ALL"); +} + +void +setup_tests (void) +{ + g_test_add ("/route-manager/ip4", test_fixture, NULL, fixture_setup, test_ip4, fixture_teardown); + g_test_add ("/route-manager/ip6", test_fixture, NULL, fixture_setup, test_ip6, fixture_teardown); + + g_test_add ("/route-manager/ip4-full-sync", test_fixture, NULL, fixture_setup, test_ip4_full_sync, fixture_teardown); +} diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c index cb07c7f9..795a8187 100644 --- a/src/vpn-manager/nm-vpn-connection.c +++ b/src/vpn-manager/nm-vpn-connection.c @@ -44,6 +44,8 @@ #include "nm-agent-manager.h" #include "nm-core-internal.h" #include "nm-default-route-manager.h" +#include "nm-route-manager.h" +#include "nm-firewall-manager.h" #include "nm-vpn-connection-glue.h" @@ -92,6 +94,9 @@ typedef struct { NMVpnServiceState service_state; + /* Firewall */ + NMFirewallPendingCall fw_call; + DBusGProxy *proxy; GHashTable *connect_hash; guint connect_timeout; @@ -227,19 +232,38 @@ call_plugin_disconnect (NMVpnConnection *self) } static void +fw_call_cleanup (NMVpnConnection *connection) +{ + NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + + if (priv->fw_call) { + nm_firewall_manager_cancel_call (nm_firewall_manager_get (), priv->fw_call); + priv->fw_call = NULL; + } +} + +static void vpn_cleanup (NMVpnConnection *connection, NMDevice *parent_dev) { NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); if (priv->ip_ifindex) { - nm_platform_link_set_down (priv->ip_ifindex); - nm_platform_route_flush (priv->ip_ifindex); - nm_platform_address_flush (priv->ip_ifindex); + nm_platform_link_set_down (NM_PLATFORM_GET, priv->ip_ifindex); + nm_route_manager_route_flush (nm_route_manager_get (), priv->ip_ifindex); + nm_platform_address_flush (NM_PLATFORM_GET, priv->ip_ifindex); } nm_device_set_vpn4_config (parent_dev, NULL); nm_device_set_vpn6_config (parent_dev, NULL); + /* Remove zone from firewall */ + if (priv->ip_iface) + nm_firewall_manager_remove_from_zone (nm_firewall_manager_get (), + priv->ip_iface, + NULL); + /* Cancel pending firewall call */ + fw_call_cleanup (connection); + g_free (priv->banner); priv->banner = NULL; @@ -252,6 +276,7 @@ vpn_cleanup (NMVpnConnection *connection, NMDevice *parent_dev) */ if (priv->connection) nm_connection_clear_secrets (priv->connection); + } static void @@ -903,10 +928,17 @@ apply_parent_device_config (NMVpnConnection *connection) * be done on the parent interface instead. */ - if (vpn4_parent_config) + /* Also clear the gateway. We don't configure the gateway as part of the + * vpn-config. Instead we tell NMDefaultRouteManager directly about the + * default route. */ + if (vpn4_parent_config) { nm_ip4_config_merge (vpn4_parent_config, priv->ip4_config); - if (vpn6_parent_config) + nm_ip4_config_set_gateway (vpn4_parent_config, 0); + } + if (vpn6_parent_config) { nm_ip6_config_merge (vpn6_parent_config, priv->ip6_config); + nm_ip6_config_set_gateway (vpn6_parent_config, NULL); + } } if (vpn4_parent_config) { @@ -933,16 +965,19 @@ nm_vpn_connection_apply_config (NMVpnConnection *connection) NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); if (priv->ip_ifindex > 0) { - nm_platform_link_set_up (priv->ip_ifindex); + nm_platform_link_set_up (NM_PLATFORM_GET, priv->ip_ifindex, NULL); if (priv->ip4_config) { if (!nm_ip4_config_commit (priv->ip4_config, priv->ip_ifindex, + TRUE, nm_vpn_connection_get_ip4_route_metric (connection))) return FALSE; } if (priv->ip6_config) { - if (!nm_ip6_config_commit (priv->ip6_config, priv->ip_ifindex)) + if (!nm_ip6_config_commit (priv->ip6_config, + priv->ip_ifindex, + TRUE)) return FALSE; } } @@ -959,10 +994,47 @@ nm_vpn_connection_apply_config (NMVpnConnection *connection) } static void +_cleanup_failed_config (NMVpnConnection *connection) +{ + NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + + g_clear_object (&priv->ip4_config); + g_clear_object (&priv->ip6_config); + + nm_log_warn (LOGD_VPN, "VPN connection '%s' did not receive valid IP config information.", + nm_connection_get_id (priv->connection)); + _set_vpn_state (connection, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID, FALSE); +} + +static void +fw_change_zone_cb (GError *error, gpointer user_data) +{ + NMVpnConnection *connection = NM_VPN_CONNECTION (user_data); + NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + priv->fw_call = NULL; + + if (error) { + nm_log_warn (LOGD_VPN, "VPN connection '%s': setting firewall zone failed: '%s'", + nm_connection_get_id (priv->connection), error->message); + // FIXME: fail the activation? + } + + if (!nm_vpn_connection_apply_config (connection)) + _cleanup_failed_config (connection); +} + +static void nm_vpn_connection_config_maybe_complete (NMVpnConnection *connection, gboolean success) { NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + NMConnection *base_con; + NMSettingConnection *s_con; + const char *zone; if (priv->vpn_state < STATE_IP_CONFIG_GET || priv->vpn_state > STATE_ACTIVATED) return; @@ -983,16 +1055,29 @@ nm_vpn_connection_config_maybe_complete (NMVpnConnection *connection, if (success) { print_vpn_config (connection); - if (nm_vpn_connection_apply_config (connection)) + /* Add the tunnel interface to the specified firewall zone */ + if (priv->ip_iface) { + base_con = nm_vpn_connection_get_connection (connection); + g_assert (base_con); + s_con = nm_connection_get_setting_connection (base_con); + zone = nm_setting_connection_get_zone (s_con); + + nm_log_dbg (LOGD_VPN, "VPN connection '%s': setting firewall zone '%s' for '%s'", + nm_connection_get_id (base_con), zone ? zone : "default", priv->ip_iface); + fw_call_cleanup (connection); + priv->fw_call = nm_firewall_manager_add_or_change_zone (nm_firewall_manager_get (), + priv->ip_iface, + zone, + FALSE, + fw_change_zone_cb, + connection); return; + } else + if (nm_vpn_connection_apply_config (connection)) + return; } - g_clear_object (&priv->ip4_config); - g_clear_object (&priv->ip6_config); - - nm_log_warn (LOGD_VPN, "VPN connection '%s' did not receive valid IP config information.", - nm_connection_get_id (priv->connection)); - _set_vpn_state (connection, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID, FALSE); + _cleanup_failed_config (connection); } #define LOG_INVALID_ARG(property) \ @@ -1027,7 +1112,7 @@ process_generic_config (NMVpnConnection *connection, if (priv->ip_iface) { /* Grab the interface index for address/routing operations */ - priv->ip_ifindex = nm_platform_link_get_ifindex (priv->ip_iface); + priv->ip_ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, priv->ip_iface); if (!priv->ip_ifindex) { nm_log_err (LOGD_VPN, "(%s): failed to look up VPN interface index", priv->ip_iface); nm_vpn_connection_config_maybe_complete (connection, FALSE); @@ -1193,13 +1278,13 @@ nm_vpn_connection_ip4_config_get (DBusGProxy *proxy, memset (&address, 0, sizeof (address)); address.plen = 24; - if (priv->ip4_external_gw) - nm_ip4_config_set_gateway (config, priv->ip4_external_gw); /* Internal address of the VPN subnet's gateway */ val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_INT_GATEWAY); - if (val) + if (val) { priv->ip4_internal_gw = g_value_get_uint (val); + nm_ip4_config_set_gateway (config, priv->ip4_internal_gw); + } val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_ADDRESS); if (val) @@ -1338,8 +1423,6 @@ nm_vpn_connection_ip6_config_get (DBusGProxy *proxy, memset (&address, 0, sizeof (address)); address.plen = 128; - if (priv->ip6_external_gw) - nm_ip6_config_set_gateway (config, priv->ip6_external_gw); /* Internal address of the VPN subnet's gateway */ g_clear_pointer (&priv->ip6_internal_gw, g_free); @@ -1347,8 +1430,10 @@ nm_vpn_connection_ip6_config_get (DBusGProxy *proxy, if (val) { GByteArray *ba = g_value_get_boxed (val); - if (ba->len == sizeof (struct in6_addr)) + if (ba->len == sizeof (struct in6_addr)) { priv->ip6_internal_gw = g_memdup (ba->data, ba->len); + nm_ip6_config_set_gateway (config, priv->ip6_internal_gw); + } } val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP6_CONFIG_ADDRESS); @@ -1894,7 +1979,7 @@ get_secrets_cb (NMSettingsConnection *connection, priv->secrets_id = 0; - if (error) { + if (error && priv->secrets_idx >= SECRETS_REQ_NEW) { nm_log_err (LOGD_VPN, "Failed to request VPN secrets #%d: (%d) %s", priv->secrets_idx + 1, error->code, error->message); _set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS, FALSE); @@ -2101,6 +2186,8 @@ dispose (GObject *object) g_clear_object (&priv->proxy); g_clear_object (&priv->connection); + fw_call_cleanup (NM_VPN_CONNECTION (object)); + G_OBJECT_CLASS (nm_vpn_connection_parent_class)->dispose (object); } diff --git a/src/vpn-manager/nm-vpn-service.c b/src/vpn-manager/nm-vpn-service.c index b46d13ec..77220dd2 100644 --- a/src/vpn-manager/nm-vpn-service.c +++ b/src/vpn-manager/nm-vpn-service.c @@ -31,7 +31,6 @@ #include "nm-vpn-service.h" #include "nm-dbus-manager.h" #include "nm-logging.h" -#include "nm-posix-signals.h" #include "nm-vpn-manager.h" #include "nm-glib-compat.h" @@ -167,20 +166,6 @@ nm_vpn_service_stop_connections (NMVpnService *service, g_clear_pointer (&priv->pending, g_slist_free); } -static void -_daemon_setup (gpointer user_data G_GNUC_UNUSED) -{ - /* We are in the child process at this point */ - pid_t pid = getpid (); - setpgid (pid, pid); - - /* - * We blocked signals in main(). We need to restore original signal - * mask for VPN service here so that it can receive signals. - */ - nm_unblock_posix_signals (NULL); -} - static gboolean _daemon_exec_timeout (gpointer data) { @@ -207,7 +192,7 @@ nm_vpn_service_daemon_exec (NMVpnService *service, GError **error) vpn_argv[0] = priv->program; vpn_argv[1] = NULL; - success = g_spawn_async (NULL, vpn_argv, NULL, 0, _daemon_setup, NULL, &pid, &spawn_error); + success = g_spawn_async (NULL, vpn_argv, NULL, 0, nm_utils_setpgid, NULL, &pid, &spawn_error); if (success) { nm_log_info (LOGD_VPN, "VPN service '%s' started (%s), PID %ld", priv->name, priv->dbus_service, (long int) pid); |