diff options
Diffstat (limited to 'src/nm-manager.c')
-rw-r--r-- | src/nm-manager.c | 4531 |
1 files changed, 2293 insertions, 2238 deletions
diff --git a/src/nm-manager.c b/src/nm-manager.c index 9037e649c..09a1985a7 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -21,6 +21,7 @@ #include <config.h> +#include <stdlib.h> #include <netinet/ether.h> #include <fcntl.h> #include <errno.h> @@ -38,37 +39,32 @@ #include "nm-logging.h" #include "nm-dbus-manager.h" #include "nm-vpn-manager.h" -#include "nm-modem-manager.h" -#include "nm-device-bt.h" #include "nm-device.h" #include "nm-device-ethernet.h" -#include "nm-device-wifi.h" -#include "nm-device-olpc-mesh.h" -#include "nm-device-modem.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-adsl.h" -#include "nm-system.h" -#include "nm-properties-changed-signal.h" -#include "nm-setting-bluetooth.h" +#include "nm-device-generic.h" +#include "nm-device-veth.h" +#include "nm-device-tun.h" +#include "nm-device-macvlan.h" +#include "nm-device-vxlan.h" +#include "nm-device-gre.h" #include "nm-setting-connection.h" #include "nm-setting-wireless.h" #include "nm-setting-vpn.h" -#include "nm-marshal.h" #include "nm-dbus-glib-types.h" -#include "nm-udev-manager.h" -#include "nm-hostname-provider.h" -#include "nm-bluez-manager.h" -#include "nm-bluez-common.h" +#include "nm-platform.h" +#include "nm-rfkill-manager.h" +#include "nm-dhcp-manager.h" #include "nm-settings.h" #include "nm-settings-connection.h" #include "nm-manager-auth.h" #include "NetworkManagerUtils.h" #include "nm-utils.h" #include "nm-device-factory.h" -#include "wifi-utils.h" #include "nm-enum-types.h" #include "nm-sleep-monitor.h" #include "nm-connectivity.h" @@ -118,10 +114,10 @@ static gboolean impl_manager_get_state (NMManager *manager, guint32 *state, GError **error); -static gboolean impl_manager_set_logging (NMManager *manager, - const char *level, - const char *domains, - GError **error); +static void impl_manager_set_logging (NMManager *manager, + const char *level, + const char *domains, + DBusGMethodInvocation *context); static void impl_manager_get_logging (NMManager *manager, char **level, @@ -132,67 +128,37 @@ static void impl_manager_check_connectivity (NMManager *manager, #include "nm-manager-glue.h" -static void bluez_manager_bdaddr_added_cb (NMBluezManager *bluez_mgr, - NMBluezDevice *bt_device, - const char *bdaddr, - const char *name, - const char *object_path, - guint32 uuids, - NMManager *manager); - -static void bluez_manager_bdaddr_removed_cb (NMBluezManager *bluez_mgr, - const char *bdaddr, - const char *object_path, - gpointer user_data); - -static void add_device (NMManager *self, NMDevice *device); - -static void hostname_provider_init (NMHostnameProvider *provider_class); - -static NMActiveConnection *internal_activate_device (NMManager *manager, - NMDevice *device, - NMConnection *connection, - const char *specific_object, - gboolean user_requested, - gulong sender_uid, - const char *dbus_sender, - gboolean assumed, - NMActiveConnection *master, - GError **error); - -static NMDevice *find_device_by_ip_iface (NMManager *self, const gchar *iface); +static void add_device (NMManager *self, NMDevice *device, gboolean generate_con); +static void remove_device (NMManager *self, NMDevice *device, gboolean quitting); -static GSList * remove_one_device (NMManager *manager, - GSList *list, - NMDevice *device, - gboolean quitting); +static NMActiveConnection *_new_active_connection (NMManager *self, + NMConnection *connection, + const char *specific_object, + NMDevice *device, + NMAuthSubject *subject, + GError **error); -static void rfkill_change_wifi (const char *desc, gboolean enabled); +static void policy_activating_device_changed (GObject *object, GParamSpec *pspec, gpointer user_data); -#define SSD_POKE_INTERVAL 120 -#define ORIGDEV_TAG "originating-device" +static NMDevice *find_device_by_ip_iface (NMManager *self, const gchar *iface); -typedef struct PendingActivation PendingActivation; -typedef void (*PendingActivationFunc) (PendingActivation *pending, - GError *error); +static void rfkill_change (const char *desc, RfKillType rtype, gboolean enabled); -struct PendingActivation { - NMManager *manager; +static gboolean find_master (NMManager *self, + NMConnection *connection, + NMDevice *device, + NMConnection **out_master_connection, + NMDevice **out_master_device, + NMActiveConnection **out_master_ac, + GError **error); - DBusGMethodInvocation *context; - PendingActivationFunc callback; - NMAuthChain *chain; - const char *wifi_shared_permission; +static void nm_manager_update_state (NMManager *manager); - char *connection_path; - NMConnection *connection; - char *specific_object_path; - char *device_path; -}; +#define SSD_POKE_INTERVAL 120 +#define ORIGDEV_TAG "originating-device" typedef struct { gboolean user_enabled; - gboolean daemon_enabled; gboolean sw_enabled; gboolean hw_enabled; RfKillType rtype; @@ -200,8 +166,6 @@ typedef struct { const char *key; const char *prop; const char *hw_prop; - RfKillState (*other_enabled_func) (NMManager *); - RfKillState (*daemon_enabled_func) (NMManager *); } RadioState; typedef struct { @@ -216,13 +180,13 @@ typedef struct { NMState state; NMConnectivity *connectivity; + int ignore_link_added_cb; + NMPolicy *policy; NMDBusManager *dbus_mgr; - guint dbus_connection_changed_id; - NMUdevManager *udev_mgr; - NMBluezManager *bluez_mgr; - NMSessionMonitor *session_monitor; + gboolean prop_filter_added; + NMRfkillManager *rfkill_mgr; /* List of NMDeviceFactoryFunc pointers sorted in priority order */ GSList *factories; @@ -236,10 +200,6 @@ typedef struct { NMVPNManager *vpn_manager; - NMModemManager *modem_manager; - guint modem_added_id; - guint modem_removed_id; - DBusGProxy *aipd_proxy; NMSleepMonitor *sleep_monitor; @@ -247,27 +207,21 @@ typedef struct { /* Firmware dir monitor */ GFileMonitor *fw_monitor; - guint fw_monitor_id; guint fw_changed_id; guint timestamp_update_id; - GHashTable *nm_bridges; - - gboolean disposed; + gboolean startup; } NMManagerPrivate; #define NM_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_MANAGER, NMManagerPrivate)) -G_DEFINE_TYPE_EXTENDED (NMManager, nm_manager, G_TYPE_OBJECT, 0, - G_IMPLEMENT_INTERFACE (NM_TYPE_HOSTNAME_PROVIDER, - hostname_provider_init)) +G_DEFINE_TYPE (NMManager, nm_manager, G_TYPE_OBJECT) enum { DEVICE_ADDED, DEVICE_REMOVED, STATE_CHANGED, - PROPERTIES_CHANGED, CHECK_PERMISSIONS, USER_PERMISSIONS_CHANGED, ACTIVE_CONNECTION_ADDED, @@ -282,6 +236,7 @@ enum { PROP_0, PROP_VERSION, PROP_STATE, + PROP_STARTUP, PROP_NETWORKING_ENABLED, PROP_WIRELESS_ENABLED, PROP_WIRELESS_HARDWARE_ENABLED, @@ -293,6 +248,7 @@ enum { PROP_CONNECTIVITY, PROP_PRIMARY_CONNECTION, PROP_ACTIVATING_CONNECTION, + PROP_DEVICES, /* Not exported */ PROP_HOSTNAME, @@ -320,13 +276,29 @@ nm_manager_error_quark (void) static void active_connection_state_changed (NMActiveConnection *active, GParamSpec *pspec, NMManager *self); +static void active_connection_default_changed (NMActiveConnection *active, + GParamSpec *pspec, + NMManager *self); -static void -active_connection_removed (NMManager *self, NMActiveConnection *active) +/* Returns: whether to notify D-Bus of the removal or not */ +static gboolean +active_connection_remove (NMManager *self, NMActiveConnection *active) { - g_signal_emit (self, signals[ACTIVE_CONNECTION_REMOVED], 0, active); - g_signal_handlers_disconnect_by_func (active, active_connection_state_changed, self); - g_object_unref (active); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + gboolean notify = !!nm_active_connection_get_path (active); + GSList *found; + + /* FIXME: switch to a GList for faster removal */ + found = g_slist_find (priv->active_connections, active); + if (found) { + priv->active_connections = g_slist_remove (priv->active_connections, active); + g_signal_emit (self, signals[ACTIVE_CONNECTION_REMOVED], 0, active); + g_signal_handlers_disconnect_by_func (active, active_connection_state_changed, self); + g_signal_handlers_disconnect_by_func (active, active_connection_default_changed, self); + g_object_unref (active); + } + + return found && notify; } static gboolean @@ -335,24 +307,21 @@ _active_connection_cleanup (gpointer user_data) NMManager *self = NM_MANAGER (user_data); NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); GSList *iter; - gboolean changed = FALSE; priv->ac_cleanup_id = 0; + g_object_freeze_notify (G_OBJECT (self)); iter = priv->active_connections; while (iter) { NMActiveConnection *ac = iter->data; iter = iter->next; if (nm_active_connection_get_state (ac) == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED) { - priv->active_connections = g_slist_remove (priv->active_connections, ac); - active_connection_removed (self, ac); - changed = TRUE; + if (active_connection_remove (self, ac)) + g_object_notify (G_OBJECT (self), NM_MANAGER_ACTIVE_CONNECTIONS); } } - - if (changed) - g_object_notify (G_OBJECT (self), NM_MANAGER_ACTIVE_CONNECTIONS); + g_object_thaw_notify (G_OBJECT (self)); return FALSE; } @@ -375,8 +344,25 @@ active_connection_state_changed (NMActiveConnection *active, if (!priv->ac_cleanup_id) priv->ac_cleanup_id = g_idle_add (_active_connection_cleanup, self); } + + nm_manager_update_state (self); +} + +static void +active_connection_default_changed (NMActiveConnection *active, + GParamSpec *pspec, + NMManager *self) +{ + nm_manager_update_state (self); } +/** + * active_connection_add(): + * @self: the #NMManager + * @active: the #NMActiveConnection to manage + * + * Begins to track and manage @active. Increases the refcount of @active. + */ static void active_connection_add (NMManager *self, NMActiveConnection *active) { @@ -384,13 +370,27 @@ active_connection_add (NMManager *self, NMActiveConnection *active) g_return_if_fail (g_slist_find (priv->active_connections, active) == FALSE); - priv->active_connections = g_slist_prepend (priv->active_connections, active); - g_signal_connect (active, "notify::" NM_ACTIVE_CONNECTION_STATE, + priv->active_connections = g_slist_prepend (priv->active_connections, + g_object_ref (active)); + + g_signal_connect (active, + "notify::" NM_ACTIVE_CONNECTION_STATE, G_CALLBACK (active_connection_state_changed), self); + g_signal_connect (active, + "notify::" NM_ACTIVE_CONNECTION_DEFAULT, + G_CALLBACK (active_connection_default_changed), + self); + g_signal_connect (active, + "notify::" NM_ACTIVE_CONNECTION_DEFAULT6, + G_CALLBACK (active_connection_default_changed), + self); g_signal_emit (self, signals[ACTIVE_CONNECTION_ADDED], 0, active); - g_object_notify (G_OBJECT (self), NM_MANAGER_ACTIVE_CONNECTIONS); + + /* Only notify D-Bus if the active connection is actually exported */ + if (nm_active_connection_get_path (active)) + g_object_notify (G_OBJECT (self), NM_MANAGER_ACTIVE_CONNECTIONS); } const GSList * @@ -400,6 +400,55 @@ nm_manager_get_active_connections (NMManager *manager) } static NMActiveConnection * +find_ac_for_connection (NMManager *manager, NMConnection *connection) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + GSList *iter; + NMActiveConnection *ac; + NMConnection *ac_connection; + NMActiveConnectionState ac_state; + const char *uuid; + + uuid = nm_connection_get_uuid (connection); + for (iter = priv->active_connections; iter; iter = iter->next) { + ac = iter->data; + ac_connection = nm_active_connection_get_connection (ac); + ac_state = nm_active_connection_get_state (ac); + + if ( !strcmp (nm_connection_get_uuid (ac_connection), uuid) + && (ac_state < NM_ACTIVE_CONNECTION_STATE_DEACTIVATED)) + return ac; + } + + return NULL; +} + +/* Filter out connections that are already active. + * nm_settings_get_connections() returns sorted list. We need to preserve the + * order so that we didn't change auto-activation order (recent timestamps + * are first). + * Caller is responsible for freeing the returned list with g_slist_free(). + */ +GSList * +nm_manager_get_activatable_connections (NMManager *manager) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + GSList *all_connections = nm_settings_get_connections (priv->settings); + GSList *connections = NULL, *iter; + NMConnection *connection; + + for (iter = all_connections; iter; iter = iter->next) { + connection = iter->data; + + if (!find_ac_for_connection (manager, connection)) + connections = g_slist_prepend (connections, connection); + } + + g_slist_free (all_connections); + return g_slist_reverse (connections); +} + +static NMActiveConnection * active_connection_get_by_path (NMManager *manager, const char *path) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); @@ -411,7 +460,7 @@ active_connection_get_by_path (NMManager *manager, const char *path) for (iter = priv->active_connections; iter; iter = g_slist_next (iter)) { NMActiveConnection *candidate = iter->data; - if (strcmp (path, nm_active_connection_get_path (candidate)) == 0) + if (g_strcmp0 (path, nm_active_connection_get_path (candidate)) == 0) return candidate; } return NULL; @@ -465,6 +514,21 @@ nm_manager_get_device_by_master (NMManager *manager, const char *master, const c return NULL; } +NMDevice * +nm_manager_get_device_by_ifindex (NMManager *manager, int ifindex) +{ + GSList *iter; + + for (iter = NM_MANAGER_GET_PRIVATE (manager)->devices; iter; iter = iter->next) { + NMDevice *device = NM_DEVICE (iter->data); + + if (nm_device_get_ifindex (device) == ifindex) + return device; + } + + return NULL; +} + static gboolean manager_sleeping (NMManager *self) { @@ -475,95 +539,41 @@ manager_sleeping (NMManager *self) return FALSE; } -static void -modem_added (NMModemManager *modem_manager, - NMModem *modem, - const char *driver, - gpointer user_data) +static const char * +_nm_state_to_string (NMState state) { - NMManager *self = NM_MANAGER (user_data); - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - NMDevice *replace_device, *device = NULL; - const char *modem_iface; - GSList *iter; - - /* Don't rely only on the data port; use the control port if available */ - modem_iface = nm_modem_get_data_port (modem); - if (!modem_iface) - modem_iface = nm_modem_get_control_port (modem); - g_return_if_fail (modem_iface); - - replace_device = find_device_by_ip_iface (NM_MANAGER (user_data), modem_iface); - if (replace_device) { - priv->devices = remove_one_device (NM_MANAGER (user_data), - priv->devices, - replace_device, - FALSE); - } - - /* Give Bluetooth DUN devices first chance to claim the modem */ - for (iter = priv->devices; iter; iter = g_slist_next (iter)) { - if (nm_device_get_device_type (iter->data) == NM_DEVICE_TYPE_BT) { - if (nm_device_bt_modem_added (NM_DEVICE_BT (iter->data), modem, driver)) - return; - } - } - - /* If it was a Bluetooth modem and no bluetooth device claimed it, ignore - * it. The rfcomm port (and thus the modem) gets created automatically - * by the Bluetooth code during the connection process. - */ - if (driver && !strcmp (driver, "bluetooth")) { - nm_log_info (LOGD_MB, "ignoring modem '%s' (no associated Bluetooth device)", modem_iface); - return; + switch (state) { + case NM_STATE_ASLEEP: + return "ASLEEP"; + case NM_STATE_DISCONNECTED: + return "DISCONNECTED"; + case NM_STATE_DISCONNECTING: + return "DISCONNECTING"; + case NM_STATE_CONNECTING: + return "CONNECTING"; + case NM_STATE_CONNECTED_LOCAL: + return "CONNECTED_LOCAL"; + case NM_STATE_CONNECTED_SITE: + return "CONNECTED_SITE"; + case NM_STATE_CONNECTED_GLOBAL: + return "CONNECTED_GLOBAL"; + case NM_STATE_UNKNOWN: + default: + return "UNKNOWN"; } - - /* Make the new modem device */ - device = nm_device_modem_new (modem, driver); - if (device) - add_device (self, device); } static void set_state (NMManager *manager, NMState state) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); - const char *state_str; if (priv->state == state) return; priv->state = state; - switch (state) { - case NM_STATE_ASLEEP: - state_str = "ASLEEP"; - break; - case NM_STATE_DISCONNECTED: - state_str = "DISCONNECTED"; - break; - case NM_STATE_DISCONNECTING: - state_str = "DISCONNECTING"; - break; - case NM_STATE_CONNECTING: - state_str = "CONNECTING"; - break; - case NM_STATE_CONNECTED_LOCAL: - state_str = "CONNECTED_LOCAL"; - break; - case NM_STATE_CONNECTED_SITE: - state_str = "CONNECTED_SITE"; - break; - case NM_STATE_CONNECTED_GLOBAL: - state_str = "CONNECTED_GLOBAL"; - break; - case NM_STATE_UNKNOWN: - default: - state_str = "UNKNOWN"; - break; - } - - nm_log_info (LOGD_CORE, "NetworkManager state is now %s", state_str); + nm_log_info (LOGD_CORE, "NetworkManager state is now %s", _nm_state_to_string (state)); g_object_notify (G_OBJECT (manager), NM_MANAGER_STATE); g_signal_emit (manager, signals[STATE_CHANGED], 0, priv->state); @@ -590,12 +600,59 @@ checked_connectivity (GObject *object, GAsyncResult *result, gpointer user_data) g_object_unref (manager); } +static NMState +find_best_device_state (NMManager *manager, gboolean *want_connectivity_check) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + NMState best_state = NM_STATE_DISCONNECTED; + GSList *iter; + + for (iter = priv->active_connections; iter; iter = iter->next) { + NMActiveConnection *ac = NM_ACTIVE_CONNECTION (iter->data); + NMActiveConnectionState ac_state = nm_active_connection_get_state (ac); + + switch (ac_state) { + case NM_ACTIVE_CONNECTION_STATE_ACTIVATED: + if ( nm_active_connection_get_default (ac) + || nm_active_connection_get_default6 (ac)) { + nm_connectivity_set_online (priv->connectivity, TRUE); + if (nm_connectivity_get_state (priv->connectivity) == NM_CONNECTIVITY_FULL) { + *want_connectivity_check = FALSE; + return NM_STATE_CONNECTED_GLOBAL; + } + + best_state = NM_STATE_CONNECTING; + *want_connectivity_check = TRUE; + } else { + if (best_state < NM_STATE_CONNECTING) + best_state = NM_STATE_CONNECTED_LOCAL; + } + break; + case NM_ACTIVE_CONNECTION_STATE_ACTIVATING: + if (!nm_active_connection_get_assumed (ac)) { + if (best_state != NM_STATE_CONNECTED_GLOBAL) + best_state = NM_STATE_CONNECTING; + } + break; + case NM_ACTIVE_CONNECTION_STATE_DEACTIVATING: + if (!nm_active_connection_get_assumed (ac)) { + if (best_state < NM_STATE_DISCONNECTING) + best_state = NM_STATE_DISCONNECTING; + } + break; + default: + break; + } + } + + return best_state; +} + static void nm_manager_update_state (NMManager *manager) { NMManagerPrivate *priv; NMState new_state = NM_STATE_DISCONNECTED; - GSList *iter; gboolean want_connectivity_check = FALSE; g_return_if_fail (NM_IS_MANAGER (manager)); @@ -604,30 +661,8 @@ nm_manager_update_state (NMManager *manager) if (manager_sleeping (manager)) new_state = NM_STATE_ASLEEP; - else { - for (iter = priv->devices; iter; iter = iter->next) { - NMDevice *dev = NM_DEVICE (iter->data); - NMDeviceState state = nm_device_get_state (dev); - - if (state == NM_DEVICE_STATE_ACTIVATED) { - nm_connectivity_set_online (priv->connectivity, TRUE); - if (nm_connectivity_get_state (priv->connectivity) != NM_CONNECTIVITY_FULL) { - new_state = NM_STATE_CONNECTING; - want_connectivity_check = TRUE; - } else { - new_state = NM_STATE_CONNECTED_GLOBAL; - break; - } - } - - if (nm_device_is_activating (dev)) - new_state = NM_STATE_CONNECTING; - else if (new_state != NM_STATE_CONNECTING) { - if (state == NM_DEVICE_STATE_DEACTIVATING) - new_state = NM_STATE_DISCONNECTING; - } - } - } + else + new_state = find_best_device_state (manager, &want_connectivity_check); if (new_state == NM_STATE_CONNECTING && want_connectivity_check) { nm_connectivity_check_async (priv->connectivity, @@ -660,65 +695,99 @@ manager_device_state_changed (NMDevice *device, default: break; } +} - nm_manager_update_state (self); +static void device_has_pending_action_changed (NMDevice *device, + GParamSpec *pspec, + NMManager *self); + +static void +check_if_startup_complete (NMManager *self) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + GSList *iter; + + if (!priv->startup) + return; + + for (iter = priv->devices; iter; iter = iter->next) { + NMDevice *dev = iter->data; + + if (nm_device_has_pending_action (dev)) { + nm_log_dbg (LOGD_CORE, "check_if_startup_complete returns FALSE because of %s", + nm_device_get_iface (dev)); + return; + } + } + + nm_log_info (LOGD_CORE, "startup complete"); + + priv->startup = FALSE; + g_object_notify (G_OBJECT (self), "startup"); + + /* We don't have to watch notify::has-pending-action any more. */ + for (iter = priv->devices; iter; iter = iter->next) { + NMDevice *dev = iter->data; + + g_signal_handlers_disconnect_by_func (dev, G_CALLBACK (device_has_pending_action_changed), self); + } +} + +static void +device_has_pending_action_changed (NMDevice *device, + GParamSpec *pspec, + NMManager *self) +{ + check_if_startup_complete (self); } -/* Removes a device from a device list; returns the start of the new device list */ -static GSList * -remove_one_device (NMManager *manager, - GSList *list, - NMDevice *device, - gboolean quitting) +static void +remove_device (NMManager *manager, NMDevice *device, gboolean quitting) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); if (nm_device_get_managed (device)) { - /* When quitting, we want to leave up interfaces & connections - * that can be taken over again (ie, "assumed") when NM restarts - * so that '/etc/init.d/NetworkManager restart' will not distrupt - * networking for interfaces that support connection assumption. - * All other devices get unmanaged when NM quits so that their - * connections get torn down and the interface is deactivated. - */ + NMActRequest *req = nm_device_get_act_request (device); + gboolean unmanage = FALSE; - if ( !nm_device_can_assume_connections (device) - || (nm_device_get_state (device) != NM_DEVICE_STATE_ACTIVATED) - || !quitting) - nm_device_set_managed (device, FALSE, NM_DEVICE_STATE_REASON_REMOVED); + /* Leave activated interfaces up when quitting so their configuration + * can be taken over when NM restarts. This ensures connectivity while + * NM is stopped. Devices which do not support connection assumption + * cannot be left up. + */ + if (!quitting) /* Forced removal; device already gone */ + unmanage = TRUE; + else if (!nm_device_can_assume_active_connection (device)) + unmanage = TRUE; + else if (!req) + unmanage = TRUE; + + if (unmanage) { + if (quitting) + nm_device_set_unmanaged_quitting (device); + else + nm_device_set_unmanaged (device, NM_UNMANAGED_INTERNAL, TRUE, NM_DEVICE_STATE_REASON_REMOVED); + } } - g_signal_handlers_disconnect_by_func (device, manager_device_state_changed, manager); + g_signal_handlers_disconnect_matched (device, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, manager); + + nm_settings_device_removed (priv->settings, device, quitting); + priv->devices = g_slist_remove (priv->devices, device); - nm_settings_device_removed (priv->settings, device); g_signal_emit (manager, signals[DEVICE_REMOVED], 0, device); + g_object_notify (G_OBJECT (manager), NM_MANAGER_DEVICES); + + nm_dbus_manager_unregister_object (priv->dbus_mgr, device); g_object_unref (device); - return g_slist_remove (list, device); + check_if_startup_complete (manager); } static void -modem_removed (NMModemManager *modem_manager, - NMModem *modem, - gpointer user_data) +device_removed_cb (NMDevice *device, gpointer user_data) { - NMManager *self = NM_MANAGER (user_data); - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - NMDevice *found; - GSList *iter; - - /* Give Bluetooth DUN devices first chance to handle the modem removal */ - for (iter = priv->devices; iter; iter = g_slist_next (iter)) { - if (nm_device_get_device_type (iter->data) == NM_DEVICE_TYPE_BT) { - if (nm_device_bt_modem_removed (NM_DEVICE_BT (iter->data), modem)) - return; - } - } - - /* Otherwise remove the standalone modem */ - found = nm_manager_get_device_by_udi (self, nm_modem_get_path (modem)); - if (found) - priv->devices = remove_one_device (self, priv->devices, found, FALSE); + remove_device (NM_MANAGER (user_data), device, FALSE); } static void @@ -760,18 +829,6 @@ aipd_handle_event (DBusGProxy *proxy, nm_log_warn (LOGD_AUTOIP4, "(%s): unhandled avahi-autoipd event", iface); } -static const char * -hostname_provider_get_hostname (NMHostnameProvider *provider) -{ - return NM_MANAGER_GET_PRIVATE (provider)->hostname; -} - -static void -hostname_provider_init (NMHostnameProvider *provider_class) -{ - provider_class->get_hostname = hostname_provider_get_hostname; -} - NMState nm_manager_get_state (NMManager *manager) { @@ -780,316 +837,54 @@ nm_manager_get_state (NMManager *manager) return NM_MANAGER_GET_PRIVATE (manager)->state; } -static gboolean -might_be_vpn (NMConnection *connection) -{ - NMSettingConnection *s_con; - const char *ctype = NULL; - - if (nm_connection_get_setting_vpn (connection)) - return TRUE; - - /* Make sure it's not a VPN, which we can't autocomplete yet */ - s_con = nm_connection_get_setting_connection (connection); - if (s_con) - ctype = nm_setting_connection_get_connection_type (s_con); - - return (g_strcmp0 (ctype, NM_SETTING_VPN_SETTING_NAME) == 0); -} - -static gboolean -try_complete_vpn (NMConnection *connection, GSList *existing, GError **error) -{ - g_assert (might_be_vpn (connection) == TRUE); - - if (!nm_connection_get_setting_vpn (connection)) { - g_set_error_literal (error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_UNSUPPORTED_CONNECTION_TYPE, - "VPN connections require a 'vpn' setting"); - return FALSE; - } - - nm_utils_complete_generic (connection, - NM_SETTING_VPN_SETTING_NAME, - existing, - _("VPN connection %d"), - NULL, - FALSE); /* No IPv6 by default for now */ - - return TRUE; -} - -static PendingActivation * -pending_activation_new (NMManager *manager, - DBusGMethodInvocation *context, - const char *device_path, - const char *connection_path, - GHashTable *settings, - const char *specific_object_path, - PendingActivationFunc callback, - GError **error) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); - PendingActivation *pending; - NMDevice *device = NULL; - NMConnection *connection = NULL; - GSList *all_connections = NULL; - gboolean success; - - g_return_val_if_fail (manager != NULL, NULL); - g_return_val_if_fail (context != NULL, NULL); - g_return_val_if_fail (device_path != NULL, NULL); - - /* A object path of "/" means NULL */ - if (g_strcmp0 (specific_object_path, "/") == 0) - specific_object_path = NULL; - if (g_strcmp0 (device_path, "/") == 0) - device_path = NULL; - - /* Create the partial connection from the given settings */ - if (settings) { - if (device_path) - device = nm_manager_get_device_by_path (manager, device_path); - if (!device) { - g_set_error_literal (error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "Device not found"); - return NULL; - } - - connection = nm_connection_new (); - nm_connection_replace_settings (connection, settings, NULL); - - all_connections = nm_settings_get_connections (priv->settings); - - if (might_be_vpn (connection)) { - /* Try to fill the VPN's connection setting and name at least */ - success = try_complete_vpn (connection, all_connections, error); - } else { - /* Let each device subclass complete the connection */ - success = nm_device_complete_connection (device, - connection, - specific_object_path, - all_connections, - error); - } - g_slist_free (all_connections); - - if (success == FALSE) { - g_object_unref (connection); - return NULL; - } - } - - pending = g_slice_new0 (PendingActivation); - pending->manager = manager; - pending->context = context; - pending->callback = callback; - - pending->connection_path = g_strdup (connection_path); - pending->connection = connection; - - /* "/" is special-cased to NULL to get through D-Bus */ - if (specific_object_path && strcmp (specific_object_path, "/")) - pending->specific_object_path = g_strdup (specific_object_path); - if (device_path && strcmp (device_path, "/")) - pending->device_path = g_strdup (device_path); - - return pending; -} - -static void -pending_auth_done (NMAuthChain *chain, - GError *error, - DBusGMethodInvocation *context, - gpointer user_data) -{ - PendingActivation *pending = user_data; - NMAuthCallResult result; - GError *tmp_error = NULL; - - /* Caller has had a chance to obtain authorization, so we only need to - * check for 'yes' here. - */ - result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL)); - if (result != NM_AUTH_CALL_RESULT_YES) { - tmp_error = g_error_new_literal (NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - "Not authorized to control networking."); - goto out; - } - - if (pending->wifi_shared_permission) { - result = nm_auth_chain_get_result (chain, pending->wifi_shared_permission); - if (result != NM_AUTH_CALL_RESULT_YES) { - tmp_error = g_error_new_literal (NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - "Not authorized to share connections via wifi."); - goto out; - } - } - - /* Otherwise authorized and available to activate */ - -out: - pending->callback (pending, tmp_error); - g_clear_error (&tmp_error); -} - -static void -pending_activation_check_authorized (PendingActivation *pending, - NMDBusManager *dbus_mgr) -{ - NMManagerPrivate *priv; - char *error_desc = NULL; - gulong sender_uid = G_MAXULONG; - GError *error; - const char *wifi_permission = NULL; - NMConnection *connection; - - g_return_if_fail (pending != NULL); - g_return_if_fail (dbus_mgr != NULL); - - priv = NM_MANAGER_GET_PRIVATE (pending->manager); - - if (!nm_auth_get_caller_uid (pending->context, - dbus_mgr, - &sender_uid, - &error_desc)) { - error = g_error_new_literal (NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - error_desc); - pending->callback (pending, error); - g_error_free (error); - g_free (error_desc); - return; - } - - /* Yay for root */ - if (0 == sender_uid) { - pending->callback (pending, NULL); - return; - } - - /* By this point we have an auto-completed connection (for AddAndActivate) - * or an existing connection (for Activate). - */ - connection = pending->connection; - if (!connection) - connection = (NMConnection *) nm_settings_get_connection_by_path (priv->settings, pending->connection_path); - - if (!connection) { - error = g_error_new_literal (NM_MANAGER_ERROR, - NM_MANAGER_ERROR_UNKNOWN_CONNECTION, - "Connection could not be found."); - pending->callback (pending, error); - g_error_free (error); - return; - } - - /* Ensure the subject has permissions for this connection */ - if (!nm_auth_uid_in_acl (connection, - priv->session_monitor, - sender_uid, - &error_desc)) { - error = g_error_new_literal (NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - error_desc); - pending->callback (pending, error); - g_error_free (error); - g_free (error_desc); - return; - } - - /* First check if the user is allowed to use networking at all, giving - * the user a chance to authenticate to gain the permission. - */ - pending->chain = nm_auth_chain_new (pending->context, - NULL, - pending_auth_done, - pending); - g_assert (pending->chain); - nm_auth_chain_add_call (pending->chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE); - - /* Shared wifi connections require special permissions too */ - wifi_permission = nm_utils_get_shared_wifi_permission (connection); - if (wifi_permission) { - pending->wifi_shared_permission = wifi_permission; - nm_auth_chain_add_call (pending->chain, wifi_permission, TRUE); - } -} - -static void -pending_activation_destroy (PendingActivation *pending, - GError *error, - NMActiveConnection *ac) -{ - g_return_if_fail (pending != NULL); - - if (error) - dbus_g_method_return_error (pending->context, error); - else if (ac) { - if (pending->connection) { - dbus_g_method_return (pending->context, - pending->connection_path, - nm_active_connection_get_path (ac)); - } else { - dbus_g_method_return (pending->context, - nm_active_connection_get_path (ac)); - } - } - - g_free (pending->connection_path); - g_free (pending->specific_object_path); - g_free (pending->device_path); - if (pending->connection) - g_object_unref (pending->connection); - - if (pending->chain) - nm_auth_chain_unref (pending->chain); - - memset (pending, 0, sizeof (PendingActivation)); - g_slice_free (PendingActivation, pending); -} - /*******************************************************************/ /* Settings stuff via NMSettings */ /*******************************************************************/ static NMDevice * -get_device_from_hwaddr (NMManager *self, NMConnection *connection) +get_device_from_hwaddr (NMManager *self, const GByteArray *setting_mac) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + const guint8 *device_mac; + guint device_mac_len; GSList *iter; + if (!setting_mac) + return NULL; + for (iter = priv->devices; iter; iter = g_slist_next (iter)) { - if (nm_device_hwaddr_matches (NM_DEVICE (iter->data), connection, NULL, 0, TRUE)) - return iter->data; + NMDevice *device = iter->data; + + device_mac = nm_device_get_hw_address (iter->data, &device_mac_len); + if ( setting_mac->len == device_mac_len + && memcmp (setting_mac->data, device_mac, device_mac_len) == 0) + return device; } return NULL; } -static NMDevice* +static NMDevice * find_vlan_parent (NMManager *self, - NMConnection *connection, - gboolean check_hwaddr) + 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 GByteArray *setting_mac; GSList *iter; - /* The 'parent' property could be either an interface name, a connection - * UUID, or even given by the MAC address of the connection's ethernet - * or WiFi setting. + /* 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); @@ -1114,18 +909,37 @@ find_vlan_parent (NMManager *self, } /* Check the hardware address of the parent connection */ - if (check_hwaddr) - return get_device_from_hwaddr (self, parent_connection); + return get_device_from_hwaddr (self, setting_mac); } return NULL; } } /* Try the hardware address from the VLAN connection's hardware setting */ - if (check_hwaddr) - return get_device_from_hwaddr (self, connection); + return get_device_from_hwaddr (self, setting_mac); +} - return NULL; +static NMDevice * +find_infiniband_parent (NMManager *self, + NMConnection *connection) +{ + NMSettingInfiniband *s_infiniband; + const char *parent_iface; + NMDevice *parent = NULL; + const GByteArray *setting_mac; + + s_infiniband = nm_connection_get_setting_infiniband (connection); + g_return_val_if_fail (s_infiniband != NULL, NULL); + + 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; + } + + setting_mac = nm_setting_infiniband_get_mac_address (s_infiniband); + return get_device_from_hwaddr (self, setting_mac); } /** @@ -1146,7 +960,6 @@ get_virtual_iface_name (NMManager *self, NMConnection *connection, NMDevice **out_parent) { - char *vname = NULL; NMDevice *parent = NULL; if (out_parent) @@ -1155,17 +968,21 @@ get_virtual_iface_name (NMManager *self, if (nm_connection_is_type (connection, NM_SETTING_BOND_SETTING_NAME)) return g_strdup (nm_connection_get_virtual_iface_name (connection)); + if (nm_connection_is_type (connection, NM_SETTING_TEAM_SETTING_NAME)) + return g_strdup (nm_connection_get_virtual_iface_name (connection)); + if (nm_connection_is_type (connection, NM_SETTING_BRIDGE_SETTING_NAME)) return g_strdup (nm_connection_get_virtual_iface_name (connection)); if (nm_connection_is_type (connection, NM_SETTING_VLAN_SETTING_NAME)) { NMSettingVlan *s_vlan; const char *ifname; + 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, TRUE); + parent = find_vlan_parent (self, connection); if (parent) { ifname = nm_connection_get_virtual_iface_name (connection); @@ -1189,113 +1006,56 @@ get_virtual_iface_name (NMManager *self, } if (out_parent) *out_parent = parent; + return vname; } } - return vname; + if (nm_connection_is_type (connection, NM_SETTING_INFINIBAND_SETTING_NAME)) { + const char *ifname; + char *name; + + parent = find_infiniband_parent (self, connection); + if (parent) { + ifname = nm_connection_get_virtual_iface_name (connection); + if (ifname) + name = g_strdup (ifname); + else { + NMSettingInfiniband *s_infiniband; + int p_key; + + ifname = nm_device_get_iface (parent); + s_infiniband = nm_connection_get_setting_infiniband (connection); + p_key = nm_setting_infiniband_get_p_key (s_infiniband); + name = g_strdup_printf ("%s.%04x", ifname, p_key); + } + if (out_parent) + *out_parent = parent; + return name; + } + } + + return NULL; } static gboolean connection_needs_virtual_device (NMConnection *connection) { if ( nm_connection_is_type (connection, NM_SETTING_BOND_SETTING_NAME) + || nm_connection_is_type (connection, NM_SETTING_TEAM_SETTING_NAME) || nm_connection_is_type (connection, NM_SETTING_BRIDGE_SETTING_NAME) || nm_connection_is_type (connection, NM_SETTING_VLAN_SETTING_NAME)) return TRUE; - return FALSE; -} - -static char * -get_virtual_iface_placeholder_udi (void) -{ - static guint32 id = 0; - - return g_strdup_printf ("/virtual/device/placeholder/%d", id++); -} - -/***************************/ - -/* FIXME: remove when we handle bridges non-destructively */ - -#define NM_BRIDGE_FILE NMRUNDIR "/nm-bridges" - -static void -read_nm_created_bridges (NMManager *self) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - char *contents; - char **lines, **iter; - GTimeVal tv; - glong ts; - - if (!g_file_get_contents (NM_BRIDGE_FILE, &contents, NULL, NULL)) - return; - - g_get_current_time (&tv); - - lines = g_strsplit_set (contents, "\n", 0); - g_free (contents); - - for (iter = lines; iter && *iter; iter++) { - if (g_str_has_prefix (*iter, "ts=")) { - errno = 0; - ts = strtol (*iter + 3, NULL, 10); - /* allow 30 minutes time difference before we ignore the file */ - if (errno || ABS (tv.tv_sec - ts) > 1800) - goto out; - } else if (g_str_has_prefix (*iter, "iface=")) - g_hash_table_insert (priv->nm_bridges, g_strdup (*iter + 6), GUINT_TO_POINTER (1)); - } - -out: - g_strfreev (lines); - unlink (NM_BRIDGE_FILE); -} - -static void -write_nm_created_bridges (NMManager *self) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - GString *br_list; - GSList *iter; - GError *error = NULL; - GTimeVal tv; - gboolean found = FALSE; - - /* write out nm-created bridges list */ - br_list = g_string_sized_new (50); - - /* Timestamp is first line */ - g_get_current_time (&tv); - g_string_append_printf (br_list, "ts=%ld\n", tv.tv_sec); - - for (iter = priv->devices; iter; iter = g_slist_next (iter)) { - NMDevice *device = iter->data; + if (nm_connection_is_type (connection, NM_SETTING_INFINIBAND_SETTING_NAME)) { + NMSettingInfiniband *s_infiniband; - if (nm_device_get_device_type (device) == NM_DEVICE_TYPE_BRIDGE) { - g_string_append_printf (br_list, "iface=%s\n", nm_device_get_iface (device)); - found = TRUE; - } + s_infiniband = nm_connection_get_setting_infiniband (connection); + g_return_val_if_fail (s_infiniband != NULL, FALSE); + if (nm_setting_infiniband_get_p_key (s_infiniband) != -1) + return TRUE; } - if (found) { - if (!g_file_set_contents (NM_BRIDGE_FILE, br_list->str, -1, &error)) { - nm_log_warn (LOGD_BRIDGE, "Failed to write NetworkManager-created bridge list; " - "on restart bridges may not be recognized. (%s)", - error ? error->message : "unknown"); - g_clear_error (&error); - } - } - g_string_free (br_list, TRUE); -} - -static gboolean -bridge_created_by_nm (NMManager *self, const char *iface) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - - return (priv->nm_bridges && g_hash_table_lookup (priv->nm_bridges, iface)); + return FALSE; } /***************************/ @@ -1315,7 +1075,7 @@ system_create_virtual_device (NMManager *self, NMConnection *connection) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); GSList *iter; - char *iface = NULL, *udi; + char *iface = NULL; NMDevice *device = NULL, *parent = NULL; iface = get_virtual_iface_name (self, connection, &parent); @@ -1328,60 +1088,37 @@ system_create_virtual_device (NMManager *self, NMConnection *connection) /* Make sure we didn't create a device for this connection already */ for (iter = priv->devices; iter; iter = g_slist_next (iter)) { NMDevice *candidate = iter->data; - GError *error = NULL; if ( g_strcmp0 (nm_device_get_iface (candidate), iface) == 0 - || nm_device_check_connection_compatible (candidate, connection, &error)) { - g_clear_error (&error); + || nm_device_check_connection_compatible (candidate, connection)) goto out; - } - g_clear_error (&error); } - if (nm_connection_is_type (connection, NM_SETTING_BOND_SETTING_NAME)) { - if (!nm_system_add_bonding_master (iface)) { - nm_log_warn (LOGD_DEVICE, "(%s): failed to add bonding master interface for '%s'", - iface, nm_connection_get_id (connection)); - goto out; - } + /* 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++; - udi = get_virtual_iface_placeholder_udi (); - device = nm_device_bond_new (udi, iface); - g_free (udi); + 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)) { - gboolean exists = FALSE; - - if (!nm_system_create_bridge (iface, &exists)) { - nm_log_warn (LOGD_DEVICE, "(%s): failed to add bridging interface for '%s'", - iface, nm_connection_get_id (connection)); - goto out; - } - - /* FIXME: remove when we handle bridges non-destructively */ - if (exists && !bridge_created_by_nm (self, iface)) { - nm_log_warn (LOGD_DEVICE, "(%s): cannot use existing bridge for '%s'", - iface, nm_connection_get_id (connection)); - goto out; - } - - udi = get_virtual_iface_placeholder_udi (); - device = nm_device_bridge_new (udi, iface); - g_free (udi); + device = nm_device_bridge_new_for_connection (connection); } else if (nm_connection_is_type (connection, NM_SETTING_VLAN_SETTING_NAME)) { - g_return_val_if_fail (parent != NULL, FALSE); + 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); + } - if (!nm_system_add_vlan_iface (connection, iface, nm_device_get_ip_ifindex (parent))) { - nm_log_warn (LOGD_DEVICE, "(%s): failed to add VLAN interface for '%s'", - iface, nm_connection_get_id (connection)); - goto out; - } - udi = get_virtual_iface_placeholder_udi (); - device = nm_device_vlan_new (udi, iface, parent); - g_free (udi); + if (device) { + nm_device_set_nm_owned (device); + add_device (self, device, FALSE); + g_object_unref (device); } - if (device) - add_device (self, device); + priv->ignore_link_added_cb--; out: g_free (iface); @@ -1399,14 +1136,11 @@ system_create_virtual_devices (NMManager *self) connections = nm_settings_get_connections (priv->settings); for (iter = connections; iter; iter = g_slist_next (iter)) { NMConnection *connection = iter->data; - NMSettingConnection *s_con = nm_connection_get_setting_connection (connection); - g_assert (s_con); - if (connection_needs_virtual_device (connection)) { - /* We only create a virtual interface if the connection can autoconnect */ - if (nm_setting_connection_get_autoconnect (s_con)) - system_create_virtual_device (self, connection); - } + /* We only create a virtual interface if the connection can autoconnect */ + if ( connection_needs_virtual_device (connection) + && nm_settings_connection_can_autoconnect (NM_SETTINGS_CONNECTION (connection))) + system_create_virtual_device (self, connection); } g_slist_free (connections); } @@ -1458,13 +1192,14 @@ system_unmanaged_devices_changed_cb (NMSettings *settings, unmanaged_specs = nm_settings_get_unmanaged_specs (priv->settings); for (iter = priv->devices; iter; iter = g_slist_next (iter)) { NMDevice *device = NM_DEVICE (iter->data); - gboolean managed; + gboolean unmanaged; - managed = !nm_device_spec_match_list (device, unmanaged_specs); - nm_device_set_managed (device, - managed, - managed ? NM_DEVICE_STATE_REASON_NOW_MANAGED : - NM_DEVICE_STATE_REASON_NOW_UNMANAGED); + unmanaged = nm_device_spec_match_list (device, unmanaged_specs); + nm_device_set_unmanaged (device, + NM_UNMANAGED_USER, + unmanaged, + unmanaged ? NM_DEVICE_STATE_REASON_NOW_UNMANAGED : + NM_DEVICE_STATE_REASON_NOW_MANAGED); } } @@ -1487,6 +1222,8 @@ system_hostname_changed_cb (NMSettings *settings, priv->hostname = (hostname && strlen (hostname)) ? g_strdup (hostname) : NULL; g_object_notify (G_OBJECT (self), NM_MANAGER_HOSTNAME); + nm_dhcp_manager_set_default_hostname (nm_dhcp_manager_get (), priv->hostname); + g_free (hostname); } @@ -1517,8 +1254,6 @@ write_value_to_state_file (const char *filename, FALSE); key_file = g_key_file_new (); - if (!key_file) - return FALSE; g_key_file_set_list_separator (key_file, ','); g_key_file_load_from_file (key_file, filename, G_KEY_FILE_KEEP_COMMENTS, NULL); @@ -1550,11 +1285,8 @@ radio_enabled_for_rstate (RadioState *rstate, gboolean check_changeable) gboolean enabled; enabled = rstate->user_enabled && rstate->hw_enabled; - if (check_changeable) { + if (check_changeable) enabled &= rstate->sw_enabled; - if (rstate->daemon_enabled_func) - enabled &= rstate->daemon_enabled; - } return enabled; } @@ -1598,95 +1330,24 @@ manager_update_radio_enabled (NMManager *self, } static void -manager_hidden_ap_found (NMDevice *device, - NMAccessPoint *ap, - gpointer user_data) -{ - NMManager *manager = NM_MANAGER (user_data); - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); - const struct ether_addr *bssid; - GSList *iter; - GSList *connections; - gboolean done = FALSE; - - g_return_if_fail (nm_ap_get_ssid (ap) == NULL); - - bssid = nm_ap_get_address (ap); - g_assert (bssid); - - /* Look for this AP's BSSID in the seen-bssids list of a connection, - * and if a match is found, copy over the SSID */ - connections = nm_settings_get_connections (priv->settings); - for (iter = connections; iter && !done; iter = g_slist_next (iter)) { - NMConnection *connection = NM_CONNECTION (iter->data); - NMSettingWireless *s_wifi; - - s_wifi = nm_connection_get_setting_wireless (connection); - if (s_wifi) { - if (nm_settings_connection_has_seen_bssid (NM_SETTINGS_CONNECTION (connection), bssid)) - nm_ap_set_ssid (ap, nm_setting_wireless_get_ssid (s_wifi)); - } - } - g_slist_free (connections); -} - -static RfKillState -nm_manager_get_ipw_rfkill_state (NMManager *self) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - GSList *iter; - RfKillState ipw_state = RFKILL_UNBLOCKED; - - for (iter = priv->devices; iter; iter = g_slist_next (iter)) { - NMDevice *candidate = NM_DEVICE (iter->data); - RfKillState candidate_state; - - if (nm_device_get_device_type (candidate) == NM_DEVICE_TYPE_WIFI) { - candidate_state = nm_device_wifi_get_ipw_rfkill_state (NM_DEVICE_WIFI (candidate)); - - if (candidate_state > ipw_state) - ipw_state = candidate_state; - } - } - - return ipw_state; -} - -static RfKillState -nm_manager_get_modem_enabled_state (NMManager *self) +update_rstate_from_rfkill (NMRfkillManager *rfkill_mgr, RadioState *rstate) { - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - GSList *iter; - RfKillState wwan_state = RFKILL_UNBLOCKED; - - for (iter = priv->devices; iter; iter = g_slist_next (iter)) { - NMDevice *candidate = NM_DEVICE (iter->data); - RfKillState candidate_state = RFKILL_UNBLOCKED; - - if (nm_device_get_rfkill_type (candidate) == RFKILL_TYPE_WWAN) { - if (!nm_device_get_enabled (candidate)) - candidate_state = RFKILL_SOFT_BLOCKED; - - if (candidate_state > wwan_state) - wwan_state = candidate_state; - } - } - - return wwan_state; -} - -static void -update_rstate_from_rfkill (RadioState *rstate, RfKillState rfkill) -{ - if (rfkill == RFKILL_UNBLOCKED) { + switch (nm_rfkill_manager_get_rfkill_state (rfkill_mgr, rstate->rtype)) { + case RFKILL_UNBLOCKED: rstate->sw_enabled = TRUE; rstate->hw_enabled = TRUE; - } else if (rfkill == RFKILL_SOFT_BLOCKED) { + break; + case RFKILL_SOFT_BLOCKED: rstate->sw_enabled = FALSE; rstate->hw_enabled = TRUE; - } else if (rfkill == RFKILL_HARD_BLOCKED) { + break; + case RFKILL_HARD_BLOCKED: rstate->sw_enabled = FALSE; rstate->hw_enabled = FALSE; + break; + default: + g_warn_if_reached (); + break; } } @@ -1696,51 +1357,19 @@ manager_rfkill_update_one_type (NMManager *self, RfKillType rtype) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - RfKillState udev_state = RFKILL_UNBLOCKED; - RfKillState other_state = RFKILL_UNBLOCKED; - RfKillState composite; - gboolean old_enabled, new_enabled, old_rfkilled, new_rfkilled; - gboolean old_hwe, old_daemon_enabled = FALSE; + gboolean old_enabled, new_enabled, old_rfkilled, new_rfkilled, old_hwe; old_enabled = radio_enabled_for_rstate (rstate, TRUE); old_rfkilled = rstate->hw_enabled && rstate->sw_enabled; old_hwe = rstate->hw_enabled; - udev_state = nm_udev_manager_get_rfkill_state (priv->udev_mgr, rtype); - - if (rstate->other_enabled_func) - other_state = rstate->other_enabled_func (self); - - /* The composite state is the "worst" of either udev or other states */ - if (udev_state == RFKILL_HARD_BLOCKED || other_state == RFKILL_HARD_BLOCKED) - composite = RFKILL_HARD_BLOCKED; - else if (udev_state == RFKILL_SOFT_BLOCKED || other_state == RFKILL_SOFT_BLOCKED) - composite = RFKILL_SOFT_BLOCKED; - else - composite = RFKILL_UNBLOCKED; - - update_rstate_from_rfkill (rstate, composite); - - /* If the device has a management daemon that can affect enabled state, check that now */ - if (rstate->daemon_enabled_func) { - old_daemon_enabled = rstate->daemon_enabled; - rstate->daemon_enabled = (rstate->daemon_enabled_func (self) == RFKILL_UNBLOCKED); - if (old_daemon_enabled != rstate->daemon_enabled) { - nm_log_info (LOGD_RFKILL, "%s now %s by management service", - rstate->desc, - rstate->daemon_enabled ? "enabled" : "disabled"); - } - } + /* recheck kernel rfkill state */ + update_rstate_from_rfkill (priv->rfkill_mgr, rstate); /* Print out all states affecting device enablement */ if (rstate->desc) { - if (rstate->daemon_enabled_func) { - nm_log_dbg (LOGD_RFKILL, "%s hw-enabled %d sw-enabled %d daemon-enabled %d", - rstate->desc, rstate->hw_enabled, rstate->sw_enabled, rstate->daemon_enabled); - } else { - nm_log_dbg (LOGD_RFKILL, "%s hw-enabled %d sw-enabled %d", - rstate->desc, rstate->hw_enabled, rstate->sw_enabled); - } + nm_log_dbg (LOGD_RFKILL, "%s hw-enabled %d sw-enabled %d", + rstate->desc, rstate->hw_enabled, rstate->sw_enabled); } /* Log new killswitch state */ @@ -1783,20 +1412,6 @@ nm_manager_rfkill_update (NMManager *self, RfKillType rtype) } static void -manager_ipw_rfkill_state_changed (NMDeviceWifi *device, - GParamSpec *pspec, - gpointer user_data) -{ - nm_manager_rfkill_update (NM_MANAGER (user_data), RFKILL_TYPE_WLAN); -} - -static void -manager_modem_enabled_changed (NMDevice *device, gpointer user_data) -{ - nm_manager_rfkill_update (NM_MANAGER (user_data), RFKILL_TYPE_WWAN); -} - -static void device_auth_done_cb (NMAuthChain *chain, GError *auth_error, DBusGMethodInvocation *context, @@ -1810,6 +1425,8 @@ device_auth_done_cb (NMAuthChain *chain, const char *permission; NMDeviceAuthRequestFunc callback; + g_assert (context); + priv->auth_chains = g_slist_remove (priv->auth_chains, chain); permission = nm_auth_chain_get_data (chain, "requested-permission"); @@ -1819,7 +1436,8 @@ device_auth_done_cb (NMAuthChain *chain, device = nm_auth_chain_get_data (chain, "device"); g_assert (device); - result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, permission)); + result = nm_auth_chain_get_result (chain, permission); + if (auth_error) { /* translate the auth error into a manager permission denied error */ nm_log_dbg (LOGD_CORE, "%s request failed: %s", permission, auth_error->message); @@ -1858,112 +1476,283 @@ device_auth_request_cb (NMDevice *device, { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); GError *error = NULL; - gulong sender_uid = G_MAXULONG; + NMAuthSubject *subject = NULL; char *error_desc = NULL; NMAuthChain *chain; - /* Get the caller's UID for the root check */ - if (!nm_auth_get_caller_uid (context, priv->dbus_mgr, &sender_uid, &error_desc)) { + /* Validate the caller */ + subject = nm_auth_subject_new_from_context (context); + if (!subject) { error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, - error_desc); - callback (device, context, error, user_data); - g_error_free (error); - g_free (error_desc); - return; + "Failed to get request UID."); + goto done; } /* Ensure the subject has permissions for this connection */ - if (!nm_auth_uid_in_acl (connection, - priv->session_monitor, - sender_uid, - &error_desc)) { + if (connection && !nm_auth_uid_in_acl (connection, + nm_session_monitor_get (), + nm_auth_subject_get_uid (subject), + &error_desc)) { error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, error_desc); + g_free (error_desc); + goto done; + } + + /* Validate the request */ + chain = nm_auth_chain_new_subject (subject, context, device_auth_done_cb, self); + if (!chain) { + error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "Unable to authenticate request."); + goto done; + } + + priv->auth_chains = g_slist_append (priv->auth_chains, chain); + nm_auth_chain_set_data (chain, "device", g_object_ref (device), g_object_unref); + nm_auth_chain_set_data (chain, "requested-permission", g_strdup (permission), g_free); + nm_auth_chain_set_data (chain, "callback", callback, NULL); + nm_auth_chain_set_data (chain, "user-data", user_data, NULL); + nm_auth_chain_add_call (chain, permission, allow_interaction); + +done: + g_clear_object (&subject); + if (error) callback (device, context, error, user_data); + g_clear_error (&error); +} + +/* This should really be moved to gsystem. */ +#define free_slist __attribute__ ((cleanup(local_slist_free))) +static void +local_slist_free (void *loc) +{ + GSList **location = loc; + + if (location) + g_slist_free (*location); +} + +static gboolean +match_connection_filter (NMConnection *connection, gpointer user_data) +{ + return nm_device_check_connection_compatible (NM_DEVICE (user_data), connection); +} + +/** + * get_existing_connection: + * @manager: #NMManager instance + * @device: #NMDevice instance + * + * Returns: a #NMSettingsConnection to be assumed by the device, or %NULL if + * the device does not support assuming existing connections. + */ +static NMConnection * +get_existing_connection (NMManager *manager, NMDevice *device) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + free_slist GSList *connections = nm_manager_get_activatable_connections (manager); + NMConnection *connection = NULL, *matched; + NMSettingsConnection *added = NULL; + GError *error = NULL; + + nm_device_capture_initial_config (device); + + /* 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); + if (!connection) + return NULL; + + /* Now we need to compare the generated connection to each configured + * connection. The comparison function is the heart of the connection + * assumption implementation and it must compare the connections very + * carefully to sort out various corner cases. Also, the comparison is + * not entirely symmetric. + * + * When no configured connection matches the generated connection, we keep + * the generated connection instead. + */ + connections = g_slist_reverse (g_slist_sort (connections, nm_settings_sort_connections)); + matched = nm_utils_match_connection (connections, + connection, + nm_device_has_carrier (device), + match_connection_filter, + device); + if (matched) { + nm_log_info (LOGD_DEVICE, "(%s): found matching connection '%s'", + nm_device_get_iface (device), + nm_connection_get_id (matched)); + g_object_unref (connection); + return matched; + } + + nm_log_dbg (LOGD_DEVICE, "(%s): generated connection '%s'", + nm_device_get_iface (device), + nm_connection_get_id (connection)); + + added = nm_settings_add_connection (priv->settings, connection, FALSE, &error); + if (added) + nm_settings_connection_set_nm_generated (added); + else { + nm_log_warn (LOGD_SETTINGS, "(%s) Couldn't save generated connection '%s': %s", + nm_device_get_iface (device), + nm_connection_get_id (connection), + (error && error->message) ? error->message : "(unknown)"); + g_clear_error (&error); + } + g_object_unref (connection); + + return added ? NM_CONNECTION (added) : NULL; +} + +static gboolean +assume_connection (NMManager *self, NMDevice *device, NMConnection *connection) +{ + NMActiveConnection *active, *master_ac; + NMAuthSubject *subject; + GError *error = NULL; + + nm_log_dbg (LOGD_DEVICE, "(%s): will attempt to assume connection", + nm_device_get_iface (device)); + + /* Move device to DISCONNECTED to activate the connection */ + if (nm_device_get_state (device) == NM_DEVICE_STATE_UNAVAILABLE) { + nm_device_state_changed (device, + NM_DEVICE_STATE_DISCONNECTED, + NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); + } + g_return_val_if_fail (nm_device_get_state (device) >= NM_DEVICE_STATE_DISCONNECTED, FALSE); + + subject = nm_auth_subject_new_internal (); + active = _new_active_connection (self, connection, NULL, device, subject, &error); + g_object_unref (subject); + + if (!active) { + nm_log_warn (LOGD_DEVICE, "assumed connection %s failed to activate: (%d) %s", + nm_connection_get_path (connection), + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); g_error_free (error); - g_free (error_desc); + return FALSE; + } + + /* If the device is a slave or VLAN, find the master ActiveConnection */ + master_ac = NULL; + if (find_master (self, connection, device, NULL, NULL, &master_ac, NULL) && master_ac) + nm_active_connection_set_master (active, master_ac); + + nm_active_connection_set_assumed (active, TRUE); + nm_active_connection_export (active); + active_connection_add (self, active); + nm_device_queue_activation (device, NM_ACT_REQUEST (active)); + g_object_unref (active); + + return TRUE; +} + +static void +recheck_assume_connection (NMDevice *device, gpointer user_data) +{ + NMManager *self = user_data; + NMConnection *connection; + gboolean was_unmanaged = FALSE; + + if (manager_sleeping (self)) + return; + if (nm_device_get_unmanaged_flag (device, NM_UNMANAGED_USER)) + return; + + connection = get_existing_connection (self, device); + if (!connection) { + nm_log_dbg (LOGD_DEVICE, "(%s): can't assume; no connection", + nm_device_get_iface (device)); return; } - /* Yay for root */ - if (0 == sender_uid) - callback (device, context, NULL, user_data); - else { - /* Otherwise validate the non-root request */ - chain = nm_auth_chain_new (context, NULL, device_auth_done_cb, self); - g_assert (chain); - priv->auth_chains = g_slist_append (priv->auth_chains, chain); + if (nm_device_get_state (device) == NM_DEVICE_STATE_UNMANAGED) { + was_unmanaged = TRUE; + nm_device_state_changed (device, + NM_DEVICE_STATE_UNAVAILABLE, + NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); + } - nm_auth_chain_set_data (chain, "device", g_object_ref (device), g_object_unref); - nm_auth_chain_set_data (chain, "requested-permission", g_strdup (permission), g_free); - nm_auth_chain_set_data (chain, "callback", callback, NULL); - nm_auth_chain_set_data (chain, "user-data", user_data, NULL); - nm_auth_chain_add_call (chain, permission, allow_interaction); + if (!assume_connection (self, device, connection)) { + if (was_unmanaged) { + nm_device_state_changed (device, + NM_DEVICE_STATE_UNAVAILABLE, + NM_DEVICE_STATE_REASON_CONFIG_FAILED); + nm_device_state_changed (device, + NM_DEVICE_STATE_UNMANAGED, + NM_DEVICE_STATE_REASON_CONFIG_FAILED); + } } } +/** + * add_device: + * @self: the #NMManager + * @device: the #NMDevice to add + * @generate_con: %TRUE if existing connection (if any) should be assumed + * + * If successful, this function will increase the references count of @device. + * Callers should decrease the reference count. + */ static void -add_device (NMManager *self, NMDevice *device) +add_device (NMManager *self, NMDevice *device, gboolean generate_con) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); const char *iface, *driver, *type_desc; - char *path; - static guint32 devcount = 0; const GSList *unmanaged_specs; - NMConnection *existing = NULL; - gboolean managed = FALSE, enabled = FALSE; + gboolean user_unmanaged, sleeping; + NMConnection *connection = NULL; + gboolean enabled = FALSE; RfKillType rtype; - NMDeviceType devtype; + GSList *iter, *remove = NULL; - iface = nm_device_get_ip_iface (device); - g_assert (iface); - - devtype = nm_device_get_device_type (device); + /* No duplicates */ + if (nm_manager_get_device_by_udi (self, nm_device_get_udi (device))) + return; - /* Ignore the device if we already know about it. But some modems will - * provide pseudo-ethernet devices that NM has already claimed while - * ModemManager is still detecting the modem's serial ports, so when the - * MM modem object finally shows up it may have the same IP interface as the - * ethernet interface we've already detected. In this case we skip the - * check for an existing device with the same IP interface name and kill - * the ethernet device later in favor of the modem device. + /* Remove existing devices owned by the new device; eg remove ethernet + * ports that are owned by a WWAN modem, since udev may announce them + * before the modem is fully discovered. + * + * FIXME: use parent/child device relationships instead of removing + * the child NMDevice entirely */ - if ((devtype != NM_DEVICE_TYPE_MODEM) && find_device_by_ip_iface (self, iface)) { - g_object_unref (device); - return; + 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); } + for (iter = remove; iter; iter = iter->next) + remove_device (self, NM_DEVICE (iter->data), FALSE); + g_slist_free (remove); - nm_device_set_connection_provider (device, NM_CONNECTION_PROVIDER (priv->settings)); - - priv->devices = g_slist_append (priv->devices, device); + priv->devices = g_slist_append (priv->devices, g_object_ref (device)); g_signal_connect (device, "state-changed", - G_CALLBACK (manager_device_state_changed), - self); + G_CALLBACK (manager_device_state_changed), + self); g_signal_connect (device, NM_DEVICE_AUTH_REQUEST, G_CALLBACK (device_auth_request_cb), self); - if (devtype == NM_DEVICE_TYPE_WIFI) { - /* Attach to the access-point-added signal so that the manager can fill - * non-SSID-broadcasting APs with an SSID. - */ - g_signal_connect (device, "hidden-ap-found", - G_CALLBACK (manager_hidden_ap_found), - self); + g_signal_connect (device, NM_DEVICE_REMOVED, + G_CALLBACK (device_removed_cb), + self); - /* Hook up rfkill handling for ipw-based cards until they get converted - * to use the kernel's rfkill subsystem in 2.6.33. - */ - g_signal_connect (device, "notify::" NM_DEVICE_WIFI_IPW_RFKILL_STATE, - G_CALLBACK (manager_ipw_rfkill_state_changed), - self); - } else if (devtype == NM_DEVICE_TYPE_MODEM) { - g_signal_connect (device, NM_DEVICE_MODEM_ENABLE_CHANGED, - G_CALLBACK (manager_modem_enabled_changed), + if (priv->startup) { + g_signal_connect (device, "notify::" NM_DEVICE_HAS_PENDING_ACTION, + G_CALLBACK (device_has_pending_action_changed), self); } @@ -1978,6 +1767,9 @@ add_device (NMManager *self, NMDevice *device) nm_device_set_enabled (device, enabled); } + iface = nm_device_get_iface (device); + g_assert (iface); + type_desc = nm_device_get_type_desc (device); g_assert (type_desc); driver = nm_device_get_driver (device); @@ -1986,121 +1778,47 @@ add_device (NMManager *self, NMDevice *device) nm_log_info (LOGD_HW, "(%s): new %s device (driver: '%s' ifindex: %d)", iface, type_desc, driver, nm_device_get_ifindex (device)); - path = g_strdup_printf ("/org/freedesktop/NetworkManager/Devices/%d", devcount++); - nm_device_set_path (device, path); - dbus_g_connection_register_g_object (nm_dbus_manager_get_connection (priv->dbus_mgr), - path, - G_OBJECT (device)); - nm_log_info (LOGD_CORE, "(%s): exported as %s", iface, path); - g_free (path); + unmanaged_specs = nm_settings_get_unmanaged_specs (priv->settings); + user_unmanaged = nm_device_spec_match_list (device, unmanaged_specs); + nm_device_set_initial_unmanaged_flag (device, NM_UNMANAGED_USER, user_unmanaged); - /* Check if we should assume the device's active connection by matching its - * config with an existing system connection. - */ - if (nm_device_can_assume_connections (device)) { - GSList *connections = NULL; + sleeping = manager_sleeping (self); + nm_device_set_initial_unmanaged_flag (device, NM_UNMANAGED_INTERNAL, sleeping); - connections = nm_settings_get_connections (priv->settings); - existing = nm_device_connection_match_config (device, (const GSList *) connections); - g_slist_free (connections); + nm_device_dbus_export (device); - if (existing) - nm_log_dbg (LOGD_DEVICE, "(%s): found existing device connection '%s'", - nm_device_get_iface (device), - nm_connection_get_id (existing)); - } + /* Don't generate a connection e.g. for devices NM just created, or + * for the loopback, or when we're sleeping. */ + if (generate_con && !user_unmanaged && !sleeping) + connection = get_existing_connection (self, device); - /* Start the device if it's supposed to be managed */ - unmanaged_specs = nm_settings_get_unmanaged_specs (priv->settings); - if ( !manager_sleeping (self) - && !nm_device_spec_match_list (device, unmanaged_specs)) { - nm_device_set_managed (device, - TRUE, - existing ? NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED : - NM_DEVICE_STATE_REASON_NOW_MANAGED); - managed = TRUE; + /* Start the device if it's supposed to be managed. Note that this will + * manage default-unmanaged devices if they have a generated connection. + */ + if (nm_device_get_managed (device) || connection) { + nm_device_state_changed (device, + NM_DEVICE_STATE_UNAVAILABLE, + connection ? NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED : + NM_DEVICE_STATE_REASON_NOW_MANAGED); } nm_settings_device_added (priv->settings, device); g_signal_emit (self, signals[DEVICE_ADDED], 0, device); + g_object_notify (G_OBJECT (self), NM_MANAGER_DEVICES); /* New devices might be master interfaces for virtual interfaces; so we may * need to create new virtual interfaces now. */ system_create_virtual_devices (self); - /* If the device has a connection it can assume, do that now */ - if (existing && managed && nm_device_is_available (device)) { - NMActiveConnection *ac; - GError *error = NULL; - - nm_log_dbg (LOGD_DEVICE, "(%s): will attempt to assume existing connection", - nm_device_get_iface (device)); - - ac = internal_activate_device (self, device, existing, NULL, FALSE, 0, NULL, TRUE, NULL, &error); - if (!ac) { - nm_log_warn (LOGD_DEVICE, "assumed connection %s failed to activate: (%d) %s", - nm_connection_get_path (existing), - error ? error->code : -1, - error && error->message ? error->message : "(unknown)"); - g_error_free (error); - } - } -} - -static void -bluez_manager_bdaddr_added_cb (NMBluezManager *bluez_mgr, - NMBluezDevice *bt_device, - const char *bdaddr, - const char *name, - const char *object_path, - guint32 capabilities, - NMManager *manager) -{ - NMDevice *device; - gboolean has_dun = (capabilities & NM_BT_CAPABILITY_DUN); - gboolean has_nap = (capabilities & NM_BT_CAPABILITY_NAP); - - g_return_if_fail (bdaddr != NULL); - g_return_if_fail (name != NULL); - g_return_if_fail (object_path != NULL); - g_return_if_fail (capabilities != NM_BT_CAPABILITY_NONE); - g_return_if_fail (NM_IS_BLUEZ_DEVICE (bt_device)); - - /* Make sure the device is not already in the device list */ - if (nm_manager_get_device_by_udi (manager, object_path)) - return; - - device = nm_device_bt_new (bt_device, object_path, bdaddr, name, capabilities, FALSE); - if (device) { - nm_log_info (LOGD_HW, "BT device %s (%s) added (%s%s%s)", - name, - bdaddr, - has_dun ? "DUN" : "", - has_dun && has_nap ? " " : "", - has_nap ? "NAP" : ""); - - add_device (manager, device); - } -} - -static void -bluez_manager_bdaddr_removed_cb (NMBluezManager *bluez_mgr, - const char *bdaddr, - const char *object_path, - gpointer user_data) -{ - NMManager *self = NM_MANAGER (user_data); - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - NMDevice *device; - - g_return_if_fail (bdaddr != NULL); - g_return_if_fail (object_path != NULL); - - device = nm_manager_get_device_by_udi (self, object_path); - if (device) { - nm_log_info (LOGD_HW, "BT device %s removed", bdaddr); - priv->devices = remove_one_device (self, priv->devices, device, FALSE); + /* If the device has a connection it can assume, do that now. If it's a + * device that we might ever want to assume a connection on, then set that up. + */ + if (connection) + assume_connection (self, device, connection); + if (generate_con) { + g_signal_connect (device, NM_DEVICE_RECHECK_ASSUME, + G_CALLBACK (recheck_assume_connection), self); } } @@ -2134,25 +1852,33 @@ find_device_by_ifindex (NMManager *self, guint32 ifindex) return NULL; } -#define PLUGIN_PREFIX "libnm-device-plugin-" - -typedef struct { - NMDeviceType t; - guint priority; - NMDeviceFactoryCreateFunc create_func; -} PluginInfo; +static void +factory_device_added_cb (NMDeviceFactory *factory, + NMDevice *device, + gpointer user_data) +{ + add_device (NM_MANAGER (user_data), device, FALSE); +} -static gint -plugin_sort (PluginInfo *a, PluginInfo *b) +static gboolean +factory_component_added_cb (NMDeviceFactory *factory, + GObject *component, + gpointer user_data) { - /* Higher priority means sort earlier in the list (ie, return -1) */ - if (a->priority > b->priority) - return -1; - else if (a->priority < b->priority) - return 1; - return 0; + 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" +#define PLUGIN_TYPEFUNC_TAG "typefunc" + static void load_device_factories (NMManager *self) { @@ -2161,7 +1887,7 @@ load_device_factories (NMManager *self) GError *error = NULL; const char *item; char *path; - GSList *list = NULL, *iter; + GSList *iter; dir = g_dir_open (NMPLUGINDIR, 0, &error); if (!dir) { @@ -2174,14 +1900,16 @@ load_device_factories (NMManager *self) while ((item = g_dir_read_name (dir))) { GModule *plugin; + NMDeviceFactory *factory; NMDeviceFactoryCreateFunc create_func; - NMDeviceFactoryPriorityFunc priority_func; - NMDeviceFactoryTypeFunc type_func; - PluginInfo *info = NULL; - NMDeviceType plugin_type; + NMDeviceFactoryDeviceTypeFunc type_func; + NMDeviceType dev_type; + const char *found = NULL; 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); @@ -2193,158 +1921,92 @@ load_device_factories (NMManager *self) continue; } - if (!g_module_symbol (plugin, "nm_device_factory_get_type", (gpointer) (&type_func))) { - nm_log_warn (LOGD_HW, "(%s): failed to find device factory: %s", item, g_module_error ()); + if (!g_module_symbol (plugin, "nm_device_factory_get_device_type", (gpointer) &type_func)) { + nm_log_warn (LOGD_HW, "(%s): failed to find device factory type: %s", item, g_module_error ()); g_module_close (plugin); continue; } /* Make sure we don't double-load plugins */ - plugin_type = type_func (); - for (iter = list; iter; iter = g_slist_next (iter)) { - PluginInfo *candidate = iter->data; + dev_type = type_func (); + for (iter = priv->factories; iter; iter = iter->next) { + NMDeviceFactoryDeviceTypeFunc loaded_type_func; - if (plugin_type == candidate->t) { - info = candidate; + loaded_type_func = g_object_get_data (G_OBJECT (iter->data), PLUGIN_TYPEFUNC_TAG); + if (dev_type == loaded_type_func ()) { + found = g_object_get_data (G_OBJECT (iter->data), PLUGIN_PATH_TAG); break; } } - if (info) { + if (found) { + nm_log_warn (LOGD_HW, "Found multiple device plugins for same type: %s vs %s", + found, g_module_name (plugin)); g_module_close (plugin); continue; } - if (!g_module_symbol (plugin, "nm_device_factory_create_device", (gpointer) (&create_func))) { - nm_log_warn (LOGD_HW, "(%s): failed to find device creator: %s", item, g_module_error ()); + 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; } - info = g_malloc0 (sizeof (*info)); - info->create_func = create_func; - info->t = plugin_type; - - /* Grab priority; higher number equals higher priority */ - if (g_module_symbol (plugin, "nm_device_factory_get_priority", (gpointer) (&priority_func))) - info->priority = priority_func (); - else { - nm_log_dbg (LOGD_HW, "(%s): failed to find device factory priority func: %s", - item, g_module_error ()); + 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); g_module_make_resident (plugin); - list = g_slist_insert_sorted (list, info, (GCompareFunc) plugin_sort); + priv->factories = g_slist_prepend (priv->factories, factory); - nm_log_info (LOGD_HW, "Loaded device factory: %s", g_module_name (plugin)); + g_signal_connect (factory, + NM_DEVICE_FACTORY_DEVICE_ADDED, + G_CALLBACK (factory_device_added_cb), + self); + g_signal_connect (factory, + 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 (g_module_name (plugin)), g_free); + g_object_set_data (G_OBJECT (factory), PLUGIN_TYPEFUNC_TAG, type_func); + + nm_log_info (LOGD_HW, "Loaded device plugin: %s", g_module_name (plugin)); }; g_dir_close (dir); - /* Ditch the priority info and copy the factory functions to our private data */ - for (iter = list; iter; iter = g_slist_next (iter)) { - PluginInfo *info = iter->data; - - priv->factories = g_slist_append (priv->factories, info->create_func); - g_free (info); - } - g_slist_free (list); -} - -static gboolean -is_wireless (GUdevDevice *device) -{ - const char *tmp; - - /* Check devtype, newer kernels (2.6.32+) have this */ - tmp = g_udev_device_get_property (device, "DEVTYPE"); - if (g_strcmp0 (tmp, "wlan") == 0) - return TRUE; - - /* Otherwise hit up WEXT directly */ - return wifi_utils_is_wifi (g_udev_device_get_name (device), - g_udev_device_get_sysfs_path (device)); -} - -static gboolean -is_olpc_mesh (GUdevDevice *device) -{ - const gchar *prop = g_udev_device_get_property (device, "ID_NM_OLPC_MESH"); - return (prop != NULL); -} - -static gboolean -is_infiniband (GUdevDevice *device) -{ - gint etype = g_udev_device_get_sysfs_attr_as_int (device, "type"); - return etype == ARPHRD_INFINIBAND; -} - -static gboolean -is_bond (int ifindex) -{ - return (nm_system_get_iface_type (ifindex, NULL) == NM_IFACE_TYPE_BOND); -} - -static gboolean -is_bridge (int ifindex) -{ - return (nm_system_get_iface_type (ifindex, NULL) == NM_IFACE_TYPE_BRIDGE); -} - -static gboolean -is_vlan (int ifindex) -{ - return (nm_system_get_iface_type (ifindex, NULL) == NM_IFACE_TYPE_VLAN); -} - -static gboolean -is_adsl (GUdevDevice *device) -{ - return (g_strcmp0 (g_udev_device_get_subsystem (device), "atm") == 0); + priv->factories = g_slist_reverse (priv->factories); } static void -udev_device_added_cb (NMUdevManager *udev_mgr, - GUdevDevice *udev_device, - const char *iface, - const char *sysfs_path, - const char *driver, - int ifindex, - gpointer user_data) +platform_link_added (NMManager *self, + int ifindex, + NMPlatformLink *plink, + NMPlatformReason reason) { - NMManager *self = NM_MANAGER (user_data); NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); NMDevice *device = NULL; GSList *iter; GError *error = NULL; - g_return_if_fail (udev_device != NULL); - g_return_if_fail (iface != NULL); - g_return_if_fail (sysfs_path != NULL); - g_return_if_fail (driver != NULL); + g_return_if_fail (ifindex > 0); - /* Most devices will have an ifindex here */ - if (ifindex > 0) { - device = find_device_by_ifindex (self, ifindex); - if (device) { - /* If it's a virtual device we may need to update its UDI */ - if (nm_system_get_iface_type (ifindex, iface) != NM_IFACE_TYPE_UNSPEC) - g_object_set (G_OBJECT (device), NM_DEVICE_UDI, sysfs_path, NULL); - return; - } - } else { - /* But ATM/ADSL devices don't */ - g_return_if_fail (is_adsl (udev_device)); - device = find_device_by_ip_iface (self, iface); - if (device) - return; - } + if (priv->ignore_link_added_cb > 0) + return; + + if (find_device_by_ifindex (self, ifindex)) + return; /* Try registered device factories */ - for (iter = priv->factories; iter; iter = g_slist_next (iter)) { - NMDeviceFactoryCreateFunc create_func = iter->data; + for (iter = priv->factories; iter; iter = iter->next) { + NMDeviceFactory *factory = NM_DEVICE_FACTORY (iter->data); - g_clear_error (&error); - device = (NMDevice *) create_func (udev_device, sysfs_path, iface, driver, &error); + device = nm_device_factory_new_link (factory, plink, &error); if (device && NM_IS_DEVICE (device)) { g_assert_no_error (error); break; /* success! */ @@ -2352,7 +2014,7 @@ udev_device_added_cb (NMUdevManager *udev_mgr, if (error) { nm_log_warn (LOGD_HW, "%s: factory failed to create device: (%d) %s", - sysfs_path, + plink->udi, error ? error->code : -1, error ? error->message : "(unknown)"); g_clear_error (&error); @@ -2360,102 +2022,128 @@ udev_device_added_cb (NMUdevManager *udev_mgr, } } + /* 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) { - if (is_olpc_mesh (udev_device)) /* must be before is_wireless */ - device = nm_device_olpc_mesh_new (sysfs_path, iface, driver); - else if (is_wireless (udev_device)) - device = nm_device_wifi_new (sysfs_path, iface, driver); - else if (is_infiniband (udev_device)) - device = nm_device_infiniband_new (sysfs_path, iface, driver); - else if (is_bond (ifindex)) - device = nm_device_bond_new (sysfs_path, iface); - else if (is_bridge (ifindex)) { - - /* FIXME: always create device when we handle bridges non-destructively */ - if (bridge_created_by_nm (self, iface)) - device = nm_device_bridge_new (sysfs_path, iface); - else - nm_log_info (LOGD_BRIDGE, "(%s): ignoring bridge not created by NetworkManager", iface); - } else if (is_vlan (ifindex)) { - int parent_ifindex = -1; - NMDevice *parent; + int parent_ifindex = -1; + NMDevice *parent; + switch (plink->type) { + case NM_LINK_TYPE_ETHERNET: + device = nm_device_ethernet_new (plink); + break; + case NM_LINK_TYPE_INFINIBAND: + device = nm_device_infiniband_new (plink); + break; + 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; + case NM_LINK_TYPE_VLAN: /* Have to find the parent device */ - if (nm_system_get_iface_vlan_info (ifindex, &parent_ifindex, NULL)) { + if (nm_platform_vlan_get_info (ifindex, &parent_ifindex, NULL)) { parent = find_device_by_ifindex (self, parent_ifindex); if (parent) - device = nm_device_vlan_new (sysfs_path, iface, parent); + device = nm_device_vlan_new (plink, parent); else { /* 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", iface); + nm_log_dbg (LOGD_HW, "(%s): VLAN parent interface unknown", plink->name); } } else - nm_log_err (LOGD_HW, "(%s): failed to get VLAN parent ifindex", iface); - } else if (is_adsl (udev_device)) - device = nm_device_adsl_new (sysfs_path, iface, driver); - else - device = nm_device_ethernet_new (sysfs_path, iface, driver); + nm_log_err (LOGD_HW, "(%s): failed to get VLAN parent ifindex", plink->name); + break; + case NM_LINK_TYPE_VETH: + device = nm_device_veth_new (plink); + break; + case NM_LINK_TYPE_TUN: + case NM_LINK_TYPE_TAP: + device = nm_device_tun_new (plink); + break; + case NM_LINK_TYPE_MACVLAN: + case NM_LINK_TYPE_MACVTAP: + device = nm_device_macvlan_new (plink); + break; + case NM_LINK_TYPE_VXLAN: + device = nm_device_vxlan_new (plink); + break; + case NM_LINK_TYPE_GRE: + case NM_LINK_TYPE_GRETAP: + device = nm_device_gre_new (plink); + break; + + 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_OLPC_MESH: + 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); + /* fall through */ + default: + device = nm_device_generic_new (plink); + break; + } } - if (device) - add_device (self, device); + if (device) { + add_device (self, device, plink->type != NM_LINK_TYPE_LOOPBACK); + g_object_unref (device); + } } static void -udev_device_removed_cb (NMUdevManager *manager, - GUdevDevice *udev_device, - gpointer user_data) -{ - NMManager *self = NM_MANAGER (user_data); - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - NMDevice *device; - guint32 ifindex; - - ifindex = g_udev_device_get_property_as_int (udev_device, "IFINDEX"); - device = find_device_by_ifindex (self, ifindex); - if (!device) { - GSList *iter; - const char *iface = g_udev_device_get_name (udev_device); - - /* On removal we aren't always be able to read properties like IFINDEX - * anymore, as they may have already been removed from sysfs. So we - * have to fall back on device name (eg, interface name). - * - * Also, some devices (namely PPPoE (pppX), ADSL (nasX, pppX), and - * mobile broadband (pppX, bnepX)) create a kernel netdevice for IP - * communication (called the "IP interface" in NM) as part of the - * connection process and thus the IP interface lifetime does not - * correspond to the NMDevice lifetime. For these devices we must - * ignore removal events for the IP interface name otherwise the - * NMDevice would be removed. Hence the usage here of - * nm_device_get_iface() rather than nm_device_get_ip_iface(). - */ - for (iter = priv->devices; iter; iter = g_slist_next (iter)) { - if (g_strcmp0 (nm_device_get_iface (NM_DEVICE (iter->data)), iface) == 0) { - device = iter->data; - break; - } - } - } +platform_link_cb (NMPlatform *platform, + int ifindex, + NMPlatformLink *plink, + NMPlatformSignalChangeType change_type, + NMPlatformReason reason, + gpointer user_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; - if (device) - priv->devices = remove_one_device (self, priv->devices, device, FALSE); + device = find_device_by_ifindex (self, ifindex); + if (device) + remove_device (self, device, FALSE); + break; + } + default: + break; + } } static void -udev_manager_rfkill_changed_cb (NMUdevManager *udev_mgr, - RfKillType rtype, - RfKillState udev_state, - gpointer user_data) +rfkill_manager_rfkill_changed_cb (NMRfkillManager *rfkill_mgr, + RfKillType rtype, + RfKillState udev_state, + gpointer user_data) { nm_manager_rfkill_update (NM_MANAGER (user_data), rtype); } -GSList * +const GSList * nm_manager_get_devices (NMManager *manager) { g_return_val_if_fail (NM_IS_MANAGER (manager), NULL); @@ -2503,54 +2191,18 @@ impl_manager_get_device_by_ip_iface (NMManager *self, return path ? TRUE : FALSE; } -static NMActiveConnection * -internal_activate_device (NMManager *manager, - NMDevice *device, - NMConnection *connection, - const char *specific_object, - gboolean user_requested, - gulong sender_uid, - const char *dbus_sender, - gboolean assumed, - NMActiveConnection *master, - GError **error) +static gboolean +is_compatible_with_slave (NMConnection *master, NMConnection *slave) { - NMActRequest *req; - NMDevice *master_device = NULL; - - g_return_val_if_fail (NM_IS_MANAGER (manager), NULL); - g_return_val_if_fail (NM_IS_DEVICE (device), NULL); - g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); - - /* Ensure the requested connection is compatible with the device */ - if (!nm_device_check_connection_compatible (device, connection, error)) - return NULL; - - /* Tear down any existing connection */ - if (nm_device_get_act_request (device)) { - nm_log_info (LOGD_DEVICE, "(%s): disconnecting for new activation request.", - nm_device_get_iface (device)); - nm_device_state_changed (device, - NM_DEVICE_STATE_DISCONNECTED, - NM_DEVICE_STATE_REASON_NONE); - } + NMSettingConnection *s_con; - if (master) - master_device = nm_active_connection_get_device (master); + g_return_val_if_fail (master, FALSE); + g_return_val_if_fail (slave, FALSE); - req = nm_act_request_new (connection, - specific_object, - user_requested, - sender_uid, - dbus_sender, - assumed, - device, - master_device); - g_assert (req); - active_connection_add (manager, NM_ACTIVE_CONNECTION (req)); - nm_device_activate (device, req); + s_con = nm_connection_get_setting_connection (slave); + g_assert (s_con); - return NM_ACTIVE_CONNECTION (req); + return nm_connection_is_type (master, nm_setting_connection_get_slave_type (s_con)); } /** @@ -2562,14 +2214,35 @@ internal_activate_device (NMManager *manager, * that master connection was found * @out_master_device: on success, the master device of @connection if that * master device was found + * @out_master_ac: on success, the master ActiveConnection of @connection if + * there already is one + * @error: the error, if an error occurred + * + * Given an #NMConnection, attempts to find its master. If @connection has + * no master, this will return %TRUE and @out_master_connection and + * @out_master_device will be untouched. * - * Given an #NMConnection, attempts to find its master connection and/or its - * master device. This function may return a master connection, a master device, - * or both. If only a connection is returned, that master connection is not - * currently active on any device. If only a device is returned, that device - * is not currently activated with any connection. If both are returned, then - * the device is currently activated or activating with the returned master - * connection. + * If @connection does have a master, then the outputs depend on what is in its + * #NMSettingConnection:master property: + * + * If "master" is the ifname of an existing #NMDevice, and that device has a + * compatible master connection activated or activating on it, then + * @out_master_device, @out_master_connection, and @out_master_ac will all be + * set. If the device exists and is idle, only @out_master_device will be set. + * If the device exists and has an incompatible connection on it, an error + * will be returned. + * + * If "master" is the ifname of a non-existent device, then @out_master_device + * will be %NULL, and @out_master_connection will be a connection whose + * activation would cause the creation of that device. @out_master_ac MAY be + * set in this case as well (if the connection has started activating, but has + * not yet created its device). + * + * If "master" is the UUID of a compatible master connection, then + * @out_master_connection will be the identified connection, and @out_master_device + * and/or @out_master_ac will be set if the connection is currently activating. + * (@out_master_device will not be set if the device exists but does not have + * @out_master_connection active/activating on it.) * * Returns: %TRUE if the master device and/or connection could be found or if * the connection did not require a master, %FALSE otherwise @@ -2579,7 +2252,9 @@ find_master (NMManager *self, NMConnection *connection, NMDevice *device, NMConnection **out_master_connection, - NMDevice **out_master_device) + NMDevice **out_master_device, + NMActiveConnection **out_master_ac, + GError **error) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); NMSettingConnection *s_con; @@ -2598,9 +2273,20 @@ find_master (NMManager *self, /* Try as an interface name first */ master_device = find_device_by_ip_iface (self, master); if (master_device) { - /* A device obviously can't be its own master */ - if (master_device == device) + if (master_device == device) { + g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_DEPENDENCY_FAILED, + "Device cannot be its own master"); return FALSE; + } + + master_connection = nm_device_get_connection (master_device); + if (master_connection && !is_compatible_with_slave (master_connection, connection)) { + g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_DEPENDENCY_FAILED, + "The active connection on %s is not a valid master for '%s'", + nm_device_get_iface (master_device), + nm_connection_get_id (connection)); + return FALSE; + } } else { /* Try master as a connection UUID */ master_connection = (NMConnection *) nm_settings_get_connection_by_uuid (priv->settings, master); @@ -2623,14 +2309,15 @@ find_master (NMManager *self, * virtual interfaces and see if one of their virtual interface * names matches the master. */ - connections = nm_settings_get_connections (priv->settings); + connections = nm_manager_get_activatable_connections (self); for (iter = connections; iter && !master_connection; iter = g_slist_next (iter)) { NMConnection *candidate = iter->data; char *vname; if (connection_needs_virtual_device (candidate)) { vname = get_virtual_iface_name (self, candidate, NULL); - if (g_strcmp0 (master, vname) == 0) + if ( g_strcmp0 (master, vname) == 0 + && is_compatible_with_slave (candidate, connection)) master_connection = candidate; g_free (vname); } @@ -2643,46 +2330,49 @@ find_master (NMManager *self, *out_master_connection = master_connection; if (out_master_device) *out_master_device = master_device; + if (out_master_ac && master_connection) + *out_master_ac = find_ac_for_connection (self, master_connection); - return master_device || master_connection; -} - -static gboolean -is_compatible_with_slave (NMConnection *master, NMConnection *slave) -{ - NMSettingConnection *s_con; - - g_return_val_if_fail (master, FALSE); - g_return_val_if_fail (slave, FALSE); - - s_con = nm_connection_get_setting_connection (slave); - g_assert (s_con); - - return nm_connection_is_type (master, nm_setting_connection_get_slave_type (s_con)); + if (master_device || master_connection) + return TRUE; + else { + g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, + "Master connection not found or invalid"); + return FALSE; + } } /** * ensure_master_active_connection: - * * @self: the #NMManager - * @dbus_sender: if the request was initiated by a user via D-Bus, the - * dbus sender name of the client that requested the activation; for auto - * activated connections use %NULL + * @subject: the #NMAuthSubject representing the requestor of this activation * @connection: the connection that should depend on @master_connection * @device: the #NMDevice, if any, which will activate @connection - * @master_connection: the master connection - * @master_device: the master device + * @master_connection: the master connection, or %NULL + * @master_device: the master device, or %NULL * @error: the error, if an error occurred * * Determines whether a given #NMConnection depends on another connection to * be activated, and if so, finds that master connection or creates it. * + * If @master_device and @master_connection are both set then @master_connection + * MUST already be activated or activating on @master_device, and the function will + * return the existing #NMActiveConnection. + * + * If only @master_device is set, and it has an #NMActiveConnection, then the + * function will return it if it is a compatible master, or an error if not. If it + * doesn't have an AC, then the function will create one if a compatible master + * connection exists, or return an error if not. + * + * If only @master_connection is set, then this will try to find or create a compatible + * #NMDevice, and either activate @master_connection on that device or return an error. + * * Returns: the master #NMActiveConnection that the caller should depend on, or * %NULL if an error occurred */ static NMActiveConnection * ensure_master_active_connection (NMManager *self, - const char *dbus_sender, + NMAuthSubject *subject, NMConnection *connection, NMDevice *device, NMConnection *master_connection, @@ -2701,20 +2391,29 @@ ensure_master_active_connection (NMManager *self, * compatible connection. If it's already activating we can just proceed. */ if (master_device) { + NMConnection *device_connection = nm_device_get_connection (master_device); + /* If we're passed a connection and a device, we require that connection * be already activated on the device, eg returned from find_master(). */ - if (master_connection) - g_assert (nm_device_get_connection (master_device) == master_connection); + g_assert (!master_connection || master_connection == device_connection); + if (device_connection && !is_compatible_with_slave (device_connection, connection)) { + g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_DEPENDENCY_FAILED, + "The active connection on %s is not a valid master for '%s'", + nm_device_get_iface (master_device), + nm_connection_get_id (connection)); + return NULL; + } master_state = nm_device_get_state (master_device); if ( (master_state == NM_DEVICE_STATE_ACTIVATED) || nm_device_is_activating (master_device)) { /* Device already using master_connection */ + g_assert (device_connection); return NM_ACTIVE_CONNECTION (nm_device_get_act_request (master_device)); } - /* If the device is disconnected, find a compabile connection and + /* If the device is disconnected, find a compatible connection and * activate it on the device. */ if (master_state == NM_DEVICE_STATE_DISCONNECTED) { @@ -2723,20 +2422,22 @@ ensure_master_active_connection (NMManager *self, g_assert (master_connection == NULL); /* Find a compatible connection and activate this device using it */ - connections = nm_settings_get_connections (priv->settings); + connections = nm_manager_get_activatable_connections (self); for (iter = connections; iter; iter = g_slist_next (iter)) { NMConnection *candidate = NM_CONNECTION (iter->data); - /* Ensure eg bond slave and the candidate master is a bond master */ + /* Ensure eg bond/team slave and the candidate master is a + * bond/team master + */ if (!is_compatible_with_slave (candidate, connection)) continue; - if (nm_device_check_connection_compatible (master_device, candidate, NULL)) { + if (nm_device_connection_is_available (master_device, candidate, TRUE)) { master_ac = nm_manager_activate_connection (self, candidate, NULL, - nm_device_get_path (master_device), - dbus_sender, + master_device, + subject, error); if (!master_ac) g_prefix_error (error, "%s", "Master device activation failed: "); @@ -2772,7 +2473,7 @@ ensure_master_active_connection (NMManager *self, continue; } - if (!nm_device_check_connection_compatible (candidate, master_connection, NULL)) + if (!nm_device_connection_is_available (candidate, master_connection, TRUE)) continue; found_device = TRUE; @@ -2783,8 +2484,8 @@ ensure_master_active_connection (NMManager *self, master_ac = nm_manager_activate_connection (self, master_connection, NULL, - nm_device_get_path (candidate), - dbus_sender, + candidate, + subject, error); if (!master_ac) g_prefix_error (error, "%s", "Master device activation failed: "); @@ -2799,7 +2500,7 @@ ensure_master_active_connection (NMManager *self, master_connection, NULL, NULL, - dbus_sender, + subject, error); if (!master_ac) g_prefix_error (error, "%s", "Master device activation failed: "); @@ -2817,239 +2518,133 @@ ensure_master_active_connection (NMManager *self, return NULL; } -static NMActiveConnection * -activate_vpn_connection (NMManager *self, - NMConnection *connection, - const char *specific_object, - gulong sender_uid, - GError **error) +static gboolean +_internal_activate_vpn (NMManager *self, NMActiveConnection *active, GError **error) { - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - NMActiveConnection *parent = NULL, *ac; - NMDevice *device = NULL; - GSList *iter; - - if (specific_object) { - /* Find the specifc connection the client requested we use */ - parent = active_connection_get_by_path (self, specific_object); - if (!parent) { - g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE, - "Base connection for VPN connection not active."); - return NULL; - } - } else { - for (iter = priv->active_connections; iter; iter = g_slist_next (iter)) { - NMActiveConnection *candidate = iter->data; - - if (nm_active_connection_get_default (candidate)) { - parent = candidate; - break; - } - } - } + gboolean success; - if (!parent) { - g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_CONNECTION, - "Could not find source connection."); - return NULL; - } + g_assert (NM_IS_VPN_CONNECTION (active)); - device = nm_active_connection_get_device (parent); - if (!device) { - g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "Source connection had no active device."); - return NULL; + success = nm_vpn_manager_activate_connection (NM_MANAGER_GET_PRIVATE (self)->vpn_manager, + NM_VPN_CONNECTION (active), + error); + if (success) { + nm_active_connection_export (active); + g_object_notify (G_OBJECT (self), NM_MANAGER_ACTIVE_CONNECTIONS); } - - ac = nm_vpn_manager_activate_connection (priv->vpn_manager, - connection, - device, - nm_active_connection_get_path (parent), - TRUE, - sender_uid, - error); - if (ac) - active_connection_add (self, ac); - return ac; + return success; } -NMActiveConnection * -nm_manager_activate_connection (NMManager *manager, - NMConnection *connection, - const char *specific_object, - const char *device_path, - const char *dbus_sender, - GError **error) +static gboolean +_internal_activate_device (NMManager *self, NMActiveConnection *active, GError **error) { - NMManagerPrivate *priv; - NMDevice *device = NULL; - gulong sender_uid = 0; - DBusError dbus_error; - NMDeviceState state; - char *iface; - NMDevice *master_device = NULL; + NMDevice *device, *master_device = NULL; + NMConnection *connection; NMConnection *master_connection = NULL; - NMConnection *existing_connection = NULL; NMActiveConnection *master_ac = NULL; - gboolean matched; - char *error_desc = NULL; - g_return_val_if_fail (manager != NULL, NULL); - g_return_val_if_fail (connection != NULL, NULL); - g_return_val_if_fail (error != NULL, NULL); - g_return_val_if_fail (*error == NULL, NULL); + g_return_val_if_fail (NM_IS_MANAGER (self), FALSE); + g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (active), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - priv = NM_MANAGER_GET_PRIVATE (manager); + g_assert (NM_IS_VPN_CONNECTION (active) == FALSE); - /* Get the UID of the user that originated the request, if any */ - if (dbus_sender) { - dbus_error_init (&dbus_error); - sender_uid = dbus_bus_get_unix_user (nm_dbus_manager_get_dbus_connection (priv->dbus_mgr), - dbus_sender, - &dbus_error); - if (dbus_error_is_set (&dbus_error)) { - g_set_error_literal (error, - NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, - "Failed to get unix user for dbus sender"); - dbus_error_free (&dbus_error); - return NULL; + connection = nm_active_connection_get_connection (active); + g_assert (connection); + + device = nm_active_connection_get_device (active); + if (!device) { + if (!connection_needs_virtual_device (connection)) { + NMSettingConnection *s_con = nm_connection_get_setting_connection (connection); + + 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; } - /* Ensure the subject has permissions for this connection */ - if (!nm_auth_uid_in_acl (connection, - priv->session_monitor, - sender_uid, - &error_desc)) { + device = system_create_virtual_device (self, connection); + if (!device) { g_set_error_literal (error, NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - error_desc); - g_free (error_desc); - return NULL; + NM_MANAGER_ERROR_UNKNOWN_DEVICE, + "Failed to create virtual interface"); + return FALSE; } - } - - /* VPN ? */ - if (nm_connection_is_type (connection, NM_SETTING_VPN_SETTING_NAME)) - return activate_vpn_connection (manager, connection, specific_object, sender_uid, error); - /* Device-based connection */ - if (device_path) { - device = nm_manager_get_device_by_path (manager, device_path); - if (!device) { - g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "Device not found"); - return NULL; + 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; } - /* If it's a virtual interface make sure the device given by the - * path matches the connection's interface details. + /* 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 (connection_needs_virtual_device (connection)) { - iface = get_virtual_iface_name (manager, 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"); - return NULL; - } - - matched = g_str_equal (iface, nm_device_get_ip_iface (device)); - g_free (iface); - if (!matched) { - g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "Device given by path did not match connection's virtual interface name"); - return NULL; - } + if ( nm_device_is_available (device) + && (nm_device_get_state (device) == NM_DEVICE_STATE_UNAVAILABLE)) { + nm_device_state_changed (device, + NM_DEVICE_STATE_DISCONNECTED, + NM_DEVICE_STATE_REASON_NONE); } } else { - /* Virtual connections (VLAN, bond, etc) may not specify a device - * path because the device may not be created yet, or it be given - * by the connection's properties instead. Find the device the - * connection refers to, or create it if needed. - */ - if (!connection_needs_virtual_device (connection)) { - g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "This connection requires an existing device."); - return NULL; - } - - iface = get_virtual_iface_name (manager, 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"); - return NULL; - } - - device = find_device_by_ip_iface (manager, iface); - g_free (iface); - if (!device) { - /* Create it */ - device = system_create_virtual_device (manager, connection); - if (!device) { - g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "Failed to create virtual interface"); - return NULL; - } - - /* A newly created device, if allowed to be managed by NM, will be - * in the UNAVAILABLE state here. Since we want to use it right - * away, we transition it immediately to DISCONNECTED. - */ - if ( nm_device_is_available (device) - && (nm_device_get_state (device) == NM_DEVICE_STATE_UNAVAILABLE)) { - nm_device_state_changed (device, - NM_DEVICE_STATE_DISCONNECTED, - NM_DEVICE_STATE_REASON_NONE); - } - } - } + NMConnection *existing_connection = NULL; + NMAuthSubject *subject; + char *error_desc = NULL; - state = nm_device_get_state (device); - if (state < NM_DEVICE_STATE_DISCONNECTED) { - g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNMANAGED_DEVICE, - "Device not managed by NetworkManager or unavailable"); - return NULL; - } - - /* If this is an autoconnect request, but the device isn't allowing autoconnect - * right now, we reject it. - */ - if (!dbus_sender && !nm_device_autoconnect_allowed (device)) { - g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_AUTOCONNECT_NOT_ALLOWED, - "%s does not allow automatic connections at this time", - nm_device_get_iface (device)); - return NULL; - } - - if (dbus_sender) { /* 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_uid_in_acl (existing_connection, - priv->session_monitor, - sender_uid, - &error_desc)) { + nm_session_monitor_get (), + nm_auth_subject_get_uid (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); + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "Private connection already active on the device: %s", + error_desc); g_free (error_desc); return FALSE; } } - /* Try to find the master connection/device if the connection has a dependency */ - if (!find_master (manager, connection, device, &master_connection, &master_device)) { - g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "Master connection not found or invalid"); - return NULL; + /* Final connection must be available on device */ + if (!nm_device_connection_is_available (device, connection, TRUE)) { + g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_CONNECTION, + "Connection '%s' is not available on the device %s at this time.", + nm_connection_get_id (connection), nm_device_get_iface (device)); + return FALSE; + } + + /* If this is an autoconnect request, but the device isn't allowing autoconnect + * right now, we reject it. + */ + if (!nm_active_connection_get_user_requested (active) && + !nm_device_autoconnect_allowed (device)) { + g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_AUTOCONNECT_NOT_ALLOWED, + "%s does not allow automatic connections at this time", + nm_device_get_iface (device)); + return FALSE; } + /* Try to find the master connection/device if the connection has a dependency */ + if (!find_master (self, connection, device, + &master_connection, &master_device, &master_ac, + error)) + return FALSE; + /* Ensure there's a master active connection the new connection we're * activating can depend on. */ @@ -3068,94 +2663,395 @@ nm_manager_activate_connection (NMManager *manager, /* Ensure eg bond slave and the candidate master is a bond master */ if (master_connection && !is_compatible_with_slave (master_connection, connection)) { g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_DEPENDENCY_FAILED, - "The master connection was not compatible"); - return NULL; + "The master connection was not compatible"); + return FALSE; } - master_ac = ensure_master_active_connection (manager, - dbus_sender, - connection, - device, - master_connection, - master_device, - error); if (!master_ac) { - if (error) - g_assert (*error); - return NULL; + master_ac = ensure_master_active_connection (self, + nm_active_connection_get_subject (active), + connection, + device, + master_connection, + master_device, + error); + if (!master_ac) { + if (error) + g_assert (*error); + return FALSE; + } } + 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_active_connection_get_path (master_ac)); } - return internal_activate_device (manager, - device, + /* 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); + nm_device_queue_activation (device, NM_ACT_REQUEST (active)); + return TRUE; +} + +static gboolean +_internal_activate_generic (NMManager *self, NMActiveConnection *active, GError **error) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + gboolean success = FALSE; + + /* Ensure activation request is still valid, eg that its device hasn't gone + * away or that some other dependency has not failed. + */ + if (nm_active_connection_get_state (active) >= NM_ACTIVE_CONNECTION_STATE_DEACTIVATING) { + g_set_error_literal (error, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_DEPENDENCY_FAILED, + "Activation failed because dependencies failed."); + return FALSE; + } + + if (NM_IS_VPN_CONNECTION (active)) + success = _internal_activate_vpn (self, active, error); + else + success = _internal_activate_device (self, active, error); + + if (success) { + /* Force an update of the Manager's activating-connection property. + * The device changes state before the AC gets exported, which causes + * the manager's 'activating-connection' property to be NULL since the + * AC only gets a D-Bus path when it's exported. So now that the AC + * is exported, make sure the manager's activating-connection property + * is up-to-date. + */ + policy_activating_device_changed (G_OBJECT (priv->policy), NULL, self); + } + + return success; +} + +static NMActiveConnection * +_new_vpn_active_connection (NMManager *self, + NMConnection *connection, + const char *specific_object, + NMAuthSubject *subject, + GError **error) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMActiveConnection *parent = NULL; + NMDevice *device = NULL; + + if (specific_object) { + /* Find the specific connection the client requested we use */ + parent = active_connection_get_by_path (self, specific_object); + if (!parent) { + g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE, + "Base connection for VPN connection not active."); + return NULL; + } + } else + parent = priv->primary_connection; + + if (!parent) { + g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_CONNECTION, + "Could not find source connection."); + return NULL; + } + + device = nm_active_connection_get_device (parent); + if (!device) { + g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, + "Source connection had no active device."); + return NULL; + } + + return (NMActiveConnection *) nm_vpn_connection_new (connection, + device, + nm_active_connection_get_path (parent), + subject); +} + +static NMActiveConnection * +_new_active_connection (NMManager *self, + NMConnection *connection, + const char *specific_object, + NMDevice *device, + NMAuthSubject *subject, + GError **error) +{ + NMActiveConnection *existing_ac; + + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + g_return_val_if_fail (NM_IS_AUTH_SUBJECT (subject), NULL); + + /* Can't create new AC for already-active connection */ + existing_ac = find_ac_for_connection (self, connection); + if (NM_IS_VPN_CONNECTION (existing_ac)) { + g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_ALREADY_ACTIVE, + "Connection '%s' is already active", + nm_connection_get_id (connection)); + 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; + + if (nm_connection_is_type (connection, NM_SETTING_VPN_SETTING_NAME)) { + return _new_vpn_active_connection (self, + connection, + specific_object, + subject, + error); + } + + return (NMActiveConnection *) nm_act_request_new (connection, + specific_object, + subject, + device); +} + +static void +_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); + + 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 +_internal_activation_auth_done (NMActiveConnection *active, + gboolean success, + const char *error_desc, + gpointer user_data1, + gpointer user_data2) +{ + NMManager *self = user_data1; + GError *error = NULL; + + if (success) { + if (_internal_activate_generic (self, active, &error)) { + g_object_unref (active); + return; + } + } + + g_assert (error_desc || error); + _internal_activation_failed (self, active, error_desc ? error_desc : error->message); + g_object_unref (active); + g_clear_error (&error); +} + +/** + * nm_manager_activate_connection(): + * @self: the #NMManager + * @connection: the #NMConnection to activate on @device + * @specific_object: the specific object path, if any, for the activation + * @device: the #NMDevice to activate @connection on + * @subject: the subject which requested activation + * @error: return location for an error + * + * Begins a new internally-initiated activation of @connection on @device. + * @subject should be the subject of the activation that triggered this + * one, or if this is an autoconnect request, a new internal subject. + * The returned #NMActiveConnection is owned by the Manager and should be + * referenced by the caller if the caller continues to use it. + * + * Returns: (transfer none): the new #NMActiveConnection that tracks + * activation of @connection on @device + */ +NMActiveConnection * +nm_manager_activate_connection (NMManager *self, + NMConnection *connection, + const char *specific_object, + NMDevice *device, + NMAuthSubject *subject, + GError **error) +{ + NMActiveConnection *active; + char *error_desc = NULL; + + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (connection != NULL, NULL); + g_return_val_if_fail (error != NULL, NULL); + g_return_val_if_fail (*error == NULL, NULL); + + /* Ensure the subject has permissions for this connection */ + if (!nm_auth_uid_in_acl (connection, + nm_session_monitor_get (), + nm_auth_subject_get_uid (subject), + &error_desc)) { + g_set_error_literal (error, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + error_desc); + g_free (error_desc); + return NULL; + } + + active = _new_active_connection (self, connection, specific_object, - dbus_sender ? TRUE : FALSE, - dbus_sender ? sender_uid : 0, - dbus_sender, - FALSE, - master_ac, + device, + subject, error); + if (active) { + nm_active_connection_authorize (active, _internal_activation_auth_done, self, NULL); + active_connection_add (self, active); + } + return active; } -/* - * TODO this function was created and named in the era of user settings, where - * we could get activation requests for a connection before we got the settings - * data of that connection. Now that user settings are gone, flatten or rename - * it. - */ -static void -pending_activate (NMManager *self, PendingActivation *pending) +static NMAuthSubject * +validate_activation_request (NMManager *self, + DBusGMethodInvocation *context, + NMConnection *connection, + const char *device_path, + NMDevice **out_device, + gboolean *out_vpn, + GError **error) { - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - NMSettingsConnection *connection; - NMActiveConnection *ac = NULL; - GError *error = NULL; - char *sender; + NMDevice *device = NULL; + gboolean vpn = FALSE; + NMAuthSubject *subject = NULL; + char *error_desc = NULL; - /* Ok, we're authorized */ + g_assert (connection); + g_assert (out_device); + g_assert (out_vpn); - connection = nm_settings_get_connection_by_path (priv->settings, pending->connection_path); - if (!connection) { - error = g_error_new_literal (NM_MANAGER_ERROR, - NM_MANAGER_ERROR_UNKNOWN_CONNECTION, - "Connection could not be found."); - goto out; + /* Validate the caller */ + subject = nm_auth_subject_new_from_context (context); + if (!subject) { + g_set_error_literal (error, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "Failed to get request UID."); + return NULL; } - sender = dbus_g_method_get_sender (pending->context); - g_assert (sender); - ac = nm_manager_activate_connection (self, - NM_CONNECTION (connection), - pending->specific_object_path, - pending->device_path, - sender, - &error); - g_free (sender); + /* Ensure the subject has permissions for this connection */ + if (!nm_auth_uid_in_acl (connection, + nm_session_monitor_get (), + nm_auth_subject_get_uid (subject), + &error_desc)) { + g_set_error_literal (error, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + error_desc); + g_free (error_desc); + goto error; + } - if (!ac) { - nm_log_warn (LOGD_CORE, "connection %s failed to activate: (%d) %s", - pending->connection_path, - error ? error->code : -1, - error && error->message ? error->message : "(unknown)"); + /* 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)) + vpn = TRUE; + + /* Normalize device path */ + if (device_path && g_strcmp0 (device_path, "/") == 0) + device_path = NULL; + + /* And validate it */ + if (device_path) { + device = nm_manager_get_device_by_path (self, device_path); + if (!device) { + g_set_error_literal (error, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_UNKNOWN_DEVICE, + "Device not found"); + goto error; + } + } else { + gboolean is_software = connection_needs_virtual_device (connection); + + /* VPN and software-device connections don't need a device yet */ + if (!vpn && !is_software) { + g_set_error_literal (error, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_UNKNOWN_DEVICE, + "This connection requires an existing device."); + 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"); + goto error; + } + + device = find_device_by_ip_iface (self, iface); + g_free (iface); + } } -out: - pending_activation_destroy (pending, error, ac); - g_clear_error (&error); + *out_device = device; + *out_vpn = vpn; + return subject; + +error: + g_object_unref (subject); + return NULL; } +/***********************************************************************/ + static void -activation_auth_done (PendingActivation *pending, GError *error) -{ - if (error) - pending_activation_destroy (pending, error, NULL); - else - pending_activate (pending->manager, pending); +_activation_auth_done (NMActiveConnection *active, + gboolean success, + const char *error_desc, + gpointer user_data1, + gpointer user_data2) +{ + NMManager *self = user_data1; + DBusGMethodInvocation *context = user_data2; + GError *error = NULL; + + if (success) { + if (_internal_activate_generic (self, active, &error)) { + dbus_g_method_return (context, nm_active_connection_get_path (active)); + g_object_unref (active); + return; + } + } else { + error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + error_desc); + } + + g_assert (error); + dbus_g_method_return_error (context, error); + _internal_activation_failed (self, active, error->message); + g_object_unref (active); + g_error_free (error); } static void @@ -3166,64 +3062,185 @@ impl_manager_activate_connection (NMManager *self, DBusGMethodInvocation *context) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - PendingActivation *pending; + NMActiveConnection *active = NULL; + NMAuthSubject *subject = NULL; + NMConnection *connection; + NMDevice *device = NULL; + gboolean is_vpn = FALSE; GError *error = NULL; - /* Need to check the caller's permissions and stuff before we can - * activate the connection. + /* Normalize object paths */ + if (g_strcmp0 (connection_path, "/") == 0) + connection_path = NULL; + if (g_strcmp0 (specific_object_path, "/") == 0) + specific_object_path = NULL; + if (g_strcmp0 (device_path, "/") == 0) + device_path = NULL; + + /* If the connection path is given and valid, that connection is activated. + * Otherwise the "best" connection for the device is chosen and activated, + * regardless of whether that connection is autoconnect-enabled or not + * (since this is an explicit request, not an auto-activation request). */ - pending = pending_activation_new (self, - context, - device_path, - connection_path, - NULL, - specific_object_path, - activation_auth_done, - &error); - if (pending) - pending_activation_check_authorized (pending, priv->dbus_mgr); - else { - g_assert (error); - dbus_g_method_return_error (context, error); - g_error_free (error); + if (!connection_path) { + GPtrArray *available; + guint64 best_timestamp = 0; + guint i; + + /* If no connection is given, find a suitable connection for the given device path */ + if (!device_path) { + error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, + "Only devices may be activated without a specifying a connection"); + goto error; + } + device = nm_manager_get_device_by_path (self, device_path); + if (!device) { + error = g_error_new (NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, + "Cannot activate unknown device %s", device_path); + goto error; + } + + available = nm_device_get_available_connections (device, specific_object_path); + for (i = 0; available && i < available->len; i++) { + NMSettingsConnection *candidate = g_ptr_array_index (available, i); + guint64 candidate_timestamp = 0; + + nm_settings_connection_get_timestamp (candidate, &candidate_timestamp); + if (!connection_path || (candidate_timestamp > best_timestamp)) { + connection_path = nm_connection_get_path (NM_CONNECTION (candidate)); + best_timestamp = candidate_timestamp; + } + } + + if (available) + g_ptr_array_free (available, TRUE); + + if (!connection_path) { + error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_UNKNOWN_CONNECTION, + "The device has no connections available."); + goto error; + } + } + + g_assert (connection_path); + connection = (NMConnection *) nm_settings_get_connection_by_path (priv->settings, connection_path); + if (!connection) { + error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_UNKNOWN_CONNECTION, + "Connection could not be found."); + goto error; } + + subject = validate_activation_request (self, + context, + connection, + device_path, + &device, + &is_vpn, + &error); + if (!subject) + goto error; + + active = _new_active_connection (self, + connection, + specific_object_path, + device, + subject, + &error); + if (!active) + goto error; + + nm_active_connection_authorize (active, _activation_auth_done, self, context); + active_connection_add (self, active); + g_clear_object (&subject); + return; + +error: + g_clear_object (&active); + g_clear_object (&subject); + + g_assert (error); + dbus_g_method_return_error (context, error); + g_error_free (error); } +/***********************************************************************/ + +typedef struct { + NMManager *manager; + NMActiveConnection *active; +} AddAndActivateInfo; + static void activation_add_done (NMSettings *self, - NMSettingsConnection *connection, + NMSettingsConnection *new_connection, GError *error, DBusGMethodInvocation *context, gpointer user_data) { - PendingActivation *pending = user_data; + AddAndActivateInfo *info = user_data; + GError *local = NULL; - if (error) - pending_activation_destroy (pending, error, NULL); - else { - /* Save the new connection's D-Bus path */ - pending->connection_path = g_strdup (nm_connection_get_path (NM_CONNECTION (connection))); + if (!error) { + nm_active_connection_set_connection (info->active, NM_CONNECTION (new_connection)); - /* And activate it */ - pending_activate (pending->manager, pending); + if (_internal_activate_generic (info->manager, info->active, &local)) { + dbus_g_method_return (context, + nm_connection_get_path (NM_CONNECTION (new_connection)), + nm_active_connection_get_path (info->active)); + goto done; + } + error = local; } + + g_assert (error); + _internal_activation_failed (info->manager, info->active, error->message); + dbus_g_method_return_error (context, error); + g_clear_error (&local); + +done: + g_object_unref (info->active); + g_free (info); } static void -add_and_activate_auth_done (PendingActivation *pending, GError *error) +_add_and_activate_auth_done (NMActiveConnection *active, + gboolean success, + const char *error_desc, + gpointer user_data1, + gpointer user_data2) { - if (error) - pending_activation_destroy (pending, error, NULL); - else { - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (pending->manager); + NMManager *self = user_data1; + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + DBusGMethodInvocation *context = user_data2; + AddAndActivateInfo *info; + GError *error = NULL; + + if (success) { + info = g_malloc0 (sizeof (*info)); + info->manager = self; + info->active = g_object_ref (active); /* Basic sender auth checks performed; try to add the connection */ - nm_settings_add_connection (priv->settings, - pending->connection, - pending->context, - activation_add_done, - pending); + nm_settings_add_connection_dbus (priv->settings, + nm_active_connection_get_connection (active), + TRUE, + context, + 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, + error_desc); + dbus_g_method_return_error (context, error); + g_error_free (error); } + + g_object_unref (active); } static void @@ -3234,29 +3251,105 @@ impl_manager_add_and_activate_connection (NMManager *self, DBusGMethodInvocation *context) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - PendingActivation *pending; + NMConnection *connection = NULL; + GSList *all_connections = NULL; + NMActiveConnection *active = NULL; + NMAuthSubject *subject = NULL; GError *error = NULL; + NMDevice *device = NULL; + gboolean vpn = FALSE; - /* Need to check the caller's permissions and stuff before we can - * activate the connection. + /* Normalize object paths */ + if (g_strcmp0 (specific_object_path, "/") == 0) + specific_object_path = NULL; + if (g_strcmp0 (device_path, "/") == 0) + device_path = NULL; + + /* Try to create a new connection with the given settings. + * We allow empty settings for AddAndActivateConnection(). In that case, + * the connection will be completed in nm_utils_complete_generic() or + * nm_device_complete_connection() below. Just make sure we don't expect + * specific data being in the connection till then (especially in + * validate_activation_request()). */ - pending = pending_activation_new (self, - context, - device_path, - NULL, - settings, - specific_object_path, - add_and_activate_auth_done, - &error); - if (pending) - pending_activation_check_authorized (pending, priv->dbus_mgr); - else { - g_assert (error); - dbus_g_method_return_error (context, error); - g_error_free (error); - } + connection = nm_connection_new (); + if (settings && g_hash_table_size (settings)) + nm_connection_replace_settings (connection, settings, NULL); + + subject = validate_activation_request (self, + context, + connection, + device_path, + &device, + &vpn, + &error); + 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 */ + if (!nm_connection_get_setting_vpn (connection)) { + error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_UNSUPPORTED_CONNECTION_TYPE, + "VPN connections require a 'vpn' setting"); + goto error; + } + + nm_utils_complete_generic (connection, + NM_SETTING_VPN_SETTING_NAME, + all_connections, + _("VPN connection %d"), + NULL, + FALSE); /* No IPv6 by default for now */ + } else { + /* Let each device subclass complete the connection */ + if (!nm_device_complete_connection (device, + connection, + specific_object_path, + all_connections, + &error)) + goto error; + } + g_slist_free (all_connections); + all_connections = NULL; + + active = _new_active_connection (self, + connection, + specific_object_path, + device, + subject, + &error); + if (!active) + 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; + +error: + g_clear_object (&connection); + g_slist_free (all_connections); + g_clear_object (&subject); + g_clear_object (&active); + + g_assert (error); + dbus_g_method_return_error (context, error); + g_error_free (error); } +/***********************************************************************/ + gboolean nm_manager_deactivate_connection (NMManager *manager, const char *connection_path, @@ -3286,9 +3379,8 @@ nm_manager_deactivate_connection (NMManager *manager, "The VPN connection was not active."); } else { g_assert (NM_IS_ACT_REQUEST (active)); - /* FIXME: use DEACTIVATING state */ nm_device_state_changed (nm_active_connection_get_device (active), - NM_DEVICE_STATE_DISCONNECTED, + NM_DEVICE_STATE_DEACTIVATING, reason); success = TRUE; } @@ -3309,11 +3401,13 @@ deactivate_net_auth_done_cb (NMAuthChain *chain, NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); GError *error = NULL; NMAuthCallResult result; - const char *active_path; + + g_assert (context); priv->auth_chains = g_slist_remove (priv->auth_chains, chain); - result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL)); + result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL); + if (auth_error) { nm_log_dbg (LOGD_CORE, "Disconnect request failed: %s", auth_error->message); error = g_error_new (NM_MANAGER_ERROR, @@ -3326,9 +3420,8 @@ deactivate_net_auth_done_cb (NMAuthChain *chain, "Not authorized to deactivate connections"); } else { /* success; deactivation allowed */ - active_path = nm_auth_chain_get_data (chain, "path"); if (!nm_manager_deactivate_connection (self, - active_path, + nm_auth_chain_get_data (chain, "path"), NM_DEVICE_STATE_REASON_USER_REQUESTED, &error)) g_assert (error); @@ -3351,9 +3444,9 @@ impl_manager_deactivate_connection (NMManager *self, NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); NMConnection *connection = NULL; GError *error = NULL; + NMAuthSubject *subject = NULL; GSList *iter; NMAuthChain *chain; - gulong sender_uid = G_MAXULONG; char *error_desc = NULL; /* Find the connection by its object path */ @@ -3370,85 +3463,100 @@ impl_manager_deactivate_connection (NMManager *self, error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE, "The connection was not active."); - dbus_g_method_return_error (context, error); - g_error_free (error); - return; + goto done; } - /* Need to check the caller's permissions and stuff before we can - * deactivate the connection. - */ - if (!nm_auth_get_caller_uid (context, - priv->dbus_mgr, - &sender_uid, - &error_desc)) { + /* Validate the caller */ + subject = nm_auth_subject_new_from_context (context); + if (!subject) { error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, - error_desc); - dbus_g_method_return_error (context, error); - g_error_free (error); - g_free (error_desc); - return; + "Failed to get request UID."); + goto done; } /* Ensure the subject has permissions for this connection */ if (!nm_auth_uid_in_acl (connection, - priv->session_monitor, - sender_uid, + nm_session_monitor_get (), + nm_auth_subject_get_uid (subject), &error_desc)) { error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, error_desc); - dbus_g_method_return_error (context, error); - g_error_free (error); g_free (error_desc); - return; + goto done; } - /* Yay for root */ - if (0 == sender_uid) { - if (!nm_manager_deactivate_connection (self, - active_path, - NM_DEVICE_STATE_REASON_USER_REQUESTED, - &error)) { - dbus_g_method_return_error (context, error); - g_clear_error (&error); - } else - dbus_g_method_return (context); - - return; + /* Validate the user request */ + chain = nm_auth_chain_new_subject (subject, context, deactivate_net_auth_done_cb, self); + if (!chain) { + error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "Unable to authenticate request."); + goto done; } - /* Otherwise validate the user request */ - chain = nm_auth_chain_new (context, NULL, deactivate_net_auth_done_cb, self); - g_assert (chain); priv->auth_chains = g_slist_append (priv->auth_chains, chain); - nm_auth_chain_set_data (chain, "path", g_strdup (active_path), g_free); nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE); + +done: + g_clear_object (&subject); + if (error) + dbus_g_method_return_error (context, error); + g_clear_error (&error); +} + +static gboolean +device_is_wake_on_lan (NMDevice *device) +{ + return nm_platform_link_get_wake_on_lan (nm_device_get_ip_ifindex (device)); } static void -do_sleep_wake (NMManager *self) +do_sleep_wake (NMManager *self, gboolean sleeping_changed) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - const GSList *unmanaged_specs; + gboolean suspending, waking_from_suspend; GSList *iter; + suspending = sleeping_changed && priv->sleeping; + waking_from_suspend = sleeping_changed && !priv->sleeping; + if (manager_sleeping (self)) { - nm_log_info (LOGD_SUSPEND, "sleeping or disabling..."); + nm_log_info (LOGD_SUSPEND, suspending ? "sleeping..." : "disabling..."); - /* Just deactivate and down all devices from the device list, - * to keep things fast the device list will get resynced when - * the manager wakes up. + /* FIXME: are there still hardware devices that need to be disabled around + * suspend/resume? */ - for (iter = priv->devices; iter; iter = iter->next) - nm_device_set_managed (NM_DEVICE (iter->data), FALSE, NM_DEVICE_STATE_REASON_SLEEPING); + for (iter = priv->devices; iter; iter = iter->next) { + NMDevice *device = iter->data; + /* FIXME: shouldn't we be unmanaging software devices if !suspending? */ + if (nm_device_is_software (device)) + continue; + /* Wake-on-LAN devices will be taken down post-suspend rather than pre- */ + if (suspending && device_is_wake_on_lan (device)) + continue; + + nm_device_set_unmanaged (device, NM_UNMANAGED_INTERNAL, TRUE, NM_DEVICE_STATE_REASON_SLEEPING); + } } else { - nm_log_info (LOGD_SUSPEND, "waking up and re-enabling..."); + nm_log_info (LOGD_SUSPEND, waking_from_suspend ? "waking up..." : "re-enabling..."); + + if (waking_from_suspend) { + /* Belatedly take down Wake-on-LAN devices; ideally we wouldn't have to do this + * but for now it's the only way to make sure we re-check their connectivity. + */ + for (iter = priv->devices; iter; iter = iter->next) { + NMDevice *device = iter->data; - unmanaged_specs = nm_settings_get_unmanaged_specs (priv->settings); + if (nm_device_is_software (device)) + continue; + if (device_is_wake_on_lan (device)) + nm_device_set_unmanaged (device, NM_UNMANAGED_INTERNAL, TRUE, NM_DEVICE_STATE_REASON_SLEEPING); + } + } /* Ensure rfkill state is up-to-date since we don't respond to state * changes during sleep. @@ -3460,6 +3568,9 @@ do_sleep_wake (NMManager *self) NMDevice *device = NM_DEVICE (iter->data); guint i; + if (nm_device_is_software (device)) + continue; + /* enable/disable wireless devices since that we don't respond * to killswitch changes during sleep. */ @@ -3479,10 +3590,7 @@ do_sleep_wake (NMManager *self) g_object_set (G_OBJECT (device), NM_DEVICE_AUTOCONNECT, TRUE, NULL); - if (nm_device_spec_match_list (device, unmanaged_specs)) - nm_device_set_managed (device, FALSE, NM_DEVICE_STATE_REASON_NOW_UNMANAGED); - else - nm_device_set_managed (device, TRUE, NM_DEVICE_STATE_REASON_NOW_MANAGED); + nm_device_set_unmanaged (device, NM_UNMANAGED_INTERNAL, FALSE, NM_DEVICE_STATE_REASON_NOW_MANAGED); } } @@ -3504,7 +3612,7 @@ _internal_sleep (NMManager *self, gboolean do_sleep) priv->sleeping = do_sleep; - do_sleep_wake (self); + do_sleep_wake (self, TRUE); g_object_notify (G_OBJECT (self), NM_MANAGER_SLEEPING); } @@ -3524,7 +3632,7 @@ sleep_auth_done_cb (NMAuthChain *chain, priv->auth_chains = g_slist_remove (priv->auth_chains, chain); - result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, NM_AUTH_PERMISSION_SLEEP_WAKE)); + result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_SLEEP_WAKE); if (error) { nm_log_dbg (LOGD_SUSPEND, "Sleep/wake request failed: %s", error->message); ret_error = g_error_new (NM_MANAGER_ERROR, @@ -3559,7 +3667,6 @@ impl_manager_sleep (NMManager *self, GError *error = NULL; #if 0 NMAuthChain *chain; - gulong sender_uid = G_MAXULONG; const char *error_desc = NULL; #endif @@ -3589,28 +3696,18 @@ impl_manager_sleep (NMManager *self, return; #if 0 - if (!nm_auth_get_caller_uid (context, priv->dbus_mgr, &sender_uid, &error_desc)) { + chain = nm_auth_chain_new (context, sleep_auth_done_cb, self, &error_desc); + if (chain) { + priv->auth_chains = g_slist_append (priv->auth_chains, chain); + nm_auth_chain_set_data (chain, "sleep", GUINT_TO_POINTER (do_sleep), NULL); + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SLEEP_WAKE, TRUE); + } else { error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, error_desc); dbus_g_method_return_error (context, error); g_error_free (error); - return; } - - /* Root doesn't need PK authentication */ - if (0 == sender_uid) { - _internal_sleep (self, do_sleep); - dbus_g_method_return (context); - return; - } - - chain = nm_auth_chain_new (context, NULL, sleep_auth_done_cb, self); - g_assert (chain); - priv->auth_chains = g_slist_append (priv->auth_chains, chain); - - nm_auth_chain_set_data (chain, "sleep", GUINT_TO_POINTER (do_sleep), NULL); - nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SLEEP_WAKE, TRUE); #endif } @@ -3655,7 +3752,7 @@ _internal_enable (NMManager *self, gboolean enable) priv->net_enabled = enable; - do_sleep_wake (self); + do_sleep_wake (self, FALSE); g_object_notify (G_OBJECT (self), NM_MANAGER_NETWORKING_ENABLED); } @@ -3668,27 +3765,25 @@ enable_net_done_cb (NMAuthChain *chain, { NMManager *self = NM_MANAGER (user_data); NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - GError *ret_error; + GError *ret_error = NULL; NMAuthCallResult result; gboolean enable; + g_assert (context); + priv->auth_chains = g_slist_remove (priv->auth_chains, chain); - result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK)); + result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK); if (error) { nm_log_dbg (LOGD_CORE, "Enable request failed: %s", error->message); ret_error = g_error_new (NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, "Enable request failed: %s", error->message); - dbus_g_method_return_error (context, ret_error); - g_error_free (ret_error); } else if (result != NM_AUTH_CALL_RESULT_YES) { ret_error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, "Not authorized to enable/disable networking"); - dbus_g_method_return_error (context, ret_error); - g_error_free (ret_error); } else { /* Auth success */ enable = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "enable")); @@ -3696,6 +3791,11 @@ enable_net_done_cb (NMAuthChain *chain, dbus_g_method_return (context); } + if (ret_error) { + dbus_g_method_return_error (context, ret_error); + g_error_free (ret_error); + } + nm_auth_chain_unref (chain); } @@ -3707,8 +3807,6 @@ impl_manager_enable (NMManager *self, NMManagerPrivate *priv; NMAuthChain *chain; GError *error = NULL; - gulong sender_uid = G_MAXULONG; - char *error_desc = NULL; g_return_if_fail (NM_IS_MANAGER (self)); @@ -3718,34 +3816,25 @@ impl_manager_enable (NMManager *self, error = g_error_new (NM_MANAGER_ERROR, NM_MANAGER_ERROR_ALREADY_ENABLED_OR_DISABLED, "Already %s", enable ? "enabled" : "disabled"); - dbus_g_method_return_error (context, error); - g_error_free (error); - return; + goto done; } - if (!nm_auth_get_caller_uid (context, priv->dbus_mgr, &sender_uid, &error_desc)) { + chain = nm_auth_chain_new_context (context, enable_net_done_cb, self); + if (!chain) { error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, - error_desc); - dbus_g_method_return_error (context, error); - g_error_free (error); - g_free (error_desc); - return; - } - - /* Root doesn't need PK authentication */ - if (0 == sender_uid) { - _internal_enable (self, enable); - dbus_g_method_return (context); - return; + "Unable to authenticate request."); + goto done; } - chain = nm_auth_chain_new (context, NULL, enable_net_done_cb, self); - g_assert (chain); priv->auth_chains = g_slist_append (priv->auth_chains, chain); - nm_auth_chain_set_data (chain, "enable", GUINT_TO_POINTER (enable), NULL); nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK, TRUE); + +done: + if (error) + dbus_g_method_return_error (context, error); + g_clear_error (&error); } /* Permissions */ @@ -3755,7 +3844,7 @@ get_perm_add_result (NMAuthChain *chain, GHashTable *results, const char *permis { NMAuthCallResult result; - result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, permission)); + result = nm_auth_chain_get_result (chain, permission); if (result == NM_AUTH_CALL_RESULT_YES) g_hash_table_insert (results, (char *) permission, "yes"); else if (result == NM_AUTH_CALL_RESULT_NO) @@ -3778,6 +3867,8 @@ get_permissions_done_cb (NMAuthChain *chain, GError *ret_error; GHashTable *results; + g_assert (context); + priv->auth_chains = g_slist_remove (priv->auth_chains, chain); if (error) { nm_log_dbg (LOGD_CORE, "Permissions request failed: %s", error->message); @@ -3789,6 +3880,7 @@ get_permissions_done_cb (NMAuthChain *chain, g_error_free (ret_error); } else { results = g_hash_table_new (g_str_hash, g_str_equal); + get_perm_add_result (chain, results, NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK); get_perm_add_result (chain, results, NM_AUTH_PERMISSION_SLEEP_WAKE); get_perm_add_result (chain, results, NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI); @@ -3800,6 +3892,7 @@ get_permissions_done_cb (NMAuthChain *chain, get_perm_add_result (chain, results, NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM); get_perm_add_result (chain, results, NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN); get_perm_add_result (chain, results, NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME); + dbus_g_method_return (context, results); g_hash_table_destroy (results); } @@ -3813,11 +3906,19 @@ impl_manager_get_permissions (NMManager *self, { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); NMAuthChain *chain; + GError *error = NULL; - chain = nm_auth_chain_new (context, NULL, get_permissions_done_cb, self); - g_assert (chain); - priv->auth_chains = g_slist_append (priv->auth_chains, chain); + chain = nm_auth_chain_new_context (context, get_permissions_done_cb, self); + if (!chain) { + error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "Unable to authenticate request."); + dbus_g_method_return_error (context, error); + g_clear_error (&error); + return; + } + priv->auth_chains = g_slist_append (priv->auth_chains, chain); nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK, FALSE); nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SLEEP_WAKE, FALSE); nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI, FALSE); @@ -3839,22 +3940,46 @@ impl_manager_get_state (NMManager *manager, guint32 *state, GError **error) return TRUE; } -static gboolean +static void impl_manager_set_logging (NMManager *manager, const char *level, const char *domains, - GError **error) + DBusGMethodInvocation *context) { - if (nm_logging_setup (level, domains, error)) { + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + GError *error = NULL; + gulong caller_uid = G_MAXULONG; + + if (!nm_dbus_manager_get_caller_info (priv->dbus_mgr, context, NULL, &caller_uid, NULL)) { + error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "Failed to get request UID."); + goto done; + } + + if (0 != caller_uid) { + error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "Permission denied"); + goto done; + } + + if (nm_logging_setup (level, domains, NULL, &error)) { + char *new_level = nm_logging_level_to_string (); char *new_domains = nm_logging_domains_to_string (); nm_log_info (LOGD_CORE, "logging: level '%s' domains '%s'", - nm_logging_level_to_string (), - new_domains); + new_level, new_domains); + g_free (new_level); g_free (new_domains); - return TRUE; } - return FALSE; + +done: + if (error) { + dbus_g_method_return_error (context, error); + g_error_free (error); + } else + dbus_g_method_return (context); } static void @@ -3862,8 +3987,8 @@ impl_manager_get_logging (NMManager *manager, char **level, char **domains) { - *level = g_strdup (nm_logging_level_to_string ()); - *domains = g_strdup (nm_logging_domains_to_string ()); + *level = nm_logging_level_to_string (); + *domains = nm_logging_domains_to_string (); } static void @@ -3929,37 +4054,20 @@ impl_manager_check_connectivity (NMManager *manager, { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); NMAuthChain *chain; - gulong sender_uid = G_MAXULONG; - char *error_desc = NULL; - GError *error; + GError *error = NULL; - /* Need to check the caller's permissions and stuff first */ - if (!nm_auth_get_caller_uid (context, - priv->dbus_mgr, - &sender_uid, - &error_desc)) { + /* Validate the request */ + chain = nm_auth_chain_new_context (context, check_connectivity_auth_done_cb, manager); + if (!chain) { error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, - error_desc); + "Unable to authenticate request."); dbus_g_method_return_error (context, error); - g_error_free (error); - g_free (error_desc); - return; - } - - /* Yay for root */ - if (0 == sender_uid) { - nm_connectivity_check_async (priv->connectivity, - connectivity_check_done, - context); + g_clear_error (&error); return; } - /* Validate the user request */ - chain = nm_auth_chain_new (context, NULL, check_connectivity_auth_done_cb, manager); - g_assert (chain); priv->auth_chains = g_slist_append (priv->auth_chains, chain); - nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE); } @@ -3972,14 +4080,13 @@ nm_manager_start (NMManager *self) /* Set initial radio enabled/disabled state */ for (i = 0; i < RFKILL_TYPE_MAX; i++) { RadioState *rstate = &priv->radio_states[i]; - RfKillState udev_state; gboolean enabled; if (!rstate->desc) continue; - udev_state = nm_udev_manager_get_rfkill_state (priv->udev_mgr, i); - update_rstate_from_rfkill (rstate, udev_state); + /* recheck kernel rfkill state */ + update_rstate_from_rfkill (priv->rfkill_mgr, rstate); if (rstate->desc) { nm_log_info (LOGD_RFKILL, "%s %s by radio killswitch; %s by state file", @@ -3998,23 +4105,7 @@ nm_manager_start (NMManager *self) system_unmanaged_devices_changed_cb (priv->settings, NULL, self); system_hostname_changed_cb (priv->settings, NULL, self); - /* FIXME: remove when we handle bridges non-destructively */ - /* Read a list of bridges NM managed when it last quit, and only - * manage those bridges to avoid conflicts with external tools. - */ - priv->nm_bridges = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - read_nm_created_bridges (self); - - nm_udev_manager_query_devices (priv->udev_mgr); - nm_bluez_manager_query_devices (priv->bluez_mgr); - - /* Query devices again to ensure that we catch all virtual interfaces (like - * VLANs) that require a parent. If during the first pass the VLAN - * interface was detected first, the parent wouldn't exist yet and creating - * the VLAN would fail. The second query ensures that we'll have a valid - * parent for the VLAN during the second pass. - */ - nm_udev_manager_query_devices (priv->udev_mgr); + nm_platform_query_devices (); /* * Connections added before the manager is started do not emit @@ -4022,9 +4113,7 @@ nm_manager_start (NMManager *self) */ system_create_virtual_devices (self); - /* FIXME: remove when we handle bridges non-destructively */ - g_hash_table_unref (priv->nm_bridges); - priv->nm_bridges = NULL; + check_if_startup_complete (self); } static gboolean @@ -4089,9 +4178,7 @@ firmware_dir_changed (GFileMonitor *monitor, switch (event_type) { case G_FILE_MONITOR_EVENT_CREATED: case G_FILE_MONITOR_EVENT_CHANGED: -#if GLIB_CHECK_VERSION(2,23,4) case G_FILE_MONITOR_EVENT_MOVED: -#endif case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED: case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT: if (!priv->fw_changed_id) { @@ -4129,7 +4216,7 @@ policy_default_device_changed (GObject *object, GParamSpec *pspec, gpointer user if (ac != priv->primary_connection) { g_clear_object (&priv->primary_connection); priv->primary_connection = ac ? g_object_ref (ac) : NULL; - nm_log_dbg (LOGD_CORE, "PrimaryConnection now %s", ac ? nm_active_connection_get_name (ac) : "(none)"); + nm_log_dbg (LOGD_CORE, "PrimaryConnection now %s", ac ? nm_active_connection_get_id (ac) : "(none)"); g_object_notify (G_OBJECT (self), NM_MANAGER_PRIMARY_CONNECTION); } } @@ -4162,7 +4249,7 @@ policy_activating_device_changed (GObject *object, GParamSpec *pspec, gpointer u if (ac != priv->activating_connection) { g_clear_object (&priv->activating_connection); priv->activating_connection = ac ? g_object_ref (ac) : NULL; - nm_log_dbg (LOGD_CORE, "ActivatingConnection now %s", ac ? nm_active_connection_get_name (ac) : "(none)"); + nm_log_dbg (LOGD_CORE, "ActivatingConnection now %s", ac ? nm_active_connection_get_id (ac) : "(none)"); g_object_notify (G_OBJECT (self), NM_MANAGER_ACTIVATING_CONNECTION); } } @@ -4178,73 +4265,37 @@ prop_set_auth_done_cb (NMAuthChain *chain, { NMManager *self = NM_MANAGER (user_data); NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - DBusGConnection *bus; - DBusConnection *dbus_connection; + DBusConnection *connection; NMAuthCallResult result; - DBusMessage *reply = NULL, *request; - GError *ret_error; - const char *permission, *prop, *objpath; + DBusMessage *reply = NULL, *message; + const char *permission, *prop; + GObject *obj; gboolean set_enabled = TRUE; - gboolean is_device = FALSE; - size_t objpath_len; - size_t devpath_len; priv->auth_chains = g_slist_remove (priv->auth_chains, chain); - request = nm_auth_chain_get_data (chain, "message"); + message = nm_auth_chain_get_data (chain, "message"); permission = nm_auth_chain_get_data (chain, "permission"); prop = nm_auth_chain_get_data (chain, "prop"); set_enabled = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "enabled")); - objpath = nm_auth_chain_get_data (chain, "objectpath"); + obj = nm_auth_chain_get_data (chain, "object"); - objpath_len = strlen (objpath); - devpath_len = strlen (NM_DBUS_PATH "/Devices"); - if ( strncmp (objpath, NM_DBUS_PATH "/Devices", devpath_len) == 0 - && objpath_len > devpath_len) - is_device = TRUE; - - if (error) { - reply = dbus_message_new_error (request, is_device ? DEV_PERM_DENIED_ERROR : NM_PERM_DENIED_ERROR, + result = nm_auth_chain_get_result (chain, permission); + if (error || (result != NM_AUTH_CALL_RESULT_YES)) { + reply = dbus_message_new_error (message, + NM_IS_DEVICE (obj) ? DEV_PERM_DENIED_ERROR : NM_PERM_DENIED_ERROR, "Not authorized to perform this operation"); } else { - /* Caller has had a chance to obtain authorization, so we only need to - * check for 'yes' here. - */ - result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, permission)); - if (result != NM_AUTH_CALL_RESULT_YES) { - reply = dbus_message_new_error (request, is_device ? DEV_PERM_DENIED_ERROR : NM_PERM_DENIED_ERROR, - "Not authorized to perform this operation"); - } else { - if (is_device) { - /* Find the device */ - NMDevice *device = nm_manager_get_device_by_path (self, objpath); - if (device) { - g_object_set (device, prop, set_enabled, NULL); - reply = dbus_message_new_method_return (request); - } - else { - ret_error = g_error_new_literal (NM_MANAGER_ERROR, - NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "Can't find device for this operation"); - dbus_g_method_return_error (context, ret_error); - g_error_free (ret_error); - } - } else { - g_object_set (self, prop, set_enabled, NULL); - reply = dbus_message_new_method_return (request); - } - } + g_object_set (obj, prop, set_enabled, NULL); + reply = dbus_message_new_method_return (message); } - if (reply) { - bus = nm_dbus_manager_get_connection (priv->dbus_mgr); - g_assert (bus); - dbus_connection = dbus_g_connection_get_connection (bus); - g_assert (dbus_connection); + g_assert (reply); + connection = nm_auth_chain_get_data (chain, "connection"); + g_assert (connection); + dbus_connection_send (connection, reply, NULL); + dbus_message_unref (reply); - dbus_connection_send (dbus_connection, reply, NULL); - dbus_message_unref (reply); - } nm_auth_chain_unref (chain); } @@ -4259,14 +4310,12 @@ prop_filter (DBusConnection *connection, DBusMessageIter sub; const char *propiface = NULL; const char *propname = NULL; - const char *sender = NULL; - const char *objpath = NULL; const char *glib_propname = NULL, *permission = NULL; - DBusError dbus_error; - gulong uid = G_MAXULONG; DBusMessage *reply = NULL; gboolean set_enabled = FALSE; + NMAuthSubject *subject = NULL; NMAuthChain *chain; + GObject *obj; /* The sole purpose of this function is to validate property accesses * on the NMManager object since dbus-glib doesn't yet give us this @@ -4315,173 +4364,47 @@ prop_filter (DBusConnection *connection, return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; dbus_message_iter_get_basic (&sub, &set_enabled); - sender = dbus_message_get_sender (message); - if (!sender) { + /* Make sure the object exists */ + obj = dbus_g_connection_lookup_g_object (dbus_connection_get_g_connection (connection), + dbus_message_get_path (message)); + if (!obj) { reply = dbus_message_new_error (message, NM_PERM_DENIED_ERROR, - "Could not determine D-Bus requestor"); + "Object does not exist"); goto out; } - objpath = dbus_message_get_path (message); - if (!objpath) { + subject = nm_auth_subject_new_from_message (connection, message); + if (!subject) { reply = dbus_message_new_error (message, NM_PERM_DENIED_ERROR, - "Could not determine D-Bus object path"); + "Could not determine request UID."); goto out; } - dbus_error_init (&dbus_error); - uid = dbus_bus_get_unix_user (connection, sender, &dbus_error); - if (dbus_error_is_set (&dbus_error)) { + /* Validate the user request */ + chain = nm_auth_chain_new_subject (subject, NULL, prop_set_auth_done_cb, self); + if (!chain) { reply = dbus_message_new_error (message, NM_PERM_DENIED_ERROR, - "Could not determine the user ID of the requestor"); - dbus_error_free (&dbus_error); + "Could not authenticate request."); goto out; } - if (uid > 0) { - /* Otherwise validate the user request */ - chain = nm_auth_chain_new_raw_message (message, prop_set_auth_done_cb, self); - g_assert (chain); - priv->auth_chains = g_slist_append (priv->auth_chains, chain); - nm_auth_chain_set_data (chain, "prop", g_strdup (glib_propname), g_free); - nm_auth_chain_set_data (chain, "permission", g_strdup (permission), g_free); - nm_auth_chain_set_data (chain, "enabled", GUINT_TO_POINTER (set_enabled), NULL); - nm_auth_chain_set_data (chain, "message", dbus_message_ref (message), (GDestroyNotify) dbus_message_unref); - nm_auth_chain_set_data (chain, "objectpath", g_strdup (objpath), g_free); - nm_auth_chain_add_call (chain, permission, TRUE); - } else { - /* Yay for root */ - g_object_set (self, glib_propname, set_enabled, NULL); - reply = dbus_message_new_method_return (message); - } + priv->auth_chains = g_slist_append (priv->auth_chains, chain); + nm_auth_chain_set_data (chain, "prop", g_strdup (glib_propname), g_free); + nm_auth_chain_set_data (chain, "permission", g_strdup (permission), g_free); + nm_auth_chain_set_data (chain, "enabled", GUINT_TO_POINTER (set_enabled), NULL); + nm_auth_chain_set_data (chain, "message", dbus_message_ref (message), (GDestroyNotify) dbus_message_unref); + nm_auth_chain_set_data (chain, "connection", dbus_connection_ref (connection), (GDestroyNotify) dbus_connection_unref); + nm_auth_chain_set_data (chain, "object", g_object_ref (obj), (GDestroyNotify) g_object_unref); + nm_auth_chain_add_call (chain, permission, TRUE); out: if (reply) { dbus_connection_send (connection, reply, NULL); dbus_message_unref (reply); } - return DBUS_HANDLER_RESULT_HANDLED; -} - -static NMManager *singleton = NULL; - -NMManager * -nm_manager_get (void) -{ - g_assert (singleton); - return g_object_ref (singleton); -} - -NMManager * -nm_manager_new (NMSettings *settings, - const char *state_file, - gboolean initial_net_enabled, - gboolean initial_wifi_enabled, - gboolean initial_wwan_enabled, - gboolean initial_wimax_enabled, - const gchar *connectivity_uri, - gint connectivity_interval, - const gchar *connectivity_response, - GError **error) -{ - NMManagerPrivate *priv; - DBusGConnection *bus; - DBusConnection *dbus_connection; - - g_assert (settings); - - /* Can only be called once */ - g_assert (singleton == NULL); - singleton = (NMManager *) g_object_new (NM_TYPE_MANAGER, NULL); - g_assert (singleton); - - priv = NM_MANAGER_GET_PRIVATE (singleton); - - priv->policy = nm_policy_new (singleton, settings); - g_signal_connect (priv->policy, "notify::" NM_POLICY_DEFAULT_IP4_DEVICE, - G_CALLBACK (policy_default_device_changed), singleton); - g_signal_connect (priv->policy, "notify::" NM_POLICY_DEFAULT_IP6_DEVICE, - G_CALLBACK (policy_default_device_changed), singleton); - g_signal_connect (priv->policy, "notify::" NM_POLICY_ACTIVATING_IP4_DEVICE, - G_CALLBACK (policy_activating_device_changed), singleton); - g_signal_connect (priv->policy, "notify::" NM_POLICY_ACTIVATING_IP6_DEVICE, - G_CALLBACK (policy_activating_device_changed), singleton); - - priv->connectivity = nm_connectivity_new (connectivity_uri, connectivity_interval, connectivity_response); - g_signal_connect (priv->connectivity, "notify::" NM_CONNECTIVITY_STATE, - G_CALLBACK (connectivity_changed), singleton); - - bus = nm_dbus_manager_get_connection (priv->dbus_mgr); - g_assert (bus); - dbus_connection = dbus_g_connection_get_connection (bus); - g_assert (dbus_connection); - - if (!dbus_connection_add_filter (dbus_connection, prop_filter, singleton, NULL)) { - nm_log_err (LOGD_CORE, "failed to register DBus connection filter"); - g_object_unref (singleton); - return NULL; - } - - priv->settings = g_object_ref (settings); - - priv->state_file = g_strdup (state_file); - - priv->net_enabled = initial_net_enabled; - - priv->radio_states[RFKILL_TYPE_WLAN].user_enabled = initial_wifi_enabled; - priv->radio_states[RFKILL_TYPE_WWAN].user_enabled = initial_wwan_enabled; - priv->radio_states[RFKILL_TYPE_WIMAX].user_enabled = initial_wimax_enabled; - - g_signal_connect (priv->settings, "notify::" NM_SETTINGS_UNMANAGED_SPECS, - G_CALLBACK (system_unmanaged_devices_changed_cb), singleton); - g_signal_connect (priv->settings, "notify::" NM_SETTINGS_HOSTNAME, - G_CALLBACK (system_hostname_changed_cb), singleton); - g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_ADDED, - G_CALLBACK (connection_added), singleton); - g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_UPDATED, - G_CALLBACK (connection_changed), singleton); - g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_REMOVED, - G_CALLBACK (connection_removed), singleton); - g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_VISIBILITY_CHANGED, - G_CALLBACK (connection_changed), singleton); - - dbus_g_connection_register_g_object (bus, NM_DBUS_PATH, G_OBJECT (singleton)); - - priv->udev_mgr = nm_udev_manager_new (); - g_signal_connect (priv->udev_mgr, - "device-added", - G_CALLBACK (udev_device_added_cb), - singleton); - g_signal_connect (priv->udev_mgr, - "device-removed", - G_CALLBACK (udev_device_removed_cb), - singleton); - g_signal_connect (priv->udev_mgr, - "rfkill-changed", - G_CALLBACK (udev_manager_rfkill_changed_cb), - singleton); - - priv->bluez_mgr = nm_bluez_manager_get (NM_CONNECTION_PROVIDER (priv->settings)); - - g_signal_connect (priv->bluez_mgr, - NM_BLUEZ_MANAGER_BDADDR_ADDED, - G_CALLBACK (bluez_manager_bdaddr_added_cb), - singleton); - - g_signal_connect (priv->bluez_mgr, - NM_BLUEZ_MANAGER_BDADDR_REMOVED, - G_CALLBACK (bluez_manager_bdaddr_removed_cb), - singleton); - - priv->session_monitor = nm_session_monitor_get (); - - /* Force kernel WiFi rfkill state to follow NM saved wifi state in case - * the BIOS doesn't save rfkill state, and to be consistent with user - * changes to the WirelessEnabled property which toggles kernel rfkill. - */ - rfkill_change_wifi (priv->radio_states[RFKILL_TYPE_WLAN].desc, initial_wifi_enabled); + g_clear_object (&subject); - return singleton; + return DBUS_HANDLER_RESULT_HANDLED; } static void @@ -4491,113 +4414,9 @@ authority_changed_cb (gpointer user_data) g_signal_emit (NM_MANAGER (user_data), signals[CHECK_PERMISSIONS], 0); } -static void -dispose (GObject *object) -{ - NMManager *manager = NM_MANAGER (object); - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); - DBusGConnection *bus; - DBusConnection *dbus_connection; - GSList *iter; - - if (priv->disposed) { - G_OBJECT_CLASS (nm_manager_parent_class)->dispose (object); - return; - } - priv->disposed = TRUE; - - g_slist_foreach (priv->auth_chains, (GFunc) nm_auth_chain_unref, NULL); - g_slist_free (priv->auth_chains); - - nm_auth_changed_func_unregister (authority_changed_cb, manager); - - /* FIXME: remove when we handle bridges non-destructively */ - write_nm_created_bridges (manager); - - /* Remove all devices */ - while (g_slist_length (priv->devices)) { - priv->devices = remove_one_device (manager, - priv->devices, - NM_DEVICE (priv->devices->data), - TRUE); - } - - if (priv->ac_cleanup_id) { - g_source_remove (priv->ac_cleanup_id); - priv->ac_cleanup_id = 0; - } - - for (iter = priv->active_connections; iter; iter = g_slist_next (iter)) - active_connection_removed (manager, NM_ACTIVE_CONNECTION (iter->data)); - g_slist_free (priv->active_connections); - priv->active_connections = NULL; - g_clear_object (&priv->primary_connection); - g_clear_object (&priv->activating_connection); - - g_clear_object (&priv->connectivity); - - g_free (priv->hostname); - - g_signal_handlers_disconnect_by_func (priv->policy, G_CALLBACK (policy_default_device_changed), singleton); - g_signal_handlers_disconnect_by_func (priv->policy, G_CALLBACK (policy_activating_device_changed), singleton); - g_object_unref (priv->policy); - - g_object_unref (priv->settings); - g_object_unref (priv->vpn_manager); - g_object_unref (priv->session_monitor); - - if (priv->modem_added_id) { - g_source_remove (priv->modem_added_id); - priv->modem_added_id = 0; - } - if (priv->modem_removed_id) { - g_source_remove (priv->modem_removed_id); - priv->modem_removed_id = 0; - } - g_object_unref (priv->modem_manager); - - /* Unregister property filter */ - bus = nm_dbus_manager_get_connection (priv->dbus_mgr); - if (bus) { - dbus_connection = dbus_g_connection_get_connection (bus); - g_assert (dbus_connection); - dbus_connection_remove_filter (dbus_connection, prop_filter, manager); - } - g_signal_handler_disconnect (priv->dbus_mgr, priv->dbus_connection_changed_id); - g_object_unref (priv->dbus_mgr); - - if (priv->bluez_mgr) - g_object_unref (priv->bluez_mgr); - - if (priv->aipd_proxy) - g_object_unref (priv->aipd_proxy); - - if (priv->sleep_monitor) - g_object_unref (priv->sleep_monitor); - - if (priv->fw_monitor) { - if (priv->fw_monitor_id) - g_signal_handler_disconnect (priv->fw_monitor, priv->fw_monitor_id); - - if (priv->fw_changed_id) - g_source_remove (priv->fw_changed_id); - - g_file_monitor_cancel (priv->fw_monitor); - g_object_unref (priv->fw_monitor); - } - - g_slist_free (priv->factories); - - if (priv->timestamp_update_id) { - g_source_remove (priv->timestamp_update_id); - priv->timestamp_update_id = 0; - } - - G_OBJECT_CLASS (nm_manager_parent_class)->dispose (object); -} - #define KERN_RFKILL_OP_CHANGE_ALL 3 #define KERN_RFKILL_TYPE_WLAN 1 +#define KERN_RFKILL_TYPE_WWAN 5 struct rfkill_event { __u32 idx; __u8 type; @@ -4606,18 +4425,19 @@ struct rfkill_event { } __attribute__((packed)); static void -rfkill_change_wifi (const char *desc, gboolean enabled) +rfkill_change (const char *desc, RfKillType rtype, gboolean enabled) { int fd; struct rfkill_event event; ssize_t len; + g_return_if_fail (rtype == RFKILL_TYPE_WLAN || rtype == RFKILL_TYPE_WWAN); + errno = 0; fd = open ("/dev/rfkill", O_RDWR); if (fd < 0) { if (errno == EACCES) - nm_log_warn (LOGD_RFKILL, "(%s): failed to open killswitch device " - "for WiFi radio control", desc); + nm_log_warn (LOGD_RFKILL, "(%s): failed to open killswitch device", desc); return; } @@ -4630,7 +4450,16 @@ rfkill_change_wifi (const char *desc, gboolean enabled) memset (&event, 0, sizeof (event)); event.op = KERN_RFKILL_OP_CHANGE_ALL; - event.type = KERN_RFKILL_TYPE_WLAN; + switch (rtype) { + case RFKILL_TYPE_WLAN: + event.type = KERN_RFKILL_TYPE_WLAN; + break; + case RFKILL_TYPE_WWAN: + event.type = KERN_RFKILL_TYPE_WWAN; + break; + default: + g_assert_not_reached (); + } event.soft = enabled ? 0 : 1; len = write (fd, &event, sizeof (event)); @@ -4657,6 +4486,10 @@ manager_radio_user_toggled (NMManager *self, GError *error = NULL; gboolean old_enabled, new_enabled; + /* Don't touch devices if asleep/networking disabled */ + if (manager_sleeping (self)) + return; + if (rstate->desc) { nm_log_dbg (LOGD_RFKILL, "(%s): setting radio %s by user", rstate->desc, @@ -4690,114 +4523,11 @@ manager_radio_user_toggled (NMManager *self, rstate->user_enabled = enabled; new_enabled = radio_enabled_for_rstate (rstate, FALSE); if (new_enabled != old_enabled) { - manager_update_radio_enabled (self, rstate, new_enabled); - - /* For WiFi only (for now) set the actual kernel rfkill state */ - if (rstate->rtype == RFKILL_TYPE_WLAN) - rfkill_change_wifi (rstate->desc, new_enabled); - } -} - -static void -set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) -{ - NMManager *self = NM_MANAGER (object); - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - - switch (prop_id) { - case PROP_NETWORKING_ENABLED: - /* Construct only for now */ - priv->net_enabled = g_value_get_boolean (value); - break; - case PROP_WIRELESS_ENABLED: - manager_radio_user_toggled (NM_MANAGER (object), - &priv->radio_states[RFKILL_TYPE_WLAN], - g_value_get_boolean (value)); - break; - case PROP_WWAN_ENABLED: - manager_radio_user_toggled (NM_MANAGER (object), - &priv->radio_states[RFKILL_TYPE_WWAN], - g_value_get_boolean (value)); - break; - case PROP_WIMAX_ENABLED: - manager_radio_user_toggled (NM_MANAGER (object), - &priv->radio_states[RFKILL_TYPE_WIMAX], - g_value_get_boolean (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} + /* Try to change the kernel rfkill state */ + if (rstate->rtype == RFKILL_TYPE_WLAN || rstate->rtype == RFKILL_TYPE_WWAN) + rfkill_change (rstate->desc, rstate->rtype, new_enabled); -static void -get_property (GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) -{ - NMManager *self = NM_MANAGER (object); - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - GSList *iter; - GPtrArray *active; - const char *path; - - switch (prop_id) { - case PROP_VERSION: - g_value_set_string (value, VERSION); - break; - case PROP_STATE: - nm_manager_update_state (self); - g_value_set_uint (value, priv->state); - break; - case PROP_NETWORKING_ENABLED: - g_value_set_boolean (value, priv->net_enabled); - break; - case PROP_WIRELESS_ENABLED: - g_value_set_boolean (value, radio_enabled_for_type (self, RFKILL_TYPE_WLAN, TRUE)); - break; - case PROP_WIRELESS_HARDWARE_ENABLED: - g_value_set_boolean (value, priv->radio_states[RFKILL_TYPE_WLAN].hw_enabled); - break; - case PROP_WWAN_ENABLED: - g_value_set_boolean (value, radio_enabled_for_type (self, RFKILL_TYPE_WWAN, TRUE)); - break; - case PROP_WWAN_HARDWARE_ENABLED: - g_value_set_boolean (value, priv->radio_states[RFKILL_TYPE_WWAN].hw_enabled); - break; - case PROP_WIMAX_ENABLED: - g_value_set_boolean (value, radio_enabled_for_type (self, RFKILL_TYPE_WIMAX, TRUE)); - break; - case PROP_WIMAX_HARDWARE_ENABLED: - g_value_set_boolean (value, priv->radio_states[RFKILL_TYPE_WIMAX].hw_enabled); - break; - case PROP_ACTIVE_CONNECTIONS: - active = g_ptr_array_sized_new (3); - for (iter = priv->active_connections; iter; iter = g_slist_next (iter)) { - path = nm_active_connection_get_path (NM_ACTIVE_CONNECTION (iter->data)); - g_ptr_array_add (active, g_strdup (path)); - } - g_value_take_boxed (value, active); - break; - case PROP_CONNECTIVITY: - g_value_set_uint (value, nm_connectivity_get_state (priv->connectivity)); - break; - case PROP_PRIMARY_CONNECTION: - path = priv->primary_connection ? nm_active_connection_get_path (priv->primary_connection) : "/"; - g_value_set_boxed (value, path); - break; - case PROP_ACTIVATING_CONNECTION: - path = priv->activating_connection ? nm_active_connection_get_path (priv->activating_connection) : "/"; - g_value_set_boxed (value, path); - break; - case PROP_HOSTNAME: - g_value_set_string (value, priv->hostname); - break; - case PROP_SLEEPING: - g_value_set_boolean (value, priv->sleeping); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; + manager_update_radio_enabled (self, rstate, new_enabled); } } @@ -4827,14 +4557,140 @@ dbus_connection_changed_cb (NMDBusManager *dbus_mgr, gpointer user_data) { NMManager *self = NM_MANAGER (user_data); + gboolean success = FALSE; if (dbus_connection) { /* Register property filter on new connection; there's no reason this * should fail except out-of-memory or program error; if it does fail * then there's no Manager property access control, which is bad. */ - g_assert (dbus_connection_add_filter (dbus_connection, prop_filter, self, NULL)); + success = dbus_connection_add_filter (dbus_connection, prop_filter, self, NULL); + g_assert (success); } + NM_MANAGER_GET_PRIVATE (self)->prop_filter_added = success; +} + +/**********************************************************************/ + +static NMManager *singleton = NULL; + +NMManager * +nm_manager_get (void) +{ + g_assert (singleton); + return singleton; +} + +NMConnectionProvider * +nm_connection_provider_get (void) +{ + g_assert (singleton); + g_assert (NM_MANAGER_GET_PRIVATE (singleton)->settings); + return NM_CONNECTION_PROVIDER (NM_MANAGER_GET_PRIVATE (singleton)->settings); +} + +NMManager * +nm_manager_new (NMSettings *settings, + const char *state_file, + gboolean initial_net_enabled, + gboolean initial_wifi_enabled, + gboolean initial_wwan_enabled, + gboolean initial_wimax_enabled, + GError **error) +{ + NMManagerPrivate *priv; + DBusGConnection *bus; + DBusConnection *dbus_connection; + + g_assert (settings); + + /* Can only be called once */ + g_assert (singleton == NULL); + singleton = (NMManager *) g_object_new (NM_TYPE_MANAGER, NULL); + g_assert (singleton); + + priv = NM_MANAGER_GET_PRIVATE (singleton); + + bus = nm_dbus_manager_get_connection (priv->dbus_mgr); + if (!bus) { + g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_INTERNAL, + "Failed to initialize D-Bus connection"); + g_object_unref (singleton); + return NULL; + } + + dbus_connection = dbus_g_connection_get_connection (bus); + g_assert (dbus_connection); + + priv->policy = nm_policy_new (singleton, settings); + g_signal_connect (priv->policy, "notify::" NM_POLICY_DEFAULT_IP4_DEVICE, + G_CALLBACK (policy_default_device_changed), singleton); + g_signal_connect (priv->policy, "notify::" NM_POLICY_DEFAULT_IP6_DEVICE, + G_CALLBACK (policy_default_device_changed), singleton); + g_signal_connect (priv->policy, "notify::" NM_POLICY_ACTIVATING_IP4_DEVICE, + G_CALLBACK (policy_activating_device_changed), singleton); + g_signal_connect (priv->policy, "notify::" NM_POLICY_ACTIVATING_IP6_DEVICE, + G_CALLBACK (policy_activating_device_changed), singleton); + + priv->connectivity = nm_connectivity_new (); + g_signal_connect (priv->connectivity, "notify::" NM_CONNECTIVITY_STATE, + G_CALLBACK (connectivity_changed), singleton); + + if (!dbus_connection_add_filter (dbus_connection, prop_filter, singleton, NULL)) { + g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_INTERNAL, + "Failed to register DBus connection filter"); + g_object_unref (singleton); + return NULL; + } + priv->prop_filter_added = TRUE; + + priv->settings = g_object_ref (settings); + + priv->state_file = g_strdup (state_file); + + priv->net_enabled = initial_net_enabled; + + priv->radio_states[RFKILL_TYPE_WLAN].user_enabled = initial_wifi_enabled; + priv->radio_states[RFKILL_TYPE_WWAN].user_enabled = initial_wwan_enabled; + priv->radio_states[RFKILL_TYPE_WIMAX].user_enabled = initial_wimax_enabled; + + g_signal_connect (priv->settings, "notify::" NM_SETTINGS_UNMANAGED_SPECS, + G_CALLBACK (system_unmanaged_devices_changed_cb), singleton); + g_signal_connect (priv->settings, "notify::" NM_SETTINGS_HOSTNAME, + G_CALLBACK (system_hostname_changed_cb), singleton); + g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_ADDED, + G_CALLBACK (connection_added), singleton); + g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_UPDATED, + G_CALLBACK (connection_changed), singleton); + g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_REMOVED, + G_CALLBACK (connection_removed), singleton); + g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_VISIBILITY_CHANGED, + G_CALLBACK (connection_changed), singleton); + + nm_dbus_manager_register_object (priv->dbus_mgr, NM_DBUS_PATH, singleton); + + g_signal_connect (nm_platform_get (), + NM_PLATFORM_SIGNAL_LINK_CHANGED, + G_CALLBACK (platform_link_cb), + singleton); + + priv->rfkill_mgr = nm_rfkill_manager_new (); + g_signal_connect (priv->rfkill_mgr, + "rfkill-changed", + G_CALLBACK (rfkill_manager_rfkill_changed_cb), + singleton); + + /* Force kernel WiFi/WWAN rfkill state to follow NM saved WiFi/WWAN state + * in case the BIOS doesn't save rfkill state, and to be consistent with user + * changes to the WirelessEnabled/WWANEnabled properties which toggle kernel + * rfkill. + */ + 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); + + return singleton; } static void @@ -4853,7 +4709,6 @@ nm_manager_init (NMManager *manager) priv->radio_states[RFKILL_TYPE_WLAN].prop = NM_MANAGER_WIRELESS_ENABLED; priv->radio_states[RFKILL_TYPE_WLAN].hw_prop = NM_MANAGER_WIRELESS_HARDWARE_ENABLED; priv->radio_states[RFKILL_TYPE_WLAN].desc = "WiFi"; - priv->radio_states[RFKILL_TYPE_WLAN].other_enabled_func = nm_manager_get_ipw_rfkill_state; priv->radio_states[RFKILL_TYPE_WLAN].rtype = RFKILL_TYPE_WLAN; priv->radio_states[RFKILL_TYPE_WWAN].user_enabled = TRUE; @@ -4861,7 +4716,6 @@ nm_manager_init (NMManager *manager) priv->radio_states[RFKILL_TYPE_WWAN].prop = NM_MANAGER_WWAN_ENABLED; priv->radio_states[RFKILL_TYPE_WWAN].hw_prop = NM_MANAGER_WWAN_HARDWARE_ENABLED; priv->radio_states[RFKILL_TYPE_WWAN].desc = "WWAN"; - priv->radio_states[RFKILL_TYPE_WWAN].daemon_enabled_func = nm_manager_get_modem_enabled_state; priv->radio_states[RFKILL_TYPE_WWAN].rtype = RFKILL_TYPE_WWAN; priv->radio_states[RFKILL_TYPE_WIMAX].user_enabled = TRUE; @@ -4869,7 +4723,6 @@ nm_manager_init (NMManager *manager) priv->radio_states[RFKILL_TYPE_WIMAX].prop = NM_MANAGER_WIMAX_ENABLED; priv->radio_states[RFKILL_TYPE_WIMAX].hw_prop = NM_MANAGER_WIMAX_HARDWARE_ENABLED; priv->radio_states[RFKILL_TYPE_WIMAX].desc = "WiMAX"; - priv->radio_states[RFKILL_TYPE_WIMAX].other_enabled_func = NULL; priv->radio_states[RFKILL_TYPE_WIMAX].rtype = RFKILL_TYPE_WIMAX; for (i = 0; i < RFKILL_TYPE_MAX; i++) @@ -4877,18 +4730,13 @@ nm_manager_init (NMManager *manager) priv->sleeping = FALSE; priv->state = NM_STATE_DISCONNECTED; + priv->startup = TRUE; priv->dbus_mgr = nm_dbus_manager_get (); - priv->dbus_connection_changed_id = g_signal_connect (priv->dbus_mgr, - NM_DBUS_MANAGER_DBUS_CONNECTION_CHANGED, - G_CALLBACK (dbus_connection_changed_cb), - manager); - - priv->modem_manager = nm_modem_manager_get (); - priv->modem_added_id = g_signal_connect (priv->modem_manager, "modem-added", - G_CALLBACK (modem_added), manager); - priv->modem_removed_id = g_signal_connect (priv->modem_manager, "modem-removed", - G_CALLBACK (modem_removed), manager); + g_signal_connect (priv->dbus_mgr, + NM_DBUS_MANAGER_DBUS_CONNECTION_CHANGED, + G_CALLBACK (dbus_connection_changed_cb), + manager); priv->vpn_manager = nm_vpn_manager_get (); @@ -4900,7 +4748,7 @@ nm_manager_init (NMManager *manager) "/", NM_AUTOIP_DBUS_IFACE); if (priv->aipd_proxy) { - dbus_g_object_register_marshaller (_nm_marshal_VOID__STRING_STRING_STRING, + dbus_g_object_register_marshaller (g_cclosure_marshal_generic, G_TYPE_NONE, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID); @@ -4935,9 +4783,9 @@ nm_manager_init (NMManager *manager) } if (priv->fw_monitor) { - priv->fw_monitor_id = g_signal_connect (priv->fw_monitor, "changed", - G_CALLBACK (firmware_dir_changed), - manager); + g_signal_connect (priv->fw_monitor, "changed", + G_CALLBACK (firmware_dir_changed), + manager); nm_log_info (LOGD_CORE, "monitoring kernel firmware directory '%s'.", KERNEL_FIRMWARE_DIR); } else { @@ -4945,13 +4793,214 @@ nm_manager_init (NMManager *manager) KERNEL_FIRMWARE_DIR); } - load_device_factories (manager); - /* Update timestamps in active connections */ priv->timestamp_update_id = g_timeout_add_seconds (300, (GSourceFunc) periodic_update_active_connection_timestamps, manager); } static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMManager *self = NM_MANAGER (object); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + GSList *iter; + GPtrArray *array; + const char *path; + + switch (prop_id) { + case PROP_VERSION: + g_value_set_string (value, VERSION); + break; + case PROP_STATE: + nm_manager_update_state (self); + g_value_set_uint (value, priv->state); + break; + case PROP_STARTUP: + g_value_set_boolean (value, priv->startup); + break; + case PROP_NETWORKING_ENABLED: + g_value_set_boolean (value, priv->net_enabled); + break; + case PROP_WIRELESS_ENABLED: + g_value_set_boolean (value, radio_enabled_for_type (self, RFKILL_TYPE_WLAN, TRUE)); + break; + case PROP_WIRELESS_HARDWARE_ENABLED: + g_value_set_boolean (value, priv->radio_states[RFKILL_TYPE_WLAN].hw_enabled); + break; + case PROP_WWAN_ENABLED: + g_value_set_boolean (value, radio_enabled_for_type (self, RFKILL_TYPE_WWAN, TRUE)); + break; + case PROP_WWAN_HARDWARE_ENABLED: + g_value_set_boolean (value, priv->radio_states[RFKILL_TYPE_WWAN].hw_enabled); + break; + case PROP_WIMAX_ENABLED: + g_value_set_boolean (value, radio_enabled_for_type (self, RFKILL_TYPE_WIMAX, TRUE)); + break; + case PROP_WIMAX_HARDWARE_ENABLED: + g_value_set_boolean (value, priv->radio_states[RFKILL_TYPE_WIMAX].hw_enabled); + break; + case PROP_ACTIVE_CONNECTIONS: + array = g_ptr_array_sized_new (3); + for (iter = priv->active_connections; iter; iter = g_slist_next (iter)) { + path = nm_active_connection_get_path (NM_ACTIVE_CONNECTION (iter->data)); + if (path) + g_ptr_array_add (array, g_strdup (path)); + } + g_value_take_boxed (value, array); + break; + case PROP_CONNECTIVITY: + g_value_set_uint (value, nm_connectivity_get_state (priv->connectivity)); + break; + case PROP_PRIMARY_CONNECTION: + path = priv->primary_connection ? nm_active_connection_get_path (priv->primary_connection) : NULL; + g_value_set_boxed (value, path ? path : "/"); + break; + case PROP_ACTIVATING_CONNECTION: + path = priv->activating_connection ? nm_active_connection_get_path (priv->activating_connection) : NULL; + g_value_set_boxed (value, path ? path : "/"); + break; + case PROP_HOSTNAME: + g_value_set_string (value, priv->hostname); + break; + case PROP_SLEEPING: + g_value_set_boolean (value, priv->sleeping); + break; + case PROP_DEVICES: + array = g_ptr_array_sized_new (5); + for (iter = priv->devices; iter; iter = g_slist_next (iter)) { + path = nm_device_get_path (NM_DEVICE (iter->data)); + if (path) + g_ptr_array_add (array, g_strdup (path)); + } + g_value_take_boxed (value, array); + 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) +{ + NMManager *self = NM_MANAGER (object); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + + switch (prop_id) { + case PROP_NETWORKING_ENABLED: + /* Construct only for now */ + priv->net_enabled = g_value_get_boolean (value); + break; + case PROP_WIRELESS_ENABLED: + manager_radio_user_toggled (NM_MANAGER (object), + &priv->radio_states[RFKILL_TYPE_WLAN], + g_value_get_boolean (value)); + break; + case PROP_WWAN_ENABLED: + manager_radio_user_toggled (NM_MANAGER (object), + &priv->radio_states[RFKILL_TYPE_WWAN], + g_value_get_boolean (value)); + break; + case PROP_WIMAX_ENABLED: + manager_radio_user_toggled (NM_MANAGER (object), + &priv->radio_states[RFKILL_TYPE_WIMAX], + g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +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; + + nm_auth_changed_func_unregister (authority_changed_cb, manager); + + /* Remove all devices */ + while (priv->devices) + remove_device (manager, NM_DEVICE (priv->devices->data), TRUE); + + if (priv->ac_cleanup_id) { + g_source_remove (priv->ac_cleanup_id); + priv->ac_cleanup_id = 0; + } + + while (priv->active_connections) + active_connection_remove (manager, NM_ACTIVE_CONNECTION (priv->active_connections->data)); + g_clear_pointer (&priv->active_connections, g_slist_free); + g_clear_object (&priv->primary_connection); + g_clear_object (&priv->activating_connection); + + g_clear_object (&priv->connectivity); + + g_free (priv->hostname); + + if (priv->policy) { + g_signal_handlers_disconnect_by_func (priv->policy, policy_default_device_changed, manager); + g_signal_handlers_disconnect_by_func (priv->policy, policy_activating_device_changed, manager); + g_clear_object (&priv->policy); + } + + g_clear_object (&priv->settings); + g_clear_object (&priv->vpn_manager); + + /* Unregister property filter */ + if (priv->dbus_mgr) { + bus = nm_dbus_manager_get_connection (priv->dbus_mgr); + if (bus) { + dbus_connection = dbus_g_connection_get_connection (bus); + if (dbus_connection && priv->prop_filter_added) { + dbus_connection_remove_filter (dbus_connection, prop_filter, manager); + priv->prop_filter_added = FALSE; + } + } + g_signal_handlers_disconnect_by_func (priv->dbus_mgr, dbus_connection_changed_cb, manager); + priv->dbus_mgr = NULL; + } + + g_clear_object (&priv->aipd_proxy); + g_clear_object (&priv->sleep_monitor); + + if (priv->fw_monitor) { + g_signal_handlers_disconnect_by_func (priv->fw_monitor, firmware_dir_changed, manager); + + if (priv->fw_changed_id) { + g_source_remove (priv->fw_changed_id); + priv->fw_changed_id = 0; + } + + g_file_monitor_cancel (priv->fw_monitor); + 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); + + if (priv->timestamp_update_id) { + g_source_remove (priv->timestamp_update_id); + priv->timestamp_update_id = 0; + } + + G_OBJECT_CLASS (nm_manager_parent_class)->dispose (object); +} + +static void nm_manager_class_init (NMManagerClass *manager_class) { GObjectClass *object_class = G_OBJECT_CLASS (manager_class); @@ -4981,6 +5030,14 @@ nm_manager_class_init (NMManagerClass *manager_class) G_PARAM_READABLE)); g_object_class_install_property + (object_class, PROP_STARTUP, + g_param_spec_boolean (NM_MANAGER_STARTUP, + "Startup", + "Is NetworkManager still starting up", + TRUE, + G_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_NETWORKING_ENABLED, g_param_spec_boolean (NM_MANAGER_NETWORKING_ENABLED, "NetworkingEnabled", @@ -5075,7 +5132,7 @@ nm_manager_class_init (NMManagerClass *manager_class) "Hostname", "Hostname", NULL, - G_PARAM_READABLE | NM_PROPERTY_PARAM_NO_EXPORT)); + G_PARAM_READABLE)); /* Sleeping is not exported over D-Bus */ g_object_class_install_property @@ -5084,7 +5141,15 @@ nm_manager_class_init (NMManagerClass *manager_class) "Sleeping", "Sleeping", FALSE, - G_PARAM_READABLE | NM_PROPERTY_PARAM_NO_EXPORT)); + G_PARAM_READABLE)); + + g_object_class_install_property + (object_class, PROP_DEVICES, + g_param_spec_boxed (NM_MANAGER_DEVICES, + "Devices", + "Devices", + DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH, + G_PARAM_READABLE)); /* signals */ signals[DEVICE_ADDED] = @@ -5092,8 +5157,7 @@ nm_manager_class_init (NMManagerClass *manager_class) G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (NMManagerClass, device_added), - NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, + NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_OBJECT); signals[DEVICE_REMOVED] = @@ -5101,8 +5165,7 @@ nm_manager_class_init (NMManagerClass *manager_class) G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (NMManagerClass, device_removed), - NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, + NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_OBJECT); signals[STATE_CHANGED] = @@ -5110,48 +5173,40 @@ nm_manager_class_init (NMManagerClass *manager_class) G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (NMManagerClass, state_changed), - NULL, NULL, - g_cclosure_marshal_VOID__UINT, + NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_UINT); - signals[PROPERTIES_CHANGED] = - nm_properties_changed_signal_new (object_class, - G_STRUCT_OFFSET (NMManagerClass, properties_changed)); - signals[CHECK_PERMISSIONS] = g_signal_new ("check-permissions", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, - 0, NULL, NULL, - g_cclosure_marshal_VOID__VOID, + 0, NULL, NULL, NULL, G_TYPE_NONE, 0); signals[USER_PERMISSIONS_CHANGED] = g_signal_new ("user-permissions-changed", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, - 0, NULL, NULL, - g_cclosure_marshal_VOID__VOID, + 0, NULL, NULL, NULL, G_TYPE_NONE, 0); signals[ACTIVE_CONNECTION_ADDED] = g_signal_new (NM_MANAGER_ACTIVE_CONNECTION_ADDED, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, - 0, NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, + 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_OBJECT); signals[ACTIVE_CONNECTION_REMOVED] = g_signal_new (NM_MANAGER_ACTIVE_CONNECTION_REMOVED, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, - 0, NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, + 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_OBJECT); - dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (manager_class), - &dbus_glib_nm_manager_object_info); + nm_dbus_manager_register_exported_type (nm_dbus_manager_get (), + G_TYPE_FROM_CLASS (manager_class), + &dbus_glib_nm_manager_object_info); dbus_g_error_domain_register (NM_MANAGER_ERROR, NULL, NM_TYPE_MANAGER_ERROR); dbus_g_error_domain_register (NM_LOGGING_ERROR, "org.freedesktop.NetworkManager.Logging", NM_TYPE_LOGGING_ERROR); |