diff options
Diffstat (limited to 'src/nm-manager.c')
-rw-r--r-- | src/nm-manager.c | 2357 |
1 files changed, 1837 insertions, 520 deletions
diff --git a/src/nm-manager.c b/src/nm-manager.c index aad650881..e34e90143 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -25,7 +25,6 @@ #include <string.h> #include <dbus/dbus-glib-lowlevel.h> #include <dbus/dbus-glib.h> -#include <glib/gi18n.h> #include "nm-glib-compat.h" #include "nm-manager.h" @@ -39,10 +38,8 @@ #include "nm-device-ethernet.h" #include "nm-device-wifi.h" #include "nm-device-olpc-mesh.h" -#if WITH_WIMAX -#include "nm-device-wimax.h" -#endif -#include "nm-device-modem.h" +#include "nm-device-cdma.h" +#include "nm-device-gsm.h" #include "nm-system.h" #include "nm-properties-changed-signal.h" #include "nm-setting-bluetooth.h" @@ -55,10 +52,11 @@ #include "nm-hostname-provider.h" #include "nm-bluez-manager.h" #include "nm-bluez-common.h" -#include "nm-settings.h" -#include "nm-settings-connection.h" +#include "nm-sysconfig-settings.h" +#include "nm-secrets-provider-interface.h" +#include "nm-settings-interface.h" +#include "nm-settings-system-interface.h" #include "nm-manager-auth.h" -#include "NetworkManagerUtils.h" #define NM_AUTOIP_DBUS_SERVICE "org.freedesktop.nm_avahi_autoipd" #define NM_AUTOIP_DBUS_IFACE "org.freedesktop.nm_avahi_autoipd" @@ -67,17 +65,12 @@ static gboolean impl_manager_get_devices (NMManager *manager, GPtrArray **devices, GError **err); static void impl_manager_activate_connection (NMManager *manager, + const char *service_name, const char *connection_path, const char *device_path, const char *specific_object_path, DBusGMethodInvocation *context); -static void impl_manager_add_and_activate_connection (NMManager *manager, - GHashTable *settings, - const char *device_path, - const char *specific_object_path, - DBusGMethodInvocation *context); - static void impl_manager_deactivate_connection (NMManager *manager, const char *connection_path, DBusGMethodInvocation *context); @@ -93,17 +86,23 @@ static void impl_manager_enable (NMManager *manager, static void impl_manager_get_permissions (NMManager *manager, DBusGMethodInvocation *context); -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); +/* Legacy 0.6 compatibility interface */ + +static void impl_manager_legacy_sleep (NMManager *manager, DBusGMethodInvocation *context); +static void impl_manager_legacy_wake (NMManager *manager, DBusGMethodInvocation *context); +static gboolean impl_manager_legacy_state (NMManager *manager, guint32 *state, GError **err); + #include "nm-manager-glue.h" +static void connection_added_default_handler (NMManager *manager, + NMConnection *connection, + NMConnectionScope scope); + static void udev_device_added_cb (NMUdevManager *udev_mgr, GUdevDevice *device, NMDeviceCreatorFn creator_fn, @@ -136,7 +135,6 @@ static const char *internal_activate_device (NMManager *manager, NMConnection *connection, const char *specific_object, gboolean user_requested, - gulong sender_uid, gboolean assumed, GError **error); @@ -178,14 +176,19 @@ struct PendingActivation { PendingActivationFunc callback; NMAuthChain *chain; + gboolean have_connection; + gboolean authorized; + + NMConnectionScope scope; char *connection_path; - NMConnection *connection; char *specific_object_path; char *device_path; + guint timeout_id; }; typedef struct { gboolean user_enabled; + gboolean daemon_enabled; gboolean sw_enabled; gboolean hw_enabled; RfKillType rtype; @@ -194,6 +197,7 @@ typedef struct { const char *prop; const char *hw_prop; RfKillState (*other_enabled_func) (NMManager *); + RfKillState (*daemon_enabled_func) (NMManager *); } RadioState; typedef struct { @@ -207,9 +211,19 @@ typedef struct { NMUdevManager *udev_mgr; NMBluezManager *bluez_mgr; - NMSettings *settings; + GHashTable *user_connections; + DBusGProxy *user_proxy; + NMAuthCallResult user_con_perm; + NMAuthCallResult user_net_perm; + + GHashTable *system_connections; + NMSysconfigSettings *sys_settings; char *hostname; + GSList *secrets_calls; + + GSList *pending_activations; + RadioState radio_states[RFKILL_TYPE_MAX]; gboolean sleeping; gboolean net_enabled; @@ -248,7 +262,12 @@ enum { DEVICE_ADDED, DEVICE_REMOVED, STATE_CHANGED, + STATE_CHANGE, /* DEPRECATED */ PROPERTIES_CHANGED, + CONNECTIONS_ADDED, + CONNECTION_ADDED, + CONNECTION_UPDATED, + CONNECTION_REMOVED, CHECK_PERMISSIONS, USER_PERMISSIONS_CHANGED, @@ -266,8 +285,6 @@ enum { PROP_WIRELESS_HARDWARE_ENABLED, PROP_WWAN_ENABLED, PROP_WWAN_HARDWARE_ENABLED, - PROP_WIMAX_ENABLED, - PROP_WIMAX_HARDWARE_ENABLED, PROP_ACTIVE_CONNECTIONS, /* Not exported */ @@ -277,19 +294,17 @@ enum { LAST_PROP }; - -/************************************************************************/ - -typedef enum { +typedef enum +{ NM_MANAGER_ERROR_UNKNOWN_CONNECTION = 0, NM_MANAGER_ERROR_UNKNOWN_DEVICE, NM_MANAGER_ERROR_UNMANAGED_DEVICE, + NM_MANAGER_ERROR_INVALID_SERVICE, NM_MANAGER_ERROR_SYSTEM_CONNECTION, NM_MANAGER_ERROR_PERMISSION_DENIED, NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE, NM_MANAGER_ERROR_ALREADY_ASLEEP_OR_AWAKE, NM_MANAGER_ERROR_ALREADY_ENABLED_OR_DISABLED, - NM_MANAGER_ERROR_UNSUPPORTED_CONNECTION_TYPE, } NMManagerError; #define NM_MANAGER_ERROR (nm_manager_error_quark ()) @@ -320,6 +335,10 @@ nm_manager_error_get_type (void) ENUM_ENTRY (NM_MANAGER_ERROR_UNKNOWN_DEVICE, "UnknownDevice"), /* Unmanaged device. */ ENUM_ENTRY (NM_MANAGER_ERROR_UNMANAGED_DEVICE, "UnmanagedDevice"), + /* Invalid settings service (not a recognized system or user + * settings service name) + */ + ENUM_ENTRY (NM_MANAGER_ERROR_INVALID_SERVICE, "InvalidService"), /* Connection was superceded by a system connection. */ ENUM_ENTRY (NM_MANAGER_ERROR_SYSTEM_CONNECTION, "SystemConnection"), /* User does not have the permission to activate this connection. */ @@ -330,8 +349,6 @@ nm_manager_error_get_type (void) ENUM_ENTRY (NM_MANAGER_ERROR_ALREADY_ASLEEP_OR_AWAKE, "AlreadyAsleepOrAwake"), /* The manager is already in the requested enabled/disabled state */ ENUM_ENTRY (NM_MANAGER_ERROR_ALREADY_ENABLED_OR_DISABLED, "AlreadyEnabledOrDisabled"), - /* The requested operation is unsupported for this type of connection */ - ENUM_ENTRY (NM_MANAGER_ERROR_UNSUPPORTED_CONNECTION_TYPE, "UnsupportedConnectionType"), { 0, 0, 0 }, }; etype = g_enum_register_static ("NMManagerError", values); @@ -339,36 +356,6 @@ nm_manager_error_get_type (void) return etype; } -/************************************************************************/ - -static NMDevice * -nm_manager_get_device_by_udi (NMManager *manager, const char *udi) -{ - GSList *iter; - - g_return_val_if_fail (udi != NULL, NULL); - - for (iter = NM_MANAGER_GET_PRIVATE (manager)->devices; iter; iter = iter->next) { - if (!strcmp (nm_device_get_udi (NM_DEVICE (iter->data)), udi)) - return NM_DEVICE (iter->data); - } - return NULL; -} - -static NMDevice * -nm_manager_get_device_by_path (NMManager *manager, const char *path) -{ - GSList *iter; - - g_return_val_if_fail (path != NULL, NULL); - - for (iter = NM_MANAGER_GET_PRIVATE (manager)->devices; iter; iter = iter->next) { - if (!strcmp (nm_device_get_path (NM_DEVICE (iter->data)), path)) - return NM_DEVICE (iter->data); - } - return NULL; -} - static gboolean manager_sleeping (NMManager *self) { @@ -428,8 +415,14 @@ modem_added (NMModemManager *modem_manager, return; } - /* Make the new modem device */ - device = nm_device_modem_new (modem, driver); + /* Otherwise make a new top-level NMDevice for it */ + if (NM_IS_MODEM_GSM (modem)) + device = nm_device_gsm_new (NM_MODEM_GSM (modem), driver); + else if (NM_IS_MODEM_CDMA (modem)) + device = nm_device_cdma_new (NM_MODEM_CDMA (modem), driver); + else + nm_log_info (LOGD_MB, "unhandled modem '%s'", ip_iface); + if (device) add_device (self, device); } @@ -439,7 +432,6 @@ nm_manager_update_state (NMManager *manager) { NMManagerPrivate *priv; NMState new_state = NM_STATE_DISCONNECTED; - GSList *iter; g_return_if_fail (NM_IS_MANAGER (manager)); @@ -448,21 +440,16 @@ nm_manager_update_state (NMManager *manager) if (manager_sleeping (manager)) new_state = NM_STATE_ASLEEP; else { + GSList *iter; + 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) { - /* FIXME: handle local-only and site too */ - new_state = NM_STATE_CONNECTED_GLOBAL; + if (nm_device_get_state (dev) == NM_DEVICE_STATE_ACTIVATED) { + new_state = NM_STATE_CONNECTED; break; - } - - if (nm_device_is_activating (dev)) + } else 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; } } } @@ -472,7 +459,72 @@ nm_manager_update_state (NMManager *manager) g_object_notify (G_OBJECT (manager), NM_MANAGER_STATE); g_signal_emit (manager, signals[STATE_CHANGED], 0, priv->state); + + /* Emit StateChange too for backwards compatibility */ + g_signal_emit (manager, signals[STATE_CHANGE], 0, priv->state); + } +} + +static void +update_active_connection_timestamp (NMManager *manager, NMDevice *device) +{ + NMActRequest *req; + NMConnection *connection; + NMSettingConnection *s_con; + NMManagerPrivate *priv; + const char *connection_uuid; + guint64 timestamp; + guint64 *ts_ptr; + GKeyFile *timestamps_file; + char *data, *tmp; + gsize len; + GError *error = NULL; + + g_return_if_fail (NM_IS_DEVICE (device)); + + priv = NM_MANAGER_GET_PRIVATE (manager); + req = nm_device_get_act_request (device); + if (!req) + return; + + connection = nm_act_request_get_connection (req); + g_assert (connection); + + if (nm_connection_get_scope (connection) != NM_CONNECTION_SCOPE_SYSTEM) + return; + + /* Update timestamp in connection's object data */ + timestamp = (guint64) time (NULL); + ts_ptr = g_new (guint64, 1); + *ts_ptr = timestamp; + g_object_set_data_full (G_OBJECT (connection), NM_SYSCONFIG_SETTINGS_TIMESTAMP_TAG, ts_ptr, g_free); + + s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (NM_CONNECTION (connection), NM_TYPE_SETTING_CONNECTION)); + g_assert (s_con); + connection_uuid = nm_setting_connection_get_uuid (s_con); + + /* Save timestamp to timestamps database file */ + timestamps_file = g_key_file_new (); + if (!g_key_file_load_from_file (timestamps_file, NM_SYSCONFIG_SETTINGS_TIMESTAMPS_FILE, G_KEY_FILE_KEEP_COMMENTS, &error)) { + if (!(error->domain == G_FILE_ERROR && error->code == G_FILE_ERROR_NOENT)) + nm_log_warn (LOGD_SYS_SET, "error parsing timestamps file '%s': %s", NM_SYSCONFIG_SETTINGS_TIMESTAMPS_FILE, error->message); + g_clear_error (&error); + } + + tmp = g_strdup_printf ("%" G_GUINT64_FORMAT, timestamp); + g_key_file_set_value (timestamps_file, "timestamps", connection_uuid, tmp); + g_free (tmp); + + data = g_key_file_to_data (timestamps_file, &len, &error); + if (data) { + g_file_set_contents (NM_SYSCONFIG_SETTINGS_TIMESTAMPS_FILE, data, len, &error); + g_free (data); + } + if (error) { + nm_log_warn (LOGD_SYS_SET, "error saving timestamp: %s", error->message); + g_error_free (error); } + g_key_file_free (timestamps_file); } static void @@ -498,14 +550,8 @@ manager_device_state_changed (NMDevice *device, nm_manager_update_state (manager); - if (new_state == NM_DEVICE_STATE_ACTIVATED) { - NMActRequest *req; - - req = nm_device_get_act_request (device); - if (req) - nm_settings_connection_update_timestamp (NM_SETTINGS_CONNECTION (nm_act_request_get_connection (req)), - (guint64) time (NULL)); - } + if (new_state == NM_DEVICE_STATE_ACTIVATED) + update_active_connection_timestamp (manager, device); } /* Removes a device from a device list; returns the start of the new device list */ @@ -534,7 +580,7 @@ remove_one_device (NMManager *manager, g_signal_handlers_disconnect_by_func (device, manager_device_state_changed, manager); - nm_settings_device_removed (priv->settings, device); + nm_sysconfig_settings_device_removed (priv->sys_settings, device); g_signal_emit (manager, signals[DEVICE_REMOVED], 0, device); g_object_unref (device); @@ -624,44 +670,24 @@ nm_manager_get_state (NMManager *manager) return NM_MANAGER_GET_PRIVATE (manager)->state; } -static gboolean -might_be_vpn (NMConnection *connection) +static void +emit_removed (gpointer key, gpointer value, gpointer user_data) { - NMSettingConnection *s_con; - const char *ctype = NULL; - - if (nm_connection_get_setting (connection, NM_TYPE_SETTING_VPN)) - return TRUE; - - /* Make sure it's not a VPN, which we can't autocomplete yet */ - s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION); - if (s_con) - ctype = nm_setting_connection_get_connection_type (s_con); + NMManager *manager = NM_MANAGER (user_data); + NMConnection *connection = NM_CONNECTION (value); - return (g_strcmp0 (ctype, NM_SETTING_VPN_SETTING_NAME) == 0); + g_signal_emit (manager, signals[CONNECTION_REMOVED], 0, + connection, + nm_connection_get_scope (connection)); } -static gboolean -try_complete_vpn (NMConnection *connection, GSList *existing, GError **error) +static void +nm_manager_pending_activation_remove (NMManager *self, + PendingActivation *pending) { - g_assert (might_be_vpn (connection) == TRUE); - - if (!nm_connection_get_setting (connection, NM_TYPE_SETTING_VPN)) { - 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 */ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - return TRUE; + priv->pending_activations = g_slist_remove (priv->pending_activations, pending); } static PendingActivation * @@ -669,65 +695,18 @@ pending_activation_new (NMManager *manager, PolkitAuthority *authority, DBusGMethodInvocation *context, const char *device_path, + NMConnectionScope scope, const char *connection_path, - GHashTable *settings, const char *specific_object_path, - PendingActivationFunc callback, - GError **error) + PendingActivationFunc callback) { - 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 (authority != 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; - } - } + g_return_val_if_fail (connection_path != NULL, NULL); pending = g_slice_new0 (PendingActivation); pending->manager = manager; @@ -736,8 +715,8 @@ pending_activation_new (NMManager *manager, pending->callback = callback; pending->device_path = g_strdup (device_path); + pending->scope = scope; 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, "/")) @@ -747,6 +726,39 @@ pending_activation_new (NMManager *manager, } static void +pending_auth_user_done (NMAuthChain *chain, + GError *error, + DBusGMethodInvocation *context, + gpointer user_data) +{ + PendingActivation *pending = user_data; + NMAuthCallResult result; + + pending->chain = NULL; + + if (error) { + pending->callback (pending, error); + goto out; + } + + /* 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_USE_USER_CONNECTIONS)); + if (result != NM_AUTH_CALL_RESULT_YES) { + error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_PERMISSION_DENIED, + "Not authorized to use user connections."); + pending->callback (pending, error); + g_error_free (error); + } else + pending->callback (pending, NULL); + +out: + nm_auth_chain_unref (chain); +} + +static void pending_auth_net_done (NMAuthChain *chain, GError *error, DBusGMethodInvocation *context, @@ -775,33 +787,86 @@ pending_auth_net_done (NMAuthChain *chain, goto out; } - pending->callback (pending, NULL); + if (pending->scope == NM_CONNECTION_SCOPE_SYSTEM) { + /* System connection and the user is authorized for that if they have + * the network-control permission. + */ + pending->callback (pending, NULL); + } else { + g_assert (pending->scope == NM_CONNECTION_SCOPE_USER); + + /* User connection, check the 'use-user-connections' permission */ + pending->chain = nm_auth_chain_new (pending->authority, + pending->context, + NULL, + pending_auth_user_done, + pending); + nm_auth_chain_add_call (pending->chain, + NM_AUTH_PERMISSION_USE_USER_CONNECTIONS, + TRUE); + } out: nm_auth_chain_unref (chain); } +static gboolean +check_user_authorized (NMDBusManager *dbus_mgr, + DBusGProxy *user_proxy, + DBusGMethodInvocation *context, + NMConnectionScope scope, + gulong *out_sender_uid, + const char **out_error_desc) +{ + g_return_val_if_fail (dbus_mgr != NULL, FALSE); + g_return_val_if_fail (context != NULL, FALSE); + g_return_val_if_fail (out_sender_uid != NULL, FALSE); + g_return_val_if_fail (out_error_desc != NULL, FALSE); + + *out_sender_uid = G_MAXULONG; + + /* Get the UID */ + if (!nm_auth_get_caller_uid (context, dbus_mgr, out_sender_uid, out_error_desc)) + return FALSE; + + /* root gets to do anything */ + if (0 == *out_sender_uid) + return TRUE; + + /* Check whether the UID is authorized for user connections */ + if ( scope == NM_CONNECTION_SCOPE_USER + && !nm_auth_uid_authorized (*out_sender_uid, + dbus_mgr, + user_proxy, + out_error_desc)) + return FALSE; + + return TRUE; +} + static void pending_activation_check_authorized (PendingActivation *pending, - NMDBusManager *dbus_mgr) + NMDBusManager *dbus_mgr, + DBusGProxy *user_proxy) { - char *error_desc = NULL; + const char *error_desc = NULL; gulong sender_uid = G_MAXULONG; GError *error; g_return_if_fail (pending != NULL); g_return_if_fail (dbus_mgr != NULL); - if (!nm_auth_get_caller_uid (pending->context, - dbus_mgr, - &sender_uid, - &error_desc)) { + if (!check_user_authorized (dbus_mgr, + user_proxy, + pending->context, + pending->scope, + &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; } @@ -832,22 +897,16 @@ pending_activation_destroy (PendingActivation *pending, { g_return_if_fail (pending != NULL); - if (error) - dbus_g_method_return_error (pending->context, error); - else if (ac_path) { - if (pending->connection) { - dbus_g_method_return (pending->context, - pending->connection_path, - ac_path); - } else - dbus_g_method_return (pending->context, ac_path); - } - + if (pending->timeout_id) + g_source_remove (pending->timeout_id); 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 (error) + dbus_g_method_return_error (pending->context, error); + else if (ac_path) + dbus_g_method_return (pending->context, ac_path); if (pending->chain) nm_auth_chain_unref (pending->chain); @@ -860,7 +919,6 @@ static GPtrArray * get_active_connections (NMManager *manager, NMConnection *filter) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); - NMVPNManager *vpn_manager; GPtrArray *active; GSList *iter; @@ -882,35 +940,582 @@ get_active_connections (NMManager *manager, NMConnection *filter) } /* Add active VPN connections */ - vpn_manager = nm_vpn_manager_get (); - nm_vpn_manager_add_active_connections (vpn_manager, filter, active); - g_object_unref (vpn_manager); + nm_vpn_manager_add_active_connections (priv->vpn_manager, filter, active); return active; } +static void +remove_connection (NMManager *manager, + NMConnection *connection, + GHashTable *hash) +{ + /* Destroys the connection, then associated DBusGProxy due to the + * weak reference notify function placed on the connection when it + * was created. + */ + g_object_ref (connection); + g_hash_table_remove (hash, nm_connection_get_path (connection)); + g_signal_emit (manager, signals[CONNECTION_REMOVED], 0, + connection, + nm_connection_get_scope (connection)); + g_object_unref (connection); + + bluez_manager_resync_devices (manager); +} + /*******************************************************************/ -/* Settings stuff via NMSettings */ +/* User settings stuff via D-Bus */ /*******************************************************************/ static void -connections_changed (NMSettings *settings, - NMSettingsConnection *connection, - NMManager *manager) +user_proxy_cleanup (NMManager *self, gboolean resync_bt) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + + if (priv->user_connections) { + g_hash_table_foreach (priv->user_connections, emit_removed, self); + g_hash_table_remove_all (priv->user_connections); + } + + priv->user_net_perm = NM_AUTH_CALL_RESULT_UNKNOWN; + priv->user_con_perm = NM_AUTH_CALL_RESULT_UNKNOWN; + + if (priv->user_proxy) { + g_object_unref (priv->user_proxy); + priv->user_proxy = NULL; + } + + if (resync_bt) { + /* Resync BT devices since they are generated from connections */ + bluez_manager_resync_devices (self); + } +} + +typedef struct GetSettingsInfo { + NMManager *manager; + NMConnection *connection; + DBusGProxy *proxy; + guint32 *calls; +} GetSettingsInfo; + +static void +free_get_settings_info (gpointer data) +{ + GetSettingsInfo *info = (GetSettingsInfo *) data; + + /* If this was the last pending call for a batch of GetSettings calls, + * send out the connections-added signal. + */ + if (info->calls) { + (*info->calls)--; + if (*info->calls == 0) { + g_slice_free (guint32, (gpointer) info->calls); + g_signal_emit (info->manager, signals[CONNECTIONS_ADDED], 0, NM_CONNECTION_SCOPE_USER); + + /* Update the Bluetooth connections for all the new connections */ + bluez_manager_resync_devices (info->manager); + } + } + + if (info->manager) { + g_object_unref (info->manager); + info->manager = NULL; + } + if (info->connection) { + g_object_unref (info->connection); + info->connection = NULL; + } + if (info->proxy) { + g_object_unref (info->proxy); + info->proxy = NULL; + } + + g_slice_free (GetSettingsInfo, data); +} + +static void +user_connection_get_settings_cb (DBusGProxy *proxy, + DBusGProxyCall *call_id, + gpointer user_data) { + GetSettingsInfo *info = (GetSettingsInfo *) user_data; + GError *err = NULL; + GHashTable *settings = NULL; + NMConnection *connection; + NMManager *manager; + + g_return_if_fail (info != NULL); + + if (!dbus_g_proxy_end_call (proxy, call_id, &err, + DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, &settings, + G_TYPE_INVALID)) { + nm_log_info (LOGD_USER_SET, "couldn't retrieve connection settings: %s.", + err && err->message ? err->message : "(unknown)"); + g_error_free (err); + goto out; + } + + manager = info->manager; + connection = info->connection; + if (connection == NULL) { + const char *path = dbus_g_proxy_get_path (proxy); + NMManagerPrivate *priv; + GError *error = NULL; + NMConnection *existing = NULL; + + connection = nm_connection_new_from_hash (settings, &error); + if (connection == NULL) { + nm_log_warn (LOGD_USER_SET, "invalid connection: '%s' / '%s' invalid: %d", + g_type_name (nm_connection_lookup_setting_type_by_quark (error->domain)), + error->message, error->code); + g_error_free (error); + goto out; + } + + nm_connection_set_path (connection, path); + nm_connection_set_scope (connection, NM_CONNECTION_SCOPE_USER); + + /* Add the new connection to the internal hashes only if the same + * connection isn't already there. + */ + priv = NM_MANAGER_GET_PRIVATE (manager); + + existing = g_hash_table_lookup (priv->user_connections, path); + if (!existing || !nm_connection_compare (existing, connection, NM_SETTING_COMPARE_FLAG_EXACT)) { + g_hash_table_insert (priv->user_connections, + g_strdup (path), + connection); + existing = NULL; + + /* Attach the D-Bus proxy representing the remote NMConnection + * to the local NMConnection object to ensure it stays alive to + * continue delivering signals. It'll be destroyed once the + * NMConnection is destroyed. + */ + g_object_set_data_full (G_OBJECT (connection), + "proxy", + g_object_ref (info->proxy), + g_object_unref); + } else + g_object_unref (connection); + + /* If the connection-added signal is supposed to be batched, don't + * emit the single connection-added here. Also, don't emit the signal + * if the connection wasn't actually added to the system or user hashes. + */ + if (!info->calls && !existing) { + g_signal_emit (manager, signals[CONNECTION_ADDED], 0, connection, NM_CONNECTION_SCOPE_USER); + /* Update the Bluetooth connections for that single new connection */ + bluez_manager_resync_devices (manager); + } + } else { + // FIXME: merge settings? or just replace? + nm_log_dbg (LOGD_USER_SET, "implement merge settings"); + } + +out: + if (settings) + g_hash_table_destroy (settings); + + return; +} + +static void +user_connection_removed_cb (DBusGProxy *proxy, gpointer user_data) +{ + NMManager *manager = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + NMConnection *connection = NULL; + const char *path; + + path = dbus_g_proxy_get_path (proxy); + if (path) { + connection = g_hash_table_lookup (priv->user_connections, path); + if (connection) + remove_connection (manager, connection, priv->user_connections); + } +} + +static void +user_connection_updated_cb (DBusGProxy *proxy, + GHashTable *settings, + gpointer user_data) +{ + NMManager *manager = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + NMConnection *new_connection; + NMConnection *old_connection = NULL; + gboolean valid = FALSE; + GError *error = NULL; + const char *path; + + path = dbus_g_proxy_get_path (proxy); + if (path) + old_connection = g_hash_table_lookup (priv->user_connections, path); + + g_return_if_fail (old_connection != NULL); + + new_connection = nm_connection_new_from_hash (settings, &error); + if (!new_connection) { + /* New connection invalid, remove existing connection */ + nm_log_warn (LOGD_USER_SET, "invalid connection: '%s' / '%s' invalid: %d", + g_type_name (nm_connection_lookup_setting_type_by_quark (error->domain)), + error->message, error->code); + g_error_free (error); + remove_connection (manager, old_connection, priv->user_connections); + return; + } + g_object_unref (new_connection); + + valid = nm_connection_replace_settings (old_connection, settings, NULL); + if (valid) { + g_signal_emit (manager, signals[CONNECTION_UPDATED], 0, + old_connection, + nm_connection_get_scope (old_connection)); + + bluez_manager_resync_devices (manager); + } else { + remove_connection (manager, old_connection, priv->user_connections); + } +} + +static void +user_internal_new_connection_cb (NMManager *manager, + const char *path, + guint32 *counter) +{ + GetSettingsInfo *info; + DBusGProxy *con_proxy; + DBusGConnection *g_connection; + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + + g_connection = nm_dbus_manager_get_connection (priv->dbus_mgr); + con_proxy = dbus_g_proxy_new_for_name (g_connection, + NM_DBUS_SERVICE_USER_SETTINGS, + path, + NM_DBUS_IFACE_SETTINGS_CONNECTION); + if (!con_proxy) { + nm_log_err (LOGD_USER_SET, "could not init user connection proxy"); + return; + } + + dbus_g_proxy_add_signal (con_proxy, "Updated", + DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, + G_TYPE_INVALID); + dbus_g_proxy_connect_signal (con_proxy, "Updated", + G_CALLBACK (user_connection_updated_cb), + manager, + NULL); + + dbus_g_proxy_add_signal (con_proxy, "Removed", G_TYPE_INVALID, G_TYPE_INVALID); + dbus_g_proxy_connect_signal (con_proxy, "Removed", + G_CALLBACK (user_connection_removed_cb), + manager, + NULL); + + info = g_slice_new0 (GetSettingsInfo); + info->manager = g_object_ref (manager); + info->proxy = con_proxy; + if (counter) { + info->calls = counter; + (*info->calls)++; + } + dbus_g_proxy_begin_call (con_proxy, "GetSettings", + user_connection_get_settings_cb, + info, + free_get_settings_info, + G_TYPE_INVALID); +} + +static void +user_list_connections_cb (DBusGProxy *proxy, + DBusGProxyCall *call_id, + gpointer user_data) +{ + NMManager *manager = NM_MANAGER (user_data); + GError *err = NULL; + GPtrArray *ops; + guint32 *counter = NULL; + int i; + + if (!dbus_g_proxy_end_call (proxy, call_id, &err, + DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH, &ops, + G_TYPE_INVALID)) { + nm_log_warn (LOGD_USER_SET, "couldn't retrieve connections: %s", + err && err->message ? err->message : "(unknown)"); + g_clear_error (&err); + return; + } + + /* Keep track of all calls made here; don't want to emit connection-added for + * each one, but emit connections-added when they are all done. + */ + counter = g_slice_new0 (guint32); + for (i = 0; i < ops->len; i++) { + char *op = g_ptr_array_index (ops, i); + + user_internal_new_connection_cb (manager, op, counter); + g_free (op); + } + g_ptr_array_free (ops, TRUE); +} + +static void +user_proxy_destroyed_cb (DBusGProxy *proxy, NMManager *self) +{ + nm_log_dbg (LOGD_USER_SET, "Removing user connections..."); + + /* At this point the user proxy is already being disposed */ + NM_MANAGER_GET_PRIVATE (self)->user_proxy = NULL; + + /* User Settings service disappeared; throw away user connections */ + user_proxy_cleanup (self, TRUE); +} + +static void +user_new_connection_cb (DBusGProxy *proxy, const char *path, gpointer user_data) +{ + user_internal_new_connection_cb (NM_MANAGER (user_data), path, NULL); +} + +static gboolean +user_settings_authorized (NMManager *self, NMAuthChain *chain) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMAuthCallResult old_net_perm = priv->user_net_perm; + NMAuthCallResult old_con_perm = priv->user_con_perm; + + /* If the user could potentially get authorization to use networking and/or + * to use user connections, the user settings service is authorized. + */ + priv->user_net_perm = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL)); + priv->user_con_perm = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, NM_AUTH_PERMISSION_USE_USER_CONNECTIONS)); + + nm_log_dbg (LOGD_USER_SET, "User connections permissions: net %d, con %d", + priv->user_net_perm, priv->user_con_perm); + + if (old_net_perm != priv->user_net_perm || old_con_perm != priv->user_con_perm) + g_signal_emit (self, signals[USER_PERMISSIONS_CHANGED], 0); + + /* If the user can't control the network they certainly aren't allowed + * to provide user connections. + */ + if ( priv->user_net_perm == NM_AUTH_CALL_RESULT_UNKNOWN + || priv->user_net_perm == NM_AUTH_CALL_RESULT_NO) + return FALSE; + + /* And of course if they aren't allowed to use user connections, they can't + * provide them either. + */ + if ( priv->user_con_perm == NM_AUTH_CALL_RESULT_UNKNOWN + || priv->user_con_perm == NM_AUTH_CALL_RESULT_NO) + return FALSE; + + return TRUE; +} + +static void +user_proxy_auth_done (NMAuthChain *chain, + GError *error, + DBusGMethodInvocation *context, + gpointer user_data) +{ + NMManager *self = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + gboolean authorized = FALSE; + + priv->auth_chains = g_slist_remove (priv->auth_chains, chain); + + if (error) { + nm_log_warn (LOGD_USER_SET, "User connections unavailable: (%d) %s", + error->code, error->message ? error->message : "(unknown)"); + } else + authorized = user_settings_authorized (self, chain); + + if (authorized) { + /* If authorized, finish setting up the user settings service proxy */ + nm_log_dbg (LOGD_USER_SET, "Requesting user connections..."); + + authorized = TRUE; + + dbus_g_proxy_add_signal (priv->user_proxy, + "NewConnection", + DBUS_TYPE_G_OBJECT_PATH, + G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->user_proxy, "NewConnection", + G_CALLBACK (user_new_connection_cb), + self, + NULL); + + /* Clean up when the user settings proxy goes away */ + g_signal_connect (priv->user_proxy, "destroy", + G_CALLBACK (user_proxy_destroyed_cb), + self); + + /* Request user connections */ + dbus_g_proxy_begin_call (priv->user_proxy, "ListConnections", + user_list_connections_cb, + self, + NULL, + G_TYPE_INVALID); + } else { + /* Otherwise, we ignore the user settings service completely */ + user_proxy_cleanup (self, TRUE); + } + + nm_auth_chain_unref (chain); +} + +static void +user_proxy_init (NMManager *self) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + DBusGConnection *bus; + NMAuthChain *chain; + GError *error = NULL; + + g_return_if_fail (self != NULL); + g_return_if_fail (priv->user_proxy == NULL); + + /* Don't try to initialize the user settings proxy if the user + * settings service doesn't actually exist. + */ + if (!nm_dbus_manager_name_has_owner (priv->dbus_mgr, NM_DBUS_SERVICE_USER_SETTINGS)) + return; + + bus = nm_dbus_manager_get_connection (priv->dbus_mgr); + priv->user_proxy = dbus_g_proxy_new_for_name_owner (bus, + NM_DBUS_SERVICE_USER_SETTINGS, + NM_DBUS_PATH_SETTINGS, + NM_DBUS_IFACE_SETTINGS, + &error); + if (!priv->user_proxy) { + nm_log_err (LOGD_USER_SET, "could not init user settings proxy: (%d) %s", + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + g_clear_error (&error); + return; + } + + /* Kick off some PolicyKit authorization requests to figure out what + * permissions this user settings service has. + */ + chain = nm_auth_chain_new (priv->authority, + NULL, + priv->user_proxy, + user_proxy_auth_done, + self); + priv->auth_chains = g_slist_prepend (priv->auth_chains, chain); + + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_USE_USER_CONNECTIONS, FALSE); + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, FALSE); +} + +/*******************************************************************/ +/* System settings stuff via NMSysconfigSettings */ +/*******************************************************************/ + +static void +system_connection_updated_cb (NMSettingsConnectionInterface *connection, + gpointer unused, + NMManager *manager) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + const char *path; + NMSettingsConnectionInterface *existing; + GError *error = NULL; + + path = nm_connection_get_path (NM_CONNECTION (connection)); + + existing = g_hash_table_lookup (priv->system_connections, path); + if (!existing) + return; + if (existing != connection) { + nm_log_warn (LOGD_SYS_SET, "existing connection didn't matched updated."); + return; + } + + if (!nm_connection_verify (NM_CONNECTION (existing), &error)) { + /* Updated connection invalid, remove existing connection */ + nm_log_warn (LOGD_SYS_SET, "invalid connection: '%s' / '%s' invalid: %d", + g_type_name (nm_connection_lookup_setting_type_by_quark (error->domain)), + error->message, error->code); + g_error_free (error); + remove_connection (manager, NM_CONNECTION (existing), priv->system_connections); + return; + } + + g_signal_emit (manager, signals[CONNECTION_UPDATED], 0, + existing, NM_CONNECTION_SCOPE_SYSTEM); + bluez_manager_resync_devices (manager); } static void -system_unmanaged_devices_changed_cb (NMSettings *settings, +system_connection_removed_cb (NMSettingsConnectionInterface *connection, + NMManager *manager) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + const char *path; + + path = nm_connection_get_path (NM_CONNECTION (connection)); + + connection = g_hash_table_lookup (priv->system_connections, path); + if (connection) + remove_connection (manager, NM_CONNECTION (connection), priv->system_connections); +} + +static void +system_internal_new_connection (NMManager *manager, + NMSettingsConnectionInterface *connection) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + const char *path; + + g_return_if_fail (connection != NULL); + + g_signal_connect (connection, NM_SETTINGS_CONNECTION_INTERFACE_UPDATED, + G_CALLBACK (system_connection_updated_cb), manager); + g_signal_connect (connection, NM_SETTINGS_CONNECTION_INTERFACE_REMOVED, + G_CALLBACK (system_connection_removed_cb), manager); + + path = nm_connection_get_path (NM_CONNECTION (connection)); + g_hash_table_insert (priv->system_connections, g_strdup (path), + g_object_ref (connection)); + g_signal_emit (manager, signals[CONNECTION_ADDED], 0, connection, NM_CONNECTION_SCOPE_SYSTEM); +} + +static void +system_new_connection_cb (NMSysconfigSettings *settings, + NMSettingsConnectionInterface *connection, + NMManager *manager) +{ + system_internal_new_connection (manager, connection); +} + +static void +system_query_connections (NMManager *manager) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + GSList *system_connections, *iter; + + system_connections = nm_settings_interface_list_connections (NM_SETTINGS_INTERFACE (priv->sys_settings)); + for (iter = system_connections; iter; iter = g_slist_next (iter)) + system_internal_new_connection (manager, NM_SETTINGS_CONNECTION_INTERFACE (iter->data)); + g_slist_free (system_connections); +} + +static void +system_unmanaged_devices_changed_cb (NMSysconfigSettings *sys_settings, GParamSpec *pspec, gpointer user_data) { - NMManager *self = NM_MANAGER (user_data); - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMManager *manager = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); const GSList *unmanaged_specs, *iter; - unmanaged_specs = nm_settings_get_unmanaged_specs (priv->settings); + unmanaged_specs = nm_sysconfig_settings_get_unmanaged_specs (sys_settings); for (iter = priv->devices; iter; iter = g_slist_next (iter)) { NMDevice *device = NM_DEVICE (iter->data); gboolean managed; @@ -919,28 +1524,30 @@ system_unmanaged_devices_changed_cb (NMSettings *settings, nm_device_set_managed (device, managed, managed ? NM_DEVICE_STATE_REASON_NOW_MANAGED : - NM_DEVICE_STATE_REASON_NOW_UNMANAGED); + NM_DEVICE_STATE_REASON_NOW_UNMANAGED); } } static void -system_hostname_changed_cb (NMSettings *settings, +system_hostname_changed_cb (NMSysconfigSettings *sys_settings, GParamSpec *pspec, gpointer user_data) { - NMManager *self = NM_MANAGER (user_data); - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMManager *manager = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); char *hostname; - hostname = nm_settings_get_hostname (priv->settings); + hostname = nm_sysconfig_settings_get_hostname (sys_settings); + if (!hostname && !priv->hostname) return; + if (hostname && priv->hostname && !strcmp (hostname, priv->hostname)) return; g_free (priv->hostname); priv->hostname = (hostname && strlen (hostname)) ? g_strdup (hostname) : NULL; - g_object_notify (G_OBJECT (self), NM_MANAGER_HOSTNAME); + g_object_notify (G_OBJECT (manager), NM_MANAGER_HOSTNAME); g_free (hostname); } @@ -949,6 +1556,49 @@ system_hostname_changed_cb (NMSettings *settings, /* General NMManager stuff */ /*******************************************************************/ +static NMDevice * +nm_manager_get_device_by_udi (NMManager *manager, const char *udi) +{ + GSList *iter; + + for (iter = NM_MANAGER_GET_PRIVATE (manager)->devices; iter; iter = iter->next) { + if (!strcmp (nm_device_get_udi (NM_DEVICE (iter->data)), udi)) + return NM_DEVICE (iter->data); + } + return NULL; +} + +static NMDevice * +nm_manager_get_device_by_path (NMManager *manager, const char *path) +{ + GSList *iter; + + for (iter = NM_MANAGER_GET_PRIVATE (manager)->devices; iter; iter = iter->next) { + if (!strcmp (nm_device_get_path (NM_DEVICE (iter->data)), path)) + return NM_DEVICE (iter->data); + } + return NULL; +} + +static void +nm_manager_name_owner_changed (NMDBusManager *mgr, + const char *name, + const char *old, + const char *new, + gpointer user_data) +{ + NMManager *manager = NM_MANAGER (user_data); + gboolean old_owner_good = (old && (strlen (old) > 0)); + gboolean new_owner_good = (new && (strlen (new) > 0)); + + if (strcmp (name, NM_DBUS_SERVICE_USER_SETTINGS) == 0) { + if (!old_owner_good && new_owner_good) + user_proxy_init (manager); + else + user_proxy_cleanup (manager, TRUE); + } +} + /* Store value into key-file; supported types: boolean, int, string */ static gboolean write_value_to_state_file (const char *filename, @@ -1000,21 +1650,28 @@ write_value_to_state_file (const char *filename, } static gboolean -radio_enabled_for_rstate (RadioState *rstate) +radio_enabled_for_rstate (RadioState *rstate, gboolean check_daemon_enabled) { - return rstate->user_enabled && rstate->sw_enabled && rstate->hw_enabled; + gboolean enabled; + + enabled = rstate->user_enabled && rstate->sw_enabled && rstate->hw_enabled; + if (rstate->daemon_enabled_func && check_daemon_enabled) + enabled &= rstate->daemon_enabled; + return enabled; } static gboolean -radio_enabled_for_type (NMManager *self, RfKillType rtype) +radio_enabled_for_type (NMManager *self, RfKillType rtype, gboolean check_daemon_enabled) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - return radio_enabled_for_rstate (&priv->radio_states[rtype]); + return radio_enabled_for_rstate (&priv->radio_states[rtype], check_daemon_enabled); } static void -manager_update_radio_enabled (NMManager *self, RadioState *rstate) +manager_update_radio_enabled (NMManager *self, + RadioState *rstate, + gboolean enabled) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); GSList *iter; @@ -1032,7 +1689,6 @@ manager_update_radio_enabled (NMManager *self, RadioState *rstate) /* enable/disable wireless devices as required */ for (iter = priv->devices; iter; iter = iter->next) { RfKillType devtype = RFKILL_TYPE_UNKNOWN; - gboolean enabled = radio_enabled_for_rstate (rstate); g_object_get (G_OBJECT (iter->data), NM_DEVICE_INTERFACE_RFKILL_TYPE, &devtype, NULL); if (devtype == rstate->rtype) { @@ -1050,7 +1706,6 @@ manager_hidden_ap_found (NMDeviceInterface *device, gpointer user_data) { NMManager *manager = NM_MANAGER (user_data); - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); const struct ether_addr *ap_addr; const GByteArray *ap_ssid; GSList *iter; @@ -1066,7 +1721,8 @@ manager_hidden_ap_found (NMDeviceInterface *device, /* 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); + connections = nm_manager_get_connections (manager, NM_CONNECTION_SCOPE_SYSTEM); + connections = g_slist_concat (connections, nm_manager_get_connections (manager, NM_CONNECTION_SCOPE_USER)); for (iter = connections; iter && !done; iter = g_slist_next (iter)) { NMConnection *connection = NM_CONNECTION (iter->data); @@ -1077,27 +1733,32 @@ manager_hidden_ap_found (NMDeviceInterface *device, s_wireless = (NMSettingWireless *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS); if (!s_wireless) - continue; + goto next; num_bssids = nm_setting_wireless_get_num_seen_bssids (s_wireless); if (num_bssids < 1) - continue; + goto next; ssid = nm_setting_wireless_get_ssid (s_wireless); g_assert (ssid); - for (i = 0; i < num_bssids && !done; i++) { + for (i = 0; i < num_bssids; i++) { const char *seen_bssid = nm_setting_wireless_get_seen_bssid (s_wireless, i); struct ether_addr seen_addr; - if (ether_aton_r (seen_bssid, &seen_addr)) { - if (memcmp (ap_addr, &seen_addr, sizeof (struct ether_addr))) { - /* Copy the SSID from the connection to the AP */ - nm_ap_set_ssid (ap, ssid); - done = TRUE; - } - } + if (!ether_aton_r (seen_bssid, &seen_addr)) + continue; + + if (memcmp (ap_addr, &seen_addr, sizeof (struct ether_addr))) + continue; + + /* Copy the SSID from the connection to the AP */ + nm_ap_set_ssid (ap, ssid); + done = TRUE; } + +next: + g_object_unref (connection); } g_slist_free (connections); } @@ -1174,9 +1835,9 @@ manager_rfkill_update_one_type (NMManager *self, RfKillState other_state = RFKILL_UNBLOCKED; RfKillState composite; gboolean old_enabled, new_enabled, old_rfkilled, new_rfkilled; - gboolean old_hwe; + gboolean old_hwe, old_daemon_enabled = FALSE; - old_enabled = radio_enabled_for_rstate (rstate); + old_enabled = radio_enabled_for_rstate (rstate, TRUE); old_rfkilled = rstate->hw_enabled && rstate->sw_enabled; old_hwe = rstate->hw_enabled; @@ -1195,9 +1856,26 @@ manager_rfkill_update_one_type (NMManager *self, 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"); + } + } + + /* Print out all states affecting device enablement */ if (rstate->desc) { - nm_log_dbg (LOGD_RFKILL, "%s hw-enabled %d sw-enabled %d", - rstate->desc, rstate->hw_enabled, rstate->sw_enabled); + 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); + } } /* Log new killswitch state */ @@ -1214,10 +1892,14 @@ manager_rfkill_update_one_type (NMManager *self, g_object_notify (G_OBJECT (self), rstate->hw_prop); } - /* And finally update the actual device radio state itself */ - new_enabled = radio_enabled_for_rstate (rstate); + /* And finally update the actual device radio state itself; respect the + * daemon state here because this is never called from user-triggered + * radio changes and we only want to ignore the daemon enabled state when + * handling user radio change requests. + */ + new_enabled = radio_enabled_for_rstate (rstate, TRUE); if (new_enabled != old_enabled) - manager_update_radio_enabled (self, rstate); + manager_update_radio_enabled (self, rstate, new_enabled); } static void @@ -1226,14 +1908,13 @@ nm_manager_rfkill_update (NMManager *self, RfKillType rtype) NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); guint i; - if (rtype != RFKILL_TYPE_UNKNOWN) { + if (rtype != RFKILL_TYPE_UNKNOWN) manager_rfkill_update_one_type (self, &priv->radio_states[rtype], rtype); - return; + else { + /* Otherwise sync all radio types */ + for (i = 0; i < RFKILL_TYPE_MAX; i++) + manager_rfkill_update_one_type (self, &priv->radio_states[i], i); } - - /* Otherwise sync all radio types */ - for (i = 0; i < RFKILL_TYPE_MAX; i++) - manager_rfkill_update_one_type (self, &priv->radio_states[i], i); } static void @@ -1264,40 +1945,90 @@ deactivate_disconnect_check_error (GError *auth_error, } else if (result != NM_AUTH_CALL_RESULT_YES) { return g_error_new (NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, - "Not authorized to %s connections", + "Not authorized to %s user connections", detail); } return NULL; } static void +disconnect_user_auth_done_cb (NMAuthChain *chain, + GError *error, + DBusGMethodInvocation *context, + gpointer user_data) +{ + NMManager *self = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + GError *ret_error = NULL; + NMAuthCallResult result; + NMDevice *device; + + priv->auth_chains = g_slist_remove (priv->auth_chains, chain); + + result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, NM_AUTH_PERMISSION_USE_USER_CONNECTIONS)); + ret_error = deactivate_disconnect_check_error (error, result, "Disconnect"); + if (!ret_error) { + /* Everything authorized, deactivate the connection */ + device = nm_auth_chain_get_data (chain, "device"); + if (nm_device_interface_disconnect (NM_DEVICE_INTERFACE (device), &ret_error)) + dbus_g_method_return (context); + } + + if (ret_error) + dbus_g_method_return_error (context, ret_error); + g_clear_error (&ret_error); + + nm_auth_chain_unref (chain); +} + +static void disconnect_net_auth_done_cb (NMAuthChain *chain, - GError *auth_error, + GError *error, DBusGMethodInvocation *context, gpointer user_data) { NMManager *self = NM_MANAGER (user_data); NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - GError *error = NULL; + GError *ret_error = NULL; NMAuthCallResult result; + NMConnectionScope scope; NMDevice *device; 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)); - error = deactivate_disconnect_check_error (auth_error, result, "Disconnect"); - if (!error) { - device = nm_auth_chain_get_data (chain, "device"); - if (!nm_device_interface_disconnect (NM_DEVICE_INTERFACE (device), &error)) - g_assert (error); + ret_error = deactivate_disconnect_check_error (error, result, "Disconnect"); + if (ret_error) { + dbus_g_method_return_error (context, ret_error); + g_error_free (ret_error); + goto done; } - if (error) - dbus_g_method_return_error (context, error); - else - dbus_g_method_return (context); + /* If it's a system connection, we're done */ + device = nm_auth_chain_get_data (chain, "device"); + scope = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "scope")); + if (scope == NM_CONNECTION_SCOPE_USER) { + NMAuthChain *user_chain; - g_clear_error (&error); + /* It's a user connection, so we need to ensure the caller is + * authorized to manipulate user connections. + */ + user_chain = nm_auth_chain_new (priv->authority, context, NULL, disconnect_user_auth_done_cb, self); + g_assert (user_chain); + priv->auth_chains = g_slist_append (priv->auth_chains, user_chain); + + nm_auth_chain_set_data (user_chain, "device", g_object_ref (device), g_object_unref); + nm_auth_chain_set_data (user_chain, "scope", GUINT_TO_POINTER (scope), NULL); + nm_auth_chain_add_call (user_chain, NM_AUTH_PERMISSION_USE_USER_CONNECTIONS, TRUE); + } else { + if (!nm_device_interface_disconnect (NM_DEVICE_INTERFACE (device), &ret_error)) { + dbus_g_method_return_error (context, ret_error); + g_clear_error (&ret_error); + } else + dbus_g_method_return (context); + } + +done: nm_auth_chain_unref (chain); } @@ -1308,9 +2039,11 @@ manager_device_disconnect_request (NMDevice *device, { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); NMActRequest *req; + NMConnection *connection; GError *error = NULL; + NMConnectionScope scope; gulong sender_uid = G_MAXULONG; - char *error_desc = NULL; + const char *error_desc = NULL; req = nm_device_get_act_request (device); if (!req) { @@ -1322,19 +2055,24 @@ manager_device_disconnect_request (NMDevice *device, return; } + connection = nm_act_request_get_connection (req); + g_assert (connection); + /* 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)) { + scope = nm_connection_get_scope (connection); + if (!check_user_authorized (priv->dbus_mgr, + priv->user_proxy, + context, + scope, + &sender_uid, + &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; } @@ -1354,6 +2092,7 @@ manager_device_disconnect_request (NMDevice *device, 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, "scope", GUINT_TO_POINTER (scope), NULL); nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE); } } @@ -1367,7 +2106,10 @@ add_device (NMManager *self, NMDevice *device) static guint32 devcount = 0; const GSList *unmanaged_specs; NMConnection *existing = NULL; + GHashTableIter iter; + gpointer value; gboolean managed = FALSE, enabled = FALSE; + RfKillType rtype = RFKILL_TYPE_UNKNOWN; iface = nm_device_get_ip_iface (device); g_assert (iface); @@ -1401,32 +2143,21 @@ add_device (NMManager *self, NMDevice *device) g_signal_connect (device, "notify::" NM_DEVICE_WIFI_IPW_RFKILL_STATE, G_CALLBACK (manager_ipw_rfkill_state_changed), self); - - /* Update global rfkill state with this device's rfkill state, and - * then set this device's rfkill state based on the global state. - */ - nm_manager_rfkill_update (self, RFKILL_TYPE_WLAN); - enabled = radio_enabled_for_type (self, RFKILL_TYPE_WLAN); - nm_device_interface_set_enabled (NM_DEVICE_INTERFACE (device), enabled); + rtype = RFKILL_TYPE_WLAN; } else if (NM_IS_DEVICE_MODEM (device)) { g_signal_connect (device, NM_DEVICE_MODEM_ENABLE_CHANGED, G_CALLBACK (manager_modem_enabled_changed), self); + rtype = RFKILL_TYPE_WWAN; + } - nm_manager_rfkill_update (self, RFKILL_TYPE_WWAN); - enabled = radio_enabled_for_type (self, RFKILL_TYPE_WWAN); - /* Until we start respecting WWAN rfkill switches the modem itself - * is the source of the enabled/disabled state, so the manager shouldn't - * touch it here. - nm_device_interface_set_enabled (NM_DEVICE_INTERFACE (device), - priv->radio_states[RFKILL_TYPE_WWAN].enabled); - */ -#if WITH_WIMAX - } else if (NM_IS_DEVICE_WIMAX (device)) { - nm_manager_rfkill_update (self, RFKILL_TYPE_WIMAX); - enabled = radio_enabled_for_type (self, RFKILL_TYPE_WIMAX); + if (rtype != RFKILL_TYPE_UNKNOWN) { + /* Update global rfkill state with this device's rfkill state, and + * then set this device's rfkill state based on the global state. + */ + nm_manager_rfkill_update (self, rtype); + enabled = radio_enabled_for_type (self, rtype, TRUE); nm_device_interface_set_enabled (NM_DEVICE_INTERFACE (device), enabled); -#endif } type_desc = nm_device_get_type_desc (device); @@ -1451,19 +2182,25 @@ add_device (NMManager *self, NMDevice *device) if (nm_device_interface_can_assume_connections (NM_DEVICE_INTERFACE (device))) { GSList *connections = NULL; - connections = nm_settings_get_connections (priv->settings); + g_hash_table_iter_init (&iter, priv->system_connections); + while (g_hash_table_iter_next (&iter, NULL, &value)) + connections = g_slist_append (connections, value); existing = nm_device_interface_connection_match_config (NM_DEVICE_INTERFACE (device), (const GSList *) connections); g_slist_free (connections); - if (existing) + if (existing) { + NMSettingConnection *s_con; + + s_con = (NMSettingConnection *) nm_connection_get_setting (existing, NM_TYPE_SETTING_CONNECTION); nm_log_dbg (LOGD_DEVICE, "(%s): found existing device connection '%s'", nm_device_get_iface (device), - nm_connection_get_id (existing)); + nm_setting_connection_get_id (s_con)); + } } /* Start the device if it's supposed to be managed */ - unmanaged_specs = nm_settings_get_unmanaged_specs (priv->settings); + unmanaged_specs = nm_sysconfig_settings_get_unmanaged_specs (priv->sys_settings); if ( !manager_sleeping (self) && !nm_device_interface_spec_match_list (NM_DEVICE_INTERFACE (device), unmanaged_specs)) { nm_device_set_managed (device, @@ -1473,7 +2210,7 @@ add_device (NMManager *self, NMDevice *device) managed = TRUE; } - nm_settings_device_added (priv->settings, device); + nm_sysconfig_settings_device_added (priv->sys_settings, device); g_signal_emit (self, signals[DEVICE_ADDED], 0, device); /* If the device has a connection it can assume, do that now */ @@ -1484,11 +2221,12 @@ add_device (NMManager *self, NMDevice *device) nm_log_dbg (LOGD_DEVICE, "(%s): will attempt to assume existing connection", nm_device_get_iface (device)); - ac_path = internal_activate_device (self, device, existing, NULL, FALSE, 0, TRUE, &error); + ac_path = internal_activate_device (self, device, existing, NULL, FALSE, TRUE, &error); if (ac_path) g_object_notify (G_OBJECT (self), NM_MANAGER_ACTIVE_CONNECTIONS); else { - nm_log_warn (LOGD_DEVICE, "assumed connection %s failed to activate: (%d) %s", + nm_log_warn (LOGD_DEVICE, "assumed connection (%d) %s failed to activate: (%d) %s", + nm_connection_get_scope (existing), nm_connection_get_path (existing), error ? error->code : -1, error && error->message ? error->message : "(unknown)"); @@ -1528,11 +2266,11 @@ bluez_manager_find_connection (NMManager *manager, const char *bdaddr, guint32 capabilities) { - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); NMConnection *found = NULL; GSList *connections, *l; - connections = nm_settings_get_connections (priv->settings); + connections = nm_manager_get_connections (manager, NM_CONNECTION_SCOPE_SYSTEM); + connections = g_slist_concat (connections, nm_manager_get_connections (manager, NM_CONNECTION_SCOPE_USER)); for (l = connections; l != NULL; l = l->next) { NMConnection *candidate = NM_CONNECTION (l->data); @@ -1812,13 +2550,312 @@ nm_manager_get_act_request_by_path (NMManager *manager, return NULL; } +typedef struct GetSecretsInfo { + NMManager *manager; + NMSecretsProviderInterface *provider; + + char *setting_name; + RequestSecretsCaller caller; + gboolean request_new; + + /* User connection bits */ + DBusGProxy *proxy; + DBusGProxyCall *call; + + /* System connection bits */ + guint32 idle_id; + char *hint1; + char *hint2; + char *connection_path; +} GetSecretsInfo; + +static void +free_get_secrets_info (gpointer data) +{ + GetSecretsInfo *info = data; + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (info->manager); + + g_object_weak_unref (G_OBJECT (info->provider), (GWeakNotify) free_get_secrets_info, info); + + priv->secrets_calls = g_slist_remove (priv->secrets_calls, info); + + if (info->proxy) { + if (info->call) + dbus_g_proxy_cancel_call (info->proxy, info->call); + g_object_unref (info->proxy); + } + + if (info->idle_id) + g_source_remove (info->idle_id); + + g_free (info->hint1); + g_free (info->hint2); + g_free (info->setting_name); + g_free (info->connection_path); + memset (info, 0, sizeof (GetSecretsInfo)); + g_free (info); +} + +static void +provider_cancel_secrets (NMSecretsProviderInterface *provider, gpointer user_data) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (user_data); + GSList *iter; + + for (iter = priv->secrets_calls; iter; iter = g_slist_next (iter)) { + GetSecretsInfo *candidate = iter->data; + + if (candidate->provider == provider) { + free_get_secrets_info (candidate); + break; + } + } +} + +static void +user_get_secrets_cb (DBusGProxy *proxy, + DBusGProxyCall *call, + gpointer user_data) +{ + GetSecretsInfo *info = (GetSecretsInfo *) user_data; + NMManagerPrivate *priv; + GHashTable *settings = NULL; + GError *error = NULL; + GObject *provider; + + g_return_if_fail (info != NULL); + g_return_if_fail (info->provider); + g_return_if_fail (info->setting_name); + + /* Remove the GetSecretsInfo from our internal list just in case + * calling the secrets provider's get_secrets_result() function tries + * to cancel the secrets request, which would cause us to double-free + * the GetSecretsInfo. We know we're going to free it at the end here, + * so there's no need to track it anymore. + */ + priv = NM_MANAGER_GET_PRIVATE (info->manager); + priv->secrets_calls = g_slist_remove (priv->secrets_calls, info); + + provider = g_object_ref (info->provider); + + if (dbus_g_proxy_end_call (proxy, call, &error, + DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, &settings, + G_TYPE_INVALID)) { + nm_secrets_provider_interface_get_secrets_result (info->provider, + info->setting_name, + info->caller, + settings, + NULL); + g_hash_table_destroy (settings); + } else { + nm_secrets_provider_interface_get_secrets_result (info->provider, + info->setting_name, + info->caller, + NULL, + error); + g_clear_error (&error); + } + + info->call = NULL; + free_get_secrets_info (info); + + g_object_unref (provider); +} + +static GetSecretsInfo * +user_get_secrets (NMManager *self, + NMSecretsProviderInterface *provider, + NMConnection *connection, + const char *setting_name, + gboolean request_new, + RequestSecretsCaller caller_id, + const char *hint1, + const char *hint2) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + DBusGConnection *g_connection; + GetSecretsInfo *info = NULL; + GPtrArray *hints = NULL; + + info = g_malloc0 (sizeof (GetSecretsInfo)); + + g_connection = nm_dbus_manager_get_connection (priv->dbus_mgr); + info->proxy = dbus_g_proxy_new_for_name (g_connection, + NM_DBUS_SERVICE_USER_SETTINGS, + nm_connection_get_path (connection), + NM_DBUS_IFACE_SETTINGS_CONNECTION_SECRETS); + if (!info->proxy) { + nm_log_warn (LOGD_USER_SET, "could not create user connection secrets proxy"); + g_free (info); + return NULL; + } + + info->manager = self; + info->provider = provider; + info->caller = caller_id; + info->setting_name = g_strdup (setting_name); + + g_object_weak_ref (G_OBJECT (provider), (GWeakNotify) free_get_secrets_info, info); + + hints = g_ptr_array_sized_new (2); + if (hint1) + g_ptr_array_add (hints, (char *) hint1); + if (hint2) + g_ptr_array_add (hints, (char *) hint2); + + info->call = dbus_g_proxy_begin_call_with_timeout (info->proxy, "GetSecrets", + user_get_secrets_cb, + info, + NULL, + G_MAXINT32, + G_TYPE_STRING, setting_name, + DBUS_TYPE_G_ARRAY_OF_STRING, hints, + G_TYPE_BOOLEAN, request_new, + G_TYPE_INVALID); + g_ptr_array_free (hints, TRUE); + return info; +} + +static void +system_get_secrets_reply_cb (NMSettingsConnectionInterface *connection, + GHashTable *secrets, + GError *error, + gpointer user_data) +{ + GetSecretsInfo *info = user_data; + GObject *provider; + + provider = g_object_ref (info->provider); + + nm_secrets_provider_interface_get_secrets_result (info->provider, + info->setting_name, + info->caller, + error ? NULL : secrets, + error); + free_get_secrets_info (info); + g_object_unref (provider); +} + +static gboolean +system_get_secrets_idle_cb (gpointer user_data) +{ + GetSecretsInfo *info = user_data; + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (info->manager); + NMSettingsConnectionInterface *connection; + GError *error = NULL; + const char *hints[3] = { NULL, NULL, NULL }; + + info->idle_id = 0; + + connection = nm_settings_interface_get_connection_by_path (NM_SETTINGS_INTERFACE (priv->sys_settings), + info->connection_path); + if (!connection) { + error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_UNKNOWN_CONNECTION, + "unknown connection (not exported by system settings)"); + nm_secrets_provider_interface_get_secrets_result (info->provider, + info->setting_name, + info->caller, + NULL, + error); + g_error_free (error); + free_get_secrets_info (info); + return FALSE; + } + + hints[0] = info->hint1; + hints[1] = info->hint2; + nm_settings_connection_interface_get_secrets (connection, + info->setting_name, + hints, + info->request_new, + system_get_secrets_reply_cb, + info); + return FALSE; +} + +static GetSecretsInfo * +system_get_secrets (NMManager *self, + NMSecretsProviderInterface *provider, + NMConnection *connection, + const char *setting_name, + gboolean request_new, + RequestSecretsCaller caller_id, + const char *hint1, + const char *hint2) +{ + GetSecretsInfo *info; + + info = g_malloc0 (sizeof (GetSecretsInfo)); + info->manager = self; + info->provider = provider; + info->caller = caller_id; + info->setting_name = g_strdup (setting_name); + info->hint1 = hint1 ? g_strdup (hint1) : NULL; + info->hint2 = hint2 ? g_strdup (hint2) : NULL; + info->connection_path = g_strdup (nm_connection_get_path (connection)); + info->request_new = request_new; + + g_object_weak_ref (G_OBJECT (provider), (GWeakNotify) free_get_secrets_info, info); + + info->idle_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, + system_get_secrets_idle_cb, + info, + NULL); + return info; +} + +static gboolean +provider_get_secrets (NMSecretsProviderInterface *provider, + NMConnection *connection, + const char *setting_name, + gboolean request_new, + RequestSecretsCaller caller_id, + const char *hint1, + const char *hint2, + gpointer user_data) +{ + NMManager *self = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + GetSecretsInfo *info = NULL; + NMConnectionScope scope; + GSList *iter; + + g_return_val_if_fail (connection != NULL, FALSE); + g_return_val_if_fail (setting_name != NULL, FALSE); + + /* Tear down any pending secrets requests for this secrets provider */ + for (iter = priv->secrets_calls; iter; iter = g_slist_next (iter)) { + GetSecretsInfo *candidate = iter->data; + + if (provider == candidate->provider) { + free_get_secrets_info (candidate); + break; + } + } + + /* Build up the new secrets request */ + scope = nm_connection_get_scope (connection); + if (scope == NM_CONNECTION_SCOPE_SYSTEM) { + info = system_get_secrets (self, provider, connection, setting_name, + request_new, caller_id, hint1, hint2); + } else if (scope == NM_CONNECTION_SCOPE_USER) { + info = user_get_secrets (self, provider, connection, setting_name, + request_new, caller_id, hint1, hint2); + } + + if (info) + priv->secrets_calls = g_slist_append (priv->secrets_calls, info); + + return !!info; +} + static const char * internal_activate_device (NMManager *manager, NMDevice *device, NMConnection *connection, const char *specific_object, gboolean user_requested, - gulong sender_uid, gboolean assumed, GError **error) { @@ -1843,24 +2880,42 @@ internal_activate_device (NMManager *manager, NM_DEVICE_STATE_REASON_NONE); } - req = nm_act_request_new (connection, - specific_object, - user_requested, - sender_uid, - assumed, - (gpointer) device); + req = nm_act_request_new (connection, specific_object, user_requested, assumed, (gpointer) device); + g_signal_connect (req, "manager-get-secrets", G_CALLBACK (provider_get_secrets), manager); + g_signal_connect (req, "manager-cancel-secrets", G_CALLBACK (provider_cancel_secrets), manager); success = nm_device_interface_activate (dev_iface, req, error); g_object_unref (req); return success ? nm_act_request_get_active_connection_path (req) : NULL; } +static gboolean +wait_for_connection_expired (gpointer data) +{ + PendingActivation *pending = data; + GError *error = NULL; + + g_return_val_if_fail (pending != NULL, FALSE); + + nm_log_warn (LOGD_CORE, "connection %s (scope %d) failed to activate (timeout)", + pending->connection_path, pending->scope); + + nm_manager_pending_activation_remove (pending->manager, pending); + + error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_UNKNOWN_CONNECTION, + "Connection was not provided by any settings service"); + pending_activation_destroy (pending, error, NULL); + g_error_free (error); + return FALSE; +} + const char * nm_manager_activate_connection (NMManager *manager, NMConnection *connection, const char *specific_object, const char *device_path, - const char *dbus_sender, + gboolean user_requested, GError **error) { NMManagerPrivate *priv; @@ -1868,8 +2923,6 @@ nm_manager_activate_connection (NMManager *manager, NMSettingConnection *s_con; NMVPNConnection *vpn_connection; const char *path = NULL; - gulong sender_uid = 0; - DBusError dbus_error; g_return_val_if_fail (manager != NULL, NULL); g_return_val_if_fail (connection != NULL, NULL); @@ -1878,34 +2931,17 @@ nm_manager_activate_connection (NMManager *manager, priv = NM_MANAGER_GET_PRIVATE (manager); - /* 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; - } - } - s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION)); g_assert (s_con); if (!strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_VPN_SETTING_NAME)) { - NMActRequest *parent_req = NULL; - NMVPNManager *vpn_manager; + NMActRequest *req = NULL; /* VPN connection */ - if (specific_object) { /* Find the specifc connection the client requested we use */ - parent_req = nm_manager_get_act_request_by_path (manager, specific_object, &device); - if (!parent_req) { + req = nm_manager_get_act_request_by_path (manager, specific_object, &device); + if (!req) { g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE, "%s", "Base connection for VPN connection not active."); @@ -1922,30 +2958,30 @@ nm_manager_activate_connection (NMManager *manager, candidate_req = nm_device_get_act_request (candidate); if (candidate_req && nm_act_request_get_default (candidate_req)) { device = candidate; - parent_req = candidate_req; + req = candidate_req; break; } } } - if (!device || !parent_req) { + if (!device || !req) { g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, "%s", "Could not find source connection, or the source connection had no active device."); return NULL; } - vpn_manager = nm_vpn_manager_get (); - vpn_connection = nm_vpn_manager_activate_connection (vpn_manager, + vpn_connection = nm_vpn_manager_activate_connection (priv->vpn_manager, connection, - parent_req, device, - TRUE, - sender_uid, error); - if (vpn_connection) + if (vpn_connection) { + g_signal_connect (vpn_connection, "manager-get-secrets", + G_CALLBACK (provider_get_secrets), manager); + g_signal_connect (vpn_connection, "manager-cancel-secrets", + G_CALLBACK (provider_cancel_secrets), manager); path = nm_vpn_connection_get_active_connection_path (vpn_connection); - g_object_unref (vpn_manager); + } } else { NMDeviceState state; @@ -1970,8 +3006,7 @@ nm_manager_activate_connection (NMManager *manager, device, connection, specific_object, - dbus_sender ? TRUE : FALSE, - dbus_sender ? sender_uid : 0, + user_requested, FALSE, error); } @@ -1979,24 +3014,40 @@ nm_manager_activate_connection (NMManager *manager, return path; } -/* - * 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 PendingActivation * +nm_manager_pending_activation_find (NMManager *self, + const char *path, + NMConnectionScope scope) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - NMSettingsConnection *connection; + GSList *iter; + + for (iter = priv->pending_activations; iter; iter = g_slist_next (iter)) { + PendingActivation *pending = iter->data; + + if (!strcmp (pending->connection_path, path) && (pending->scope == scope)) + return pending; + } + return NULL; +} + +static void +check_pending_ready (NMManager *self, PendingActivation *pending) +{ + NMConnection *connection; const char *path = NULL; GError *error = NULL; - char *sender; - /* Ok, we're authorized */ + if (!pending->have_connection || !pending->authorized) + return; + + /* Ok, we're authorized and the connection is available */ - connection = nm_settings_get_connection_by_path (priv->settings, pending->connection_path); + nm_manager_pending_activation_remove (self, pending); + + connection = nm_manager_get_connection_by_object_path (self, + pending->scope, + pending->connection_path); if (!connection) { error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_CONNECTION, @@ -2004,19 +3055,15 @@ pending_activate (NMManager *self, PendingActivation *pending) goto out; } - sender = dbus_g_method_get_sender (pending->context); - g_assert (sender); path = nm_manager_activate_connection (self, - NM_CONNECTION (connection), + connection, pending->specific_object_path, pending->device_path, - sender, + TRUE, &error); - g_free (sender); - if (!path) { - nm_log_warn (LOGD_CORE, "connection %s failed to activate: (%d) %s", - pending->connection_path, error->code, error->message); + nm_log_warn (LOGD_CORE, "connection (%d) %s failed to activate: (%d) %s", + pending->scope, pending->connection_path, error->code, error->message); } else g_object_notify (G_OBJECT (pending->manager), NM_MANAGER_ACTIVE_CONNECTIONS); @@ -2026,93 +3073,68 @@ out: } static void +connection_added_default_handler (NMManager *self, + NMConnection *connection, + NMConnectionScope scope) +{ + PendingActivation *pending; + + pending = nm_manager_pending_activation_find (self, + nm_connection_get_path (connection), + scope); + if (pending) { + pending->have_connection = TRUE; + check_pending_ready (self, pending); + } +} + +static void activation_auth_done (PendingActivation *pending, GError *error) { - if (error) + if (error) { + nm_manager_pending_activation_remove (pending->manager, pending); pending_activation_destroy (pending, error, NULL); - else - pending_activate (pending->manager, pending); + return; + } else { + pending->authorized = TRUE; + + /* Now that we're authorized, if the connection hasn't shown up yet, + * start a timer and wait for it. + */ + if (!pending->have_connection && !pending->timeout_id) + pending->timeout_id = g_timeout_add_seconds (5, wait_for_connection_expired, pending); + + check_pending_ready (pending->manager, pending); + } } static void impl_manager_activate_connection (NMManager *self, + const char *service_name, const char *connection_path, const char *device_path, const char *specific_object_path, DBusGMethodInvocation *context) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMConnectionScope scope = NM_CONNECTION_SCOPE_UNKNOWN; PendingActivation *pending; GError *error = NULL; - /* Need to check the caller's permissions and stuff before we can - * activate the connection. - */ - pending = pending_activation_new (self, - priv->authority, - context, - device_path, - connection_path, - NULL, - specific_object_path, - activation_auth_done, - &error); - if (pending) - pending_activation_check_authorized (pending, priv->dbus_mgr); + if (!strcmp (service_name, NM_DBUS_SERVICE_USER_SETTINGS)) + scope = NM_CONNECTION_SCOPE_USER; + else if (!strcmp (service_name, NM_DBUS_SERVICE_SYSTEM_SETTINGS)) + scope = NM_CONNECTION_SCOPE_SYSTEM; else { - g_assert (error); + error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_INVALID_SERVICE, + "Invalid settings service name"); dbus_g_method_return_error (context, error); + nm_log_warn (LOGD_CORE, "connection (%d) %s failed to activate: (%d) %s", + scope, connection_path, error->code, error->message); g_error_free (error); + return; } -} - -static void -activation_add_done (NMSettings *self, - NMSettingsConnection *connection, - GError *error, - DBusGMethodInvocation *context, - gpointer user_data) -{ - PendingActivation *pending = user_data; - - 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))); - - /* And activate it */ - pending_activate (pending->manager, pending); - } -} - -static void -add_and_activate_auth_done (PendingActivation *pending, GError *error) -{ - if (error) - pending_activation_destroy (pending, error, NULL); - else { - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (pending->manager); - - /* Basic sender auth checks performed; try to add the connection */ - nm_settings_add_connection (priv->settings, - pending->connection, - pending->context, - activation_add_done, - pending); - } -} - -static void -impl_manager_add_and_activate_connection (NMManager *self, - GHashTable *settings, - const char *device_path, - const char *specific_object_path, - DBusGMethodInvocation *context) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - PendingActivation *pending; - GError *error = NULL; /* Need to check the caller's permissions and stuff before we can * activate the connection. @@ -2121,18 +3143,16 @@ impl_manager_add_and_activate_connection (NMManager *self, priv->authority, context, device_path, - NULL, - settings, + scope, + connection_path, 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); - } + activation_auth_done); + priv->pending_activations = g_slist_prepend (priv->pending_activations, pending); + + if (nm_manager_get_connection_by_object_path (self, scope, connection_path)) + pending->have_connection = TRUE; + + pending_activation_check_authorized (pending, priv->dbus_mgr, priv->user_proxy); } gboolean @@ -2142,7 +3162,6 @@ nm_manager_deactivate_connection (NMManager *manager, GError **error) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); - NMVPNManager *vpn_manager; GSList *iter; gboolean success = FALSE; NMVPNConnectionStateReason vpn_reason = NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED; @@ -2166,17 +3185,15 @@ nm_manager_deactivate_connection (NMManager *manager, } /* Check for VPN connections next */ - vpn_manager = nm_vpn_manager_get (); if (reason == NM_DEVICE_STATE_REASON_CONNECTION_REMOVED) vpn_reason = NM_VPN_CONNECTION_STATE_REASON_CONNECTION_REMOVED; - if (nm_vpn_manager_deactivate_connection (vpn_manager, connection_path, vpn_reason)) { + if (nm_vpn_manager_deactivate_connection (priv->vpn_manager, connection_path, vpn_reason)) { success = TRUE; } else { g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE, "%s", "The connection was not active."); } - g_object_unref (vpn_manager); done: g_object_notify (G_OBJECT (manager), NM_MANAGER_ACTIVE_CONNECTIONS); @@ -2184,6 +3201,37 @@ done: } static void +deactivate_user_auth_done_cb (NMAuthChain *chain, + GError *error, + DBusGMethodInvocation *context, + gpointer user_data) +{ + NMManager *self = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + GError *ret_error = NULL; + NMAuthCallResult result; + + priv->auth_chains = g_slist_remove (priv->auth_chains, chain); + + result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, NM_AUTH_PERMISSION_USE_USER_CONNECTIONS)); + ret_error = deactivate_disconnect_check_error (error, result, "Deactivate"); + if (!ret_error) { + /* Everything authorized, deactivate the connection */ + if (nm_manager_deactivate_connection (self, + nm_auth_chain_get_data (chain, "path"), + NM_DEVICE_STATE_REASON_USER_REQUESTED, + &ret_error)) + dbus_g_method_return (context); + } + + if (ret_error) + dbus_g_method_return_error (context, ret_error); + g_clear_error (&ret_error); + + nm_auth_chain_unref (chain); +} + +static void deactivate_net_auth_done_cb (NMAuthChain *chain, GError *error, DBusGMethodInvocation *context, @@ -2194,6 +3242,7 @@ deactivate_net_auth_done_cb (NMAuthChain *chain, GError *ret_error = NULL; NMAuthCallResult result; const char *active_path; + NMConnectionScope scope; priv->auth_chains = g_slist_remove (priv->auth_chains, chain); @@ -2205,15 +3254,32 @@ deactivate_net_auth_done_cb (NMAuthChain *chain, goto done; } + /* If it's a system connection, we're done */ active_path = nm_auth_chain_get_data (chain, "path"); - if (!nm_manager_deactivate_connection (self, - active_path, - NM_DEVICE_STATE_REASON_USER_REQUESTED, - &ret_error)) { - dbus_g_method_return_error (context, ret_error); - g_clear_error (&ret_error); - } else - dbus_g_method_return (context); + scope = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "scope")); + if (scope == NM_CONNECTION_SCOPE_USER) { + NMAuthChain *user_chain; + + /* It's a user connection, so we need to ensure the caller is + * authorized to manipulate user connections. + */ + user_chain = nm_auth_chain_new (priv->authority, context, NULL, deactivate_user_auth_done_cb, self); + g_assert (user_chain); + priv->auth_chains = g_slist_append (priv->auth_chains, user_chain); + + nm_auth_chain_set_data (user_chain, "path", g_strdup (active_path), g_free); + nm_auth_chain_set_data (user_chain, "scope", GUINT_TO_POINTER (scope), NULL); + nm_auth_chain_add_call (user_chain, NM_AUTH_PERMISSION_USE_USER_CONNECTIONS, TRUE); + } else { + if (!nm_manager_deactivate_connection (self, + active_path, + NM_DEVICE_STATE_REASON_USER_REQUESTED, + &ret_error)) { + dbus_g_method_return_error (context, ret_error); + g_clear_error (&ret_error); + } else + dbus_g_method_return (context); + } done: nm_auth_chain_unref (chain); @@ -2230,7 +3296,8 @@ impl_manager_deactivate_connection (NMManager *self, GSList *iter; NMAuthChain *chain; gulong sender_uid = G_MAXULONG; - char *error_desc = NULL; + NMConnectionScope scope; + const char *error_desc = NULL; /* Check for device connections first */ for (iter = priv->devices; iter; iter = g_slist_next (iter)) { @@ -2263,16 +3330,18 @@ impl_manager_deactivate_connection (NMManager *self, /* 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)) { + scope = nm_connection_get_scope (connection); + if (!check_user_authorized (priv->dbus_mgr, + priv->user_proxy, + context, + scope, + &sender_uid, + &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; } @@ -2296,6 +3365,7 @@ impl_manager_deactivate_connection (NMManager *self, 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_set_data (chain, "scope", GUINT_TO_POINTER (scope), NULL); nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE); } @@ -2319,7 +3389,7 @@ do_sleep_wake (NMManager *self) } else { nm_log_info (LOGD_SUSPEND, "waking up and re-enabling..."); - unmanaged_specs = nm_settings_get_unmanaged_specs (priv->settings); + unmanaged_specs = nm_sysconfig_settings_get_unmanaged_specs (priv->sys_settings); /* Ensure rfkill state is up-to-date since we don't respond to state * changes during sleep. @@ -2336,7 +3406,7 @@ do_sleep_wake (NMManager *self) */ for (i = 0; i < RFKILL_TYPE_MAX; i++) { RadioState *rstate = &priv->radio_states[i]; - gboolean enabled = radio_enabled_for_rstate (rstate); + gboolean enabled = radio_enabled_for_rstate (rstate, TRUE); RfKillType devtype = RFKILL_TYPE_UNKNOWN; if (rstate->desc) { @@ -2605,7 +3675,7 @@ impl_manager_enable (NMManager *self, NMAuthChain *chain; GError *error = NULL; gulong sender_uid = G_MAXULONG; - char *error_desc = NULL; + const char *error_desc = NULL; g_return_if_fail (NM_IS_MANAGER (self)); @@ -2626,7 +3696,6 @@ impl_manager_enable (NMManager *self, error_desc); dbus_g_method_return_error (context, error); g_error_free (error); - g_free (error_desc); return; } @@ -2651,8 +3720,60 @@ impl_manager_enable (NMManager *self, /* Permissions */ static void +user_proxy_permissions_changed_done (NMAuthChain *chain, + GError *error, + DBusGMethodInvocation *context, + gpointer user_data) +{ + NMManager *self = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + gboolean authorized = FALSE; + + priv->auth_chains = g_slist_remove (priv->auth_chains, chain); + + if (error) { + nm_log_warn (LOGD_USER_SET, "User connections unavailable: (%d) %s", + error->code, error->message ? error->message : "(unknown)"); + } else + authorized = user_settings_authorized (self, chain); + + if (authorized) { + /* User connections are authorized */ + if (!priv->user_proxy) + user_proxy_init (self); + } else + user_proxy_cleanup (self, TRUE); + + nm_auth_chain_unref (chain); +} + +static void pk_authority_changed_cb (GObject *object, gpointer user_data) { + NMManager *self = NM_MANAGER (user_data); + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + NMAuthChain *chain; + + /* If the user settings service wasn't previously authorized, we wouldn't + * care about it. But it might be authorized now, so lets check. + */ + if (!priv->user_proxy) + user_proxy_init (self); + else { + /* Otherwise the user settings permissions could have changed so we + * need to recheck them. + */ + chain = nm_auth_chain_new (priv->authority, + NULL, + priv->user_proxy, + user_proxy_permissions_changed_done, + self); + priv->auth_chains = g_slist_prepend (priv->auth_chains, chain); + + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_USE_USER_CONNECTIONS, FALSE); + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, FALSE); + } + /* Let clients know they should re-check their authorization */ g_signal_emit (NM_MANAGER (user_data), signals[CHECK_PERMISSIONS], 0); } @@ -2700,13 +3821,8 @@ get_permissions_done_cb (NMAuthChain *chain, get_perm_add_result (chain, results, NM_AUTH_PERMISSION_SLEEP_WAKE); get_perm_add_result (chain, results, NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI); get_perm_add_result (chain, results, NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN); - get_perm_add_result (chain, results, NM_AUTH_PERMISSION_ENABLE_DISABLE_WIMAX); + get_perm_add_result (chain, results, NM_AUTH_PERMISSION_USE_USER_CONNECTIONS); get_perm_add_result (chain, results, NM_AUTH_PERMISSION_NETWORK_CONTROL); - get_perm_add_result (chain, results, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED); - get_perm_add_result (chain, results, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN); - 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); } @@ -2732,20 +3848,31 @@ impl_manager_get_permissions (NMManager *self, 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); nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN, FALSE); - nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_WIMAX, FALSE); + nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_USE_USER_CONNECTIONS, FALSE); nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, FALSE); - nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED, FALSE); - nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN, FALSE); - nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM, FALSE); - nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN, FALSE); - nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME, FALSE); +} + +/* Legacy 0.6 compatibility interface */ + +static void +impl_manager_legacy_sleep (NMManager *manager, DBusGMethodInvocation *context) +{ + return impl_manager_sleep (manager, TRUE, context); +} + +static void +impl_manager_legacy_wake (NMManager *manager, DBusGMethodInvocation *context) +{ + return impl_manager_sleep (manager, FALSE, context); } static gboolean -impl_manager_get_state (NMManager *manager, guint32 *state, GError **error) +impl_manager_legacy_state (NMManager *manager, guint32 *state, GError **err) { + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + nm_manager_update_state (manager); - *state = NM_MANAGER_GET_PRIVATE (manager)->state; + *state = priv->state; return TRUE; } @@ -2767,6 +3894,115 @@ impl_manager_set_logging (NMManager *manager, return FALSE; } +/* Connections */ + +gboolean +nm_manager_auto_user_connections_allowed (NMManager *self) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + + return priv->user_net_perm == NM_AUTH_CALL_RESULT_YES + && priv->user_con_perm == NM_AUTH_CALL_RESULT_YES; +} + +static guint64 +get_connection_timestamp (NMConnection *connection) +{ + if (nm_connection_get_scope (connection) == NM_CONNECTION_SCOPE_SYSTEM) { + guint64 *ts_p; + + ts_p = (guint64 *) g_object_get_data (G_OBJECT (connection), NM_SYSCONFIG_SETTINGS_TIMESTAMP_TAG); + return ts_p != NULL ? *ts_p : 0; + } else { + NMSettingConnection *s_con; + + s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION); + g_assert (s_con); + return nm_setting_connection_get_timestamp (s_con); + } +} + +static int +connection_sort (gconstpointer pa, gconstpointer pb) +{ + NMConnection *a = NM_CONNECTION (pa); + NMSettingConnection *con_a; + NMConnection *b = NM_CONNECTION (pb); + NMSettingConnection *con_b; + guint64 ts_a, ts_b; + + con_a = (NMSettingConnection *) nm_connection_get_setting (a, NM_TYPE_SETTING_CONNECTION); + g_assert (con_a); + con_b = (NMSettingConnection *) nm_connection_get_setting (b, NM_TYPE_SETTING_CONNECTION); + g_assert (con_b); + + if (nm_setting_connection_get_autoconnect (con_a) != nm_setting_connection_get_autoconnect (con_b)) { + if (nm_setting_connection_get_autoconnect (con_a)) + return -1; + return 1; + } + + ts_a = get_connection_timestamp (a); + ts_b = get_connection_timestamp (b); + if (ts_a > ts_b) + return -1; + else if (ts_a == ts_b) + return 0; + + return 1; +} + +static void +connections_to_slist (gpointer key, gpointer value, gpointer user_data) +{ + GSList **list = (GSList **) user_data; + + *list = g_slist_insert_sorted (*list, g_object_ref (value), connection_sort); +} + +/* Returns a GSList of referenced NMConnection objects, caller must + * unref the connections in the list and destroy the list. + */ +GSList * +nm_manager_get_connections (NMManager *manager, + NMConnectionScope scope) +{ + NMManagerPrivate *priv; + GSList *list = NULL; + + g_return_val_if_fail (NM_IS_MANAGER (manager), NULL); + + priv = NM_MANAGER_GET_PRIVATE (manager); + if (scope == NM_CONNECTION_SCOPE_USER) + g_hash_table_foreach (priv->user_connections, connections_to_slist, &list); + else if (scope == NM_CONNECTION_SCOPE_SYSTEM) + g_hash_table_foreach (priv->system_connections, connections_to_slist, &list); + else + nm_log_err (LOGD_CORE, "unknown NMConnectionScope %d", scope); + return list; +} + +NMConnection * +nm_manager_get_connection_by_object_path (NMManager *manager, + NMConnectionScope scope, + const char *path) +{ + NMManagerPrivate *priv; + NMConnection *connection = NULL; + + g_return_val_if_fail (NM_IS_MANAGER (manager), NULL); + g_return_val_if_fail (path != NULL, NULL); + + priv = NM_MANAGER_GET_PRIVATE (manager); + if (scope == NM_CONNECTION_SCOPE_USER) + connection = (NMConnection *) g_hash_table_lookup (priv->user_connections, path); + else if (scope == NM_CONNECTION_SCOPE_SYSTEM) + connection = (NMConnection *) g_hash_table_lookup (priv->system_connections, path); + else + nm_log_err (LOGD_CORE, "unknown NMConnectionScope %d", scope); + return connection; +} + GPtrArray * nm_manager_get_active_connections_by_connection (NMManager *manager, NMConnection *connection) @@ -2784,6 +4020,7 @@ nm_manager_start (NMManager *self) for (i = 0; i < RFKILL_TYPE_MAX; i++) { RadioState *rstate = &priv->radio_states[i]; RfKillState udev_state; + gboolean enabled; if (!rstate->desc) continue; @@ -2797,15 +4034,23 @@ nm_manager_start (NMManager *self) (rstate->hw_enabled && rstate->sw_enabled) ? "enabled" : "disabled", rstate->user_enabled ? "enabled" : "disabled"); } - manager_update_radio_enabled (self, rstate); + enabled = radio_enabled_for_rstate (rstate, TRUE); + manager_update_radio_enabled (self, rstate, enabled); } /* Log overall networking status - enabled/disabled */ nm_log_info (LOGD_CORE, "Networking is %s by state file", priv->net_enabled ? "enabled" : "disabled"); - system_unmanaged_devices_changed_cb (priv->settings, NULL, self); - system_hostname_changed_cb (priv->settings, NULL, self); + system_unmanaged_devices_changed_cb (priv->sys_settings, NULL, self); + system_hostname_changed_cb (priv->sys_settings, NULL, self); + system_query_connections (self); + + /* Get user connections if the user settings service is around, otherwise + * they will be queried when the user settings service shows up on the + * bus in nm_manager_name_owner_changed(). + */ + user_proxy_init (self); nm_udev_manager_query_devices (priv->udev_mgr); bluez_manager_resync_devices (self); @@ -2974,9 +4219,6 @@ prop_filter (DBusConnection *connection, } else if (!strcmp (propname, "WwanEnabled")) { glib_propname = NM_MANAGER_WWAN_ENABLED; permission = NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN; - } else if (!strcmp (propname, "WimaxEnabled")) { - glib_propname = NM_MANAGER_WIMAX_ENABLED; - permission = NM_AUTH_PERMISSION_ENABLE_DISABLE_WIMAX; } else return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; @@ -3029,14 +4271,12 @@ out: } NMManager * -nm_manager_get (NMSettings *settings, - const char *config_file, +nm_manager_get (const char *config_file, const char *plugins, const char *state_file, gboolean initial_net_enabled, gboolean initial_wifi_enabled, gboolean initial_wwan_enabled, - gboolean initial_wimax_enabled, GError **error) { static NMManager *singleton = NULL; @@ -3047,8 +4287,6 @@ nm_manager_get (NMSettings *settings, if (singleton) return g_object_ref (singleton); - g_assert (settings); - singleton = (NMManager *) g_object_new (NM_TYPE_MANAGER, NULL); g_assert (singleton); @@ -3065,31 +4303,37 @@ nm_manager_get (NMSettings *settings, return NULL; } - priv->settings = g_object_ref (settings); + priv->sys_settings = nm_sysconfig_settings_new (config_file, plugins, bus, error); + if (!priv->sys_settings) { + g_object_unref (singleton); + return NULL; + } + nm_settings_service_export (NM_SETTINGS_SERVICE (priv->sys_settings)); priv->config_file = g_strdup (config_file); + 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_signal_connect (priv->sys_settings, "notify::" NM_SYSCONFIG_SETTINGS_UNMANAGED_SPECS, G_CALLBACK (system_unmanaged_devices_changed_cb), singleton); - g_signal_connect (priv->settings, "notify::" NM_SETTINGS_HOSTNAME, + g_signal_connect (priv->sys_settings, "notify::" NM_SETTINGS_SYSTEM_INTERFACE_HOSTNAME, G_CALLBACK (system_hostname_changed_cb), singleton); - g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_ADDED, - G_CALLBACK (connections_changed), singleton); - g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_UPDATED, - G_CALLBACK (connections_changed), singleton); - g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_REMOVED, - G_CALLBACK (connections_changed), singleton); - g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_VISIBILITY_CHANGED, - G_CALLBACK (connections_changed), singleton); + g_signal_connect (priv->sys_settings, "new-connection", + G_CALLBACK (system_new_connection_cb), singleton); - dbus_g_connection_register_g_object (bus, NM_DBUS_PATH, G_OBJECT (singleton)); + dbus_g_connection_register_g_object (nm_dbus_manager_get_connection (priv->dbus_mgr), + NM_DBUS_PATH, + G_OBJECT (singleton)); + + g_signal_connect (priv->dbus_mgr, + "name-owner-changed", + G_CALLBACK (nm_manager_name_owner_changed), + singleton); priv->udev_mgr = nm_udev_manager_new (); g_signal_connect (priv->udev_mgr, @@ -3125,6 +4369,7 @@ dispose (GObject *object) { NMManager *manager = NM_MANAGER (object); NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); + GSList *iter, *list; DBusGConnection *bus; DBusConnection *dbus_connection; @@ -3134,10 +4379,18 @@ dispose (GObject *object) } priv->disposed = TRUE; + for (iter = priv->pending_activations; iter; iter = g_slist_next (iter)) + pending_activation_destroy ((PendingActivation *) iter->data, NULL, NULL); + g_slist_free (priv->pending_activations); + priv->pending_activations = NULL; + g_slist_foreach (priv->auth_chains, (GFunc) nm_auth_chain_unref, NULL); g_slist_free (priv->auth_chains); g_object_unref (priv->authority); + while (g_slist_length (priv->secrets_calls)) + free_get_secrets_info ((GetSecretsInfo *) priv->secrets_calls->data); + while (g_slist_length (priv->devices)) { priv->devices = remove_one_device (manager, priv->devices, @@ -3145,11 +4398,37 @@ dispose (GObject *object) TRUE); } + user_proxy_cleanup (manager, FALSE); + g_hash_table_destroy (priv->user_connections); + priv->user_connections = NULL; + + g_hash_table_foreach (priv->system_connections, emit_removed, manager); + g_hash_table_remove_all (priv->system_connections); + g_hash_table_destroy (priv->system_connections); + priv->system_connections = NULL; + g_free (priv->hostname); g_free (priv->config_file); - g_object_unref (priv->settings); + if (priv->sys_settings) { + g_object_unref (priv->sys_settings); + priv->sys_settings = NULL; + } + /* Make sure we disconnect signal handlers from VPN connections that + * might still be alive when the manager dies. + */ + list = nm_vpn_manager_get_active_connections (priv->vpn_manager); + for (iter = list; iter; iter = g_slist_next (iter)) { + g_signal_handlers_disconnect_by_func (G_OBJECT (iter->data), + G_CALLBACK (provider_get_secrets), + manager); + g_signal_handlers_disconnect_by_func (G_OBJECT (iter->data), + G_CALLBACK (provider_cancel_secrets), + manager); + /* unref to balance returned objects from the VPN manager */ + g_object_unref (iter->data); + } if (priv->vpn_manager_id) { g_source_remove (priv->vpn_manager_id); priv->vpn_manager_id = 0; @@ -3232,11 +4511,20 @@ manager_radio_user_toggled (NMManager *self, } } - old_enabled = radio_enabled_for_rstate (rstate); + /* When the user toggles the radio, their request should override any + * daemon (like ModemManager) enabled state that can be changed. For WWAN + * for example, we want the WwanEnabled property to reflect the daemon state + * too so that users can toggle the modem powered, but we don't want that + * daemon state to affect whether or not the user *can* turn it on, which is + * what the kernel rfkill state does. So we ignore daemon enabled state + * when determining what the new state should be since it shouldn't block + * the user's request. + */ + old_enabled = radio_enabled_for_rstate (rstate, TRUE); rstate->user_enabled = enabled; - new_enabled = radio_enabled_for_rstate (rstate); + new_enabled = radio_enabled_for_rstate (rstate, FALSE); if (new_enabled != old_enabled) - manager_update_radio_enabled (self, rstate); + manager_update_radio_enabled (self, rstate, new_enabled); } static void @@ -3261,11 +4549,6 @@ set_property (GObject *object, guint prop_id, &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; @@ -3291,23 +4574,17 @@ get_property (GObject *object, guint prop_id, 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)); + 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)); + 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)); - break; - case PROP_WIMAX_HARDWARE_ENABLED: - g_value_set_boolean (value, priv->radio_states[RFKILL_TYPE_WIMAX].hw_enabled); - break; case PROP_ACTIVE_CONNECTIONS: g_value_take_boxed (value, get_active_connections (self, NULL)); break; @@ -3339,8 +4616,7 @@ periodic_update_active_connection_timestamps (gpointer user_data) req = nm_manager_get_act_request_by_path (manager, active_path, &device); if (device && nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED) - nm_settings_connection_update_timestamp (NM_SETTINGS_CONNECTION (nm_act_request_get_connection (req)), - (guint64) time (NULL)); + update_active_connection_timestamp (manager, device); } return TRUE; @@ -3371,13 +4647,13 @@ 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].other_enabled_func = nm_manager_get_modem_enabled_state; + 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; priv->radio_states[RFKILL_TYPE_WIMAX].key = "WiMAXEnabled"; - 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].prop = NULL; + priv->radio_states[RFKILL_TYPE_WIMAX].hw_prop = NULL; 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; @@ -3390,6 +4666,16 @@ nm_manager_init (NMManager *manager) priv->dbus_mgr = nm_dbus_manager_get (); + priv->user_connections = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + g_object_unref); + + priv->system_connections = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + g_object_unref); + priv->modem_manager = nm_modem_manager_get (); priv->modem_added_id = g_signal_connect (priv->modem_manager, "modem-added", G_CALLBACK (modem_added), manager); @@ -3487,6 +4773,8 @@ nm_manager_class_init (NMManagerClass *manager_class) g_type_class_add_private (manager_class, sizeof (NMManagerPrivate)); /* virtual methods */ + manager_class->connection_added = connection_added_default_handler; + object_class->set_property = set_property; object_class->get_property = get_property; object_class->dispose = dispose; @@ -3549,22 +4837,6 @@ nm_manager_class_init (NMManagerClass *manager_class) G_PARAM_READABLE)); g_object_class_install_property - (object_class, PROP_WIMAX_ENABLED, - g_param_spec_boolean (NM_MANAGER_WIMAX_ENABLED, - "WimaxEnabled", - "Is WiMAX enabled", - TRUE, - G_PARAM_READWRITE)); - - g_object_class_install_property - (object_class, PROP_WIMAX_HARDWARE_ENABLED, - g_param_spec_boolean (NM_MANAGER_WIMAX_HARDWARE_ENABLED, - "WimaxHardwareEnabled", - "Whether WiMAX is disabled by a hardware switch or not", - TRUE, - G_PARAM_READABLE)); - - g_object_class_install_property (object_class, PROP_ACTIVE_CONNECTIONS, g_param_spec_boxed (NM_MANAGER_ACTIVE_CONNECTIONS, "Active connections", @@ -3622,6 +4894,42 @@ nm_manager_class_init (NMManagerClass *manager_class) nm_properties_changed_signal_new (object_class, G_STRUCT_OFFSET (NMManagerClass, properties_changed)); + signals[CONNECTIONS_ADDED] = + g_signal_new ("connections-added", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMManagerClass, connections_added), + NULL, NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, 1, G_TYPE_UINT); + + signals[CONNECTION_ADDED] = + g_signal_new ("connection-added", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMManagerClass, connection_added), + NULL, NULL, + _nm_marshal_VOID__OBJECT_UINT, + G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_UINT); + + signals[CONNECTION_UPDATED] = + g_signal_new ("connection-updated", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMManagerClass, connection_updated), + NULL, NULL, + _nm_marshal_VOID__OBJECT_UINT, + G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_UINT); + + signals[CONNECTION_REMOVED] = + g_signal_new ("connection-removed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMManagerClass, connection_removed), + NULL, NULL, + _nm_marshal_VOID__OBJECT_UINT, + G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_UINT); + signals[CHECK_PERMISSIONS] = g_signal_new ("check-permissions", G_OBJECT_CLASS_TYPE (object_class), @@ -3638,6 +4946,15 @@ nm_manager_class_init (NMManagerClass *manager_class) g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + /* StateChange is DEPRECATED */ + signals[STATE_CHANGE] = + g_signal_new ("state-change", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, 1, G_TYPE_UINT); + dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (manager_class), &dbus_glib_nm_manager_object_info); |