diff --git a/configure.ac b/configure.ac index 8634203e2d..848a1970b7 100644 --- a/configure.ac +++ b/configure.ac @@ -811,6 +811,7 @@ src/rdisc/tests/Makefile src/devices/adsl/Makefile src/devices/wimax/Makefile src/devices/bluetooth/Makefile +src/devices/team/Makefile src/devices/wifi/Makefile src/devices/wifi/tests/Makefile src/devices/wwan/Makefile diff --git a/contrib/fedora/rpm/NetworkManager.spec b/contrib/fedora/rpm/NetworkManager.spec index 99cb9099fc..f4b22e8c92 100644 --- a/contrib/fedora/rpm/NetworkManager.spec +++ b/contrib/fedora/rpm/NetworkManager.spec @@ -36,6 +36,7 @@ %global with_adsl 1 %global with_bluetooth 1 +%global with_team 1 %global with_wifi 1 %global with_wimax 0 %global with_wwan 1 @@ -59,8 +60,8 @@ %global with_wwan 0 %endif -%if 0%{?rhel} || (0%{?fedora} > 19) -%global with_teamctl 1 +%if (0%{?fedora} && 0%{?fedora} <= 19) +%global with_team 0 %endif @@ -155,9 +156,6 @@ BuildRequires: ModemManager-glib-devel >= 1.0 %if 0%{?with_nmtui} BuildRequires: newt-devel %endif -%if 0%{?with_teamctl} -BuildRequires: teamd-devel -%endif %description @@ -200,6 +198,19 @@ This package contains NetworkManager support for Bluetooth devices. %endif +%if 0%{?with_team} +%package team +Summary: Team device plugin for NetworkManager +Group: System Environment/Base +BuildRequires: teamd-devel +Requires: %{name}%{?_isa} = %{epoch}:%{version}-%{release} +Obsoletes: NetworkManager < %{obsoletes_nmver} + +%description team +This package contains NetworkManager support for team devices. +%endif + + %if 0%{?with_wifi} %package wifi Summary: Wifi plugin for NetworkManager @@ -347,7 +358,7 @@ by nm-connection-editor and nm-applet in a non-graphical environment. %else --with-wext=no \ %endif -%if 0%{?with_teamctl} +%if 0%{?with_team} --enable-teamdctl=yes \ %else --enable-teamdctl=no \ @@ -489,6 +500,12 @@ fi %exclude %{_libdir}/%{name}/libnm-device-plugin-bluetooth.so %endif +%if 0%{?with_team} +%files team +%defattr(-,root,root,0755) +%{_libdir}/%{name}/libnm-device-plugin-team.so +%endif + %if 0%{?with_wifi} %files wifi %defattr(-,root,root,0755) diff --git a/po/POTFILES.in b/po/POTFILES.in index 6368a9f59f..673a9e6244 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -53,8 +53,8 @@ src/devices/nm-device-bond.c src/devices/nm-device-bridge.c src/devices/nm-device-ethernet.c src/devices/nm-device-infiniband.c -src/devices/nm-device-team.c src/devices/nm-device-vlan.c +src/devices/team/nm-device-team.c src/devices/wifi/nm-device-olpc-mesh.c src/devices/wwan/nm-modem-broadband.c src/devices/wwan/nm-modem-old.c diff --git a/src/Makefile.am b/src/Makefile.am index c454127176..244574af44 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -5,7 +5,7 @@ include $(GLIB_MAKEFILE) SUBDIRS = \ . \ devices/adsl \ - devices/wifi \ + devices/wifi \ dhcp-manager \ ppp-manager \ settings/plugins @@ -20,6 +20,10 @@ if WITH_WIMAX SUBDIRS += devices/wimax endif +if WITH_TEAMDCTL +SUBDIRS += devices/team +endif + if ENABLE_TESTS SUBDIRS += \ config/tests \ @@ -84,8 +88,6 @@ nm_sources = \ devices/nm-device-macvlan.c \ devices/nm-device-macvlan.h \ devices/nm-device-private.h \ - devices/nm-device-team.c \ - devices/nm-device-team.h \ devices/nm-device-tun.c \ devices/nm-device-tun.h \ devices/nm-device-veth.c \ @@ -296,7 +298,6 @@ glue_sources = \ nm-device-gre-glue.h \ nm-device-infiniband-glue.h \ nm-device-macvlan-glue.h \ - nm-device-team-glue.h \ nm-device-tun-glue.h \ nm-device-veth-glue.h \ nm-device-vlan-glue.h \ @@ -351,10 +352,6 @@ if SESSION_TRACKING_CK AM_CPPFLAGS += -DCKDB_PATH=\"${CKDB_PATH}\" endif -if WITH_TEAMDCTL -AM_CPPFLAGS += ${LIBTEAMDCTL_CFLAGS} -endif - libNetworkManager_la_SOURCES = \ $(nm_sources) \ $(glue_sources) @@ -375,10 +372,6 @@ if WITH_CONCHECK libNetworkManager_la_LIBADD += $(LIBSOUP_LIBS) endif -if WITH_TEAMDCTL -libNetworkManager_la_LIBADD += $(LIBTEAMDCTL_LIBS) -endif - NetworkManager_LDFLAGS = -rdynamic dbusservicedir = $(DBUS_SYS_DIR) diff --git a/src/devices/nm-device-bond.c b/src/devices/nm-device-bond.c index 917d457141..cd5e95f908 100644 --- a/src/devices/nm-device-bond.c +++ b/src/devices/nm-device-bond.c @@ -243,6 +243,19 @@ update_connection (NMDevice *device, NMConnection *connection) } } +static gboolean +master_update_slave_connection (NMDevice *self, + NMDevice *slave, + NMConnection *connection, + GError **error) +{ + g_object_set (nm_connection_get_setting_connection (connection), + NM_SETTING_CONNECTION_MASTER, nm_device_get_iface (self), + NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BOND_SETTING_NAME, + NULL); + return TRUE; +} + static void set_arp_targets (NMDevice *device, const char *value, @@ -590,6 +603,7 @@ nm_device_bond_class_init (NMDeviceBondClass *klass) parent_class->complete_connection = complete_connection; parent_class->update_connection = update_connection; + parent_class->master_update_slave_connection = master_update_slave_connection; parent_class->act_stage1_prepare = act_stage1_prepare; parent_class->enslave_slave = enslave_slave; diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c index cbf6ccf7d8..b00446af0c 100644 --- a/src/devices/nm-device-bridge.c +++ b/src/devices/nm-device-bridge.c @@ -322,27 +322,21 @@ update_connection (NMDevice *device, NMConnection *connection) } } -/** - * nm_bridge_update_slave_connection: - * @slave: the slave #NMDevice, is *not* necessarily a bridge interface - * @connection: the #NMConnection to update with the bridge port settings - * - * Reads bridge port configuration and updates @connection with those - * properties. - * - * Returns: %TRUE if the port configuration was read and @connection updated, - * %FALSE if not. - */ -gboolean -nm_bridge_update_slave_connection (NMDevice *slave, NMConnection *connection) +static gboolean +master_update_slave_connection (NMDevice *self, + NMDevice *slave, + NMConnection *connection, + GError **error) { + NMSettingConnection *s_con; NMSettingBridgePort *s_port; - int ifindex = nm_device_get_ifindex (slave); + int ifindex_slave = nm_device_get_ifindex (slave); + const char *iface = nm_device_get_iface (self); const Option *option; - g_return_val_if_fail (NM_IS_DEVICE (slave), FALSE); - g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); + g_return_val_if_fail (ifindex_slave > 0, FALSE); + s_con = nm_connection_get_setting_connection (connection); s_port = nm_connection_get_setting_bridge_port (connection); if (!s_port) { s_port = (NMSettingBridgePort *) nm_setting_bridge_port_new (); @@ -350,7 +344,7 @@ nm_bridge_update_slave_connection (NMDevice *slave, NMConnection *connection) } for (option = slave_options; option->name; option++) { - gs_free char *str = nm_platform_slave_get_option (ifindex, option->sysname); + gs_free char *str = nm_platform_slave_get_option (ifindex_slave, option->sysname); int value; if (str) { @@ -367,6 +361,10 @@ nm_bridge_update_slave_connection (NMDevice *slave, NMConnection *connection) } } + g_object_set (s_con, + NM_SETTING_CONNECTION_MASTER, iface, + NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BRIDGE_SETTING_NAME, + NULL); return TRUE; } @@ -566,6 +564,7 @@ nm_device_bridge_class_init (NMDeviceBridgeClass *klass) parent_class->complete_connection = complete_connection; parent_class->update_connection = update_connection; + parent_class->master_update_slave_connection = master_update_slave_connection; parent_class->act_stage1_prepare = act_stage1_prepare; parent_class->enslave_slave = enslave_slave; diff --git a/src/devices/nm-device-bridge.h b/src/devices/nm-device-bridge.h index 4194f5a411..5570c73555 100644 --- a/src/devices/nm-device-bridge.h +++ b/src/devices/nm-device-bridge.h @@ -57,8 +57,6 @@ GType nm_device_bridge_get_type (void); NMDevice *nm_device_bridge_new (NMPlatformLink *platform_device); NMDevice *nm_device_bridge_new_for_connection (NMConnection *connection); -gboolean nm_bridge_update_slave_connection (NMDevice *slave, NMConnection *connection); - G_END_DECLS #endif /* NM_DEVICE_BRIDGE_H */ diff --git a/src/devices/nm-device-factory.c b/src/devices/nm-device-factory.c index fc4d1c248a..5e84531135 100644 --- a/src/devices/nm-device-factory.c +++ b/src/devices/nm-device-factory.c @@ -104,3 +104,20 @@ nm_device_factory_new_link (NMDeviceFactory *factory, return NULL; } +NMDevice * +nm_device_factory_create_virtual_device_for_connection (NMDeviceFactory *factory, + NMConnection *connection, + GError **error) +{ + NMDeviceFactory *interface; + + g_return_val_if_fail (factory, NULL); + g_return_val_if_fail (connection, NULL); + g_return_val_if_fail (!error || !*error, NULL); + + interface = NM_DEVICE_FACTORY_GET_INTERFACE (factory); + if (interface->create_virtual_device_for_connection) + return interface->create_virtual_device_for_connection (factory, connection, error); + return NULL; +} + diff --git a/src/devices/nm-device-factory.h b/src/devices/nm-device-factory.h index f0e3dc1978..ef412c96d2 100644 --- a/src/devices/nm-device-factory.h +++ b/src/devices/nm-device-factory.h @@ -95,6 +95,24 @@ struct _NMDeviceFactory { NMPlatformLink *plink, GError **error); + /** + * create_virtual_device_for_connection: + * @factory: the #NMDeviceFactory + * @connection: the #NMConnection + * @error: a @GError in case of failure + * + * Virtual device types (such as team, bond, bridge) may need to be created. + * This function tries to create a device based on the given @connection. + * + * Returns: the newly created #NMDevice. If the factory does not support the + * connection type, it should return %NULL and leave @error unset. On error + * it should set @error and return %NULL. + */ + NMDevice * (*create_virtual_device_for_connection) (NMDeviceFactory *factory, + NMConnection *connection, + GError **error); + + /* Signals */ /** @@ -128,6 +146,10 @@ NMDevice * nm_device_factory_new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error); +NMDevice * nm_device_factory_create_virtual_device_for_connection (NMDeviceFactory *factory, + NMConnection *connection, + GError **error); + /* For use by implementations */ gboolean nm_device_factory_emit_component_added (NMDeviceFactory *factory, GObject *component); diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 84c714a102..2ee1c4937b 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -70,10 +70,6 @@ #include "nm-config.h" #include "nm-dns-manager.h" -#include "nm-device-bridge.h" -#include "nm-device-bond.h" -#include "nm-device-team.h" - static void impl_device_disconnect (NMDevice *device, DBusGMethodInvocation *context); #include "nm-device-glue.h" @@ -1627,20 +1623,67 @@ device_has_config (NMDevice *device) return FALSE; } +/** + * nm_device_master_update_slave_connection: + * @self: the master #NMDevice + * @slave: the slave #NMDevice + * @connection: the #NMConnection to update with the slave settings + * @GError: (out): error description + * + * Reads the slave configuration for @slave and updates @connection with those + * properties. This invokes a virtual function on the master device @self. + * + * Returns: %TRUE if the configuration was read and @connection updated, + * %FALSE on failure. + */ +gboolean +nm_device_master_update_slave_connection (NMDevice *self, + NMDevice *slave, + NMConnection *connection, + GError **error) +{ + NMDeviceClass *klass; + gboolean success; + const char *iface; + + g_return_val_if_fail (self, FALSE); + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + g_return_val_if_fail (slave, FALSE); + g_return_val_if_fail (connection, FALSE); + g_return_val_if_fail (!error || !*error, FALSE); + g_return_val_if_fail (nm_connection_get_setting_connection (connection), FALSE); + + iface = nm_device_get_iface (self); + g_return_val_if_fail (iface, FALSE); + + klass = NM_DEVICE_GET_CLASS (self); + if (klass->master_update_slave_connection) { + success = klass->master_update_slave_connection (self, slave, connection, error); + + g_return_val_if_fail (!error || (success && !*error) || *error, success); + return success; + } + + g_set_error (error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_UNSUPPORTED_DEVICE_TYPE, + "master device '%s' cannot update a slave connection for slave device '%s' (master type not supported?)", + iface, nm_device_get_iface (slave)); + return FALSE; +} + NMConnection * -nm_device_generate_connection (NMDevice *device) +nm_device_generate_connection (NMDevice *device, NMDevice *master) { NMDeviceClass *klass = NM_DEVICE_GET_CLASS (device); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); const char *ifname = nm_device_get_iface (device); - int ifindex = nm_device_get_ifindex (device); NMConnection *connection; NMSetting *s_con; NMSetting *s_ip4; NMSetting *s_ip6; gs_free char *uuid = NULL; gs_free char *name = NULL; - int master_ifindex = 0; const char *ip4_method, *ip6_method; GError *error = NULL; @@ -1654,19 +1697,6 @@ nm_device_generate_connection (NMDevice *device) return NULL; } - if (ifindex) - master_ifindex = nm_platform_link_get_master (ifindex); - if (master_ifindex) { - NMDevice *master; - - master = nm_manager_get_device_by_ifindex (nm_manager_get (), master_ifindex); - if (!master || !nm_device_get_act_request (master)) { - nm_log_dbg (LOGD_DEVICE, "(%s): cannot generate connection for slave before its master (%s)", - ifname, nm_platform_link_get_name (master_ifindex)); - return NULL; - } - } - connection = nm_connection_new (); s_con = nm_setting_connection_new (); uuid = nm_utils_uuid_generate (); @@ -1684,36 +1714,18 @@ nm_device_generate_connection (NMDevice *device) nm_connection_add_setting (connection, s_con); /* If the device is a slave, update various slave settings */ - if (master_ifindex) { - const char *master_iface = nm_platform_link_get_name (master_ifindex); - const char *slave_type = NULL; - gboolean success = FALSE; - - switch (nm_platform_link_get_type (master_ifindex)) { - case NM_LINK_TYPE_BRIDGE: - slave_type = NM_SETTING_BRIDGE_SETTING_NAME; - success = nm_bridge_update_slave_connection (device, connection); - break; - case NM_LINK_TYPE_BOND: - slave_type = NM_SETTING_BOND_SETTING_NAME; - success = TRUE; - break; - case NM_LINK_TYPE_TEAM: - slave_type = NM_SETTING_TEAM_SETTING_NAME; - success = nm_team_update_slave_connection (device, connection); - break; - default: - g_warn_if_reached (); - break; + if (master) { + if (!nm_device_master_update_slave_connection (master, + device, + connection, + &error)) + { + nm_log_err (LOGD_DEVICE, "(%s): master device '%s' failed to update slave connection: %s", + ifname, nm_device_get_iface (master), error ? error->message : "(unknown error)"); + g_error_free (error); + g_object_unref (connection); + return NULL; } - - if (!success) - nm_log_err (LOGD_DEVICE, "(%s): failed to read slave configuration", ifname); - - g_object_set (s_con, - NM_SETTING_CONNECTION_MASTER, master_iface, - NM_SETTING_CONNECTION_SLAVE_TYPE, slave_type, - NULL); } else { /* Only regular and master devices get IP configuration; slaves do not */ s_ip4 = nm_ip4_config_create_setting (priv->ip4_config); diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index f74486e6fc..d034c946dc 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -94,6 +94,7 @@ typedef enum { NM_DEVICE_ERROR_CONNECTION_ACTIVATING = 0, /*< nick=ConnectionActivating >*/ NM_DEVICE_ERROR_CONNECTION_INVALID, /*< nick=ConnectionInvalid >*/ NM_DEVICE_ERROR_NOT_ACTIVE, /*< nick=NotActive >*/ + NM_DEVICE_ERROR_UNSUPPORTED_DEVICE_TYPE, /*< nick=UnsupportedDeviceType >*/ } NMDeviceError; struct _NMDevice { @@ -192,6 +193,11 @@ typedef struct { /* Update the connection with currently configured L2 settings */ void (* update_connection) (NMDevice *device, NMConnection *connection); + gboolean (*master_update_slave_connection) (NMDevice *self, + NMDevice *slave, + NMConnection *connection, + GError **error); + gboolean (* enslave_slave) (NMDevice *self, NMDevice *slave, NMConnection *connection, @@ -258,7 +264,12 @@ NMConnection * nm_device_get_connection (NMDevice *dev); gboolean nm_device_is_available (NMDevice *dev); gboolean nm_device_has_carrier (NMDevice *dev); -NMConnection * nm_device_generate_connection (NMDevice *device); +NMConnection * nm_device_generate_connection (NMDevice *device, NMDevice *master); + +gboolean nm_device_master_update_slave_connection (NMDevice *master, + NMDevice *slave, + NMConnection *connection, + GError **error); NMConnection * nm_device_get_best_auto_connection (NMDevice *dev, GSList *connections, diff --git a/src/devices/team/Makefile.am b/src/devices/team/Makefile.am new file mode 100644 index 0000000000..e2bfd0a04a --- /dev/null +++ b/src/devices/team/Makefile.am @@ -0,0 +1,74 @@ +include $(GLIB_MAKEFILE) + +@GNOME_CODE_COVERAGE_RULES@ + +AM_CPPFLAGS = \ + -I${top_srcdir}/src \ + -I${top_builddir}/src \ + -I${top_srcdir}/src/logging \ + -I${top_srcdir}/src/devices \ + -I${top_srcdir}/src/settings \ + -I${top_srcdir}/src/platform \ + -I${top_srcdir}/src/posix-signals \ + -I${top_builddir}/include \ + -I${top_srcdir}/include \ + -I${top_builddir}/libnm-util \ + -I${top_srcdir}/libnm-util \ + -DG_LOG_DOMAIN=\""NetworkManager-team"\" \ + -DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \ + $(DBUS_CFLAGS) \ + $(POLKIT_CFLAGS) \ + $(LIBNL_CFLAGS) \ + $(GUDEV_CFLAGS) + +if WITH_TEAMDCTL +AM_CPPFLAGS += ${LIBTEAMDCTL_CFLAGS} +endif + +GLIB_GENERATED = nm-team-enum-types.h nm-team-enum-types.c +GLIB_MKENUMS_H_FLAGS = --identifier-prefix NM +GLIB_MKENUMS_C_FLAGS = --identifier-prefix NM +nm_team_enum_types_sources = $(srcdir)/nm-device-team.h + +glue_sources = \ + nm-device-team-glue.h + +%-glue.h: $(top_srcdir)/introspection/%.xml + $(AM_V_GEN) dbus-binding-tool --prefix=$(subst -,_,$(subst -glue.h,,$@)) --mode=glib-server --output=$@ $< + +BUILT_SOURCES = $(GLIB_GENERATED) $(glue_sources) + +pkglib_LTLIBRARIES = libnm-device-plugin-team.la + +SYMBOL_VIS_FILE=$(srcdir)/exports.ver + +libnm_device_plugin_team_la_SOURCES = \ + nm-device-team.c \ + nm-device-team.h \ + nm-team-factory.c \ + nm-team-factory.h \ + \ + $(BUILT_SOURCES) + +libnm_device_plugin_team_la_LDFLAGS = \ + -module -avoid-version \ + -Wl,--version-script=$(SYMBOL_VIS_FILE) + +libnm_device_plugin_team_la_LIBADD = \ + $(DBUS_LIBS) \ + $(GUDEV_LIBS) + +if WITH_TEAMDCTL +libnm_device_plugin_team_la_LIBADD += $(LIBTEAMDCTL_LIBS) +endif + +CLEANFILES = $(BUILT_SOURCES) +EXTRA_DIST = $(SYMBOL_VIS_FILE) + +if ENABLE_TESTS + +check-local: + $(top_srcdir)/tools/check-exports.sh $(builddir)/.libs/libnm-device-plugin-team.so $(SYMBOL_VIS_FILE) + +endif + diff --git a/src/devices/team/exports.ver b/src/devices/team/exports.ver new file mode 100644 index 0000000000..d2c451244b --- /dev/null +++ b/src/devices/team/exports.ver @@ -0,0 +1,7 @@ +{ +global: + nm_device_factory_create; + nm_device_factory_get_device_type; +local: + *; +}; diff --git a/src/devices/nm-device-team.c b/src/devices/team/nm-device-team.c similarity index 91% rename from src/devices/nm-device-team.c rename to src/devices/team/nm-device-team.c index 29fb881836..fa7be78843 100644 --- a/src/devices/nm-device-team.c +++ b/src/devices/team/nm-device-team.c @@ -28,9 +28,7 @@ #include #include #include -#if WITH_TEAMDCTL #include -#endif #include #include "nm-device-team.h" @@ -42,6 +40,7 @@ #include "nm-dbus-glib-types.h" #include "nm-dbus-manager.h" #include "nm-enum-types.h" +#include "nm-team-enum-types.h" #include "nm-posix-signals.h" #include "nm-device-team-glue.h" @@ -51,14 +50,12 @@ G_DEFINE_TYPE (NMDeviceTeam, nm_device_team, NM_TYPE_DEVICE) #define NM_DEVICE_TEAM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_TEAM, NMDeviceTeamPrivate)) -#define NM_TEAM_ERROR (nm_team_error_quark ()) +#define NM_DEVICE_TEAM_ERROR (nm_device_team_error_quark ()) static gboolean teamd_start (NMDevice *dev, NMSettingTeam *s_team); typedef struct { -#if WITH_TEAMDCTL struct teamdctl *tdc; -#endif GPid teamd_pid; guint teamd_process_watch; guint teamd_timeout; @@ -75,7 +72,7 @@ enum { /******************************************************************/ static GQuark -nm_team_error_quark (void) +nm_device_team_error_quark (void) { static GQuark quark = 0; if (!quark) @@ -190,7 +187,6 @@ complete_connection (NMDevice *device, return TRUE; } -#if WITH_TEAMDCTL static gboolean ensure_teamd_connection (NMDevice *self) { @@ -212,7 +208,6 @@ ensure_teamd_connection (NMDevice *self) return !!priv->tdc; } -#endif static void update_connection (NMDevice *device, NMConnection *connection) @@ -227,7 +222,6 @@ update_connection (NMDevice *device, NMConnection *connection) } g_object_set (G_OBJECT (s_team), NM_SETTING_TEAM_CONFIG, NULL, NULL); -#if WITH_TEAMDCTL teamd_start (device, s_team); if (NM_DEVICE_TEAM_GET_PRIVATE (device)->teamd_pid > 0 && ensure_teamd_connection (device)) { const char *config = NULL; @@ -240,49 +234,57 @@ update_connection (NMDevice *device, NMConnection *connection) else nm_log_err (LOGD_TEAM, "(%s): failed to read teamd config (err=%d)", iface, err); } -#endif } /******************************************************************/ -gboolean -nm_team_update_slave_connection (NMDevice *slave, NMConnection *connection) +static gboolean +master_update_slave_connection (NMDevice *self, + NMDevice *slave, + NMConnection *connection, + GError **error) { NMSettingTeamPort *s_port; - const char *iface = nm_device_get_iface (slave); char *port_config = NULL; - gboolean with_teamdctl = FALSE; int err = 0; -#if WITH_TEAMDCTL - const char *master_iface; - int master_ifindex; struct teamdctl *tdc; const char *team_port_config = NULL; -#endif - - g_return_val_if_fail (NM_IS_DEVICE (slave), FALSE); - g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); - -#if WITH_TEAMDCTL - master_ifindex = nm_platform_link_get_master (nm_device_get_ifindex (slave)); - g_assert (master_ifindex > 0); - master_iface = nm_platform_link_get_name (master_ifindex); - g_assert (master_iface); + const char *iface = nm_device_get_iface (self); + const char *iface_slave = nm_device_get_iface (slave); tdc = teamdctl_alloc (); - g_assert (tdc); - err = teamdctl_connect (tdc, master_iface, NULL, NULL); + if (!tdc) { + g_set_error (error, + NM_DEVICE_TEAM_ERROR, + NM_DEVICE_TEAM_ERROR_TEAMCTL_FAILURE, + "update slave connection for slave '%s' failed to connect to teamd for master %s (out of memory?)", + iface_slave, iface); + g_return_val_if_reached (FALSE); + } + + err = teamdctl_connect (tdc, iface, NULL, NULL); if (err) { - nm_log_err (LOGD_TEAM, "(%s): failed to connect to teamd for master %s (err=%d)", - iface, master_iface, err); teamdctl_free (tdc); + g_set_error (error, + NM_DEVICE_TEAM_ERROR, + NM_DEVICE_TEAM_ERROR_TEAMCTL_FAILURE, + "update slave connection for slave '%s' failed to connect to teamd for master %s (err=%d)", + iface_slave, iface, err); return FALSE; } - err = teamdctl_port_config_get_raw_direct (tdc, iface, (char **)&team_port_config); + + err = teamdctl_port_config_get_raw_direct (tdc, iface_slave, (char **)&team_port_config); port_config = g_strdup (team_port_config); teamdctl_free (tdc); - with_teamdctl = TRUE; -#endif + if (err) { + g_set_error (error, + NM_DEVICE_TEAM_ERROR, + NM_DEVICE_TEAM_ERROR_TEAMCTL_FAILURE, + "update slave connection for slave '%s' failed to get configuration from teamd master %s (err=%d)", + iface_slave, iface, err); + g_free (port_config); + return FALSE; + } s_port = nm_connection_get_setting_team_port (connection); if (!s_port) { @@ -293,16 +295,10 @@ nm_team_update_slave_connection (NMDevice *slave, NMConnection *connection) g_object_set (G_OBJECT (s_port), NM_SETTING_TEAM_PORT_CONFIG, port_config, NULL); g_free (port_config); - if (!with_teamdctl || err != 0) { - if (!with_teamdctl) - nm_log_err (LOGD_TEAM, "(%s): failed to read teamd port configuration " - " (compiled without libteamdctl support)", iface); - else - nm_log_err (LOGD_TEAM, "(%s): failed to read teamd port configuration (err=%d)", - iface, err); - return FALSE; - } - + g_object_set (nm_connection_get_setting_connection (connection), + NM_SETTING_CONNECTION_MASTER, iface, + NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_TEAM_SETTING_NAME, + NULL); return TRUE; } @@ -370,13 +366,11 @@ teamd_cleanup (NMDevice *dev, gboolean device_state_failed) priv->teamd_pid = 0; } -#if WITH_TEAMDCTL if (priv->tdc) { teamdctl_disconnect (priv->tdc); teamdctl_free (priv->tdc); priv->tdc = NULL; } -#endif teamd_timeout_remove (dev); @@ -414,12 +408,10 @@ teamd_dbus_appeared (GDBusConnection *connection, nm_log_info (LOGD_TEAM, "(%s): teamd appeared on D-Bus", nm_device_get_iface (dev)); teamd_timeout_remove (dev); -#if WITH_TEAMDCTL if (!ensure_teamd_connection (dev)) { nm_device_state_changed (dev, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED); return; } -#endif nm_device_activate_schedule_stage2_device_config (dev); } @@ -501,9 +493,7 @@ teamd_start (NMDevice *dev, NMSettingTeam *s_team) if (priv->teamd_dbus_watch || priv->teamd_process_watch || priv->teamd_pid > 0 || -#if WITH_TEAMDCTL priv->tdc || -#endif priv->teamd_timeout) { /* Just return if teamd_start() was already called */ @@ -649,9 +639,7 @@ enslave_slave (NMDevice *device, NMConnection *connection, gboolean configure) { -#if WITH_TEAMDCTL NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (device); -#endif gboolean success = TRUE, no_firmware = FALSE; const char *iface = nm_device_get_ip_iface (device); const char *slave_iface = nm_device_get_ip_iface (slave); @@ -667,7 +655,6 @@ enslave_slave (NMDevice *device, const char *config = nm_setting_team_port_get_config (s_team_port); if (config) { -#if WITH_TEAMDCTL if (!priv->tdc) { nm_log_warn (LOGD_TEAM, "(%s): enslaved team port %s config not changed, not connected to teamd", iface, slave_iface); @@ -684,10 +671,6 @@ enslave_slave (NMDevice *device, return FALSE; } } -#else - nm_log_warn (LOGD_TEAM, "(%s): enslaved team port %s config not changed due to lack of Teamd control support", - iface, slave_iface); -#endif } } success = nm_platform_link_enslave (nm_device_get_ip_ifindex (device), @@ -766,7 +749,7 @@ nm_device_team_new (NMPlatformLink *platform_device) } NMDevice * -nm_device_team_new_for_connection (NMConnection *connection) +nm_device_team_new_for_connection (NMConnection *connection, GError **error) { const char *iface; @@ -777,7 +760,10 @@ nm_device_team_new_for_connection (NMConnection *connection) if ( !nm_platform_team_add (iface) && nm_platform_get_error () != NM_PLATFORM_ERROR_EXISTS) { - nm_log_warn (LOGD_DEVICE | LOGD_TEAM, "(%s): failed to create team master interface for '%s': %s", + g_set_error (error, + NM_DEVICE_TEAM_ERROR, + NM_DEVICE_TEAM_ERROR_PLATFORM_FAILURE, + "failed to create team master interface '%s' for connection '%s': %s", iface, nm_connection_get_id (connection), nm_platform_get_error_msg ()); return NULL; @@ -871,6 +857,7 @@ nm_device_team_class_init (NMDeviceTeamClass *klass) parent_class->check_connection_available = check_connection_available; parent_class->complete_connection = complete_connection; parent_class->update_connection = update_connection; + parent_class->master_update_slave_connection = master_update_slave_connection; parent_class->act_stage1_prepare = act_stage1_prepare; parent_class->deactivate = deactivate; @@ -889,5 +876,5 @@ nm_device_team_class_init (NMDeviceTeamClass *klass) G_TYPE_FROM_CLASS (klass), &dbus_glib_nm_device_team_object_info); - dbus_g_error_domain_register (NM_TEAM_ERROR, NULL, NM_TYPE_TEAM_ERROR); + dbus_g_error_domain_register (NM_DEVICE_TEAM_ERROR, NULL, NM_TYPE_TEAM_ERROR); } diff --git a/src/devices/nm-device-team.h b/src/devices/team/nm-device-team.h similarity index 80% rename from src/devices/nm-device-team.h rename to src/devices/team/nm-device-team.h index fe1275c6b0..fb2a184115 100644 --- a/src/devices/nm-device-team.h +++ b/src/devices/team/nm-device-team.h @@ -35,9 +35,12 @@ G_BEGIN_DECLS #define NM_DEVICE_TEAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_TEAM, NMDeviceTeamClass)) typedef enum { - NM_TEAM_ERROR_CONNECTION_NOT_TEAM = 0, /*< nick=ConnectionNotTeam >*/ - NM_TEAM_ERROR_CONNECTION_INVALID, /*< nick=ConnectionInvalid >*/ - NM_TEAM_ERROR_CONNECTION_INCOMPATIBLE, /*< nick=ConnectionIncompatible >*/ + NM_DEVICE_TEAM_ERROR_CONNECTION_NOT_TEAM = 0, /*< nick=ConnectionNotTeam >*/ + NM_DEVICE_TEAM_ERROR_CONNECTION_INVALID, /*< nick=ConnectionInvalid >*/ + NM_DEVICE_TEAM_ERROR_CONNECTION_INCOMPATIBLE, /*< nick=ConnectionIncompatible >*/ + NM_DEVICE_TEAM_ERROR_TEAMCTL_FAILURE, /*< nick=TeamCtlFailure >*/ + NM_DEVICE_TEAM_ERROR_NO_SUPPORT, /*< nick=NoSupport >*/ + NM_DEVICE_TEAM_ERROR_PLATFORM_FAILURE, /*< nick=PlatformFailure >*/ } NMTeamError; #define NM_DEVICE_TEAM_SLAVES "slaves" @@ -55,9 +58,7 @@ typedef struct { GType nm_device_team_get_type (void); NMDevice *nm_device_team_new (NMPlatformLink *platform_device); -NMDevice *nm_device_team_new_for_connection (NMConnection *connection); - -gboolean nm_team_update_slave_connection (NMDevice *slave, NMConnection *connection); +NMDevice *nm_device_team_new_for_connection (NMConnection *connection, GError **error); G_END_DECLS diff --git a/src/devices/team/nm-team-factory.c b/src/devices/team/nm-team-factory.c new file mode 100644 index 0000000000..ab97de320b --- /dev/null +++ b/src/devices/team/nm-team-factory.c @@ -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) 2014 Red Hat, Inc. + */ + +#include +#include + +#include "config.h" +#include "nm-device-factory.h" +#include "nm-team-factory.h" +#include "nm-device-team.h" +#include "nm-logging.h" + +static GType nm_team_factory_get_type (void); + +static void device_factory_interface_init (NMDeviceFactory *factory_iface); + +G_DEFINE_TYPE_EXTENDED (NMTeamFactory, nm_team_factory, G_TYPE_OBJECT, 0, + G_IMPLEMENT_INTERFACE (NM_TYPE_DEVICE_FACTORY, device_factory_interface_init)) + +#define NM_TEAM_FACTORY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_TEAM_FACTORY, NMTeamFactoryPrivate)) + +typedef struct { + char dummy; +} NMTeamFactoryPrivate; + + +/************************************************************************/ + +static NMDevice * +new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error) +{ + if (plink->type == NM_LINK_TYPE_TEAM) + return nm_device_team_new (plink); + return NULL; +} + +static NMDevice * +create_virtual_device_for_connection (NMDeviceFactory *factory, + NMConnection *connection, + GError **error) +{ + if (nm_connection_is_type (connection, NM_SETTING_TEAM_SETTING_NAME)) + return nm_device_team_new_for_connection (connection, error); + return NULL; +} + +/************************************************************************/ + +#define PLUGIN_TYPE NM_DEVICE_TYPE_TEAM + +G_MODULE_EXPORT NMDeviceFactory * +nm_device_factory_create (GError **error) +{ + return (NMDeviceFactory *) g_object_new (NM_TYPE_TEAM_FACTORY, NULL); +} + +G_MODULE_EXPORT NMDeviceType +nm_device_factory_get_device_type (void) +{ + return PLUGIN_TYPE; +} + +/************************************************************************/ + +static void +nm_team_factory_init (NMTeamFactory *self) +{ +} + +static void +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; +} + +static void +dispose (GObject *object) +{ + /* Chain up to the parent class */ + G_OBJECT_CLASS (nm_team_factory_parent_class)->dispose (object); +} + +static void +nm_team_factory_class_init (NMTeamFactoryClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (NMTeamFactoryPrivate)); + + object_class->dispose = dispose; +} diff --git a/src/devices/team/nm-team-factory.h b/src/devices/team/nm-team-factory.h new file mode 100644 index 0000000000..f637371d77 --- /dev/null +++ b/src/devices/team/nm-team-factory.h @@ -0,0 +1,37 @@ +/* -*- 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_TEAM_FACTORY_H +#define NM_TEAM_FACTORY_H + +#include + +#define NM_TYPE_TEAM_FACTORY (nm_team_factory_get_type ()) +#define NM_TEAM_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_TEAM_FACTORY, NMTeamFactory)) + +typedef struct { + GObject parent; +} NMTeamFactory; + +typedef struct { + GObjectClass parent; +} NMTeamFactoryClass; + +#endif /* NM_TEAM_FACTORY_H */ diff --git a/src/nm-manager.c b/src/nm-manager.c index b806809d80..ac8fb6c489 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -43,7 +43,6 @@ #include "nm-device-ethernet.h" #include "nm-device-infiniband.h" #include "nm-device-bond.h" -#include "nm-device-team.h" #include "nm-device-bridge.h" #include "nm-device-vlan.h" #include "nm-device-generic.h" @@ -1074,6 +1073,7 @@ static NMDevice * system_create_virtual_device (NMManager *self, NMConnection *connection) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + GError *error = NULL; GSList *iter; char *iface = NULL; NMDevice *device = NULL, *parent = NULL; @@ -1102,14 +1102,27 @@ system_create_virtual_device (NMManager *self, NMConnection *connection) if (nm_connection_is_type (connection, NM_SETTING_BOND_SETTING_NAME)) { device = nm_device_bond_new_for_connection (connection); - } else if (nm_connection_is_type (connection, NM_SETTING_TEAM_SETTING_NAME)) { - device = nm_device_team_new_for_connection (connection); } else if (nm_connection_is_type (connection, NM_SETTING_BRIDGE_SETTING_NAME)) { device = nm_device_bridge_new_for_connection (connection); } else if (nm_connection_is_type (connection, NM_SETTING_VLAN_SETTING_NAME)) { device = nm_device_vlan_new_for_connection (connection, parent); } else if (nm_connection_is_type (connection, NM_SETTING_INFINIBAND_SETTING_NAME)) { device = nm_device_infiniband_new_partition (connection, parent); + } else { + for (iter = priv->factories; iter; iter = iter->next) { + device = nm_device_factory_create_virtual_device_for_connection (NM_DEVICE_FACTORY (iter->data), connection, &error); + + if (device || error) { + if (device) + g_assert_no_error (error); + else { + nm_log_err (LOGD_DEVICE, "(%s) failed to create virtual device: %s", + nm_connection_get_id (connection), error ? error->message : "(unknown error)"); + g_clear_error (&error); + } + break; + } + } } if (device) { @@ -1557,16 +1570,36 @@ get_existing_connection (NMManager *manager, NMDevice *device) NMConnection *connection = NULL, *matched; NMSettingsConnection *added = NULL; GError *error = NULL; + NMDevice *master = NULL; + int ifindex = nm_device_get_ifindex (device); nm_device_capture_initial_config (device); + if (ifindex) { + int master_ifindex = nm_platform_link_get_master (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); + return NULL; + } + if (!nm_device_get_act_request (master)) { + nm_log_dbg (LOGD_DEVICE, "(%s): cannot generate connection for slave before master %s activates", + nm_device_get_iface (device), nm_device_get_iface (master)); + return NULL; + } + } + } + /* The core of the API is nm_device_generate_connection() function and * update_connection() virtual method and the convenient connection_type * class attribute. Subclasses supporting the new API must have * update_connection() implemented, otherwise nm_device_generate_connection() * returns NULL. */ - connection = nm_device_generate_connection (device); + connection = nm_device_generate_connection (device, master); if (!connection) return NULL; @@ -1879,42 +1912,116 @@ factory_component_added_cb (NMDeviceFactory *factory, #define PLUGIN_PATH_TAG "NMManager-plugin-path" #define PLUGIN_TYPEFUNC_TAG "typefunc" -static void -load_device_factories (NMManager *self) +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; +} + +static char** +read_device_factory_paths () { - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); GDir *dir; GError *error = NULL; const char *item; - char *path; - GSList *iter; + GArray *paths; + char **result; + guint i; dir = g_dir_open (NMPLUGINDIR, 0, &error); if (!dir) { - nm_log_warn (LOGD_HW, "Failed to open plugin directory %s: %s", + 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; + return NULL; } + paths = g_array_new (FALSE, FALSE, sizeof (struct read_device_factory_paths_data)); + while ((item = g_dir_read_name (dir))) { - GModule *plugin; - NMDeviceFactory *factory; - NMDeviceFactoryCreateFunc create_func; - NMDeviceFactoryDeviceTypeFunc type_func; - NMDeviceType dev_type; - const char *found = NULL; + struct read_device_factory_paths_data data; if (!g_str_has_prefix (item, PLUGIN_PREFIX)) continue; if (g_str_has_suffix (item, ".la")) continue; - path = g_module_build_path (NMPLUGINDIR, item); - g_assert (path); - plugin = g_module_open (path, G_MODULE_BIND_LOCAL); - g_free (path); + data.path = g_build_filename (NMPLUGINDIR, item, NULL); + + if (stat (data.path, &data.st) != 0) + goto continue_with_error; + if (!S_ISREG (data.st.st_mode)) + goto continue_silently; + if (data.st.st_uid != 0) + goto continue_with_error; + if (data.st.st_mode & (S_IWGRP | S_IWOTH | S_ISUID)) + goto continue_with_error; + + g_array_append_val (paths, data); + continue; + +continue_with_error: + nm_log_dbg (LOGD_HW, "device plugin: skip invalid file %s", data.path); +continue_silently: + 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 void +load_device_factories (NMManager *self) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + char **path; + char **paths; + + paths = read_device_factory_paths (); + if (!paths) + return; + + for (path = paths; *path; path++) { + GError *error = NULL; + GModule *plugin; + NMDeviceFactory *factory; + NMDeviceFactoryCreateFunc create_func; + NMDeviceFactoryDeviceTypeFunc type_func; + NMDeviceType dev_type; + const char *found = NULL; + GSList *iter; + 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 ()); @@ -1939,7 +2046,7 @@ load_device_factories (NMManager *self) } } if (found) { - nm_log_warn (LOGD_HW, "Found multiple device plugins for same type: %s vs %s", + nm_log_warn (LOGD_HW, "Found multiple device plugins for same type: use '%s' instead of '%s'", found, g_module_name (plugin)); g_module_close (plugin); continue; @@ -1978,7 +2085,7 @@ load_device_factories (NMManager *self) nm_log_info (LOGD_HW, "Loaded device plugin: %s", g_module_name (plugin)); }; - g_dir_close (dir); + g_strfreev (paths); priv->factories = g_slist_reverse (priv->factories); } @@ -2036,9 +2143,6 @@ platform_link_added (NMManager *self, case NM_LINK_TYPE_BOND: device = nm_device_bond_new (plink); break; - case NM_LINK_TYPE_TEAM: - device = nm_device_team_new (plink); - break; case NM_LINK_TYPE_BRIDGE: device = nm_device_bridge_new (plink); break;