diff options
-rw-r--r-- | src/devices/nm-device.c | 40 | ||||
-rw-r--r-- | src/devices/nm-device.h | 2 | ||||
-rw-r--r-- | src/nm-activation-request.c | 6 | ||||
-rw-r--r-- | src/nm-active-connection.c | 15 | ||||
-rw-r--r-- | src/nm-active-connection.h | 2 | ||||
-rw-r--r-- | src/nm-manager.c | 260 |
6 files changed, 192 insertions, 133 deletions
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 39590893eb..ed9d45456f 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -2133,6 +2133,18 @@ nm_device_master_release_slaves (NMDevice *self) } /** + * nm_device_is_master: + * @self: the device + * + * Returns: %TRUE is the device can have slaves + */ +gboolean +nm_device_is_master (NMDevice *self) +{ + return NM_DEVICE_GET_PRIVATE (self)->is_master; +} + +/** * nm_device_get_master: * @self: the device * @@ -2713,6 +2725,34 @@ nm_device_check_connection_compatible (NMDevice *self, NMConnection *connection) return NM_DEVICE_GET_CLASS (self)->check_connection_compatible (self, connection); } +gboolean +nm_device_check_slave_connection_compatible (NMDevice *self, NMConnection *slave) +{ + NMDevicePrivate *priv; + NMSettingConnection *s_con; + const char *connection_type, *slave_type; + + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + g_return_val_if_fail (NM_IS_CONNECTION (slave), FALSE); + + priv = NM_DEVICE_GET_PRIVATE (self); + + if (!priv->is_master) + return FALSE; + + /* All masters should have connection type set */ + connection_type = NM_DEVICE_GET_CLASS (self)->connection_type; + g_return_val_if_fail (connection_type, FALSE); + + s_con = nm_connection_get_setting_connection (slave); + g_assert (s_con); + slave_type = nm_setting_connection_get_slave_type (s_con); + if (!slave_type) + return FALSE; + + return strcmp (connection_type, slave_type) == 0; +} + /** * nm_device_can_assume_connections: * @self: #NMDevice instance diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index 53e97bf33c..6f273854c6 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -385,6 +385,7 @@ void nm_device_capture_initial_config (NMDevice *dev); /* Master */ GSList * nm_device_master_get_slaves (NMDevice *dev); +gboolean nm_device_is_master (NMDevice *dev); /* Slave */ NMDevice * nm_device_get_master (NMDevice *dev); @@ -415,6 +416,7 @@ gboolean nm_device_complete_connection (NMDevice *device, GError **error); gboolean nm_device_check_connection_compatible (NMDevice *device, NMConnection *connection); +gboolean nm_device_check_slave_connection_compatible (NMDevice *device, NMConnection *connection); gboolean nm_device_uses_assumed_connection (NMDevice *device); diff --git a/src/nm-activation-request.c b/src/nm-activation-request.c index 2c084b874e..6e531b6305 100644 --- a/src/nm-activation-request.c +++ b/src/nm-activation-request.c @@ -396,9 +396,7 @@ master_failed (NMActiveConnection *self) * @specific_object: the object path of the specific object (ie, WiFi access point, * etc) that will be used to activate @connection and @device * @subject: the #NMAuthSubject representing the requestor of the activation - * @device: the device/interface to configure according to @connection; or %NULL - * if the connection describes a software device which will be created during - * connection activation + * @device: the device/interface to configure according to @connection * * Creates a new device-based activation request. * @@ -411,7 +409,7 @@ nm_act_request_new (NMConnection *connection, NMDevice *device) { g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); - g_return_val_if_fail (!device || NM_IS_DEVICE (device), NULL); + g_return_val_if_fail (NM_IS_DEVICE (device), NULL); g_return_val_if_fail (NM_IS_AUTH_SUBJECT (subject), NULL); return (NMActRequest *) g_object_new (NM_TYPE_ACT_REQUEST, diff --git a/src/nm-active-connection.c b/src/nm-active-connection.c index 2a8f53ace1..7a3fe185d4 100644 --- a/src/nm-active-connection.c +++ b/src/nm-active-connection.c @@ -432,8 +432,13 @@ nm_active_connection_set_device (NMActiveConnection *self, NMDevice *device) priv->pending_activation_id = g_strdup_printf ("activation::%p", (void *)self); nm_device_add_pending_action (device, priv->pending_activation_id, TRUE); } - } else + } else { + /* The ActiveConnection's device can only be cleared after the + * connection is activated. + */ + g_warn_if_fail (priv->state > NM_ACTIVE_CONNECTION_STATE_UNKNOWN); priv->device = NULL; + } g_object_notify (G_OBJECT (self), NM_ACTIVE_CONNECTION_INT_DEVICE); g_signal_emit (self, signals[DEVICE_CHANGED], 0, priv->device, old_device); @@ -553,10 +558,7 @@ nm_active_connection_set_master (NMActiveConnection *self, NMActiveConnection *m /* Master is write-once, and must be set before exporting the object */ g_return_if_fail (priv->master == NULL); g_return_if_fail (priv->path == NULL); - if (priv->device) { - /* Note, the master ActiveConnection may not yet have a device */ - g_return_if_fail (priv->device != nm_active_connection_get_device (master)); - } + g_return_if_fail (priv->device != nm_active_connection_get_device (master)); nm_log_dbg (LOGD_DEVICE, "(%p): master ActiveConnection is [%p] %s", self, master, nm_active_connection_get_id (master)); @@ -720,6 +722,7 @@ set_property (GObject *object, guint prop_id, priv->connection = g_value_dup_object (value); break; case PROP_INT_DEVICE: + g_return_if_fail (priv->device == NULL); nm_active_connection_set_device (NM_ACTIVE_CONNECTION (object), g_value_get_object (value)); break; case PROP_INT_SUBJECT: @@ -1012,7 +1015,7 @@ nm_active_connection_class_init (NMActiveConnectionClass *ac_class) (object_class, PROP_INT_DEVICE, g_param_spec_object (NM_ACTIVE_CONNECTION_INT_DEVICE, "", "", NM_TYPE_DEVICE, - G_PARAM_READWRITE | + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_property diff --git a/src/nm-active-connection.h b/src/nm-active-connection.h index 710cfeed9f..5adc664778 100644 --- a/src/nm-active-connection.h +++ b/src/nm-active-connection.h @@ -49,7 +49,7 @@ #define NM_ACTIVE_CONNECTION_VPN "vpn" #define NM_ACTIVE_CONNECTION_MASTER "master" -/* Internal non-exported properties */ +/* Internal non-exported construct-time properties */ #define NM_ACTIVE_CONNECTION_INT_CONNECTION "int-connection" #define NM_ACTIVE_CONNECTION_INT_DEVICE "int-device" #define NM_ACTIVE_CONNECTION_INT_SUBJECT "int-subject" diff --git a/src/nm-manager.c b/src/nm-manager.c index b354608c1f..5097e4d5d6 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -535,16 +535,53 @@ find_device_by_ip_iface (NMManager *self, const gchar *iface) return NULL; } +/** + * find_device_by_iface: + * @self: the #NMManager + * @iface: the device interface to find + * @connection: a connection to ensure the returned device is compatible with + * @slave: a slave connection to ensure a master is compatible with + * + * Finds a device by interface name, preferring realized devices. If @slave + * is given, this function will only return master devices and will ensure + * @slave, when activated, can be a slave of the returned master device. If + * @connection is given, this function will only consider devices that are + * compatible with @connection. + * + * Returns: the matching #NMDevice + */ static NMDevice * -find_device_by_iface (NMManager *self, const gchar *iface) +find_device_by_iface (NMManager *self, + const char *iface, + NMConnection *connection, + NMConnection *slave) { + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMDevice *fallback = NULL; 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); + g_return_val_if_fail (iface != NULL, NULL); + + for (iter = priv->devices; iter; iter = iter->next) { + NMDevice *candidate = iter->data; + + if (strcmp (nm_device_get_iface (candidate), iface)) + continue; + if (connection && !nm_device_check_connection_compatible (candidate, connection)) + continue; + if (slave) { + if (!nm_device_is_master (candidate)) + continue; + if (!nm_device_check_slave_connection_compatible (candidate, slave)) + continue; + } + + if (nm_device_is_real (candidate)) + return candidate; + else if (!fallback) + fallback = candidate; } - return NULL; + return fallback; } static gboolean @@ -860,8 +897,8 @@ find_parent_device_for_connection (NMManager *self, NMConnection *connection) if (!parent_name) return NULL; - /* Try as an interface name */ - parent = find_device_by_iface (self, parent_name); + /* Try as an interface name of a parent device */ + parent = find_device_by_iface (self, parent_name, NULL, NULL); if (parent) return parent; @@ -988,18 +1025,16 @@ system_create_virtual_device (NMManager *self, NMConnection *connection, GError if (!iface) return NULL; - /* Make sure we didn't create a device for this connection already */ + /* If some other device is already compatible with this connection, + * don't create a new device for it. + */ for (iter = priv->devices; iter; iter = g_slist_next (iter)) { NMDevice *candidate = iter->data; if ( g_strcmp0 (nm_device_get_iface (candidate), iface) == 0 - || nm_device_check_connection_compatible (candidate, connection)) { + && nm_device_check_connection_compatible (candidate, connection)) { nm_log_dbg (LOGD_DEVICE, "(%s) already created virtual interface name %s", nm_connection_get_id (connection), iface); - g_set_error (error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_FAILED, - "interface name '%s' already created", iface); return NULL; } } @@ -1660,7 +1695,8 @@ device_ip_iface_changed (NMDevice *device, NMDevice *candidate = NM_DEVICE (iter->data); if ( candidate != device - && g_strcmp0 (nm_device_get_iface (candidate), ip_iface) == 0) { + && g_strcmp0 (nm_device_get_iface (candidate), ip_iface) == 0 + && nm_device_is_real (candidate)) { remove_device (self, candidate, FALSE, FALSE); break; } @@ -1728,14 +1764,6 @@ add_device (NMManager *self, NMDevice *device) const GSList *unmanaged_specs; RfKillType rtype; GSList *iter, *remove = NULL; - int ifindex; - - /* No duplicates */ - 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 * ports that are owned by a WWAN modem, since udev may announce them @@ -1745,9 +1773,11 @@ add_device (NMManager *self, NMDevice *device) * the child NMDevice entirely */ for (iter = priv->devices; iter; iter = iter->next) { - iface = nm_device_get_ip_iface (iter->data); - if (nm_device_owns_iface (device, iface)) - remove = g_slist_prepend (remove, iter->data); + NMDevice *candidate = iter->data; + + iface = nm_device_get_ip_iface (candidate); + if (nm_device_is_real (candidate) && nm_device_owns_iface (device, iface)) + remove = g_slist_prepend (remove, candidate); } for (iter = remove; iter; iter = iter->next) remove_device (self, NM_DEVICE (iter->data), FALSE, FALSE); @@ -1868,37 +1898,37 @@ platform_link_added (NMManager *self, NMDevice *device = NULL; GError *error = NULL; gboolean nm_plugin_missing = FALSE; + GSList *iter; g_return_if_fail (ifindex > 0); if (nm_manager_get_device_by_ifindex (self, ifindex)) return; - device = find_device_by_iface (self, plink->name); - if (device) { - gboolean compatible = FALSE; + /* Let unrealized devices try to realize themselves with the link */ + for (iter = NM_MANAGER_GET_PRIVATE (self)->devices; iter; iter = iter->next) { + NMDevice *candidate = iter->data; + gboolean compatible = TRUE; - if (nm_device_is_real (device)) - return; + if (strcmp (nm_device_get_iface (candidate), plink->name)) + continue; - if (nm_device_realize (device, plink, &compatible, &error)) { + if (nm_device_is_real (candidate)) { + /* Ignore the link added event since there's already a realized + * device with the link's name. + */ + return; + } else if (nm_device_realize (candidate, plink, &compatible, &error)) { /* Success */ - nm_device_setup_finish (device, plink); + nm_device_setup_finish (candidate, plink); return; } - nm_log_warn (LOGD_DEVICE, "(%s): %s", plink->name, error->message); - remove_device (self, device, FALSE, FALSE); + nm_log_dbg (LOGD_DEVICE, "(%s): failed to realize from plink: '%s'", + plink->name, error->message); g_clear_error (&error); - if (compatible) { - /* Device compatible with platform link, but some other fatal error - * happened during realization. - */ - return; - } - - /* Fall through and create new compatible device for the link */ + /* Try next unrealized device */ } /* Try registered device factories */ @@ -2191,7 +2221,7 @@ find_master (NMManager *self, return TRUE; /* success, but no master */ /* Try as an interface name first */ - master_device = find_device_by_iface (self, master); + master_device = find_device_by_iface (self, master, NULL, connection); if (master_device) { if (master_device == device) { g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_DEPENDENCY_FAILED, @@ -2443,8 +2473,10 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError * NMDevice *device, *existing, *master_device = NULL; NMConnection *connection; NMConnection *master_connection = NULL; + NMConnection *existing_connection = NULL; NMActiveConnection *master_ac = NULL; - GError *local_err = NULL; + NMAuthSubject *subject; + char *error_desc = NULL; g_return_val_if_fail (NM_IS_MANAGER (self), FALSE); g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (active), FALSE); @@ -2456,72 +2488,26 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError * g_assert (connection); device = nm_active_connection_get_device (active); - if (!device) { - if (!nm_connection_is_virtual (connection)) { - NMSettingConnection *s_con = nm_connection_get_setting_connection (connection); + g_return_val_if_fail (device != NULL, FALSE); - g_assert (s_con); - g_set_error (error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "Unsupported virtual interface type '%s'", - nm_setting_connection_get_connection_type (s_con)); - return FALSE; - } - - device = system_create_virtual_device (self, connection, &local_err); - if (!device) { - g_set_error (error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "Failed to create virtual interface: %s", - local_err ? local_err->message : "(unknown)"); - g_clear_error (&local_err); - return FALSE; - } - - if (!nm_active_connection_set_device (active, device)) { - g_set_error_literal (error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "The device could not be activated with this connection"); - return FALSE; - } - - /* A newly created device, if allowed to be managed by NM, will be - * in the UNAVAILABLE state here. To ensure it can be activated - * immediately, we transition it to DISCONNECTED. - */ - if ( nm_device_is_available (device, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE) - && (nm_device_get_state (device) == NM_DEVICE_STATE_UNAVAILABLE)) { - nm_device_state_changed (device, - NM_DEVICE_STATE_DISCONNECTED, - NM_DEVICE_STATE_REASON_NONE); - } - } else { - NMConnection *existing_connection = NULL; - NMAuthSubject *subject; - char *error_desc = NULL; - - /* If the device is active and its connection is not visible to the - * user that's requesting this new activation, fail, since other users - * should not be allowed to implicitly deactivate private connections - * by activating a connection of their own. - */ - existing_connection = nm_device_get_connection (device); - subject = nm_active_connection_get_subject (active); - if (existing_connection && - !nm_auth_is_subject_in_acl (existing_connection, - subject, - &error_desc)) { - g_set_error (error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - "Private connection already active on the device: %s", - error_desc); - g_free (error_desc); - return FALSE; - } + /* If the device is active and its connection is not visible to the + * user that's requesting this new activation, fail, since other users + * should not be allowed to implicitly deactivate private connections + * by activating a connection of their own. + */ + existing_connection = nm_device_get_connection (device); + subject = nm_active_connection_get_subject (active); + if (existing_connection && + !nm_auth_is_subject_in_acl (existing_connection, + subject, + &error_desc)) { + g_set_error (error, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "Private connection already active on the device: %s", + error_desc); + g_free (error_desc); + return FALSE; } /* Final connection must be available on device */ @@ -2598,8 +2584,8 @@ _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_connection_get_id (connection), + nm_log_dbg (LOGD_CORE, "Activation of '%s' depends on active connection %p %s", + nm_connection_get_id (connection), master_ac, nm_active_connection_get_path (master_ac)); } @@ -2608,6 +2594,19 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError * if (existing) nm_device_steal_connection (existing, connection); + if (nm_device_get_state (device) == NM_DEVICE_STATE_UNMANAGED) { + nm_device_state_changed (device, + NM_DEVICE_STATE_UNAVAILABLE, + NM_DEVICE_STATE_REASON_USER_REQUESTED); + } + + if ( nm_device_is_available (device, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE) + && (nm_device_get_state (device) == NM_DEVICE_STATE_UNAVAILABLE)) { + nm_device_state_changed (device, + NM_DEVICE_STATE_DISCONNECTED, + NM_DEVICE_STATE_REASON_USER_REQUESTED); + } + /* 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); @@ -2828,6 +2827,23 @@ nm_manager_activate_connection (NMManager *self, return active; } +/** + * validate_activation_request: + * @self: the #NMManager + * @context: the D-Bus context of the requestor + * @connection: the partial or complete #NMConnection to be activated + * @device_path: the object path of the device to be activated, or "/" + * @out_device: on successful reutrn, the #NMDevice to be activated with @connection + * @out_vpn: on successful return, %TRUE if @connection is a VPN connection + * @error: location to store an error on failure + * + * Performs basic validation on an activation request, including ensuring that + * the requestor is a valid Unix process, is not disallowed in @connection + * permissions, and that a device exists that can activate @connection. + * + * Returns: on success, the #NMAuthSubject representing the requestor, or + * %NULL on error + */ static NMAuthSubject * validate_activation_request (NMManager *self, DBusGMethodInvocation *context, @@ -2902,11 +2918,11 @@ validate_activation_request (NMManager *self, } else device = nm_manager_get_best_device_for_connection (self, connection); - if (!device) { + if (!device && !vpn) { gboolean is_software = nm_connection_is_virtual (connection); /* VPN and software-device connections don't need a device yet */ - if (!vpn && !is_software) { + if (!is_software) { g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, @@ -2922,11 +2938,19 @@ validate_activation_request (NMManager *self, if (!iface) goto error; - device = find_device_by_iface (self, iface); + device = find_device_by_iface (self, iface, connection, NULL); g_free (iface); } } + if ((!vpn || device_path) && !device) { + g_set_error_literal (error, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_UNKNOWN_DEVICE, + "Failed to find a compatible device for this connection"); + goto error; + } + *out_device = device; *out_vpn = vpn; return subject; @@ -3203,14 +3227,6 @@ impl_manager_add_and_activate_connection (NMManager *self, if (!subject) goto error; - /* AddAndActivate() requires a device to complete the connection with */ - if (!device) { - error = g_error_new_literal (NM_MANAGER_ERROR, - NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "This connection requires an existing device."); - goto error; - } - all_connections = nm_settings_get_connections (priv->settings); if (vpn) { /* Try to fill the VPN's connection setting and name at least */ |