diff options
| author | Michael Biebl <biebl@debian.org> | 2021-02-11 18:11:46 +0100 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2021-02-11 18:11:46 +0100 |
| commit | 80ec1decc49c72efec2a8b87c06245c92c0ab807 (patch) | |
| tree | e3b229aa94e8dcf0590f2317664176e7b8f7607b /src/nm-manager.c | |
| parent | 65f86e8f56267192d42f2b629fc6b0c99fb9cd0c (diff) | |
New upstream version 1.29.90 upstream/1.29.90
Diffstat (limited to 'src/nm-manager.c')
| -rw-r--r-- | src/nm-manager.c | 8652 |
1 files changed, 0 insertions, 8652 deletions
diff --git a/src/nm-manager.c b/src/nm-manager.c deleted file mode 100644 index 46f81c7c..00000000 --- a/src/nm-manager.c +++ /dev/null @@ -1,8652 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2007 - 2009 Novell, Inc. - * Copyright (C) 2007 - 2017 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nm-manager.h" - -#include <stdlib.h> -#include <fcntl.h> -#include <unistd.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/sendfile.h> -#include <limits.h> - -#include "nm-glib-aux/nm-c-list.h" - -#include "nm-libnm-core-intern/nm-common-macros.h" -#include "nm-dbus-manager.h" -#include "vpn/nm-vpn-manager.h" -#include "devices/nm-device.h" -#include "devices/nm-device-generic.h" -#include "platform/nm-platform.h" -#include "platform/nmp-object.h" -#include "nm-hostname-manager.h" -#include "nm-keep-alive.h" -#include "nm-rfkill-manager.h" -#include "dhcp/nm-dhcp-manager.h" -#include "settings/nm-settings.h" -#include "settings/nm-settings-connection.h" -#include "nm-auth-utils.h" -#include "nm-auth-manager.h" -#include "NetworkManagerUtils.h" -#include "devices/nm-device-factory.h" -#include "nm-sleep-monitor.h" -#include "nm-connectivity.h" -#include "nm-policy.h" -#include "nm-session-monitor.h" -#include "nm-act-request.h" -#include "nm-core-internal.h" -#include "nm-config.h" -#include "nm-audit-manager.h" -#include "nm-std-aux/nm-dbus-compat.h" -#include "nm-checkpoint.h" -#include "nm-checkpoint-manager.h" -#include "nm-dbus-object.h" -#include "nm-dispatcher.h" -#include "NetworkManagerUtils.h" - -#define DEVICE_STATE_PRUNE_RATELIMIT_MAX 100u - -/*****************************************************************************/ - -typedef struct { - gboolean user_enabled; - gboolean sw_enabled; - gboolean hw_enabled; - RfKillType rtype; - NMConfigRunStatePropertyType key; - const char * desc; - const char * prop; - const char * hw_prop; -} RadioState; - -typedef enum { - ASYNC_OP_TYPE_AC_AUTH_ACTIVATE_INTERNAL, - ASYNC_OP_TYPE_AC_AUTH_ACTIVATE_USER, - ASYNC_OP_TYPE_AC_AUTH_ADD_AND_ACTIVATE, - ASYNC_OP_TYPE_AC_AUTH_ADD_AND_ACTIVATE2, -} AsyncOpType; - -typedef struct { - CList async_op_lst; - NMManager * self; - AsyncOpType async_op_type; - union { - struct { - NMActiveConnection *active; - union { - struct { - GDBusMethodInvocation *invocation; - } activate_user; - struct { - GDBusMethodInvocation * invocation; - NMConnection * connection; - NMSettingsConnectionPersistMode persist_mode; - bool is_volatile : 1; - } add_and_activate; - }; - } ac_auth; - }; -} AsyncOpData; - -enum { - DEVICE_ADDED, - INTERNAL_DEVICE_ADDED, - DEVICE_REMOVED, - INTERNAL_DEVICE_REMOVED, - ACTIVE_CONNECTION_ADDED, - ACTIVE_CONNECTION_REMOVED, - CONFIGURE_QUIT, - - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL] = {0}; - -NM_GOBJECT_PROPERTIES_DEFINE(NMManager, - PROP_VERSION, - PROP_CAPABILITIES, - PROP_STATE, - PROP_STARTUP, - PROP_NETWORKING_ENABLED, - PROP_WIRELESS_ENABLED, - PROP_WIRELESS_HARDWARE_ENABLED, - PROP_WWAN_ENABLED, - PROP_WWAN_HARDWARE_ENABLED, - PROP_WIMAX_ENABLED, - PROP_WIMAX_HARDWARE_ENABLED, - PROP_ACTIVE_CONNECTIONS, - PROP_CONNECTIVITY, - PROP_CONNECTIVITY_CHECK_AVAILABLE, - PROP_CONNECTIVITY_CHECK_ENABLED, - PROP_CONNECTIVITY_CHECK_URI, - PROP_PRIMARY_CONNECTION, - PROP_PRIMARY_CONNECTION_TYPE, - PROP_ACTIVATING_CONNECTION, - PROP_DEVICES, - PROP_METERED, - PROP_GLOBAL_DNS_CONFIGURATION, - PROP_ALL_DEVICES, - PROP_CHECKPOINTS, - - /* Not exported */ - PROP_SLEEPING, ); - -typedef struct { - NMPlatform *platform; - - GArray *capabilities; - - CList active_connections_lst_head; - CList async_op_lst_head; - guint ac_cleanup_id; - NMActiveConnection *primary_connection; - NMActiveConnection *activating_connection; - NMMetered metered; - - CList devices_lst_head; - - NMState state; - NMConfig * config; - NMConnectivity * concheck_mgr; - NMPolicy * policy; - NMHostnameManager *hostname_manager; - - struct { - GDBusConnection *connection; - guint id; - } prop_filter; - NMRfkillManager *rfkill_mgr; - - CList link_cb_lst; - - NMCheckpointManager *checkpoint_mgr; - - NMSettings *settings; - - RadioState radio_states[RFKILL_TYPE_MAX]; - NMVpnManager *vpn_manager; - - NMSleepMonitor *sleep_monitor; - - NMAuthManager *auth_mgr; - - GHashTable *device_route_metrics; - - CList auth_lst_head; - - GHashTable *sleep_devices; - - /* Firmware dir monitor */ - GFileMonitor *fw_monitor; - guint fw_changed_id; - - guint timestamp_update_id; - - guint devices_inited_id; - - NMConnectivityState connectivity_state; - - guint8 device_state_prune_ratelimit_count; - - bool startup : 1; - bool devices_inited : 1; - - bool sleeping : 1; - bool net_enabled : 1; - - unsigned connectivity_check_enabled_last : 2; - - guint delete_volatile_connection_idle_id; - CList delete_volatile_connection_lst_head; -} NMManagerPrivate; - -struct _NMManager { - NMDBusObject parent; - NMManagerPrivate _priv; -}; - -typedef struct { - NMDBusObjectClass parent; -} NMManagerClass; - -G_DEFINE_TYPE(NMManager, nm_manager, NM_TYPE_DBUS_OBJECT) - -#define NM_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMManager, NM_IS_MANAGER) - -/*****************************************************************************/ - -NM_DEFINE_SINGLETON_INSTANCE(NMManager); - -/*****************************************************************************/ - -#define _NMLOG_PREFIX_NAME "manager" -#define _NMLOG(level, domain, ...) \ - G_STMT_START \ - { \ - const NMLogLevel _level = (level); \ - const NMLogDomain _domain = (domain); \ - \ - if (nm_logging_enabled(_level, _domain)) { \ - const NMManager *const _self = (self); \ - char _sbuf[32]; \ - \ - _nm_log(_level, \ - _domain, \ - 0, \ - NULL, \ - NULL, \ - "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - _NMLOG_PREFIX_NAME, \ - ((_self && _self != singleton_instance) ? nm_sprintf_buf(_sbuf, "[%p]", _self) \ - : "") \ - _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ - } \ - G_STMT_END - -#define _NMLOG2(level, domain, device, ...) \ - G_STMT_START \ - { \ - const NMLogLevel _level = (level); \ - const NMLogDomain _domain = (domain); \ - \ - if (nm_logging_enabled(_level, _domain)) { \ - const NMManager *const _self = (self); \ - const char *const _ifname = _nm_device_get_iface(device); \ - char _sbuf[32]; \ - \ - _nm_log(_level, \ - _domain, \ - 0, \ - _ifname, \ - NULL, \ - "%s%s: %s%s%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - _NMLOG_PREFIX_NAME, \ - ((_self && _self != singleton_instance) ? nm_sprintf_buf(_sbuf, "[%p]", _self) \ - : ""), \ - NM_PRINT_FMT_QUOTED(_ifname, "(", _ifname, "): ", "") \ - _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ - } \ - G_STMT_END - -#define _NMLOG3(level, domain, connection, ...) \ - G_STMT_START \ - { \ - const NMLogLevel _level = (level); \ - const NMLogDomain _domain = (domain); \ - \ - if (nm_logging_enabled(_level, _domain)) { \ - const NMManager *const _self = (self); \ - NMConnection *const _connection = (connection); \ - const char *const _con_id = _nm_connection_get_id(_connection); \ - char _sbuf[32]; \ - \ - _nm_log(_level, \ - _domain, \ - 0, \ - NULL, \ - _nm_connection_get_uuid(_connection), \ - "%s%s: %s%s%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - _NMLOG_PREFIX_NAME, \ - ((_self && _self != singleton_instance) ? nm_sprintf_buf(_sbuf, "[%p]", _self) \ - : ""), \ - NM_PRINT_FMT_QUOTED(_con_id, "(", _con_id, ") ", "") \ - _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ - } \ - G_STMT_END - -/*****************************************************************************/ - -static const NMDBusInterfaceInfoExtended interface_info_manager; -static const GDBusSignalInfo signal_info_check_permissions; -static const GDBusSignalInfo signal_info_state_changed; -static const GDBusSignalInfo signal_info_device_added; -static const GDBusSignalInfo signal_info_device_removed; - -static void update_connectivity_value(NMManager *self); - -static gboolean add_device(NMManager *self, NMDevice *device, GError **error); - -static void _emit_device_added_removed(NMManager *self, NMDevice *device, gboolean is_added); - -static NMActiveConnection *_new_active_connection(NMManager * self, - gboolean is_vpn, - NMSettingsConnection * sett_conn, - NMConnection * incompl_conn, - NMConnection * applied, - const char * specific_object, - NMDevice * device, - NMAuthSubject * subject, - NMActivationType activation_type, - NMActivationReason activation_reason, - NMActivationStateFlags initial_state_flags, - GError ** error); - -static void policy_activating_ac_changed(GObject *object, GParamSpec *pspec, gpointer user_data); - -static gboolean find_master(NMManager * self, - NMConnection * connection, - NMDevice * device, - NMSettingsConnection **out_master_connection, - NMDevice ** out_master_device, - NMActiveConnection ** out_master_ac, - GError ** error); - -static void nm_manager_update_state(NMManager *manager); - -static void connection_changed(NMManager *self, NMSettingsConnection *sett_conn); -static void device_sleep_cb(NMDevice *device, GParamSpec *pspec, NMManager *self); - -static void -settings_startup_complete_changed(NMSettings *settings, GParamSpec *pspec, NMManager *self); - -static void retry_connections_for_parent_device(NMManager *self, NMDevice *device); - -static void -active_connection_state_changed(NMActiveConnection *active, GParamSpec *pspec, NMManager *self); -static void -active_connection_default_changed(NMActiveConnection *active, GParamSpec *pspec, NMManager *self); -static void active_connection_parent_active(NMActiveConnection *active, - NMActiveConnection *parent_ac, - NMManager * self); - -static NMActiveConnection *active_connection_find(NMManager * self, - NMSettingsConnection * sett_conn, - const char * uuid, - NMActiveConnectionState max_state, - GPtrArray ** out_all_matching); - -static NMConnectivity *concheck_get_mgr(NMManager *self); - -static void _internal_activation_auth_done(NMManager * self, - NMActiveConnection *active, - gboolean success, - const char * error_desc); -static void _add_and_activate_auth_done(NMManager * self, - AsyncOpType async_op_type, - NMActiveConnection * active, - NMConnection * connection, - GDBusMethodInvocation * invocation, - NMSettingsConnectionPersistMode persist_mode, - gboolean is_volatile, - gboolean success, - const char * error_desc); -static void _activation_auth_done(NMManager * self, - NMActiveConnection * active, - GDBusMethodInvocation *invocation, - gboolean success, - const char * error_desc); - -/*****************************************************************************/ - -static NM_CACHED_QUARK_FCN("autoconnect-root", autoconnect_root_quark); - -/*****************************************************************************/ - -static gboolean -_connection_is_vpn(NMConnection *connection) -{ - const char *type; - - type = nm_connection_get_connection_type(connection); - if (type) - return nm_streq(type, NM_SETTING_VPN_SETTING_NAME); - - /* we have an incomplete (invalid) connection at hand. That can only - * happen during AddAndActivate. Determine whether it's VPN type based - * on the existence of a [vpn] section. */ - return !!nm_connection_get_setting_vpn(connection); -} - -/*****************************************************************************/ - -static gboolean -concheck_enabled(NMManager *self, gboolean *out_changed) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - guint check_enabled; - - check_enabled = nm_connectivity_check_enabled(concheck_get_mgr(self)) ? 1 : 2; - if (priv->connectivity_check_enabled_last == check_enabled) - NM_SET_OUT(out_changed, FALSE); - else { - NM_SET_OUT(out_changed, TRUE); - priv->connectivity_check_enabled_last = check_enabled; - } - return check_enabled == 1; -} - -static void -concheck_config_changed_cb(NMConnectivity *connectivity, NMManager *self) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - NMDevice * device; - gboolean changed; - - concheck_enabled(self, &changed); - if (changed) - _notify(self, PROP_CONNECTIVITY_CHECK_ENABLED); - - c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) - nm_device_check_connectivity_update_interval(device); -} - -static NMConnectivity * -concheck_get_mgr(NMManager *self) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - - if (G_UNLIKELY(!priv->concheck_mgr)) { - priv->concheck_mgr = g_object_ref(nm_connectivity_get()); - g_signal_connect(priv->concheck_mgr, - NM_CONNECTIVITY_CONFIG_CHANGED, - G_CALLBACK(concheck_config_changed_cb), - self); - } - return priv->concheck_mgr; -} - -/*****************************************************************************/ - -static AsyncOpData * -_async_op_data_new_authorize_activate_internal(NMManager *self, NMActiveConnection *active_take) -{ - AsyncOpData *async_op_data; - - async_op_data = g_slice_new0(AsyncOpData); - async_op_data->async_op_type = ASYNC_OP_TYPE_AC_AUTH_ACTIVATE_INTERNAL; - async_op_data->self = g_object_ref(self); - async_op_data->ac_auth.active = active_take; - c_list_link_tail(&NM_MANAGER_GET_PRIVATE(self)->async_op_lst_head, - &async_op_data->async_op_lst); - return async_op_data; -} - -static AsyncOpData * -_async_op_data_new_ac_auth_activate_user(NMManager * self, - NMActiveConnection * active_take, - GDBusMethodInvocation *invocation_take) -{ - AsyncOpData *async_op_data; - - async_op_data = g_slice_new0(AsyncOpData); - async_op_data->async_op_type = ASYNC_OP_TYPE_AC_AUTH_ACTIVATE_USER; - async_op_data->self = g_object_ref(self); - async_op_data->ac_auth.active = active_take; - async_op_data->ac_auth.activate_user.invocation = invocation_take; - c_list_link_tail(&NM_MANAGER_GET_PRIVATE(self)->async_op_lst_head, - &async_op_data->async_op_lst); - return async_op_data; -} - -static AsyncOpData * -_async_op_data_new_ac_auth_add_and_activate(NMManager * self, - AsyncOpType async_op_type, - NMActiveConnection * active_take, - GDBusMethodInvocation * invocation_take, - NMConnection * connection_take, - NMSettingsConnectionPersistMode persist_mode, - gboolean is_volatile) -{ - AsyncOpData *async_op_data; - - nm_assert(NM_IN_SET(async_op_type, - ASYNC_OP_TYPE_AC_AUTH_ADD_AND_ACTIVATE, - ASYNC_OP_TYPE_AC_AUTH_ADD_AND_ACTIVATE2)); - - async_op_data = g_slice_new0(AsyncOpData); - async_op_data->async_op_type = async_op_type; - async_op_data->self = g_object_ref(self); - async_op_data->ac_auth.active = active_take; - async_op_data->ac_auth.add_and_activate.invocation = invocation_take; - async_op_data->ac_auth.add_and_activate.connection = connection_take; - async_op_data->ac_auth.add_and_activate.persist_mode = persist_mode; - async_op_data->ac_auth.add_and_activate.is_volatile = is_volatile; - c_list_link_tail(&NM_MANAGER_GET_PRIVATE(self)->async_op_lst_head, - &async_op_data->async_op_lst); - return async_op_data; -} - -static void -_async_op_complete_ac_auth_cb(NMActiveConnection *active, - gboolean success, - const char * error_desc, - gpointer user_data) -{ - AsyncOpData *async_op_data = user_data; - - nm_assert(async_op_data); - nm_assert(NM_IS_MANAGER(async_op_data->self)); - nm_assert( - nm_c_list_contains_entry(&NM_MANAGER_GET_PRIVATE(async_op_data->self)->async_op_lst_head, - async_op_data, - async_op_lst)); - nm_assert(NM_IS_ACTIVE_CONNECTION(active)); - nm_assert(active == async_op_data->ac_auth.active); - - c_list_unlink(&async_op_data->async_op_lst); - - switch (async_op_data->async_op_type) { - case ASYNC_OP_TYPE_AC_AUTH_ACTIVATE_INTERNAL: - _internal_activation_auth_done(async_op_data->self, - async_op_data->ac_auth.active, - success, - error_desc); - break; - case ASYNC_OP_TYPE_AC_AUTH_ACTIVATE_USER: - _activation_auth_done(async_op_data->self, - async_op_data->ac_auth.active, - async_op_data->ac_auth.activate_user.invocation, - success, - error_desc); - break; - case ASYNC_OP_TYPE_AC_AUTH_ADD_AND_ACTIVATE: - case ASYNC_OP_TYPE_AC_AUTH_ADD_AND_ACTIVATE2: - _add_and_activate_auth_done(async_op_data->self, - async_op_data->async_op_type, - async_op_data->ac_auth.active, - async_op_data->ac_auth.add_and_activate.connection, - async_op_data->ac_auth.add_and_activate.invocation, - async_op_data->ac_auth.add_and_activate.persist_mode, - async_op_data->ac_auth.add_and_activate.is_volatile, - success, - error_desc); - g_object_unref(async_op_data->ac_auth.add_and_activate.connection); - break; - default: - nm_assert_not_reached(); - break; - } - - g_object_unref(async_op_data->ac_auth.active); - g_object_unref(async_op_data->self); - g_slice_free(AsyncOpData, async_op_data); -} - -/*****************************************************************************/ - -typedef struct { - int ifindex; - guint32 aspired_metric; - guint32 effective_metric; -} DeviceRouteMetricData; - -static DeviceRouteMetricData * -_device_route_metric_data_new(int ifindex, guint32 aspired_metric, guint32 effective_metric) -{ - DeviceRouteMetricData *data; - - nm_assert(ifindex > 0); - - /* For IPv4, metrics can use the entire uint32 bit range. For IPv6, - * zero is treated like 1024. Since we handle IPv4 and IPv6 identically, - * we cannot allow a zero metric here. - */ - nm_assert(aspired_metric > 0); - nm_assert(effective_metric == 0 || aspired_metric <= effective_metric); - - data = g_slice_new0(DeviceRouteMetricData); - data->ifindex = ifindex; - data->aspired_metric = aspired_metric; - data->effective_metric = effective_metric ?: aspired_metric; - return data; -} - -static guint -_device_route_metric_data_by_ifindex_hash(gconstpointer p) -{ - const DeviceRouteMetricData *data = p; - NMHashState h; - - nm_hash_init(&h, 1030338191); - nm_hash_update_vals(&h, data->ifindex); - return nm_hash_complete(&h); -} - -static gboolean -_device_route_metric_data_by_ifindex_equal(gconstpointer pa, gconstpointer pb) -{ - const DeviceRouteMetricData *a = pa; - const DeviceRouteMetricData *b = pb; - - return a->ifindex == b->ifindex; -} - -static guint32 -_device_route_metric_get(NMManager * self, - int ifindex, - NMDeviceType device_type, - gboolean lookup_only, - guint32 * out_aspired_metric) -{ - NMManagerPrivate * priv; - const DeviceRouteMetricData *d2; - DeviceRouteMetricData * data; - DeviceRouteMetricData data_lookup; - const NMDedupMultiHeadEntry *all_links_head; - NMPObject links_needle; - guint n_links; - gboolean cleaned = FALSE; - GHashTableIter h_iter; - guint32 metric; - - g_return_val_if_fail(NM_IS_MANAGER(self), 0); - - NM_SET_OUT(out_aspired_metric, 0); - - if (ifindex <= 0) { - if (lookup_only) - return 0; - metric = nm_device_get_route_metric_default(device_type); - NM_SET_OUT(out_aspired_metric, metric); - return metric; - } - - priv = NM_MANAGER_GET_PRIVATE(self); - - if (lookup_only && !priv->device_route_metrics) - return 0; - - if (G_UNLIKELY(!priv->device_route_metrics)) { - const GHashTable * h; - const NMConfigDeviceStateData *device_state; - - priv->device_route_metrics = - g_hash_table_new_full(_device_route_metric_data_by_ifindex_hash, - _device_route_metric_data_by_ifindex_equal, - NULL, - nm_g_slice_free_fcn(DeviceRouteMetricData)); - cleaned = TRUE; - - /* we need to pre-populate the cache for all (still existing) devices from the state-file */ - h = nm_config_device_state_get_all(priv->config); - if (!h) - goto initited; - - g_hash_table_iter_init(&h_iter, (GHashTable *) h); - while (g_hash_table_iter_next(&h_iter, NULL, (gpointer *) &device_state)) { - if (!device_state->route_metric_default_effective) - continue; - if (!nm_platform_link_get(priv->platform, device_state->ifindex)) { - /* we have the entry in the state file, but (currently) no such - * ifindex exists in platform. Most likely the entry is obsolete, - * hence we skip it. */ - continue; - } - if (!g_hash_table_add( - priv->device_route_metrics, - _device_route_metric_data_new(device_state->ifindex, - device_state->route_metric_default_aspired, - device_state->route_metric_default_effective))) - nm_assert_not_reached(); - } - } - -initited: - data_lookup.ifindex = ifindex; - - data = g_hash_table_lookup(priv->device_route_metrics, &data_lookup); - if (data) - goto out; - if (lookup_only) - return 0; - - if (!cleaned) { - /* get the number of all links in the platform cache. */ - all_links_head = nm_platform_lookup_all(priv->platform, - NMP_CACHE_ID_TYPE_OBJECT_TYPE, - nmp_object_stackinit_id_link(&links_needle, 1)); - n_links = all_links_head ? all_links_head->len : 0; - - /* on systems where a lot of devices are created and go away, the index contains - * a lot of stale entries. We must from time to time clean them up. - * - * Do do this cleanup, whenever we have more entries then 2 times the number of links. */ - if (G_UNLIKELY(g_hash_table_size(priv->device_route_metrics) > NM_MAX(20, n_links * 2))) { - /* from time to time, we need to do some house-keeping and prune stale entries. - * Otherwise, on a system where interfaces frequently come and go (docker), we - * keep growing this cache for ifindexes that no longer exist. */ - g_hash_table_iter_init(&h_iter, priv->device_route_metrics); - while (g_hash_table_iter_next(&h_iter, NULL, (gpointer *) &d2)) { - if (!nm_platform_link_get(priv->platform, d2->ifindex)) - g_hash_table_iter_remove(&h_iter); - } - cleaned = TRUE; - } - } - - data = - _device_route_metric_data_new(ifindex, nm_device_get_route_metric_default(device_type), 0); - - /* unfortunately, there is no stright forward way to lookup all reserved metrics. - * Note, that we don't only have to know which metrics are currently reserved, - * but also, which metrics are now seemingly un-used but caused another reserved - * metric to be bumped. Hence, the naive O(n^2) search :( - * - * Well, technically, since we limit bumping the metric to 50, this entire - * loop runs at most 50 times, so it's still O(n). Let's just say, it's not - * very efficient. */ -again: - g_hash_table_iter_init(&h_iter, priv->device_route_metrics); - while (g_hash_table_iter_next(&h_iter, NULL, (gpointer *) &d2)) { - if (data->effective_metric < d2->aspired_metric - || data->effective_metric > d2->effective_metric) { - /* no overlap. Skip. */ - continue; - } - if (!cleaned && !nm_platform_link_get(priv->platform, d2->ifindex)) { - /* the metric seems taken, but there is no such interface. This entry - * is stale, forget about it. */ - g_hash_table_iter_remove(&h_iter); - continue; - } - - if (d2->effective_metric == G_MAXUINT32) { - /* we cannot bump the metric any further. Done. - * - * Actually, this can currently not happen because the aspired_metric - * are small numbers and we limit the bumping to 50. Still, for - * completeness... */ - data->effective_metric = G_MAXUINT32; - break; - } - - if (d2->effective_metric - data->aspired_metric >= 50) { - /* as one active interface reserves an entire range of metrics - * (from aspired_metric to effective_metric), that means if you - * alternatingly activate two interfaces, their metric will - * bump each other. - * - * Limit this, bump the metric at most 50 points. */ - data->effective_metric = data->aspired_metric + 50; - break; - } - - /* bump the metric, and search again. */ - data->effective_metric = d2->effective_metric + 1; - goto again; - } - - _LOGT(LOGD_DEVICE, - "default-route-metric: ifindex %d reserves metric %u (aspired %u)", - data->ifindex, - data->effective_metric, - data->aspired_metric); - - if (!g_hash_table_add(priv->device_route_metrics, data)) - nm_assert_not_reached(); - -out: - NM_SET_OUT(out_aspired_metric, data->aspired_metric); - return data->effective_metric; -} - -guint32 -nm_manager_device_route_metric_reserve(NMManager *self, int ifindex, NMDeviceType device_type) -{ - guint32 metric; - - metric = _device_route_metric_get(self, ifindex, device_type, FALSE, NULL); - nm_assert(metric != 0); - return metric; -} - -void -nm_manager_device_route_metric_clear(NMManager *self, int ifindex) -{ - NMManagerPrivate * priv; - DeviceRouteMetricData data_lookup; - - priv = NM_MANAGER_GET_PRIVATE(self); - - if (!priv->device_route_metrics) - return; - data_lookup.ifindex = ifindex; - if (g_hash_table_remove(priv->device_route_metrics, &data_lookup)) { - _LOGT(LOGD_DEVICE, "default-route-metric: ifindex %d released", ifindex); - } -} - -/*****************************************************************************/ - -static void -_delete_volatile_connection_do(NMManager *self, NMSettingsConnection *connection) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - - if (!NM_FLAGS_ANY(nm_settings_connection_get_flags(connection), - NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE - | NM_SETTINGS_CONNECTION_INT_FLAGS_EXTERNAL)) - return; - if (!nm_settings_has_connection(priv->settings, connection)) - return; - if (active_connection_find(self, - connection, - NULL, - NM_ACTIVE_CONNECTION_STATE_DEACTIVATED, - NULL)) - return; - - _LOGD(LOGD_DEVICE, - "volatile connection disconnected. Deleting connection '%s' (%s)", - nm_settings_connection_get_id(connection), - nm_settings_connection_get_uuid(connection)); - nm_settings_connection_delete(connection, FALSE); -} - -/* Returns: whether to notify D-Bus of the removal or not */ -static gboolean -active_connection_remove(NMManager *self, NMActiveConnection *active) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - gs_unref_object NMSettingsConnection *connection = NULL; - gboolean notify; - - nm_assert(NM_IS_ACTIVE_CONNECTION(active)); - nm_assert(c_list_contains(&priv->active_connections_lst_head, &active->active_connections_lst)); - - notify = nm_dbus_object_is_exported(NM_DBUS_OBJECT(active)); - - c_list_unlink(&active->active_connections_lst); - g_signal_emit(self, signals[ACTIVE_CONNECTION_REMOVED], 0, active); - g_signal_handlers_disconnect_by_func(active, active_connection_state_changed, self); - g_signal_handlers_disconnect_by_func(active, active_connection_default_changed, self); - g_signal_handlers_disconnect_by_func(active, active_connection_parent_active, self); - - connection = nm_g_object_ref(nm_active_connection_get_settings_connection(active)); - - nm_dbus_object_clear_and_unexport(&active); - - if (connection) - _delete_volatile_connection_do(self, connection); - - return notify; -} - -static gboolean -_active_connection_cleanup(gpointer user_data) -{ - NMManager * self = NM_MANAGER(user_data); - NMManagerPrivate * priv = NM_MANAGER_GET_PRIVATE(self); - NMActiveConnection *ac, *ac_safe; - - priv->ac_cleanup_id = 0; - - g_object_freeze_notify(G_OBJECT(self)); - c_list_for_each_entry_safe (ac, - ac_safe, - &priv->active_connections_lst_head, - active_connections_lst) { - if (nm_active_connection_get_state(ac) == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED) { - if (active_connection_remove(self, ac)) - _notify(self, PROP_ACTIVE_CONNECTIONS); - } - } - g_object_thaw_notify(G_OBJECT(self)); - - return FALSE; -} - -static void -active_connection_state_changed(NMActiveConnection *active, GParamSpec *pspec, NMManager *self) -{ - NMManagerPrivate * priv = NM_MANAGER_GET_PRIVATE(self); - NMActiveConnectionState state; - NMSettingsConnection * con; - - state = nm_active_connection_get_state(active); - if (state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED) { - /* Destroy active connections from an idle handler to ensure that - * their last property change notifications go out, which wouldn't - * happen if we destroyed them immediately when their state was set - * to DEACTIVATED. - */ - if (!priv->ac_cleanup_id) - priv->ac_cleanup_id = g_idle_add(_active_connection_cleanup, self); - - con = nm_active_connection_get_settings_connection(active); - if (con) - g_object_set_qdata(G_OBJECT(con), autoconnect_root_quark(), NULL); - } - - nm_manager_update_state(self); -} - -static void -active_connection_default_changed(NMActiveConnection *active, GParamSpec *pspec, NMManager *self) -{ - nm_manager_update_state(self); -} - -/** - * active_connection_add(): - * @self: the #NMManager - * @active: the #NMActiveConnection to manage - * - * Begins to track and manage @active. Increases the refcount of @active. - */ -static void -active_connection_add(NMManager *self, NMActiveConnection *active) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - - nm_assert(NM_IS_ACTIVE_CONNECTION(active)); - nm_assert(!c_list_is_linked(&active->active_connections_lst)); - - c_list_link_front(&priv->active_connections_lst_head, &active->active_connections_lst); - g_object_ref(active); - - g_signal_connect(active, - "notify::" NM_ACTIVE_CONNECTION_STATE, - G_CALLBACK(active_connection_state_changed), - self); - g_signal_connect(active, - "notify::" NM_ACTIVE_CONNECTION_DEFAULT, - G_CALLBACK(active_connection_default_changed), - self); - g_signal_connect(active, - "notify::" NM_ACTIVE_CONNECTION_DEFAULT6, - G_CALLBACK(active_connection_default_changed), - self); - - if (!nm_dbus_object_is_exported(NM_DBUS_OBJECT(active))) - nm_dbus_object_export(NM_DBUS_OBJECT(active)); - - g_signal_emit(self, signals[ACTIVE_CONNECTION_ADDED], 0, active); - - _notify(self, PROP_ACTIVE_CONNECTIONS); -} - -const CList * -nm_manager_get_active_connections(NMManager *manager) -{ - return &NM_MANAGER_GET_PRIVATE(manager)->active_connections_lst_head; -} - -static NMActiveConnection * -active_connection_find( - NMManager * self, - NMSettingsConnection * sett_conn, - const char * uuid, - NMActiveConnectionState max_state /* candidates in state @max_state will be found */, - GPtrArray ** out_all_matching) -{ - NMManagerPrivate * priv = NM_MANAGER_GET_PRIVATE(self); - NMActiveConnection *ac; - NMActiveConnection *best_ac = NULL; - GPtrArray * all = NULL; - - nm_assert(!sett_conn || NM_IS_SETTINGS_CONNECTION(sett_conn)); - nm_assert(!out_all_matching || !*out_all_matching); - - c_list_for_each_entry (ac, &priv->active_connections_lst_head, active_connections_lst) { - NMSettingsConnection *ac_conn; - - ac_conn = nm_active_connection_get_settings_connection(ac); - if (sett_conn && sett_conn != ac_conn) - continue; - if (uuid && !nm_streq0(uuid, nm_settings_connection_get_uuid(ac_conn))) - continue; - if (nm_active_connection_get_state(ac) > max_state) - continue; - - if (!out_all_matching) - return ac; - - if (!best_ac) { - best_ac = ac; - continue; - } - - if (!all) { - all = g_ptr_array_new_with_free_func(g_object_unref); - g_ptr_array_add(all, g_object_ref(best_ac)); - } - g_ptr_array_add(all, g_object_ref(ac)); - } - - if (!best_ac) - return NULL; - - /* as an optimization, we only allocate out_all_matching, if there are more - * than one result. If there is only one result, we only return the single - * element and don't bother allocating an array. That's the common case. - * - * Also, in case we have multiple results, we return the *first* one - * as @best_ac. */ - nm_assert(!all || (all->len >= 2 && all->pdata[0] == best_ac)); - - *out_all_matching = all; - return best_ac; -} - -static NMActiveConnection * -active_connection_find_by_connection(NMManager * self, - NMSettingsConnection * sett_conn, - NMConnection * connection, - NMActiveConnectionState max_state, - GPtrArray ** out_all_matching) -{ - nm_assert(NM_IS_MANAGER(self)); - nm_assert(!sett_conn || NM_IS_SETTINGS_CONNECTION(sett_conn)); - nm_assert(!connection || NM_IS_CONNECTION(connection)); - nm_assert(sett_conn || connection); - nm_assert(!connection || !sett_conn - || connection == nm_settings_connection_get_connection(sett_conn)); - - /* Depending on whether connection is a settings connection, - * either lookup by object-identity of @connection, or compare the UUID */ - return active_connection_find(self, - sett_conn, - sett_conn ? NULL : nm_connection_get_uuid(connection), - max_state, - out_all_matching); -} - -typedef struct { - NMManager *self; - gboolean for_auto_activation; -} GetActivatableConnectionsFilterData; - -static gboolean -_get_activatable_connections_filter(NMSettings * settings, - NMSettingsConnection *sett_conn, - gpointer user_data) -{ - const GetActivatableConnectionsFilterData *d = user_data; - NMConnectionMultiConnect multi_connect; - - if (NM_FLAGS_ANY(nm_settings_connection_get_flags(sett_conn), - NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE - | NM_SETTINGS_CONNECTION_INT_FLAGS_EXTERNAL)) - return FALSE; - - multi_connect = - _nm_connection_get_multi_connect(nm_settings_connection_get_connection(sett_conn)); - if (multi_connect == NM_CONNECTION_MULTI_CONNECT_MULTIPLE - || (multi_connect == NM_CONNECTION_MULTI_CONNECT_MANUAL_MULTIPLE - && !d->for_auto_activation)) - return TRUE; - - /* the connection is activatable, if it has no active-connections that are in state - * activated, activating, or waiting to be activated. */ - return !active_connection_find(d->self, - sett_conn, - NULL, - NM_ACTIVE_CONNECTION_STATE_ACTIVATED, - NULL); -} - -NMSettingsConnection ** -nm_manager_get_activatable_connections(NMManager *manager, - gboolean for_auto_activation, - gboolean sort, - guint * out_len) -{ - NMManagerPrivate * priv = NM_MANAGER_GET_PRIVATE(manager); - const GetActivatableConnectionsFilterData d = { - .self = manager, - .for_auto_activation = for_auto_activation, - }; - - return nm_settings_get_connections_clone( - priv->settings, - out_len, - _get_activatable_connections_filter, - (gpointer) &d, - sort ? nm_settings_connection_cmp_autoconnect_priority_p_with_data : NULL, - NULL); -} - -static NMActiveConnection * -active_connection_get_by_path(NMManager *self, const char *path) -{ - NMManagerPrivate * priv = NM_MANAGER_GET_PRIVATE(self); - NMActiveConnection *ac; - - ac = nm_dbus_manager_lookup_object(nm_dbus_object_get_manager(NM_DBUS_OBJECT(self)), path); - if (!ac || !NM_IS_ACTIVE_CONNECTION(ac) || c_list_is_empty(&ac->active_connections_lst)) - return NULL; - - nm_assert(c_list_contains(&priv->active_connections_lst_head, &ac->active_connections_lst)); - return ac; -} - -/*****************************************************************************/ - -static void -_config_changed_cb(NMConfig * config, - NMConfigData * config_data, - NMConfigChangeFlags changes, - NMConfigData * old_data, - NMManager * self) -{ - g_object_freeze_notify(G_OBJECT(self)); - - if (NM_FLAGS_HAS(changes, NM_CONFIG_CHANGE_GLOBAL_DNS_CONFIG)) - _notify(self, PROP_GLOBAL_DNS_CONFIGURATION); - - if (!nm_streq0(nm_config_data_get_connectivity_uri(config_data), - nm_config_data_get_connectivity_uri(old_data))) { - if ((!nm_config_data_get_connectivity_uri(config_data)) - != (!nm_config_data_get_connectivity_uri(old_data))) - _notify(self, PROP_CONNECTIVITY_CHECK_AVAILABLE); - _notify(self, PROP_CONNECTIVITY_CHECK_URI); - } - - g_object_thaw_notify(G_OBJECT(self)); -} - -static void -_reload_auth_cb(NMAuthChain *chain, GDBusMethodInvocation *context, gpointer user_data) -{ - NMManager * self = NM_MANAGER(user_data); - NMManagerPrivate * priv = NM_MANAGER_GET_PRIVATE(self); - GError * ret_error = NULL; - NMAuthCallResult result; - guint32 flags; - NMAuthSubject * subject; - char s_buf[60]; - NMConfigChangeFlags reload_type = NM_CONFIG_CHANGE_NONE; - - nm_assert(G_IS_DBUS_METHOD_INVOCATION(context)); - - c_list_unlink(nm_auth_chain_parent_lst_list(chain)); - flags = GPOINTER_TO_UINT(nm_auth_chain_get_data(chain, "flags")); - - subject = nm_auth_chain_get_subject(chain); - - result = nm_auth_chain_get_result(chain, NM_AUTH_PERMISSION_RELOAD); - if (result != NM_AUTH_CALL_RESULT_YES) { - ret_error = g_error_new_literal(NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - "Not authorized to reload configuration"); - } else { - if (NM_FLAGS_ANY(flags, ~NM_MANAGER_RELOAD_FLAG_ALL)) { - /* invalid flags */ - } else if (flags == 0) - reload_type = NM_CONFIG_CHANGE_CAUSE_SIGHUP; - else { - if (NM_FLAGS_HAS(flags, NM_MANAGER_RELOAD_FLAG_CONF)) - reload_type |= NM_CONFIG_CHANGE_CAUSE_CONF; - if (NM_FLAGS_HAS(flags, NM_MANAGER_RELOAD_FLAG_DNS_RC)) - reload_type |= NM_CONFIG_CHANGE_CAUSE_DNS_RC; - if (NM_FLAGS_HAS(flags, NM_MANAGER_RELOAD_FLAG_DNS_FULL)) - reload_type |= NM_CONFIG_CHANGE_CAUSE_DNS_FULL; - } - - if (reload_type == NM_CONFIG_CHANGE_NONE) { - ret_error = g_error_new_literal(NM_MANAGER_ERROR, - NM_MANAGER_ERROR_INVALID_ARGUMENTS, - "Invalid flags for reload"); - } - } - - nm_audit_log_control_op(NM_AUDIT_OP_RELOAD, - nm_sprintf_buf(s_buf, "%u", flags), - ret_error == NULL, - subject, - ret_error ? ret_error->message : NULL); - - if (ret_error) { - g_dbus_method_invocation_take_error(context, ret_error); - return; - } - - nm_config_reload(priv->config, reload_type, TRUE); - g_dbus_method_invocation_return_value(context, NULL); -} - -static void -impl_manager_reload(NMDBusObject * obj, - const NMDBusInterfaceInfoExtended *interface_info, - const NMDBusMethodInfoExtended * method_info, - GDBusConnection * connection, - const char * sender, - GDBusMethodInvocation * invocation, - GVariant * parameters) -{ - NMManager * self = NM_MANAGER(obj); - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - NMAuthChain * chain; - guint32 flags; - - g_variant_get(parameters, "(u)", &flags); - - chain = nm_auth_chain_new_context(invocation, _reload_auth_cb, self); - if (!chain) { - g_dbus_method_invocation_return_error_literal(invocation, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - NM_UTILS_ERROR_MSG_REQ_AUTH_FAILED); - return; - } - - c_list_link_tail(&priv->auth_lst_head, nm_auth_chain_parent_lst_list(chain)); - nm_auth_chain_set_data(chain, "flags", GUINT_TO_POINTER(flags), NULL); - nm_auth_chain_add_call(chain, NM_AUTH_PERMISSION_RELOAD, TRUE); -} - -/*****************************************************************************/ - -NMDevice * -nm_manager_get_device_by_path(NMManager *self, const char *path) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - NMDevice * device; - - g_return_val_if_fail(path, NULL); - - device = nm_dbus_manager_lookup_object(nm_dbus_object_get_manager(NM_DBUS_OBJECT(self)), path); - if (!device || !NM_IS_DEVICE(device) || c_list_is_empty(&device->devices_lst)) - return NULL; - - nm_assert(c_list_contains(&priv->devices_lst_head, &device->devices_lst)); - return device; -} - -NMDevice * -nm_manager_get_device_by_ifindex(NMManager *self, int ifindex) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - NMDevice * device; - - if (ifindex > 0) { - c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { - if (nm_device_get_ifindex(device) == ifindex) - return device; - } - } - - return NULL; -} - -static NMDevice * -find_device_by_permanent_hw_addr(NMManager *self, const char *hwaddr) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - NMDevice * device; - const char * device_addr; - guint8 hwaddr_bin[NM_UTILS_HWADDR_LEN_MAX]; - gsize hwaddr_len; - - g_return_val_if_fail(hwaddr != NULL, NULL); - - if (!_nm_utils_hwaddr_aton(hwaddr, hwaddr_bin, sizeof(hwaddr_bin), &hwaddr_len)) - return NULL; - - c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { - device_addr = nm_device_get_permanent_hw_address(device); - if (device_addr && nm_utils_hwaddr_matches(hwaddr_bin, hwaddr_len, device_addr, -1)) - return device; - } - return NULL; -} - -static NMDevice * -find_device_by_ip_iface(NMManager *self, const char *iface) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - NMDevice * device; - - g_return_val_if_fail(iface, NULL); - - c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { - if (nm_device_is_real(device) && nm_streq0(nm_device_get_ip_iface(device), iface)) - return device; - } - return NULL; -} - -/** - * find_device_by_iface: - * @self: the #NMManager - * @iface: the device interface to find - * @connection: a connection to ensure the returned device is compatible with - * @slave: a slave connection to ensure a master is compatible with - * - * Finds a device by interface name, preferring realized devices. If @slave - * is given, this function will only return master devices and will ensure - * @slave, when activated, can be a slave of the returned master device. If - * @connection is given, this function will only consider devices that are - * compatible with @connection. - * - * Returns: the matching #NMDevice - */ -static NMDevice * -find_device_by_iface(NMManager * self, - const char * iface, - NMConnection *connection, - NMConnection *slave) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - NMDevice * fallback = NULL; - NMDevice * candidate; - - g_return_val_if_fail(iface != NULL, NULL); - - c_list_for_each_entry (candidate, &priv->devices_lst_head, devices_lst) { - if (!nm_streq(nm_device_get_iface(candidate), iface)) - continue; - if (connection && !nm_device_check_connection_compatible(candidate, connection, NULL)) - continue; - if (slave) { - if (!nm_device_is_master(candidate)) - continue; - if (!nm_device_check_slave_connection_compatible(candidate, slave)) - continue; - } - - if (nm_device_is_real(candidate)) - return candidate; - else if (!fallback) - fallback = candidate; - } - return fallback; -} - -static gboolean -manager_sleeping(NMManager *self) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - - if (priv->sleeping || !priv->net_enabled) - return TRUE; - return FALSE; -} - -static const char * -_nm_state_to_string(NMState state) -{ - switch (state) { - case NM_STATE_ASLEEP: - return "ASLEEP"; - case NM_STATE_DISCONNECTED: - return "DISCONNECTED"; - case NM_STATE_DISCONNECTING: - return "DISCONNECTING"; - case NM_STATE_CONNECTING: - return "CONNECTING"; - case NM_STATE_CONNECTED_LOCAL: - return "CONNECTED_LOCAL"; - case NM_STATE_CONNECTED_SITE: - return "CONNECTED_SITE"; - case NM_STATE_CONNECTED_GLOBAL: - return "CONNECTED_GLOBAL"; - case NM_STATE_UNKNOWN: - default: - return "UNKNOWN"; - } -} - -static NMState -find_best_device_state(NMManager *manager) -{ - NMManagerPrivate * priv = NM_MANAGER_GET_PRIVATE(manager); - NMState best_state = NM_STATE_DISCONNECTED; - NMActiveConnection *ac; - - c_list_for_each_entry (ac, &priv->active_connections_lst_head, active_connections_lst) { - NMActiveConnectionState ac_state = nm_active_connection_get_state(ac); - - switch (ac_state) { - case NM_ACTIVE_CONNECTION_STATE_ACTIVATED: - if (nm_active_connection_get_default(ac, AF_UNSPEC)) { - if (priv->connectivity_state == NM_CONNECTIVITY_FULL) - return NM_STATE_CONNECTED_GLOBAL; - - best_state = NM_STATE_CONNECTED_SITE; - } else { - if (best_state < NM_STATE_CONNECTING) - best_state = NM_STATE_CONNECTED_LOCAL; - } - break; - case NM_ACTIVE_CONNECTION_STATE_ACTIVATING: - if (!NM_IN_SET(nm_active_connection_get_activation_type(ac), - NM_ACTIVATION_TYPE_EXTERNAL, - NM_ACTIVATION_TYPE_ASSUME)) { - if (best_state != NM_STATE_CONNECTED_GLOBAL) - best_state = NM_STATE_CONNECTING; - } - break; - case NM_ACTIVE_CONNECTION_STATE_DEACTIVATING: - if (!NM_IN_SET(nm_active_connection_get_activation_type(ac), - NM_ACTIVATION_TYPE_EXTERNAL, - NM_ACTIVATION_TYPE_ASSUME)) { - if (best_state < NM_STATE_DISCONNECTING) - best_state = NM_STATE_DISCONNECTING; - } - break; - default: - break; - } - } - - return best_state; -} - -static void -nm_manager_update_metered(NMManager *self) -{ - NMManagerPrivate *priv; - NMDevice * device; - NMMetered value = NM_METERED_UNKNOWN; - - g_return_if_fail(NM_IS_MANAGER(self)); - priv = NM_MANAGER_GET_PRIVATE(self); - - if (priv->primary_connection) { - device = nm_active_connection_get_device(priv->primary_connection); - if (device) - value = nm_device_get_metered(device); - } - - if (value != priv->metered) { - priv->metered = value; - _LOGD(LOGD_CORE, "new metered value: %d", (int) priv->metered); - _notify(self, PROP_METERED); - } -} - -NMMetered -nm_manager_get_metered(NMManager *self) -{ - g_return_val_if_fail(NM_IS_MANAGER(self), NM_METERED_UNKNOWN); - - return NM_MANAGER_GET_PRIVATE(self)->metered; -} - -static void -nm_manager_update_state(NMManager *self) -{ - NMManagerPrivate *priv; - NMState new_state = NM_STATE_DISCONNECTED; - - g_return_if_fail(NM_IS_MANAGER(self)); - - priv = NM_MANAGER_GET_PRIVATE(self); - - if (manager_sleeping(self)) - new_state = NM_STATE_ASLEEP; - else - new_state = find_best_device_state(self); - - if (new_state >= NM_STATE_CONNECTED_LOCAL && priv->connectivity_state == NM_CONNECTIVITY_FULL) { - new_state = NM_STATE_CONNECTED_GLOBAL; - } - - if (priv->state == new_state) - return; - - priv->state = new_state; - - _LOGI(LOGD_CORE, "NetworkManager state is now %s", _nm_state_to_string(new_state)); - - _notify(self, PROP_STATE); - nm_dbus_object_emit_signal(NM_DBUS_OBJECT(self), - &interface_info_manager, - &signal_info_state_changed, - "(u)", - (guint32) priv->state); -} - -static void -manager_device_state_changed(NMDevice * device, - NMDeviceState new_state, - NMDeviceState old_state, - NMDeviceStateReason reason, - gpointer user_data) -{ - NMManager * self = NM_MANAGER(user_data); - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - - if (old_state == NM_DEVICE_STATE_UNMANAGED && new_state > NM_DEVICE_STATE_UNMANAGED) - retry_connections_for_parent_device(self, device); - - if (NM_IN_SET(new_state, - NM_DEVICE_STATE_UNMANAGED, - NM_DEVICE_STATE_UNAVAILABLE, - NM_DEVICE_STATE_DISCONNECTED, - NM_DEVICE_STATE_PREPARE, - NM_DEVICE_STATE_FAILED)) - _notify(self, PROP_ACTIVE_CONNECTIONS); - - if (NM_IN_SET(new_state, - NM_DEVICE_STATE_UNMANAGED, - NM_DEVICE_STATE_DISCONNECTED, - NM_DEVICE_STATE_ACTIVATED)) { - nm_manager_write_device_state(self, device, NULL); - - G_STATIC_ASSERT_EXPR(DEVICE_STATE_PRUNE_RATELIMIT_MAX < G_MAXUINT8); - if (priv->device_state_prune_ratelimit_count++ > DEVICE_STATE_PRUNE_RATELIMIT_MAX) { - /* We write the device state to /run. The state files are named after the - * ifindex (which is assumed to be unique and not repeat -- in practice - * it may repeat). So from time to time, we prune device state files - * for interfaces that no longer exist. - * - * Otherwise, the files might pile up if you create (and destroy) a large - * number of software devices. */ - priv->device_state_prune_ratelimit_count = 0; - nm_config_device_state_prune_stale(NULL, priv->platform); - } - } - - if (NM_IN_SET(new_state, NM_DEVICE_STATE_UNAVAILABLE, NM_DEVICE_STATE_DISCONNECTED)) - nm_settings_device_added(priv->settings, device); -} - -static void device_has_pending_action_changed(NMDevice *device, GParamSpec *pspec, NMManager *self); - -static void -check_if_startup_complete(NMManager *self) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - NMDevice * device; - const char * reason; - - if (!priv->startup) - return; - - if (!priv->devices_inited) - return; - - c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { - reason = nm_device_has_pending_action_reason(device); - if (reason) { - _LOGD(LOGD_CORE, - "startup complete is waiting for device '%s' (%s)", - nm_device_get_iface(device), - reason); - return; - } - } - - /* All NMDevice must be ready. But also NMSettings tracks profiles that wait for - * ready devices via "connection.wait-device-timeout". - * - * Note that we only re-check nm_settings_get_startup_complete_blocked_reason() when - * all of the devices become ready (again). - * - * For example, assume we have device "eth1" and "profile-eth2" which waits for "eth2". - * If "eth1" is ready (no pending action), we only need to re-evaluate "profile-eth2" - * if we have another device ("eth2"), that becomes non-ready (had pending actions) - * and again become ready. We don't need to check "profile-eth2" until "eth2" becomes - * non-ready. - * That is why nm_settings_get_startup_complete_blocked_reason() only has any significance - * if all devices are ready too. It allows us to cut down the number of checks whether - * NMSettings is ready. That's because we don't need to re-evaluate on minor changes of - * a device, only when all devices become managed and ready. */ - - g_signal_handlers_block_by_func(priv->settings, settings_startup_complete_changed, self); - reason = nm_settings_get_startup_complete_blocked_reason(priv->settings, TRUE); - g_signal_handlers_unblock_by_func(priv->settings, settings_startup_complete_changed, self); - if (reason) { - _LOGD(LOGD_CORE, "startup complete is waiting for connection (%s)", reason); - return; - } - - _LOGI(LOGD_CORE, "startup complete"); - - priv->startup = FALSE; - - /* we no longer care about these signals. Startup-complete only - * happens once. */ - g_signal_handlers_disconnect_by_func(priv->settings, - G_CALLBACK(settings_startup_complete_changed), - self); - c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { - g_signal_handlers_disconnect_by_func(device, - G_CALLBACK(device_has_pending_action_changed), - self); - } - - _notify(self, PROP_STARTUP); - - if (nm_config_get_configure_and_quit(priv->config)) - g_signal_emit(self, signals[CONFIGURE_QUIT], 0); -} - -static void -device_has_pending_action_changed(NMDevice *device, GParamSpec *pspec, NMManager *self) -{ - check_if_startup_complete(self); -} - -static void -settings_startup_complete_changed(NMSettings *settings, GParamSpec *pspec, NMManager *self) -{ - check_if_startup_complete(self); -} - -static void -_parent_notify_changed(NMManager *self, NMDevice *device, gboolean device_removed) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - NMDevice * candidate; - - nm_assert(NM_IS_DEVICE(device)); - -again: - c_list_for_each_entry (candidate, &priv->devices_lst_head, devices_lst) { - if (nm_device_parent_notify_changed(candidate, device, device_removed)) { - /* in the unlikely event that this changes anything, we start iterating - * again, to be sure that the device list is up-to-date. */ - goto again; - } - } -} - -static gboolean -device_is_wake_on_lan(NMPlatform *platform, NMDevice *device) -{ - int ifindex; - - ifindex = nm_device_get_ip_ifindex(device); - if (ifindex <= 0) - return FALSE; - return nm_platform_link_get_wake_on_lan(platform, ifindex); -} - -static void -remove_device(NMManager *self, NMDevice *device, gboolean quitting) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - gboolean unmanage = FALSE; - - _LOG2D(LOGD_DEVICE, - device, - "removing device (managed %d, wol %d)", - nm_device_get_managed(device, FALSE), - device_is_wake_on_lan(priv->platform, device)); - - if (nm_device_get_managed(device, FALSE)) { - if (quitting) { - /* Leave configured if wo(w)lan and quitting */ - if (device_is_wake_on_lan(priv->platform, device)) - unmanage = FALSE; - else - unmanage = nm_device_unmanage_on_quit(device); - } else { - /* the device is already gone. Unmanage it. */ - unmanage = TRUE; - } - - if (unmanage) { - if (quitting) - nm_device_set_unmanaged_by_quitting(device); - else { - nm_device_sys_iface_state_set(device, NM_DEVICE_SYS_IFACE_STATE_REMOVED); - nm_device_set_unmanaged_by_flags(device, - NM_UNMANAGED_PLATFORM_INIT, - TRUE, - NM_DEVICE_STATE_REASON_REMOVED); - } - } else if (quitting - && nm_config_get_configure_and_quit(priv->config) - == NM_CONFIG_CONFIGURE_AND_QUIT_ENABLED) { - nm_device_spawn_iface_helper(device); - } - } - - g_signal_handlers_disconnect_matched(device, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self); - - nm_settings_device_removed(priv->settings, device, quitting); - - c_list_unlink(&device->devices_lst); - - _parent_notify_changed(self, device, TRUE); - - if (nm_device_is_real(device)) { - gboolean unconfigure_ip_config = !quitting || unmanage; - - /* When we don't unmanage the device on shutdown, we want to preserve the DNS - * configuration in resolv.conf. For that, we must leak the configuration - * in NMPolicy/NMDnsManager. We do that, by emitting the device-removed signal - * with device's ip-config object still uncleared. In that case, NMPolicy - * never learns to unconfigure the ip-config objects and does not remove them - * from DNS on shutdown (which is ugly, because we don't cleanup the memory - * properly). - * - * Control that by passing @unconfigure_ip_config. */ - nm_device_removed(device, unconfigure_ip_config); - - _emit_device_added_removed(self, device, FALSE); - } else { - /* unrealize() does not release a slave device from master and - * clear IP configurations, do it here */ - nm_device_removed(device, TRUE); - } - - g_signal_emit(self, signals[INTERNAL_DEVICE_REMOVED], 0, device); - _notify(self, PROP_ALL_DEVICES); - - update_connectivity_value(self); - - nm_dbus_object_clear_and_unexport(&device); - - check_if_startup_complete(self); -} - -static void -device_removed_cb(NMDevice *device, gpointer user_data) -{ - remove_device(NM_MANAGER(user_data), device, FALSE); -} - -NMState -nm_manager_get_state(NMManager *manager) -{ - g_return_val_if_fail(NM_IS_MANAGER(manager), NM_STATE_UNKNOWN); - - return NM_MANAGER_GET_PRIVATE(manager)->state; -} - -/*****************************************************************************/ - -static NMDevice * -find_parent_device_for_connection(NMManager * self, - NMConnection * connection, - NMDeviceFactory *cached_factory, - const char ** out_parent_spec) -{ - NMManagerPrivate * priv = NM_MANAGER_GET_PRIVATE(self); - NMDeviceFactory * factory; - const char * parent_name = NULL; - NMSettingsConnection *parent_connection; - NMDevice * parent, *first_compatible = NULL; - NMDevice * candidate; - - g_return_val_if_fail(NM_IS_CONNECTION(connection), NULL); - NM_SET_OUT(out_parent_spec, NULL); - - if (!cached_factory) { - factory = nm_device_factory_manager_find_factory_for_connection(connection); - if (!factory) - return NULL; - } else - factory = cached_factory; - - parent_name = nm_device_factory_get_connection_parent(factory, connection); - if (!parent_name) - return NULL; - - NM_SET_OUT(out_parent_spec, parent_name); - - /* Try as an interface name of a parent device */ - parent = find_device_by_iface(self, parent_name, NULL, NULL); - if (parent) - return parent; - - /* Maybe a hardware address */ - parent = find_device_by_permanent_hw_addr(self, parent_name); - if (parent) - return parent; - - /* Maybe a connection UUID */ - parent_connection = nm_settings_get_connection_by_uuid(priv->settings, parent_name); - if (!parent_connection) - return NULL; - - /* Check if the parent connection is currently activated or is compatible - * with some known device. - */ - c_list_for_each_entry (candidate, &priv->devices_lst_head, devices_lst) { - /* Unmanaged devices are not compatible with any connection */ - if (!nm_device_get_managed(candidate, FALSE)) - continue; - - if (nm_device_get_settings_connection(candidate) == parent_connection) - return candidate; - - if (!first_compatible - && nm_device_check_connection_compatible( - candidate, - nm_settings_connection_get_connection(parent_connection), - NULL)) - first_compatible = candidate; - } - - return first_compatible; -} - -/** - * nm_manager_get_connection_iface: - * @self: the #NMManager - * @connection: the #NMConnection to get the interface for - * @out_parent: on success, the parent device if any - * @out_parent_spec: on return, a string specifying the parent device - * in the connection. This can be a device name, a MAC address or a - * connection UUID. - * @error: an error if determining the virtual interface name failed - * - * Given @connection, returns the interface name that the connection - * would need to use when activated. %NULL is returned if the name - * is not specified in connection or a the name for a virtual device - * could not be generated. - * - * Returns: the expected interface name (caller takes ownership), or %NULL - */ -char * -nm_manager_get_connection_iface(NMManager * self, - NMConnection *connection, - NMDevice ** out_parent, - const char ** out_parent_spec, - GError ** error) -{ - NMDeviceFactory *factory; - char * iface = NULL; - NMDevice * parent = NULL; - - NM_SET_OUT(out_parent, NULL); - NM_SET_OUT(out_parent_spec, NULL); - - factory = nm_device_factory_manager_find_factory_for_connection(connection); - if (!factory) { - if (nm_streq0(nm_connection_get_connection_type(connection), - NM_SETTING_GENERIC_SETTING_NAME)) { - /* the generic type doesn't have a factory. */ - goto return_ifname_fom_connection; - } - - g_set_error(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_FAILED, - "NetworkManager plugin for '%s' unavailable", - nm_connection_get_connection_type(connection)); - return NULL; - } - - if (!out_parent && !NM_DEVICE_FACTORY_GET_CLASS(factory)->get_connection_iface) { - /* optimization. Shortcut lookup of the partent device. */ - goto return_ifname_fom_connection; - } - - parent = find_parent_device_for_connection(self, connection, factory, out_parent_spec); - iface = nm_device_factory_get_connection_iface(factory, - connection, - parent ? nm_device_get_ip_iface(parent) : NULL, - error); - if (!iface) - return NULL; - - if (out_parent) - *out_parent = parent; - return iface; - -return_ifname_fom_connection: - iface = g_strdup(nm_connection_get_interface_name(connection)); - if (!iface) { - g_set_error(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_FAILED, - "failed to determine interface name: error determine name for %s", - nm_connection_get_connection_type(connection)); - } - return iface; -} - -/** - * nm_manager_iface_for_uuid: - * @self: the #NMManager - * @uuid: the connection uuid - * - * Gets a link name for the given UUID. Useful for the settings plugins that - * wish to write configuration files compatible with tooling that can't - * interpret our UUIDs. - * - * Returns: An interface name; %NULL if none matches - */ -const char * -nm_manager_iface_for_uuid(NMManager *self, const char *uuid) -{ - NMManagerPrivate * priv = NM_MANAGER_GET_PRIVATE(self); - NMSettingsConnection *sett_conn; - - sett_conn = nm_settings_get_connection_by_uuid(priv->settings, uuid); - if (!sett_conn) - return NULL; - - return nm_connection_get_interface_name(nm_settings_connection_get_connection(sett_conn)); -} - -NMDevice * -nm_manager_get_device(NMManager *self, const char *ifname, NMDeviceType device_type) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - NMDevice * device; - - g_return_val_if_fail(ifname, NULL); - g_return_val_if_fail(device_type != NM_DEVICE_TYPE_UNKNOWN, NULL); - - c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { - if (nm_device_get_device_type(device) == device_type - && nm_streq0(nm_device_get_iface(device), ifname)) - return device; - } - - return NULL; -} - -gboolean -nm_manager_remove_device(NMManager *self, const char *ifname, NMDeviceType device_type) -{ - NMDevice *d; - - d = nm_manager_get_device(self, ifname, device_type); - if (!d) - return FALSE; - - remove_device(self, d, FALSE); - return TRUE; -} - -/** - * system_create_virtual_device: - * @self: the #NMManager - * @connection: the connection which might require a virtual device - * - * If @connection requires a virtual device and one does not yet exist for it, - * creates that device. - * - * Returns: A #NMDevice that was just realized; %NULL if none - */ -static NMDevice * -system_create_virtual_device(NMManager *self, NMConnection *connection) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - NMDeviceFactory * factory; - gs_free NMSettingsConnection **connections = NULL; - guint i; - gs_free char * iface = NULL; - const char * parent_spec; - NMDevice * device = NULL, *parent = NULL; - NMDevice * dev_candidate; - GError * error = NULL; - NMLogLevel log_level; - - g_return_val_if_fail(NM_IS_MANAGER(self), NULL); - g_return_val_if_fail(NM_IS_CONNECTION(connection), NULL); - - iface = nm_manager_get_connection_iface(self, connection, &parent, &parent_spec, &error); - if (!iface) { - _LOG3D(LOGD_DEVICE, connection, "can't get a name of a virtual device: %s", error->message); - g_error_free(error); - return NULL; - } - - if (parent_spec && !parent) { - /* parent is not ready, wait */ - return NULL; - } - - /* See if there's a device that is already compatible with this connection */ - c_list_for_each_entry (dev_candidate, &priv->devices_lst_head, devices_lst) { - if (nm_device_check_connection_compatible(dev_candidate, connection, NULL)) { - if (nm_device_is_real(dev_candidate)) { - _LOG3D(LOGD_DEVICE, connection, "already created virtual interface name %s", iface); - return NULL; - } - - device = dev_candidate; - break; - } - } - - if (!device) { - /* No matching device found. Proceed creating a new one. */ - - factory = nm_device_factory_manager_find_factory_for_connection(connection); - if (!factory) { - _LOG3E(LOGD_DEVICE, - connection, - "(%s) NetworkManager plugin for '%s' unavailable", - iface, - nm_connection_get_connection_type(connection)); - return NULL; - } - - device = nm_device_factory_create_device(factory, iface, NULL, connection, NULL, &error); - if (!device) { - _LOG3W(LOGD_DEVICE, connection, "factory can't create the device: %s", error->message); - g_error_free(error); - return NULL; - } - - _LOG3D(LOGD_DEVICE, connection, "create virtual device %s", nm_device_get_iface(device)); - - if (!add_device(self, device, &error)) { - _LOG3W(LOGD_DEVICE, - connection, - "can't register the device with manager: %s", - error->message); - g_error_free(error); - g_object_unref(device); - return NULL; - } - - /* Add device takes a reference that NMManager still owns, so it's - * safe to unref here and still return @device. - */ - g_object_unref(device); - } - - if (!nm_device_check_unrealized_device_managed(device)) { - _LOG3D(LOGD_DEVICE, - connection, - "skip activation because virtual device '%s' is unmanaged", - nm_device_get_iface(device)); - return device; - } - - if (!find_master(self, connection, device, NULL, NULL, NULL, &error)) { - _LOG3D(LOGD_DEVICE, connection, "skip activation: %s", error->message); - g_error_free(error); - return device; - } - - /* Create backing resources if the device has any autoconnect connections */ - connections = nm_settings_get_connections_clone( - priv->settings, - NULL, - NULL, - NULL, - nm_settings_connection_cmp_autoconnect_priority_p_with_data, - NULL); - for (i = 0; connections[i]; i++) { - NMConnection * candidate = nm_settings_connection_get_connection(connections[i]); - NMSettingConnection *s_con; - - if (!nm_device_check_connection_compatible(device, candidate, NULL)) - continue; - - s_con = nm_connection_get_setting_connection(candidate); - g_assert(s_con); - if (!nm_setting_connection_get_autoconnect(s_con) - || nm_settings_connection_autoconnect_is_blocked(connections[i])) - continue; - - /* Create any backing resources the device needs */ - if (!nm_device_create_and_realize(device, connection, parent, &error)) { - log_level = - g_error_matches(error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_MISSING_DEPENDENCIES) - ? LOGL_DEBUG - : LOGL_ERR; - _NMLOG3(log_level, - LOGD_DEVICE, - connection, - "couldn't create the device: %s", - error->message); - g_error_free(error); - return NULL; - } - - retry_connections_for_parent_device(self, device); - break; - } - - return device; -} - -static void -retry_connections_for_parent_device(NMManager *self, NMDevice *device) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - gs_free NMSettingsConnection **connections = NULL; - guint i; - - g_return_if_fail(device); - - connections = nm_settings_get_connections_clone( - priv->settings, - NULL, - NULL, - NULL, - nm_settings_connection_cmp_autoconnect_priority_p_with_data, - NULL); - for (i = 0; connections[i]; i++) { - NMSettingsConnection *sett_conn = connections[i]; - NMConnection * connection = nm_settings_connection_get_connection(sett_conn); - gs_free_error GError *error = NULL; - gs_free char * ifname = NULL; - NMDevice * parent; - - parent = find_parent_device_for_connection(self, connection, NULL, NULL); - if (parent == device) { - /* Only try to activate devices that don't already exist */ - ifname = nm_manager_get_connection_iface(self, connection, &parent, NULL, &error); - if (ifname) { - if (!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, ifname)) - connection_changed(self, sett_conn); - } - } - } -} - -static void -connection_changed(NMManager *self, NMSettingsConnection *sett_conn) -{ - NMConnection *connection; - NMDevice * device; - - if (NM_FLAGS_ANY(nm_settings_connection_get_flags(sett_conn), - NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE - | NM_SETTINGS_CONNECTION_INT_FLAGS_EXTERNAL)) - return; - - connection = nm_settings_connection_get_connection(sett_conn); - - if (!nm_connection_is_virtual(connection)) - return; - - device = system_create_virtual_device(self, connection); - if (!device) - return; - - /* Maybe the device that was created was needed by some other - * connection's device (parent of a VLAN). Let the connections - * can use the newly created device as a parent know. */ - retry_connections_for_parent_device(self, device); -} - -static void -connection_added_cb(NMSettings *settings, NMSettingsConnection *sett_conn, NMManager *self) -{ - connection_changed(self, sett_conn); -} - -static void -connection_updated_cb(NMSettings * settings, - NMSettingsConnection *sett_conn, - guint update_reason_u, - NMManager * self) -{ - connection_changed(self, sett_conn); -} - -/*****************************************************************************/ - -static void -_delete_volatile_connection_all(NMManager *self, gboolean do_delete) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - NMCListElem * elem; - - while ( - (elem = c_list_first_entry(&priv->delete_volatile_connection_lst_head, NMCListElem, lst))) { - gs_unref_object NMSettingsConnection *connection = NULL; - - connection = nm_c_list_elem_free_steal(elem); - if (do_delete) - _delete_volatile_connection_do(self, connection); - } -} - -static gboolean -_delete_volatile_connection_cb(gpointer user_data) -{ - NMManager * self = user_data; - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - - priv->delete_volatile_connection_idle_id = 0; - _delete_volatile_connection_all(self, TRUE); - return G_SOURCE_REMOVE; -} - -static void -connection_flags_changed(NMSettings *settings, NMSettingsConnection *connection, gpointer user_data) -{ - NMManager * self = user_data; - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - - if (!NM_FLAGS_ANY(nm_settings_connection_get_flags(connection), - NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE - | NM_SETTINGS_CONNECTION_INT_FLAGS_EXTERNAL)) - return; - - if (active_connection_find(self, - connection, - NULL, - NM_ACTIVE_CONNECTION_STATE_DEACTIVATED, - NULL)) { - /* the connection still has an active-connection. It will be purged - * when the active connection(s) get(s) removed. */ - return; - } - - c_list_link_tail(&priv->delete_volatile_connection_lst_head, - &nm_c_list_elem_new_stale(g_object_ref(connection))->lst); - if (!priv->delete_volatile_connection_idle_id) - priv->delete_volatile_connection_idle_id = g_idle_add(_delete_volatile_connection_cb, self); -} - -/*****************************************************************************/ - -static void -system_unmanaged_devices_changed_cb(NMSettings *settings, GParamSpec *pspec, gpointer user_data) -{ - NMManager * self = NM_MANAGER(user_data); - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - NMDevice * device; - - c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) - nm_device_set_unmanaged_by_user_settings(device); -} - -static void -hostname_changed_cb(NMHostnameManager *hostname_manager, GParamSpec *pspec, NMManager *self) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - const char * hostname; - - hostname = nm_hostname_manager_get_hostname(priv->hostname_manager); - - nm_dispatcher_call_hostname(NULL, NULL, NULL); - nm_dhcp_manager_set_default_hostname(nm_dhcp_manager_get(), hostname); -} - -/*****************************************************************************/ -/* General NMManager stuff */ -/*****************************************************************************/ - -static gboolean -radio_enabled_for_rstate(RadioState *rstate, gboolean check_changeable) -{ - gboolean enabled; - - enabled = rstate->user_enabled && rstate->hw_enabled; - if (check_changeable) - enabled &= rstate->sw_enabled; - return enabled; -} - -static gboolean -radio_enabled_for_type(NMManager *self, RfKillType rtype, gboolean check_changeable) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - - return radio_enabled_for_rstate(&priv->radio_states[rtype], check_changeable); -} - -static void -manager_update_radio_enabled(NMManager *self, RadioState *rstate, gboolean enabled) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - NMDevice * device; - - /* Do nothing for radio types not yet implemented */ - if (!rstate->prop) - return; - - g_object_notify(G_OBJECT(self), rstate->prop); - - /* Don't touch devices if asleep/networking disabled */ - if (manager_sleeping(self)) - return; - - /* enable/disable wireless devices as required */ - c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { - if (nm_device_get_rfkill_type(device) == rstate->rtype) { - _LOG2D(LOGD_RFKILL, - device, - "rfkill: setting radio %s", - enabled ? "enabled" : "disabled"); - nm_device_set_enabled(device, enabled); - } - } -} - -static void -update_rstate_from_rfkill(NMRfkillManager *rfkill_mgr, RadioState *rstate) -{ - switch (nm_rfkill_manager_get_rfkill_state(rfkill_mgr, rstate->rtype)) { - case RFKILL_UNBLOCKED: - rstate->sw_enabled = TRUE; - rstate->hw_enabled = TRUE; - break; - case RFKILL_SOFT_BLOCKED: - rstate->sw_enabled = FALSE; - rstate->hw_enabled = TRUE; - break; - case RFKILL_HARD_BLOCKED: - rstate->sw_enabled = FALSE; - rstate->hw_enabled = FALSE; - break; - default: - g_warn_if_reached(); - break; - } -} - -static void -manager_rfkill_update_one_type(NMManager *self, RadioState *rstate, RfKillType rtype) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - gboolean old_enabled, new_enabled, old_rfkilled, new_rfkilled, old_hwe; - - old_enabled = radio_enabled_for_rstate(rstate, TRUE); - old_rfkilled = rstate->hw_enabled && rstate->sw_enabled; - old_hwe = rstate->hw_enabled; - - /* recheck kernel rfkill state */ - update_rstate_from_rfkill(priv->rfkill_mgr, rstate); - - /* Print out all states affecting device enablement */ - if (rstate->desc) { - _LOGD(LOGD_RFKILL, - "rfkill: %s hw-enabled %d sw-enabled %d", - rstate->desc, - rstate->hw_enabled, - rstate->sw_enabled); - } - - /* Log new killswitch state */ - new_rfkilled = rstate->hw_enabled && rstate->sw_enabled; - if (old_rfkilled != new_rfkilled) { - _LOGI(LOGD_RFKILL, - "rfkill: %s now %s by radio killswitch", - rstate->desc, - new_rfkilled ? "enabled" : "disabled"); - } - - /* Send out property changed signal for HW enabled */ - if (rstate->hw_enabled != old_hwe) { - if (rstate->hw_prop) - g_object_notify(G_OBJECT(self), rstate->hw_prop); - } - - /* 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, new_enabled); -} - -static void -nm_manager_rfkill_update(NMManager *self, RfKillType rtype) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - guint i; - - if (rtype != RFKILL_TYPE_UNKNOWN) - manager_rfkill_update_one_type(self, &priv->radio_states[rtype], rtype); - 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); - } -} - -static void -device_auth_done_cb(NMAuthChain *chain, GDBusMethodInvocation *context, gpointer user_data) -{ - NMManager * self = NM_MANAGER(user_data); - gs_free_error GError * error = NULL; - NMAuthCallResult result; - NMDevice * device; - GCancellable * cancellable; - const char * permission; - NMManagerDeviceAuthRequestFunc callback; - NMAuthSubject * subject; - - nm_assert(G_IS_DBUS_METHOD_INVOCATION(context)); - - c_list_unlink(nm_auth_chain_parent_lst_list(chain)); - - permission = nm_auth_chain_get_data(chain, "perm"); - nm_assert(permission); - callback = nm_auth_chain_get_data(chain, "callback"); - nm_assert(callback); - device = nm_auth_chain_get_data(chain, "device"); - nm_assert(NM_IS_DEVICE(device)); - - cancellable = nm_auth_chain_get_cancellable(chain); - nm_assert(!cancellable || G_IS_CANCELLABLE(cancellable)); - - result = nm_auth_chain_get_result(chain, permission); - subject = nm_auth_chain_get_subject(chain); - - if (cancellable && g_cancellable_set_error_if_cancelled(cancellable, &error)) { - /* pass. */ - } else { - if (result != NM_AUTH_CALL_RESULT_YES) { - _LOGD(LOGD_CORE, "%s request failed: not authorized", permission); - error = g_error_new(NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - "%s request failed: not authorized", - permission); - } - - nm_assert(error || (result == NM_AUTH_CALL_RESULT_YES)); - } - - callback(device, context, subject, error, nm_auth_chain_get_data(chain, "user-data")); -} - -static void -_device_auth_done_fail_on_idle(gpointer user_data, GCancellable *cancellable) -{ - gs_unref_object NMManager *self = NULL; - gs_unref_object NMDevice *device = NULL; - gs_unref_object GDBusMethodInvocation *context = NULL; - gs_unref_object NMAuthSubject *subject = NULL; - gs_free_error GError *error_original = NULL; - gs_free_error GError * error_cancelled = NULL; - NMManagerDeviceAuthRequestFunc callback; - gpointer callback_user_data; - - nm_utils_user_data_unpack(user_data, - &self, - &device, - &context, - &subject, - &error_original, - &callback, - &callback_user_data); - - g_cancellable_set_error_if_cancelled(cancellable, &error_cancelled); - - callback(device, context, subject, error_cancelled ?: error_original, callback_user_data); -} - -void -nm_manager_device_auth_request(NMManager * self, - NMDevice * device, - GDBusMethodInvocation * context, - NMConnection * connection, - const char * permission, - gboolean allow_interaction, - GCancellable * cancellable, - NMManagerDeviceAuthRequestFunc callback, - gpointer user_data) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - gs_free_error GError *error = NULL; - gs_unref_object NMAuthSubject *subject = NULL; - NMAuthChain * chain; - char * permission_dup; - - /* Validate the caller */ - subject = nm_dbus_manager_new_auth_subject_from_context(context); - if (!subject) { - g_set_error_literal(&error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - NM_UTILS_ERROR_MSG_REQ_UID_UKNOWN); - goto fail_on_idle; - } - - /* Ensure the subject has permissions for this connection */ - if (connection - && !nm_auth_is_subject_in_acl_set_error(connection, - subject, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - &error)) - goto fail_on_idle; - - /* Validate the request */ - chain = nm_auth_chain_new_subject(subject, context, device_auth_done_cb, self); - if (!chain) { - g_set_error(&error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - NM_UTILS_ERROR_MSG_REQ_AUTH_FAILED); - goto fail_on_idle; - } - - if (cancellable) - nm_auth_chain_set_cancellable(chain, cancellable); - - permission_dup = g_strdup(permission); - - c_list_link_tail(&priv->auth_lst_head, nm_auth_chain_parent_lst_list(chain)); - nm_auth_chain_set_data(chain, "device", g_object_ref(device), g_object_unref); - nm_auth_chain_set_data(chain, "callback", callback, NULL); - nm_auth_chain_set_data(chain, "user-data", user_data, NULL); - nm_auth_chain_set_data(chain, "perm", permission_dup /* transfer ownership */, g_free); - nm_auth_chain_add_call_unsafe(chain, permission_dup, allow_interaction); - return; - -fail_on_idle: - nm_utils_invoke_on_idle(cancellable, - _device_auth_done_fail_on_idle, - nm_utils_user_data_pack(g_object_ref(self), - g_object_ref(device), - g_object_ref(context), - g_steal_pointer(&subject), - g_steal_pointer(&error), - callback, - user_data)); -} - -static gboolean -new_activation_allowed_for_connection(NMManager *self, NMSettingsConnection *connection) -{ - if (NM_IN_SET( - _nm_connection_get_multi_connect(nm_settings_connection_get_connection(connection)), - NM_CONNECTION_MULTI_CONNECT_MANUAL_MULTIPLE, - NM_CONNECTION_MULTI_CONNECT_MULTIPLE)) - return TRUE; - - return !active_connection_find(self, - connection, - NULL, - NM_ACTIVE_CONNECTION_STATE_ACTIVATED, - NULL); -} - -/** - * get_existing_connection: - * @manager: #NMManager instance - * @device: #NMDevice instance - * @out_generated: (allow-none): return TRUE, if the connection was generated. - * - * Returns: a #NMSettingsConnection to be assumed by the device, or %NULL if - * the device does not support assuming existing connections. - */ -static NMSettingsConnection * -get_existing_connection(NMManager *self, NMDevice *device, gboolean *out_generated) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - gs_unref_object NMConnection *connection = NULL; - NMSettingsConnection * added; - GError * error = NULL; - gs_free_error GError *gen_error = NULL; - NMDevice * master = NULL; - int ifindex = nm_device_get_ifindex(device); - NMSettingsConnection *matched = NULL; - NMSettingsConnection *connection_checked = NULL; - gboolean assume_state_guess_assume = FALSE; - const char * assume_state_connection_uuid = NULL; - gboolean maybe_later, only_by_uuid = FALSE; - - if (out_generated) - *out_generated = FALSE; - - nm_device_capture_initial_config(device); - - if (ifindex) { - int master_ifindex = nm_platform_link_get_master(priv->platform, ifindex); - - /* Check that the master is activating before assuming a - * slave connection. However, ignore ovs-system master as - * we never manage it. - */ - if (master_ifindex - && nm_platform_link_get_type(priv->platform, master_ifindex) - != NM_LINK_TYPE_OPENVSWITCH) { - master = nm_manager_get_device_by_ifindex(self, master_ifindex); - if (!master) { - _LOG2D(LOGD_DEVICE, - device, - "assume: don't assume because " - "cannot generate connection for slave before its master (%s/%d)", - nm_platform_link_get_name(priv->platform, master_ifindex), - master_ifindex); - return NULL; - } - if (!nm_device_get_act_request(master)) { - _LOG2D(LOGD_DEVICE, - device, - "assume: don't assume because " - "cannot generate connection for slave before master %s activates", - nm_device_get_iface(master)); - return NULL; - } - } - } - - /* The core of the API is nm_device_generate_connection() function and - * update_connection() virtual method and the convenient connection_type - * class attribute. Subclasses supporting the new API must have - * update_connection() implemented, otherwise nm_device_generate_connection() - * returns NULL. - */ - connection = nm_device_generate_connection(device, master, &maybe_later, &gen_error); - if (!connection) { - if (maybe_later) { - /* The device can generate a connection, but it failed for now. - * Give it a chance to match a connection from the state file. */ - only_by_uuid = TRUE; - } else { - nm_device_assume_state_reset(device); - _LOG2D(LOGD_DEVICE, - device, - "assume: cannot generate connection: %s", - gen_error->message); - return NULL; - } - } - - nm_device_assume_state_get(device, &assume_state_guess_assume, &assume_state_connection_uuid); - - /* Now we need to compare the generated connection to each configured - * connection. The comparison function is the heart of the connection - * assumption implementation and it must compare the connections very - * carefully to sort out various corner cases. Also, the comparison is - * not entirely symmetric. - * - * When no configured connection matches the generated connection, we keep - * the generated connection instead. - */ - if (assume_state_connection_uuid - && (connection_checked = - nm_settings_get_connection_by_uuid(priv->settings, assume_state_connection_uuid)) - && new_activation_allowed_for_connection(self, connection_checked) - && nm_device_check_connection_compatible( - device, - nm_settings_connection_get_connection(connection_checked), - NULL)) { - if (connection) { - NMConnection *con = nm_settings_connection_get_connection(connection_checked); - - if (nm_utils_match_connection((NMConnection *[]){con, NULL}, - connection, - TRUE, - nm_device_has_carrier(device), - nm_device_get_route_metric(device, AF_INET), - nm_device_get_route_metric(device, AF_INET6), - NULL, - NULL)) - matched = connection_checked; - } else - matched = connection_checked; - } - - if (!matched && only_by_uuid) { - _LOG2D(LOGD_DEVICE, device, "assume: cannot generate connection: %s", gen_error->message); - return NULL; - } - - if (!matched && assume_state_guess_assume) { - gs_free NMSettingsConnection **sett_conns = NULL; - guint len, i, j; - - /* the state file doesn't indicate a connection UUID to assume. Search the - * persistent connections for a matching candidate. */ - sett_conns = nm_manager_get_activatable_connections(self, FALSE, FALSE, &len); - if (len > 0) { - for (i = 0, j = 0; i < len; i++) { - NMSettingsConnection *sett_conn = sett_conns[i]; - - if (sett_conn != connection_checked - && nm_device_check_connection_compatible( - device, - nm_settings_connection_get_connection(sett_conn), - NULL)) - sett_conns[j++] = sett_conn; - } - sett_conns[j] = NULL; - len = j; - if (len > 0) { - gs_free NMConnection **conns = NULL; - NMConnection * con; - - g_qsort_with_data(sett_conns, - len, - sizeof(sett_conns[0]), - nm_settings_connection_cmp_timestamp_p_with_data, - NULL); - - conns = nm_settings_connections_array_to_connections(sett_conns, len); - - con = nm_utils_match_connection(conns, - connection, - FALSE, - nm_device_has_carrier(device), - nm_device_get_route_metric(device, AF_INET), - nm_device_get_route_metric(device, AF_INET6), - NULL, - NULL); - if (con) { - for (i = 0; i < len; i++) { - if (conns[i] == con) { - matched = sett_conns[i]; - break; - } - } - nm_assert(matched); - } - } - } - } - - if (matched) { - if (NM_FLAGS_HAS(nm_settings_connection_get_flags(matched), - NM_SETTINGS_CONNECTION_INT_FLAGS_EXTERNAL)) { - _LOG2D(LOGD_DEVICE, - device, - "assume: take over previous connection '%s' (%s)", - nm_settings_connection_get_id(matched), - nm_settings_connection_get_uuid(matched)); - NM_SET_OUT(out_generated, TRUE); - } else { - _LOG2I(LOGD_DEVICE, - device, - "assume: will attempt to assume matching connection '%s' (%s)%s", - nm_settings_connection_get_id(matched), - nm_settings_connection_get_uuid(matched), - assume_state_connection_uuid - && nm_streq(assume_state_connection_uuid, - nm_settings_connection_get_uuid(matched)) - ? " (indicated)" - : " (guessed)"); - } - nm_device_assume_state_reset(device); - return matched; - } - - _LOG2D(LOGD_DEVICE, - device, - "assume: generated connection '%s' (%s)", - nm_connection_get_id(connection), - nm_connection_get_uuid(connection)); - - nm_device_assume_state_reset(device); - - if (!nm_settings_add_connection(priv->settings, - connection, - NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY, - NM_SETTINGS_CONNECTION_ADD_REASON_NONE, - NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED - | NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE - | NM_SETTINGS_CONNECTION_INT_FLAGS_EXTERNAL, - &added, - &error)) { - _LOG2W(LOGD_SETTINGS, - device, - "assume: failure to save generated connection '%s': %s", - nm_connection_get_id(connection), - error->message); - g_error_free(error); - return NULL; - } - - NM_SET_OUT(out_generated, TRUE); - return added; -} - -static gboolean -copy_lease(const char *src, const char *dst) -{ - nm_auto_close int src_fd = -1; - int dst_fd; - ssize_t res, size = SSIZE_MAX; - - src_fd = open(src, O_RDONLY | O_CLOEXEC); - if (src_fd < 0) - return FALSE; - - dst_fd = open(dst, O_CREAT | O_EXCL | O_CLOEXEC | O_WRONLY, 0644); - if (dst_fd < 0) - return FALSE; - - while ((res = sendfile(dst_fd, src_fd, NULL, size)) > 0) - size -= res; - - nm_close(dst_fd); - - if (res != 0) { - unlink(dst); - return FALSE; - } - - return TRUE; -} - -static gboolean -recheck_assume_connection(NMManager *self, NMDevice *device) -{ - NMSettingsConnection *sett_conn; - gboolean was_unmanaged = FALSE; - gboolean generated = FALSE; - NMDeviceState state; - gboolean activation_type_assume; - - g_return_val_if_fail(NM_IS_MANAGER(self), FALSE); - g_return_val_if_fail(NM_IS_DEVICE(device), FALSE); - - if (!nm_device_get_managed(device, FALSE)) { - nm_device_assume_state_reset(device); - _LOG2D(LOGD_DEVICE, device, "assume: don't assume because %s", "not managed"); - return FALSE; - } - - state = nm_device_get_state(device); - if (state > NM_DEVICE_STATE_DISCONNECTED) { - nm_device_assume_state_reset(device); - _LOG2D(LOGD_DEVICE, - device, - "assume: don't assume due to device state %s", - nm_device_state_to_str(state)); - return FALSE; - } - - sett_conn = get_existing_connection(self, device, &generated); - /* log no reason. get_existing_connection() already does it. */ - if (!sett_conn) - return FALSE; - - activation_type_assume = !generated; - - if (state == NM_DEVICE_STATE_UNMANAGED) { - gs_free char *initramfs_lease = - g_strdup_printf(RUNSTATEDIR "/initramfs/net.%s.lease", nm_device_get_iface(device)); - gs_free char *connection_lease = g_strdup_printf(NMRUNDIR "/dhclient-%s-%s.lease", - nm_settings_connection_get_uuid(sett_conn), - nm_device_get_iface(device)); - - if (copy_lease(initramfs_lease, connection_lease)) { - unlink(initramfs_lease); - /* - * We've managed to steal the lease used by initramfs before it - * killed off the dhclient. We need to take ownership of the configured - * connection and act like the device was configured by us. - * Otherwise, the address would just expire. - */ - _LOG2I(LOGD_DEVICE, device, "assume: taking over an initramfs-configured connection"); - activation_type_assume = TRUE; - - if (generated) { - gs_unref_object NMConnection *con2 = NULL; - - con2 = nm_simple_connection_new_clone( - nm_settings_connection_get_connection(sett_conn)); - - /* Reset the IPv4 setting to empty method=auto, regardless of what assumption guessed. */ - nm_connection_add_setting(con2, - g_object_new(NM_TYPE_SETTING_IP4_CONFIG, - NM_SETTING_IP_CONFIG_METHOD, - NM_SETTING_IP4_CONFIG_METHOD_AUTO, - NULL)); - - nm_settings_connection_update(sett_conn, - con2, - NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP, - NM_SETTINGS_CONNECTION_INT_FLAGS_NONE, - NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE - | NM_SETTINGS_CONNECTION_INT_FLAGS_EXTERNAL, - NM_SETTINGS_CONNECTION_UPDATE_REASON_NONE, - "assume-initrd", - NULL); - } - } - } - - nm_device_sys_iface_state_set(device, - activation_type_assume ? NM_DEVICE_SYS_IFACE_STATE_ASSUME - : NM_DEVICE_SYS_IFACE_STATE_EXTERNAL); - - /* Move device to DISCONNECTED to activate the connection */ - if (state == NM_DEVICE_STATE_UNMANAGED) { - was_unmanaged = TRUE; - nm_device_state_changed(device, - NM_DEVICE_STATE_UNAVAILABLE, - NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); - } - if (nm_device_get_state(device) == NM_DEVICE_STATE_UNAVAILABLE) { - nm_device_state_changed(device, - NM_DEVICE_STATE_DISCONNECTED, - NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); - } - - g_return_val_if_fail(nm_device_get_state(device) >= NM_DEVICE_STATE_DISCONNECTED, FALSE); - - { - gs_unref_object NMActiveConnection *active = NULL; - gs_unref_object NMAuthSubject *subject = NULL; - NMActiveConnection * master_ac; - GError * error = NULL; - - subject = nm_auth_subject_new_internal(); - - /* Note: the lifetime of the activation connection is always bound to the profiles visibility - * via NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY. - * - * This only makes a difference, if the profile actually has "connection.permissions" - * set to limit visibility (which is not the case for externally managed, generated profiles). - * - * If we assume a previously active connection whose lifetime was unbound, we now bind it - * after restart. That is not correct, and can mean that the profile becomes subject to - * deactivation after restart (if the user logs out). - * - * This should be improved, but it's unclear how. */ - active = _new_active_connection( - self, - FALSE, - sett_conn, - NULL, - NULL, - NULL, - device, - subject, - activation_type_assume ? NM_ACTIVATION_TYPE_ASSUME : NM_ACTIVATION_TYPE_EXTERNAL, - activation_type_assume ? NM_ACTIVATION_REASON_ASSUME : NM_ACTIVATION_REASON_EXTERNAL, - NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY, - &error); - - if (!active) { - _LOGW(LOGD_DEVICE, - "assume: assumed connection %s failed to activate: %s", - nm_dbus_object_get_path(NM_DBUS_OBJECT(sett_conn)), - error->message); - g_error_free(error); - - if (was_unmanaged) { - nm_device_state_changed(device, - NM_DEVICE_STATE_UNAVAILABLE, - NM_DEVICE_STATE_REASON_CONFIG_FAILED); - } - - if (generated && !activation_type_assume) { - _LOG2D(LOGD_DEVICE, - device, - "assume: deleting generated connection after assuming failed"); - nm_settings_connection_delete(sett_conn, FALSE); - } else { - if (nm_device_sys_iface_state_get(device) == NM_DEVICE_SYS_IFACE_STATE_ASSUME) - nm_device_sys_iface_state_set(device, NM_DEVICE_SYS_IFACE_STATE_EXTERNAL); - } - return FALSE; - } - - /* If the device is a slave or VLAN, find the master ActiveConnection */ - master_ac = NULL; - if (find_master(self, - nm_settings_connection_get_connection(sett_conn), - device, - NULL, - NULL, - &master_ac, - NULL) - && master_ac) - nm_active_connection_set_master(active, master_ac); - - active_connection_add(self, active); - nm_device_queue_activation(device, NM_ACT_REQUEST(active)); - } - - return TRUE; -} - -static void -recheck_assume_connection_cb(NMManager *self, NMDevice *device) -{ - recheck_assume_connection(self, device); -} - -static void -device_ifindex_changed(NMDevice *device, GParamSpec *pspec, NMManager *self) -{ - _parent_notify_changed(self, device, FALSE); -} - -static void -device_ip_iface_changed(NMDevice *device, GParamSpec *pspec, NMManager *self) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - const char * ip_iface = nm_device_get_ip_iface(device); - NMDeviceType device_type = nm_device_get_device_type(device); - NMDevice * candidate; - - /* Remove NMDevice objects that are actually child devices of others, - * when the other device finally knows its IP interface name. For example, - * remove the PPP interface that's a child of a WWAN device, since it's - * not really a standalone NMDevice. - */ - c_list_for_each_entry (candidate, &priv->devices_lst_head, devices_lst) { - if (candidate != device && nm_streq0(nm_device_get_iface(candidate), ip_iface) - && nm_device_get_device_type(candidate) == device_type - && nm_device_is_real(candidate)) { - remove_device(self, candidate, FALSE); - break; - } - } -} - -static void -device_iface_changed(NMDevice *device, GParamSpec *pspec, NMManager *self) -{ - /* Virtual connections may refer to the new device name as - * parent device, retry to activate them. - */ - retry_connections_for_parent_device(self, device); -} - -static void -_emit_device_added_removed(NMManager *self, NMDevice *device, gboolean is_added) -{ - nm_dbus_object_emit_signal(NM_DBUS_OBJECT(self), - &interface_info_manager, - is_added ? &signal_info_device_added : &signal_info_device_removed, - "(o)", - nm_dbus_object_get_path(NM_DBUS_OBJECT(device))); - g_signal_emit(self, signals[is_added ? DEVICE_ADDED : DEVICE_REMOVED], 0, device); - _notify(self, PROP_DEVICES); -} - -static void -device_realized(NMDevice *device, GParamSpec *pspec, NMManager *self) -{ - _emit_device_added_removed(self, device, nm_device_is_real(device)); -} - -static NMConnectivityState -_get_best_connectivity(NMManager *self, int addr_family) -{ - NMManagerPrivate * priv = NM_MANAGER_GET_PRIVATE(self); - NMConnectivityState best_state; - NMDevice * dev; - gint64 best_metric; - - if (addr_family == AF_UNSPEC) { - best_state = _get_best_connectivity(self, AF_INET); - if (nm_connectivity_state_cmp(best_state, NM_CONNECTIVITY_FULL) >= 0) { - /* already FULL IPv4 connectivity. No need to check IPv6, it doesn't get - * better. */ - return best_state; - } - return NM_MAX_WITH_CMP(nm_connectivity_state_cmp, - best_state, - _get_best_connectivity(self, AF_INET6)); - } - - nm_assert_addr_family(addr_family); - - best_state = NM_CONNECTIVITY_UNKNOWN; - best_metric = G_MAXINT64; - c_list_for_each_entry (dev, &priv->devices_lst_head, devices_lst) { - const NMPObject * r; - NMConnectivityState state; - gint64 metric; - - r = nm_device_get_best_default_route(dev, addr_family); - if (r) - metric = NMP_OBJECT_CAST_IP_ROUTE(r)->metric; - else { - /* if all devices have no default-route, we still include the best - * of all connectivity state of all the devices. */ - metric = G_MAXINT64; - } - - if (metric > best_metric) { - /* we already have a default route with better metric. The connectivity state - * of this device is irreleavnt. */ - continue; - } - - state = nm_device_get_connectivity_state(dev, addr_family); - if (metric < best_metric) { - /* this device has a better default route. It wins. */ - best_metric = metric; - best_state = state; - } else { - best_state = NM_MAX_WITH_CMP(nm_connectivity_state_cmp, best_state, state); - } - - if (nm_connectivity_state_cmp(best_state, NM_CONNECTIVITY_FULL) >= 0) { - /* it doesn't get better than FULL. We are done. */ - break; - } - } - - return best_state; -} - -static void -device_connectivity_changed(NMDevice *device, GParamSpec *pspec, NMManager *self) -{ - update_connectivity_value(self); -} - -static void -update_connectivity_value(NMManager *self) -{ - NMManagerPrivate * priv = NM_MANAGER_GET_PRIVATE(self); - NMConnectivityState best_state; - - best_state = _get_best_connectivity(self, AF_UNSPEC); - if (best_state == priv->connectivity_state) - return; - - priv->connectivity_state = best_state; - - _LOGD(LOGD_CORE, - "connectivity checking indicates %s", - nm_connectivity_state_to_string(priv->connectivity_state)); - - nm_manager_update_state(self); - _notify(self, PROP_CONNECTIVITY); - nm_dispatcher_call_connectivity(priv->connectivity_state, NULL, NULL, NULL); -} - -static void -_device_realize_finish(NMManager *self, NMDevice *device, const NMPlatformLink *plink) -{ - g_return_if_fail(NM_IS_MANAGER(self)); - g_return_if_fail(NM_IS_DEVICE(device)); - - nm_device_realize_finish(device, plink); - - if (!nm_device_get_managed(device, FALSE)) { - nm_device_assume_state_reset(device); - return; - } - - if (recheck_assume_connection(self, device)) - return; - - /* if we failed to assume a connection for the managed device, but the device - * is still unavailable. Set UNAVAILABLE state again, this time with NOW_MANAGED. */ - nm_device_state_changed(device, - NM_DEVICE_STATE_UNAVAILABLE, - NM_DEVICE_STATE_REASON_NOW_MANAGED); - nm_device_emit_recheck_auto_activate(device); -} - -/** - * add_device: - * @self: the #NMManager - * @device: the #NMDevice to add - * @error: (out): the #GError - * - * If successful, this function will increase the references count of @device. - * Callers should decrease the reference count. - */ -static gboolean -add_device(NMManager *self, NMDevice *device, GError **error) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - const char * iface, *type_desc; - RfKillType rtype; - GSList * iter, *remove = NULL; - int ifindex; - const char * dbus_path; - NMDevice * candidate; - - /* No duplicates */ - ifindex = nm_device_get_ifindex(device); - if (ifindex > 0 && nm_manager_get_device_by_ifindex(self, ifindex)) { - g_set_error(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_FAILED, - "A device with ifindex %d already exists", - ifindex); - return FALSE; - } - - /* Remove existing devices owned by the new device; eg remove ethernet - * ports that are owned by a WWAN modem, since udev may announce them - * before the modem is fully discovered. - * - * FIXME: use parent/child device relationships instead of removing - * the child NMDevice entirely - */ - c_list_for_each_entry (candidate, &priv->devices_lst_head, devices_lst) { - if (nm_device_is_real(candidate) && (iface = nm_device_get_ip_iface(candidate)) - && nm_device_owns_iface(device, iface)) - remove = g_slist_prepend(remove, candidate); - } - for (iter = remove; iter; iter = iter->next) - remove_device(self, NM_DEVICE(iter->data), FALSE); - g_slist_free(remove); - - g_object_ref(device); - - nm_assert(c_list_is_empty(&device->devices_lst)); - c_list_link_tail(&priv->devices_lst_head, &device->devices_lst); - - g_signal_connect(device, - NM_DEVICE_STATE_CHANGED, - G_CALLBACK(manager_device_state_changed), - self); - - g_signal_connect(device, NM_DEVICE_REMOVED, G_CALLBACK(device_removed_cb), self); - - g_signal_connect_data(device, - NM_DEVICE_RECHECK_ASSUME, - G_CALLBACK(recheck_assume_connection_cb), - self, - NULL, - G_CONNECT_SWAPPED); - - g_signal_connect(device, - "notify::" NM_DEVICE_IP_IFACE, - G_CALLBACK(device_ip_iface_changed), - self); - - g_signal_connect(device, - "notify::" NM_DEVICE_IFINDEX, - G_CALLBACK(device_ifindex_changed), - self); - - g_signal_connect(device, "notify::" NM_DEVICE_IFACE, G_CALLBACK(device_iface_changed), self); - - g_signal_connect(device, "notify::" NM_DEVICE_REAL, G_CALLBACK(device_realized), self); - - g_signal_connect(device, - "notify::" NM_DEVICE_IP4_CONNECTIVITY, - G_CALLBACK(device_connectivity_changed), - self); - g_signal_connect(device, - "notify::" NM_DEVICE_IP6_CONNECTIVITY, - G_CALLBACK(device_connectivity_changed), - self); - - if (priv->startup) { - g_signal_connect(device, - "notify::" NM_DEVICE_HAS_PENDING_ACTION, - G_CALLBACK(device_has_pending_action_changed), - self); - } - - /* Update global rfkill state for this device type with the device's - * rfkill state, and then set this device's rfkill state based on the - * global state. - */ - rtype = nm_device_get_rfkill_type(device); - if (rtype != RFKILL_TYPE_UNKNOWN) { - nm_manager_rfkill_update(self, rtype); - nm_device_set_enabled(device, radio_enabled_for_type(self, rtype, TRUE)); - } - - iface = nm_device_get_iface(device); - g_assert(iface); - type_desc = nm_device_get_type_desc(device); - g_assert(type_desc); - - nm_device_set_unmanaged_by_user_settings(device); - - nm_device_set_unmanaged_flags(device, NM_UNMANAGED_SLEEPING, manager_sleeping(self)); - - dbus_path = nm_dbus_object_export(NM_DBUS_OBJECT(device)); - _LOG2I(LOGD_DEVICE, device, "new %s device (%s)", type_desc, dbus_path); - - nm_settings_device_added(priv->settings, device); - g_signal_emit(self, signals[INTERNAL_DEVICE_ADDED], 0, device); - _notify(self, PROP_ALL_DEVICES); - - _parent_notify_changed(self, device, FALSE); - - return TRUE; -} - -/*****************************************************************************/ - -static void -factory_device_added_cb(NMDeviceFactory *factory, NMDevice *device, gpointer user_data) -{ - NMManager *self = user_data; - GError * error = NULL; - - g_return_if_fail(NM_IS_MANAGER(self)); - - if (nm_device_realize_start(device, - NULL, - FALSE, /* assume_state_guess_assume */ - NULL, /* assume_state_connection_uuid */ - FALSE, /* set_nm_owned */ - NM_UNMAN_FLAG_OP_FORGET, - NULL, - &error)) { - add_device(self, device, NULL); - _device_realize_finish(self, device, NULL); - retry_connections_for_parent_device(self, device); - } else { - _LOG2W(LOGD_DEVICE, device, "failed to realize device: %s", error->message); - g_error_free(error); - } -} - -static void -_register_device_factory(NMDeviceFactory *factory, gpointer user_data) -{ - NMManager *self = NM_MANAGER(user_data); - - g_signal_connect(factory, - NM_DEVICE_FACTORY_DEVICE_ADDED, - G_CALLBACK(factory_device_added_cb), - self); -} - -/*****************************************************************************/ - -void -nm_manager_notify_device_availability_maybe_changed(NMManager *self) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - NMDevice * device; - - c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) - nm_device_notify_availability_maybe_changed(device); -} - -/*****************************************************************************/ - -static void -platform_link_added(NMManager * self, - int ifindex, - const NMPlatformLink * plink, - gboolean guess_assume, - const NMConfigDeviceStateData *dev_state) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - NMDeviceFactory * factory; - NMDevice * device = NULL; - NMDevice * candidate; - - g_return_if_fail(ifindex > 0); - - if (nm_manager_get_device_by_ifindex(self, ifindex)) - return; - - /* Let unrealized devices try to realize themselves with the link */ - c_list_for_each_entry (candidate, &priv->devices_lst_head, devices_lst) { - gboolean compatible = TRUE; - gs_free_error GError *error = NULL; - - if (nm_device_get_link_type(candidate) != plink->type) - continue; - - if (!nm_streq(nm_device_get_iface(candidate), plink->name)) - continue; - - if (nm_device_is_real(candidate)) { - /* There's already a realized device with the link's name - * and a different ifindex. - */ - if (nm_device_get_ifindex(candidate) <= 0) - nm_device_update_from_platform_link(candidate, plink); - else { - /* The ifindex of a device can't be changed after - * initialization because it is used as a key by - * the dns-manager. - */ - _LOGD(LOGD_DEVICE, - "(%s): removing old device %p after ifindex change from %d to %d", - plink->name, - candidate, - nm_device_get_ifindex(candidate), - ifindex); - remove_device(self, candidate, FALSE); - goto add; - } - return; - } else if (nm_device_realize_start(candidate, - plink, - FALSE, /* assume_state_guess_assume */ - NULL, /* assume_state_connection_uuid */ - FALSE, /* set_nm_owned */ - NM_UNMAN_FLAG_OP_FORGET, - &compatible, - &error)) { - _device_realize_finish(self, candidate, plink); - return; - } - - _LOGD(LOGD_DEVICE, "(%s): failed to realize from plink: '%s'", plink->name, error->message); - - /* Try next unrealized device */ - } - -add: - /* Try registered device factories */ - factory = nm_device_factory_manager_find_factory_for_link_type(plink->type); - if (factory) { - gboolean ignore = FALSE; - gs_free_error GError *error = NULL; - - device = - nm_device_factory_create_device(factory, plink->name, plink, NULL, &ignore, &error); - if (!device) { - if (!ignore) { - _LOGW(LOGD_PLATFORM, - "%s: factory failed to create device: %s", - plink->name, - error->message); - } else { - _LOGD(LOGD_PLATFORM, - "%s: factory failed to create device: %s", - plink->name, - error->message); - } - return; - } - } - - if (device == NULL) { - gboolean nm_plugin_missing = FALSE; - - switch (plink->type) { - case NM_LINK_TYPE_WWAN_NET: - case NM_LINK_TYPE_BNEP: - case NM_LINK_TYPE_OLPC_MESH: - case NM_LINK_TYPE_TEAM: - case NM_LINK_TYPE_WIFI: - _LOGI(LOGD_PLATFORM, - "(%s): '%s' plugin not available; creating generic device", - plink->name, - nm_link_type_to_string(plink->type)); - nm_plugin_missing = TRUE; - /* fall-through */ - default: - device = nm_device_generic_new(plink, nm_plugin_missing); - break; - } - } - - if (device) { - gs_free_error GError *error = NULL; - NMUnmanFlagOp unmanaged_user_explicit = NM_UNMAN_FLAG_OP_FORGET; - - if (dev_state) { - switch (dev_state->managed) { - case NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_MANAGED: - unmanaged_user_explicit = NM_UNMAN_FLAG_OP_SET_MANAGED; - break; - case NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_UNMANAGED: - unmanaged_user_explicit = NM_UNMAN_FLAG_OP_SET_UNMANAGED; - break; - case NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_UNKNOWN: - break; - } - } - - if (nm_device_realize_start(device, - plink, - guess_assume, - dev_state ? dev_state->connection_uuid : NULL, - dev_state ? (dev_state->nm_owned == 1) : FALSE, - unmanaged_user_explicit, - NULL, - &error)) { - add_device(self, device, NULL); - _device_realize_finish(self, device, plink); - retry_connections_for_parent_device(self, device); - } else { - _LOGW(LOGD_DEVICE, "%s: failed to realize device: %s", plink->name, error->message); - } - g_object_unref(device); - } -} - -typedef struct { - CList lst; - NMManager *self; - int ifindex; - guint idle_id; -} PlatformLinkCbData; - -static gboolean -_platform_link_cb_idle(PlatformLinkCbData *data) -{ - int ifindex = data->ifindex; - NMManager * self = data->self; - NMManagerPrivate * priv = NM_MANAGER_GET_PRIVATE(self); - const NMPlatformLink *plink; - - c_list_unlink_stale(&data->lst); - g_slice_free(PlatformLinkCbData, data); - - plink = nm_platform_link_get(priv->platform, ifindex); - if (plink) { - const NMPObject *plink_keep_alive = nmp_object_ref(NMP_OBJECT_UP_CAST(plink)); - - platform_link_added(self, ifindex, plink, FALSE, NULL); - nmp_object_unref(plink_keep_alive); - } else { - NMDevice *device; - GError * error = NULL; - - device = nm_manager_get_device_by_ifindex(self, ifindex); - if (device) { - if (nm_device_is_software(device)) { - nm_device_sys_iface_state_set(device, NM_DEVICE_SYS_IFACE_STATE_REMOVED); - /* Our software devices stick around until their connection is removed */ - if (!nm_device_unrealize(device, FALSE, &error)) { - _LOG2W(LOGD_DEVICE, device, "failed to unrealize: %s", error->message); - g_clear_error(&error); - remove_device(self, device, FALSE); - } else { - nm_device_update_from_platform_link(device, NULL); - } - } else { - /* Hardware and external devices always get removed when their kernel link is gone */ - remove_device(self, device, FALSE); - } - } - } - - return G_SOURCE_REMOVE; -} - -static void -platform_link_cb(NMPlatform * platform, - int obj_type_i, - int ifindex, - NMPlatformLink *plink, - int change_type_i, - gpointer user_data) -{ - NMManager * self; - NMManagerPrivate * priv; - const NMPlatformSignalChangeType change_type = change_type_i; - PlatformLinkCbData * data; - - switch (change_type) { - case NM_PLATFORM_SIGNAL_ADDED: - case NM_PLATFORM_SIGNAL_REMOVED: - self = NM_MANAGER(user_data); - priv = NM_MANAGER_GET_PRIVATE(self); - - data = g_slice_new(PlatformLinkCbData); - data->self = self; - data->ifindex = ifindex; - c_list_link_tail(&priv->link_cb_lst, &data->lst); - data->idle_id = g_idle_add((GSourceFunc) _platform_link_cb_idle, data); - break; - default: - break; - } -} - -static void -platform_query_devices(NMManager *self) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - gs_unref_ptrarray GPtrArray *links = NULL; - int i; - gboolean guess_assume; - gs_free char * order = NULL; - - guess_assume = nm_config_get_first_start(nm_config_get()); - order = nm_config_data_get_value(NM_CONFIG_GET_DATA, - NM_CONFIG_KEYFILE_GROUP_MAIN, - NM_CONFIG_KEYFILE_KEY_MAIN_SLAVES_ORDER, - NM_CONFIG_GET_VALUE_STRIP); - links = nm_platform_link_get_all(priv->platform, !nm_streq0(order, "index")); - if (!links) - return; - for (i = 0; i < links->len; i++) { - const NMPlatformLink * link = NMP_OBJECT_CAST_LINK(links->pdata[i]); - const NMConfigDeviceStateData *dev_state; - - dev_state = nm_config_device_state_get(priv->config, link->ifindex); - platform_link_added(self, - link->ifindex, - link, - guess_assume && (!dev_state || !dev_state->connection_uuid), - dev_state); - } -} - -static void -rfkill_manager_rfkill_changed_cb(NMRfkillManager *rfkill_mgr, - RfKillType rtype, - RfKillState udev_state, - gpointer user_data) -{ - nm_manager_rfkill_update(NM_MANAGER(user_data), rtype); -} - -const CList * -nm_manager_get_devices(NMManager *manager) -{ - g_return_val_if_fail(NM_IS_MANAGER(manager), NULL); - - return &NM_MANAGER_GET_PRIVATE(manager)->devices_lst_head; -} - -typedef enum { - DEVICE_ACTIVATION_PRIO_NONE, - DEVICE_ACTIVATION_PRIO_UNMANAGED, - DEVICE_ACTIVATION_PRIO_UNAVAILABLE, - DEVICE_ACTIVATION_PRIO_DEACTIVATING, - DEVICE_ACTIVATION_PRIO_ACTIVATING, - DEVICE_ACTIVATION_PRIO_ACTIVATED, - DEVICE_ACTIVATION_PRIO_DISCONNECTED, - - _DEVICE_ACTIVATION_PRIO_BEST = DEVICE_ACTIVATION_PRIO_DISCONNECTED, -} DeviceActivationPrio; - -static DeviceActivationPrio -_device_get_activation_prio(NMDevice *device) -{ - if (!nm_device_get_managed(device, TRUE)) - return DEVICE_ACTIVATION_PRIO_NONE; - - switch (nm_device_get_state(device)) { - case NM_DEVICE_STATE_DISCONNECTED: - return DEVICE_ACTIVATION_PRIO_DISCONNECTED; - case NM_DEVICE_STATE_ACTIVATED: - return DEVICE_ACTIVATION_PRIO_ACTIVATED; - case NM_DEVICE_STATE_PREPARE: - case NM_DEVICE_STATE_CONFIG: - case NM_DEVICE_STATE_NEED_AUTH: - case NM_DEVICE_STATE_IP_CONFIG: - case NM_DEVICE_STATE_IP_CHECK: - case NM_DEVICE_STATE_SECONDARIES: - return DEVICE_ACTIVATION_PRIO_ACTIVATING; - case NM_DEVICE_STATE_DEACTIVATING: - case NM_DEVICE_STATE_FAILED: - return DEVICE_ACTIVATION_PRIO_DEACTIVATING; - case NM_DEVICE_STATE_UNAVAILABLE: - return DEVICE_ACTIVATION_PRIO_UNAVAILABLE; - case NM_DEVICE_STATE_UNKNOWN: - case NM_DEVICE_STATE_UNMANAGED: - return DEVICE_ACTIVATION_PRIO_UNMANAGED; - } - - g_return_val_if_reached(DEVICE_ACTIVATION_PRIO_UNAVAILABLE); -} - -static NMDevice * -nm_manager_get_best_device_for_connection(NMManager * self, - NMSettingsConnection *sett_conn, - NMConnection * connection, - gboolean for_user_request, - GHashTable * unavailable_devices, - GError ** error) -{ - NMManagerPrivate * priv = NM_MANAGER_GET_PRIVATE(self); - NMActiveConnectionState ac_state; - NMActiveConnection * ac; - NMDevice * ac_device; - NMDevice * device; - struct { - NMDevice * device; - DeviceActivationPrio prio; - } best = { - .device = NULL, - .prio = DEVICE_ACTIVATION_PRIO_NONE, - }; - NMDeviceCheckConAvailableFlags flags; - gs_unref_ptrarray GPtrArray *all_ac_arr = NULL; - gs_free_error GError * local_best = NULL; - NMConnectionMultiConnect multi_connect; - - nm_assert(!sett_conn || NM_IS_SETTINGS_CONNECTION(sett_conn)); - nm_assert(!connection || NM_IS_CONNECTION(connection)); - nm_assert(sett_conn || connection); - nm_assert(!connection || !sett_conn - || connection == nm_settings_connection_get_connection(sett_conn)); - - if (!connection) - connection = nm_settings_connection_get_connection(sett_conn); - - multi_connect = _nm_connection_get_multi_connect(connection); - - if (!for_user_request) - flags = NM_DEVICE_CHECK_CON_AVAILABLE_NONE; - else { - /* if the profile is multi-connect=single, we also consider devices which - * are marked as unmanaged. And explicit user-request shows sufficient user - * intent to make the device managed. - * That is also, because we expect that such profile is suitably tied - * to the intended device. So when an unmanaged device matches, the user's - * intent is clear. - * - * For multi-connect != single devices that is different. The profile - * is not restricted to a particular device. - * For that reason, plain `nmcli connection up "$MULIT_PROFILE"` seems - * less suitable for multi-connect profiles, because the target device is - * left unspecified. Anyway, if a user issues - * - * $ nmcli device set "$DEVICE" managed no - * $ nmcli connection up "$MULIT_PROFILE" - * - * then it is reasonable for multi-connect profiles to not consider - * the device a suitable candidate. - * - * This may be seen inconsistent, but I think that it makes a lot of - * sense. Also note that "connection.multi-connect" work quite differently - * in aspects like activation. E.g. `nmcli connection up` of multi-connect - * "single" profile, will deactivate the profile if it is active already. - * That is different from multi-connect profiles, where it will aim to - * activate the profile one more time on an hitherto disconnected device. - */ - if (multi_connect == NM_CONNECTION_MULTI_CONNECT_SINGLE) - flags = NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST; - else - flags = NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST - & ~_NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_OVERRULE_UNMANAGED; - } - - if (multi_connect == NM_CONNECTION_MULTI_CONNECT_SINGLE - && (ac = active_connection_find_by_connection(self, - sett_conn, - connection, - NM_ACTIVE_CONNECTION_STATE_DEACTIVATING, - &all_ac_arr))) { - /* if we have a profile which may activate on only one device (multi-connect single), then - * we prefer the device on which the profile is already active. It means to reactivate - * the profile on the same device. - * - * If the profile can be activated on multiple devices, we don't do this. In fact, the - * check below for the DeviceActivationPrio will prefer devices which are not already - * activated (with this or another) profile. */ - - ac_device = nm_active_connection_get_device(ac); - if (ac_device - && ((unavailable_devices && g_hash_table_contains(unavailable_devices, ac_device)) - || !nm_device_check_connection_available(ac_device, connection, flags, NULL, NULL))) - ac_device = NULL; - - if (all_ac_arr) { - guint i; - - ac_state = nm_active_connection_get_state(ac); - - /* we found several active connections. See which one is the most suitable... */ - nm_assert(ac == all_ac_arr->pdata[0]); - for (i = 1; i < all_ac_arr->len; i++) { - NMActiveConnection * ac2 = all_ac_arr->pdata[i]; - NMDevice * ac_device2 = nm_active_connection_get_device(ac2); - NMActiveConnectionState ac_state2; - - if (!ac_device2 - || (unavailable_devices - && g_hash_table_contains(unavailable_devices, ac_device2)) - || !nm_device_check_connection_available(ac_device2, - connection, - flags, - NULL, - NULL)) - continue; - - ac_state2 = nm_active_connection_get_state(ac2); - - if (!ac_device) - goto found_better; - - if (ac_state == ac_state2) { - /* active-connections are in their list in the order in which they are connected. - * If we have two with same state, the later (newer) one is preferred. */ - goto found_better; - } - - switch (ac_state) { - case NM_ACTIVE_CONNECTION_STATE_UNKNOWN: - if (NM_IN_SET(ac_state2, - NM_ACTIVE_CONNECTION_STATE_ACTIVATING, - NM_ACTIVE_CONNECTION_STATE_ACTIVATED, - NM_ACTIVE_CONNECTION_STATE_DEACTIVATING)) - goto found_better; - break; - case NM_ACTIVE_CONNECTION_STATE_ACTIVATING: - if (NM_IN_SET(ac_state2, NM_ACTIVE_CONNECTION_STATE_ACTIVATED)) - goto found_better; - break; - case NM_ACTIVE_CONNECTION_STATE_ACTIVATED: - break; - case NM_ACTIVE_CONNECTION_STATE_DEACTIVATING: - if (NM_IN_SET(ac_state2, - NM_ACTIVE_CONNECTION_STATE_ACTIVATING, - NM_ACTIVE_CONNECTION_STATE_ACTIVATED)) - goto found_better; - break; - default: - nm_assert_not_reached(); - goto found_better; - } - - continue; -found_better: - ac = ac2; - ac_state = ac_state2; - ac_device = ac_device2; - } - } - - if (ac_device) - return ac_device; - } - - /* Pick the first device that's compatible with the connection. */ - c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { - GError * local = NULL; - DeviceActivationPrio prio; - - if (unavailable_devices && g_hash_table_contains(unavailable_devices, device)) - continue; - - /* determine the priority of this device. Currently, this priority is independent - * of the profile (connection) and the device's details (aside the state). - * - * Maybe nm_device_check_connection_available() should instead return a priority, - * as it has more information available. - * - * For example, if you have multiple Wi-Fi devices, currently a user-request would - * also select the device if the AP is not visible. Optimally, if one of the two - * devices sees the AP and the other one doesn't, the former would be preferred. - * For that, the priority would need to be determined by nm_device_check_connection_available(). */ - prio = _device_get_activation_prio(device); - if (prio <= best.prio && best.device) { - /* we already have a matching device with a better priority. This candidate - * cannot be better. Skip the check. - * - * Also note, that below we collect the best error message @local_best. - * Since we already have best.device, the error message does not matter - * either, and we can skip nm_device_check_connection_available() altogether. */ - continue; - } - - if (nm_device_check_connection_available(device, - connection, - flags, - NULL, - error ? &local : NULL)) { - if (prio == _DEVICE_ACTIVATION_PRIO_BEST) { - /* this device already has the best priority. It cannot get better - * and finish the search. */ - return device; - } - best.prio = prio; - best.device = device; - continue; - } - - if (error) { - gboolean reset_error; - - if (!local_best) - reset_error = TRUE; - else if (local_best->domain != NM_UTILS_ERROR) - reset_error = (local->domain == NM_UTILS_ERROR); - else { - reset_error = (local->domain == NM_UTILS_ERROR && local_best->code < local->code); - } - - if (reset_error) { - g_clear_error(&local_best); - g_set_error(&local_best, - local->domain, - local->code, - "device %s not available because %s", - nm_device_get_iface(device), - local->message); - } - g_error_free(local); - } - } - - if (best.device) - return best.device; - - if (error) { - if (local_best) - g_propagate_error(error, g_steal_pointer(&local_best)); - else { - nm_utils_error_set_literal(error, NM_UTILS_ERROR_UNKNOWN, "no suitable device found"); - } - } - return NULL; -} - -static const char ** -_get_devices_paths(NMManager *self, gboolean all_devices) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - const char ** paths = NULL; - guint i; - NMDevice * device; - - paths = g_new(const char *, c_list_length(&priv->devices_lst_head) + 1); - - i = 0; - c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { - const char *path; - - path = nm_dbus_object_get_path(NM_DBUS_OBJECT(device)); - if (!path) - continue; - - if (!all_devices && !nm_device_is_real(device)) - continue; - - paths[i++] = path; - } - paths[i++] = NULL; - - return paths; -} - -static void -impl_manager_get_devices(NMDBusObject * obj, - const NMDBusInterfaceInfoExtended *interface_info, - const NMDBusMethodInfoExtended * method_info, - GDBusConnection * connection, - const char * sender, - GDBusMethodInvocation * invocation, - GVariant * parameters) -{ - NMManager * self = NM_MANAGER(obj); - gs_free const char **paths = NULL; - - paths = _get_devices_paths(self, FALSE); - g_dbus_method_invocation_return_value(invocation, g_variant_new("(^ao)", (char **) paths)); -} - -static void -impl_manager_get_all_devices(NMDBusObject * obj, - const NMDBusInterfaceInfoExtended *interface_info, - const NMDBusMethodInfoExtended * method_info, - GDBusConnection * connection, - const char * sender, - GDBusMethodInvocation * invocation, - GVariant * parameters) -{ - NMManager * self = NM_MANAGER(obj); - gs_free const char **paths = NULL; - - paths = _get_devices_paths(self, TRUE); - g_dbus_method_invocation_return_value(invocation, g_variant_new("(^ao)", (char **) paths)); -} - -static void -impl_manager_get_device_by_ip_iface(NMDBusObject * obj, - const NMDBusInterfaceInfoExtended *interface_info, - const NMDBusMethodInfoExtended * method_info, - GDBusConnection * connection, - const char * sender, - GDBusMethodInvocation * invocation, - GVariant * parameters) -{ - NMManager * self = NM_MANAGER(obj); - NMDevice * device; - const char *path = NULL; - const char *iface; - - g_variant_get(parameters, "(&s)", &iface); - - device = find_device_by_ip_iface(self, iface); - if (device) - path = nm_dbus_object_get_path(NM_DBUS_OBJECT(device)); - - if (!path) { - g_dbus_method_invocation_return_error(invocation, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "No device found for the requested iface."); - return; - } - - g_dbus_method_invocation_return_value(invocation, g_variant_new("(o)", path)); -} - -static gboolean -is_compatible_with_slave(NMConnection *master, NMConnection *slave) -{ - NMSettingConnection *s_con; - - g_return_val_if_fail(master, FALSE); - g_return_val_if_fail(slave, FALSE); - - s_con = nm_connection_get_setting_connection(slave); - g_assert(s_con); - - return nm_connection_is_type(master, nm_setting_connection_get_slave_type(s_con)); -} - -/** - * find_master: - * @self: #NMManager object - * @connection: the #NMConnection to find the master connection and device for - * @device: the #NMDevice, if any, which will activate @connection - * @out_master_connection: on success, the master connection of @connection if - * that master connection was found - * @out_master_device: on success, the master device of @connection if that - * master device was found - * @out_master_ac: on success, the master ActiveConnection of @connection if - * there already is one - * @error: the error, if an error occurred - * - * Given an #NMConnection, attempts to find its master. If @connection has - * no master, this will return %TRUE and @out_master_connection and - * @out_master_device will be untouched. - * - * If @connection does have a master, then the outputs depend on what is in its - * #NMSettingConnection:master property: - * - * If "master" is the ifname of an existing #NMDevice, and that device has a - * compatible master connection activated or activating on it, then - * @out_master_device, @out_master_connection, and @out_master_ac will all be - * set. If the device exists and is idle, only @out_master_device will be set. - * If the device exists and has an incompatible connection on it, an error - * will be returned. - * - * If "master" is the ifname of a non-existent device, then @out_master_device - * will be %NULL, and @out_master_connection will be a connection whose - * activation would cause the creation of that device. @out_master_ac MAY be - * set in this case as well (if the connection has started activating, but has - * not yet created its device). - * - * If "master" is the UUID of a compatible master connection, then - * @out_master_connection will be the identified connection, and @out_master_device - * and/or @out_master_ac will be set if the connection is currently activating. - * (@out_master_device will not be set if the device exists but does not have - * @out_master_connection active/activating on it.) - * - * Returns: %TRUE if the master device and/or connection could be found or if - * the connection did not require a master, %FALSE otherwise - **/ -static gboolean -find_master(NMManager * self, - NMConnection * connection, - NMDevice * device, - NMSettingsConnection **out_master_connection, - NMDevice ** out_master_device, - NMActiveConnection ** out_master_ac, - GError ** error) -{ - NMManagerPrivate * priv = NM_MANAGER_GET_PRIVATE(self); - NMSettingConnection * s_con; - const char * master; - NMDevice * master_device = NULL; - NMSettingsConnection *master_connection; - - s_con = nm_connection_get_setting_connection(connection); - g_assert(s_con); - master = nm_setting_connection_get_master(s_con); - - if (master == NULL) - return TRUE; /* success, but no master */ - - /* Try as an interface name first */ - master_device = find_device_by_iface(self, master, NULL, connection); - if (master_device) { - if (master_device == device) { - g_set_error_literal(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_DEPENDENCY_FAILED, - "Device cannot be its own master"); - return FALSE; - } - - master_connection = nm_device_get_settings_connection(master_device); - if (master_connection - && !is_compatible_with_slave(nm_settings_connection_get_connection(master_connection), - connection)) { - g_set_error(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_DEPENDENCY_FAILED, - "The active connection on %s is not compatible", - nm_device_get_iface(master_device)); - return FALSE; - } - } else { - /* Try master as a connection UUID */ - master_connection = nm_settings_get_connection_by_uuid(priv->settings, master); - if (master_connection) { - NMDevice *candidate; - - /* Check if the master connection is activated on some device already */ - c_list_for_each_entry (candidate, &priv->devices_lst_head, devices_lst) { - if (candidate == device) - continue; - - if (nm_device_get_settings_connection(candidate) == master_connection) { - master_device = candidate; - break; - } - } - } - } - - if (out_master_connection) - *out_master_connection = master_connection; - if (out_master_device) - *out_master_device = master_device; - if (out_master_ac && master_connection) { - *out_master_ac = active_connection_find(self, - master_connection, - NULL, - NM_ACTIVE_CONNECTION_STATE_DEACTIVATING, - NULL); - } - - if (master_device || master_connection) - return TRUE; - else { - g_set_error_literal(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "Master connection not found or invalid"); - return FALSE; - } -} - -/** - * ensure_master_active_connection: - * @self: the #NMManager - * @subject: the #NMAuthSubject representing the requestor of this activation - * @connection: the connection that should depend on @master_connection - * @device: the #NMDevice, if any, which will activate @connection - * @master_connection: the master connection, or %NULL - * @master_device: the master device, or %NULL - * @activation_reason: the reason for activation - * @error: the error, if an error occurred - * - * Determines whether a given #NMConnection depends on another connection to - * be activated, and if so, finds that master connection or creates it. - * - * If @master_device and @master_connection are both set then @master_connection - * MUST already be activated or activating on @master_device, and the function will - * return the existing #NMActiveConnection. - * - * If only @master_device is set, and it has an #NMActiveConnection, then the - * function will return it if it is a compatible master, or an error if not. If it - * doesn't have an AC, then the function will create one if a compatible master - * connection exists, or return an error if not. - * - * If only @master_connection is set, then this will try to find or create a compatible - * #NMDevice, and either activate @master_connection on that device or return an error. - * - * Returns: the master #NMActiveConnection that the caller should depend on, or - * %NULL if an error occurred - */ -static NMActiveConnection * -ensure_master_active_connection(NMManager * self, - NMAuthSubject * subject, - NMConnection * connection, - NMDevice * device, - NMSettingsConnection *master_connection, - NMDevice * master_device, - NMActivationReason activation_reason, - GError ** error) -{ - NMManagerPrivate * priv = NM_MANAGER_GET_PRIVATE(self); - NMActiveConnection *ac; - NMActiveConnection *master_ac = NULL; - NMDeviceState master_state; - gboolean bind_lifetime_to_profile_visibility; - - g_return_val_if_fail(connection, NULL); - g_return_val_if_fail(master_connection || master_device, FALSE); - - bind_lifetime_to_profile_visibility = - NM_FLAGS_HAS(nm_device_get_activation_state_flags(device), - NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY); - - /* If the master device isn't activated then we need to activate it using - * compatible connection. If it's already activating we can just proceed. - */ - if (master_device) { - NMSettingsConnection *device_connection = nm_device_get_settings_connection(master_device); - - /* If we're passed a connection and a device, we require that connection - * be already activated on the device, eg returned from find_master(). - */ - g_assert(!master_connection || master_connection == device_connection); - if (device_connection - && !is_compatible_with_slave(nm_settings_connection_get_connection(device_connection), - connection)) { - g_set_error(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_DEPENDENCY_FAILED, - "The active connection %s is not compatible", - nm_connection_get_id(connection)); - return NULL; - } - - master_state = nm_device_get_state(master_device); - if ((master_state == NM_DEVICE_STATE_ACTIVATED) || nm_device_is_activating(master_device)) { - /* Device already using master_connection */ - ac = NM_ACTIVE_CONNECTION(nm_device_get_act_request(master_device)); - g_return_val_if_fail(device_connection, ac); - - if (!bind_lifetime_to_profile_visibility) { - /* unbind the lifetime. */ - nm_active_connection_set_state_flags_clear( - ac, - NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY); - } - - return ac; - } - - /* If the device is disconnected, find a compatible connection and - * activate it on the device. - */ - if (master_state == NM_DEVICE_STATE_DISCONNECTED || !nm_device_is_real(master_device)) { - gs_free NMSettingsConnection **connections = NULL; - guint i; - - g_assert(master_connection == NULL); - - /* Find a compatible connection and activate this device using it */ - connections = nm_manager_get_activatable_connections(self, FALSE, TRUE, NULL); - for (i = 0; connections[i]; i++) { - NMSettingsConnection *candidate = connections[i]; - NMConnection * cand_conn = nm_settings_connection_get_connection(candidate); - - /* Ensure eg bond/team slave and the candidate master is a - * bond/team master - */ - if (!is_compatible_with_slave(cand_conn, connection)) - continue; - - if (nm_device_check_connection_available( - master_device, - cand_conn, - NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST, - NULL, - NULL)) { - master_ac = nm_manager_activate_connection( - self, - candidate, - NULL, - NULL, - master_device, - subject, - NM_ACTIVATION_TYPE_MANAGED, - activation_reason, - bind_lifetime_to_profile_visibility - ? NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY - : NM_ACTIVATION_STATE_FLAG_NONE, - error); - return master_ac; - } - } - - g_set_error(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_UNKNOWN_CONNECTION, - "No compatible connection found."); - return NULL; - } - - /* Otherwise, the device is unmanaged, unavailable, or disconnecting */ - g_set_error(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_DEPENDENCY_FAILED, - "Device unmanaged or not available for activation"); - } else if (master_connection) { - NMDevice *candidate; - - /* Find a compatible device and activate it using this connection */ - c_list_for_each_entry (candidate, &priv->devices_lst_head, devices_lst) { - if (candidate == device) { - /* A device obviously can't be its own master */ - continue; - } - - if (!nm_device_check_connection_available( - candidate, - nm_settings_connection_get_connection(master_connection), - NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST, - NULL, - NULL)) - continue; - - if (!nm_device_is_software(candidate)) { - master_state = nm_device_get_state(candidate); - if (nm_device_is_real(candidate) && master_state != NM_DEVICE_STATE_DISCONNECTED) - continue; - } - - master_ac = nm_manager_activate_connection( - self, - master_connection, - NULL, - NULL, - candidate, - subject, - NM_ACTIVATION_TYPE_MANAGED, - activation_reason, - bind_lifetime_to_profile_visibility - ? NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY - : NM_ACTIVATION_STATE_FLAG_NONE, - error); - return master_ac; - } - - g_set_error(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "No device available"); - } else - g_assert_not_reached(); - - return NULL; -} - -typedef struct { - NMSettingsConnection *connection; - NMDevice * device; -} SlaveConnectionInfo; - -/** - * find_slaves: - * @manager: #NMManager object - * @sett_conn: the master #NMSettingsConnection to find slave connections for - * @device: the master #NMDevice for the @sett_conn - * @out_n_slaves: on return, the number of slaves found - * - * Given an #NMSettingsConnection, attempts to find its slaves. If @sett_conn is not - * master, or has not any slaves, this will return %NULL. - * - * Returns: an array of #SlaveConnectionInfo for given master @sett_conn, or %NULL - **/ -static SlaveConnectionInfo * -find_slaves(NMManager * manager, - NMSettingsConnection *sett_conn, - NMDevice * device, - guint * out_n_slaves, - gboolean for_user_request) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(manager); - gs_free NMSettingsConnection **all_connections = NULL; - guint n_all_connections; - guint i; - SlaveConnectionInfo * slaves = NULL; - guint n_slaves = 0; - NMSettingConnection * s_con; - gs_unref_hashtable GHashTable *devices = NULL; - - nm_assert(out_n_slaves); - - s_con = nm_connection_get_setting_connection(nm_settings_connection_get_connection(sett_conn)); - g_return_val_if_fail(s_con, NULL); - - devices = g_hash_table_new(nm_direct_hash, NULL); - - /* Search through all connections, not only inactive ones, because - * even if a slave was already active, it might be deactivated during - * master reactivation. - */ - all_connections = nm_settings_get_connections_clone( - priv->settings, - &n_all_connections, - NULL, - NULL, - nm_settings_connection_cmp_autoconnect_priority_p_with_data, - NULL); - for (i = 0; i < n_all_connections; i++) { - NMSettingsConnection *master_connection = NULL; - NMDevice * master_device = NULL, *slave_device; - NMSettingsConnection *candidate = all_connections[i]; - - find_master(manager, - nm_settings_connection_get_connection(candidate), - NULL, - &master_connection, - &master_device, - NULL, - NULL); - if ((master_connection && master_connection == sett_conn) - || (master_device && master_device == device)) { - slave_device = nm_manager_get_best_device_for_connection(manager, - candidate, - NULL, - for_user_request, - devices, - NULL); - - if (!slaves) { - /* what we allocate is quite likely much too large. Don't bother, it is only - * a temporary buffer. */ - slaves = g_new(SlaveConnectionInfo, n_all_connections); - } - - nm_assert(n_slaves < n_all_connections); - slaves[n_slaves].connection = candidate, slaves[n_slaves].device = slave_device, - n_slaves++; - - if (slave_device) - g_hash_table_add(devices, slave_device); - } - } - - *out_n_slaves = n_slaves; - - /* Warning: returns NULL if n_slaves is zero. */ - return slaves; -} - -static gboolean -should_connect_slaves(NMConnection *connection, NMDevice *device) -{ - NMSettingConnection * s_con; - NMSettingConnectionAutoconnectSlaves val; - - s_con = nm_connection_get_setting_connection(connection); - g_assert(s_con); - - val = nm_setting_connection_get_autoconnect_slaves(s_con); - if (val != NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_DEFAULT) - goto out; - - val = - nm_config_data_get_connection_default_int64(NM_CONFIG_GET_DATA, - NM_CON_DEFAULT("connection.autoconnect-slaves"), - device, - 0, - 1, - -1); - -out: - if (val == NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_NO) - return FALSE; - if (val == NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_YES) - return TRUE; - return FALSE; -} - -static int -compare_slaves(gconstpointer a, gconstpointer b, gpointer sort_by_name) -{ - const SlaveConnectionInfo *a_info = a; - const SlaveConnectionInfo *b_info = b; - - /* Slaves without a device at the end */ - if (!a_info->device) - return 1; - if (!b_info->device) - return -1; - - if (GPOINTER_TO_INT(sort_by_name)) { - return nm_strcmp0(nm_device_get_iface(a_info->device), nm_device_get_iface(b_info->device)); - } - - return nm_device_get_ifindex(a_info->device) - nm_device_get_ifindex(b_info->device); -} - -static void -autoconnect_slaves(NMManager * self, - NMSettingsConnection *master_connection, - NMDevice * master_device, - NMAuthSubject * subject, - gboolean for_user_request) -{ - GError *local_err = NULL; - - if (should_connect_slaves(nm_settings_connection_get_connection(master_connection), - master_device)) { - gs_free SlaveConnectionInfo *slaves = NULL; - guint i, n_slaves = 0; - gboolean bind_lifetime_to_profile_visibility; - - slaves = find_slaves(self, master_connection, master_device, &n_slaves, for_user_request); - if (n_slaves > 1) { - gs_free char *value = NULL; - - value = nm_config_data_get_value(NM_CONFIG_GET_DATA, - NM_CONFIG_KEYFILE_GROUP_MAIN, - NM_CONFIG_KEYFILE_KEY_MAIN_SLAVES_ORDER, - NM_CONFIG_GET_VALUE_STRIP); - g_qsort_with_data(slaves, - n_slaves, - sizeof(slaves[0]), - compare_slaves, - GINT_TO_POINTER(!nm_streq0(value, "index"))); - } - - bind_lifetime_to_profile_visibility = - n_slaves > 0 - && NM_FLAGS_HAS(nm_device_get_activation_state_flags(master_device), - NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY); - - for (i = 0; i < n_slaves; i++) { - SlaveConnectionInfo *slave = &slaves[i]; - const char * uuid; - - /* To avoid loops when autoconnecting slaves, we propagate - * the UUID of the initial connection down to slaves until - * the same connection is found. - */ - uuid = g_object_get_qdata(G_OBJECT(master_connection), autoconnect_root_quark()); - if (nm_streq0(nm_settings_connection_get_uuid(slave->connection), uuid)) { - _LOGI(LOGD_CORE, - "will NOT activate slave connection '%s' (%s) as a dependency for master " - "'%s' (%s): " - "circular dependency detected", - nm_settings_connection_get_id(slave->connection), - nm_settings_connection_get_uuid(slave->connection), - nm_settings_connection_get_id(master_connection), - nm_settings_connection_get_uuid(master_connection)); - continue; - } - - if (!uuid) - uuid = nm_settings_connection_get_uuid(master_connection); - g_object_set_qdata_full(G_OBJECT(slave->connection), - autoconnect_root_quark(), - g_strdup(uuid), - g_free); - - if (!slave->device) { - _LOGD(LOGD_CORE, - "will NOT activate slave connection '%s' (%s) as a dependency for master " - "'%s' (%s): " - "no compatible device found", - nm_settings_connection_get_id(slave->connection), - nm_settings_connection_get_uuid(slave->connection), - nm_settings_connection_get_id(master_connection), - nm_settings_connection_get_uuid(master_connection)); - continue; - } - - _LOGD(LOGD_CORE, - "will activate slave connection '%s' (%s) as a dependency for master '%s' (%s)", - nm_settings_connection_get_id(slave->connection), - nm_settings_connection_get_uuid(slave->connection), - nm_settings_connection_get_id(master_connection), - nm_settings_connection_get_uuid(master_connection)); - - /* Schedule slave activation */ - nm_manager_activate_connection( - self, - slave->connection, - NULL, - NULL, - slave->device, - subject, - NM_ACTIVATION_TYPE_MANAGED, - NM_ACTIVATION_REASON_AUTOCONNECT_SLAVES, - bind_lifetime_to_profile_visibility - ? NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY - : NM_ACTIVATION_STATE_FLAG_NONE, - &local_err); - if (local_err) { - _LOGW(LOGD_CORE, "Slave connection activation failed: %s", local_err->message); - g_clear_error(&local_err); - } - } - } -} - -static gboolean -_internal_activate_vpn(NMManager *self, NMActiveConnection *active, GError **error) -{ - nm_assert(NM_IS_VPN_CONNECTION(active)); - - nm_dbus_object_export(NM_DBUS_OBJECT(active)); - if (!nm_vpn_manager_activate_connection(NM_MANAGER_GET_PRIVATE(self)->vpn_manager, - NM_VPN_CONNECTION(active), - error)) { - nm_dbus_object_unexport(NM_DBUS_OBJECT(active)); - return FALSE; - } - - active_connection_add(self, active); - return TRUE; -} - -/* Traverse the device to disconnected state. This means that the device is ready - * for connection and will proceed activating if there's an activation request - * enqueued. - */ -static void -unmanaged_to_disconnected(NMDevice *device) -{ - /* when creating the software device, it can happen that the device is - * still unmanaged by NM_UNMANAGED_PLATFORM_INIT because we didn't yet - * get the udev event. At this point, we can no longer delay the activation - * and force the device to be managed. */ - nm_device_set_unmanaged_by_flags(device, - NM_UNMANAGED_PLATFORM_INIT, - FALSE, - NM_DEVICE_STATE_REASON_USER_REQUESTED); - - nm_device_set_unmanaged_by_flags(device, - NM_UNMANAGED_USER_EXPLICIT, - FALSE, - NM_DEVICE_STATE_REASON_USER_REQUESTED); - - if (!nm_device_get_managed(device, FALSE)) { - /* the device is still marked as unmanaged. Nothing to do. */ - return; - } - - if (nm_device_get_state(device) == NM_DEVICE_STATE_UNMANAGED) { - nm_device_state_changed(device, - NM_DEVICE_STATE_UNAVAILABLE, - NM_DEVICE_STATE_REASON_USER_REQUESTED); - } - - if (nm_device_get_state(device) == NM_DEVICE_STATE_UNAVAILABLE - && nm_device_is_available(device, NM_DEVICE_CHECK_DEV_AVAILABLE_FOR_USER_REQUEST)) { - nm_device_state_changed(device, - NM_DEVICE_STATE_DISCONNECTED, - NM_DEVICE_STATE_REASON_USER_REQUESTED); - } -} - -static NMActivationStateFlags -_activation_bind_lifetime_to_profile_visibility(NMAuthSubject *subject) -{ - if (nm_auth_subject_get_subject_type(subject) == NM_AUTH_SUBJECT_TYPE_INTERNAL - || nm_auth_subject_get_unix_process_uid(subject) == 0) { - /* internal requests and requests from root are always unbound. */ - return NM_ACTIVATION_STATE_FLAG_NONE; - } - - /* if the activation was not done by internal decision nor root, there - * are the following cases: - * - * - the connection has "connection.permissions" unset and the profile - * is not restricted to a user and commonly always visible. It does - * not hurt to bind the lifetime, because we expect the profile to be - * visible at the moment. If the profile changes (while still being active), - * we want to pick-up changes to the visibility and possibly disconnect. - * - * - the connection has "connection.permissions" set, and the current user - * is the owner: - * - * - Usually, we would expect that the profile is visible at the moment, - * and of course we want to bind the lifetime. The moment the user - * logs out, the connection becomes invisible and disconnects. - * - * - the profile at this time could already be invisible (e.g. if the - * user didn't create a proper session (sudo) and manually activates - * an invisible profile. In this case, we still want to bind the - * lifetime, and it will disconnect after the user logs in and logs - * out again. NMKeepAlive takes care of that. - */ - return NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY; -} - -/* The parent connection is ready; we can proceed realizing the device and - * progressing the device to disconencted state. - */ -static void -active_connection_parent_active(NMActiveConnection *active, - NMActiveConnection *parent_ac, - NMManager * self) -{ - NMDevice * device = nm_active_connection_get_device(active); - GError * error = NULL; - NMSettingsConnection *sett_conn; - NMDevice * parent; - - g_signal_handlers_disconnect_by_func(active, (GCallback) active_connection_parent_active, self); - - if (!parent_ac) { - _LOGW(LOGD_CORE, - "The parent connection device '%s' depended on disappeared.", - nm_device_get_iface(device)); - nm_active_connection_set_state_fail(active, - NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_REMOVED, - "parent device disappeared"); - return; - } - - sett_conn = nm_active_connection_get_settings_connection(active); - parent = nm_active_connection_get_device(parent_ac); - - if (!nm_device_create_and_realize(device, - nm_settings_connection_get_connection(sett_conn), - parent, - &error)) { - _LOGW(LOGD_CORE, - "Could not realize device '%s': %s", - nm_device_get_iface(device), - error->message); - nm_active_connection_set_state_fail(active, - NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_REALIZE_FAILED, - "failure to realize device"); - return; - } - - /* We can now proceed to disconnected state so that activation proceeds. */ - unmanaged_to_disconnected(device); -} - -static gboolean -_internal_activate_device(NMManager *self, NMActiveConnection *active, GError **error) -{ - NMDevice * device, *master_device = NULL; - NMConnection * applied; - NMSettingsConnection * sett_conn; - NMSettingsConnection * master_connection = NULL; - NMConnection * existing_connection = NULL; - NMActiveConnection * master_ac = NULL; - NMAuthSubject * subject; - GError * local = NULL; - NMConnectionMultiConnect multi_connect; - const char * parent_spec; - - g_return_val_if_fail(NM_IS_MANAGER(self), FALSE); - g_return_val_if_fail(NM_IS_ACTIVE_CONNECTION(active), FALSE); - g_return_val_if_fail(error == NULL || *error == NULL, FALSE); - - g_assert(NM_IS_VPN_CONNECTION(active) == FALSE); - - device = nm_active_connection_get_device(active); - g_return_val_if_fail(device != NULL, FALSE); - - sett_conn = nm_active_connection_get_settings_connection(active); - nm_assert(sett_conn); - - applied = nm_active_connection_get_applied_connection(active); - - /* If the device is active and its connection is not visible to the - * user that's requesting this new activation, fail, since other users - * should not be allowed to implicitly deactivate private connections - * by activating a connection of their own. - */ - existing_connection = nm_device_get_applied_connection(device); - subject = nm_active_connection_get_subject(active); - if (existing_connection - && !nm_auth_is_subject_in_acl_set_error(existing_connection, - subject, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - error)) { - g_prefix_error(error, "Private connection already active on the device: "); - return FALSE; - } - - /* Final connection must be available on device */ - if (!nm_device_check_connection_available(device, - applied, - NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST, - NULL, - &local)) { - g_set_error(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_UNKNOWN_CONNECTION, - "Connection '%s' is not available on device %s because %s", - nm_settings_connection_get_id(sett_conn), - nm_device_get_iface(device), - local->message); - g_error_free(local); - return FALSE; - } - - if (nm_active_connection_get_activation_type(active) == NM_ACTIVATION_TYPE_MANAGED) - nm_device_sys_iface_state_set(device, NM_DEVICE_SYS_IFACE_STATE_MANAGED); - - /* Try to find the master connection/device if the connection has a dependency */ - if (!find_master(self, - applied, - device, - &master_connection, - &master_device, - &master_ac, - error)) { - g_prefix_error(error, - "Can not find a master for %s: ", - nm_settings_connection_get_id(sett_conn)); - return FALSE; - } - - /* Create any backing resources the device needs */ - if (!nm_device_is_real(device)) { - NMDevice *parent; - - parent = find_parent_device_for_connection(self, - nm_settings_connection_get_connection(sett_conn), - NULL, - &parent_spec); - - if (parent_spec && !parent) { - g_set_error(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_DEPENDENCY_FAILED, - "parent device '%s' not found", - parent_spec); - return FALSE; - } - - if (parent && !nm_device_is_real(parent)) { - NMSettingsConnection *parent_con; - NMActiveConnection * parent_ac; - - parent_con = nm_device_get_best_connection(parent, NULL, error); - if (!parent_con) { - g_prefix_error(error, "%s failed to create parent: ", nm_device_get_iface(device)); - return FALSE; - } - - if (nm_active_connection_get_activation_reason(active) - == NM_ACTIVATION_REASON_AUTOCONNECT - && NM_FLAGS_HAS(nm_settings_connection_autoconnect_blocked_reason_get(parent_con), - NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_USER_REQUEST)) { - g_set_error(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_DEPENDENCY_FAILED, - "the parent connection of %s cannot autoactivate because it is blocked " - "due to user request", - nm_device_get_iface(device)); - return FALSE; - } - - parent_ac = nm_manager_activate_connection( - self, - parent_con, - NULL, - NULL, - parent, - subject, - NM_ACTIVATION_TYPE_MANAGED, - nm_active_connection_get_activation_reason(active), - nm_active_connection_get_state_flags(active) - & NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY, - error); - if (!parent_ac) { - g_prefix_error(error, - "%s failed to activate parent: ", - nm_device_get_iface(device)); - return FALSE; - } - - /* We can't realize now; defer until the parent device is ready. */ - g_signal_connect(active, - NM_ACTIVE_CONNECTION_PARENT_ACTIVE, - (GCallback) active_connection_parent_active, - self); - nm_active_connection_set_parent(active, parent_ac); - } else { - /* We can realize now; no need to wait for a parent device. */ - if (!nm_device_create_and_realize(device, - nm_settings_connection_get_connection(sett_conn), - parent, - error)) { - g_prefix_error(error, - "%s failed to create resources: ", - nm_device_get_iface(device)); - return FALSE; - } - } - } - - /* Ensure there's a master active connection the new connection we're - * activating can depend on. - */ - if (master_connection || master_device) { - if (master_connection) { - _LOGD(LOGD_CORE, - "Activation of '%s' requires master connection '%s'", - nm_settings_connection_get_id(sett_conn), - nm_settings_connection_get_id(master_connection)); - } - if (master_device) { - _LOGD(LOGD_CORE, - "Activation of '%s' requires master device '%s'", - nm_settings_connection_get_id(sett_conn), - nm_device_get_ip_iface(master_device)); - } - - /* Ensure eg bond slave and the candidate master is a bond master */ - if (master_connection - && !is_compatible_with_slave(nm_settings_connection_get_connection(master_connection), - applied)) { - g_set_error(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_DEPENDENCY_FAILED, - "The master connection '%s' is not compatible with '%s'", - nm_settings_connection_get_id(master_connection), - nm_settings_connection_get_id(sett_conn)); - return FALSE; - } - - if (!master_ac) { - master_ac = - ensure_master_active_connection(self, - nm_active_connection_get_subject(active), - applied, - device, - master_connection, - master_device, - nm_active_connection_get_activation_reason(active), - error); - if (!master_ac) { - if (master_device) { - g_prefix_error(error, - "Master device '%s' can't be activated: ", - nm_device_get_ip_iface(device)); - } else { - g_prefix_error(error, - "Master connection '%s' can't be activated: ", - nm_settings_connection_get_id(sett_conn)); - } - return FALSE; - } - } - - /* Now that we're activating a slave for that master, make sure the master just - * decides to go unmanaged while we're activating (perhaps because other slaves - * go away leaving him with no kids). - */ - if (master_device) { - nm_device_set_unmanaged_by_flags(master_device, - NM_UNMANAGED_EXTERNAL_DOWN, - NM_UNMAN_FLAG_OP_FORGET, - NM_DEVICE_STATE_REASON_USER_REQUESTED); - } - - nm_active_connection_set_master(active, master_ac); - _LOGD(LOGD_CORE, - "Activation of '%s' depends on active connection %p %s", - nm_settings_connection_get_id(sett_conn), - master_ac, - nm_dbus_object_get_path(NM_DBUS_OBJECT(master_ac)) ?: ""); - } - - /* Check slaves for master connection and possibly activate them */ - autoconnect_slaves(self, - sett_conn, - device, - nm_active_connection_get_subject(active), - nm_active_connection_get_activation_reason(active) - == NM_ACTIVATION_REASON_USER_REQUEST); - - multi_connect = - _nm_connection_get_multi_connect(nm_settings_connection_get_connection(sett_conn)); - if (multi_connect == NM_CONNECTION_MULTI_CONNECT_MULTIPLE - || (multi_connect == NM_CONNECTION_MULTI_CONNECT_MANUAL_MULTIPLE - && NM_IN_SET(nm_active_connection_get_activation_reason(active), - NM_ACTIVATION_REASON_ASSUME, - NM_ACTIVATION_REASON_AUTOCONNECT_SLAVES, - NM_ACTIVATION_REASON_USER_REQUEST))) { - /* the profile can be activated multiple times. Proceed. */ - } else { - gs_unref_ptrarray GPtrArray *all_ac_arr = NULL; - NMActiveConnection * ac; - guint i, n_all; - - /* Disconnect the connection if already connected or queued for activation. - * The connection cannot be active multiple times (at the same time). */ - ac = active_connection_find(self, - sett_conn, - NULL, - NM_ACTIVE_CONNECTION_STATE_ACTIVATED, - &all_ac_arr); - if (ac) { - n_all = all_ac_arr ? all_ac_arr->len : ((guint) 1); - for (i = 0; i < n_all; i++) { - nm_device_disconnect_active_connection(all_ac_arr ? all_ac_arr->pdata[i] : ac, - NM_DEVICE_STATE_REASON_NEW_ACTIVATION, - NM_ACTIVE_CONNECTION_STATE_REASON_UNKNOWN); - } - } - } - - /* If the device is there, we can ready it for the activation. */ - if (nm_device_is_real(device)) { - unmanaged_to_disconnected(device); - - if (!nm_device_get_managed(device, FALSE)) { - /* Unexpectedly, the device is still unmanaged. That can happen for example, - * if the device is forcibly unmanaged due to NM_UNMANAGED_USER_SETTINGS. */ - g_set_error_literal(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_DEPENDENCY_FAILED, - "Activation failed because the device is unmanaged"); - return FALSE; - } - } - - /* Export the new ActiveConnection to clients and start it on the device */ - active_connection_add(self, active); - nm_device_queue_activation(device, NM_ACT_REQUEST(active)); - return TRUE; -} - -static gboolean -_internal_activate_generic(NMManager *self, NMActiveConnection *active, GError **error) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - gboolean success = FALSE; - - /* Ensure activation request is still valid, eg that its device hasn't gone - * away or that some other dependency has not failed. - */ - if (nm_active_connection_get_state(active) >= NM_ACTIVE_CONNECTION_STATE_DEACTIVATING) { - g_set_error_literal(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_DEPENDENCY_FAILED, - "Activation failed because dependencies failed."); - return FALSE; - } - - if (NM_IS_VPN_CONNECTION(active)) - success = _internal_activate_vpn(self, active, error); - else - success = _internal_activate_device(self, active, error); - - if (success) { - /* Force an update of the Manager's activating-connection property. - * The device changes state before the AC gets exported, which causes - * the manager's 'activating-connection' property to be NULL since the - * AC only gets a D-Bus path when it's exported. So now that the AC - * is exported, make sure the manager's activating-connection property - * is up-to-date. - */ - policy_activating_ac_changed(G_OBJECT(priv->policy), NULL, self); - } - - return success; -} - -static NMActiveConnection * -_new_active_connection(NMManager * self, - gboolean is_vpn, - NMSettingsConnection * sett_conn, - NMConnection * incompl_conn, - NMConnection * applied, - const char * specific_object, - NMDevice * device, - NMAuthSubject * subject, - NMActivationType activation_type, - NMActivationReason activation_reason, - NMActivationStateFlags initial_state_flags, - GError ** error) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - NMDevice * parent_device; - - nm_assert(!sett_conn || NM_IS_SETTINGS_CONNECTION(sett_conn)); - nm_assert(!incompl_conn || NM_IS_CONNECTION(incompl_conn)); - nm_assert((!incompl_conn) ^ (!sett_conn)); - nm_assert(NM_IS_AUTH_SUBJECT(subject)); - nm_assert(is_vpn - == _connection_is_vpn(sett_conn ? nm_settings_connection_get_connection(sett_conn) - : incompl_conn)); - nm_assert(is_vpn || NM_IS_DEVICE(device)); - nm_assert(!nm_streq0(specific_object, "/")); - nm_assert(!applied || NM_IS_CONNECTION(applied)); - nm_assert(!is_vpn || !applied); - - if (is_vpn) { - NMActiveConnection *parent; - - /* FIXME: for VPN connections, we don't allow re-activating an - * already active connection. It's a bug, and should be fixed together - * when reworking VPN handling. */ - if (active_connection_find_by_connection(self, - sett_conn, - incompl_conn, - NM_ACTIVE_CONNECTION_STATE_ACTIVATED, - NULL)) { - g_set_error(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_CONNECTION_ALREADY_ACTIVE, - "Connection '%s' is already active", - sett_conn ? nm_settings_connection_get_id(sett_conn) - : nm_connection_get_id(incompl_conn)); - return NULL; - } - - if (activation_type != NM_ACTIVATION_TYPE_MANAGED) - g_return_val_if_reached(NULL); - - if (specific_object) { - /* Find the specific connection the client requested we use */ - parent = active_connection_get_by_path(self, specific_object); - if (!parent) { - g_set_error_literal(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE, - "Base connection for VPN connection not active."); - return NULL; - } - } else - parent = priv->primary_connection; - - if (!parent) { - g_set_error_literal(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_UNKNOWN_CONNECTION, - "Could not find source connection."); - return NULL; - } - - parent_device = nm_active_connection_get_device(parent); - if (!parent_device) { - g_set_error_literal(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "Source connection had no active device"); - return NULL; - } - - if (device && device != parent_device) { - g_set_error_literal(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "The device doesn't match the active connection."); - return NULL; - } - - return (NMActiveConnection *) nm_vpn_connection_new( - sett_conn, - parent_device, - nm_dbus_object_get_path(NM_DBUS_OBJECT(parent)), - activation_reason, - initial_state_flags, - subject); - } - - return (NMActiveConnection *) nm_act_request_new(sett_conn, - applied, - specific_object, - subject, - activation_type, - activation_reason, - initial_state_flags, - device); -} - -static void -_internal_activation_auth_done(NMManager * self, - NMActiveConnection *active, - gboolean success, - const char * error_desc) -{ - NMManagerPrivate * priv = NM_MANAGER_GET_PRIVATE(self); - NMActiveConnection *ac; - gs_free_error GError *error = NULL; - - nm_assert(NM_IS_ACTIVE_CONNECTION(active)); - - if (!success) - goto fail; - - /* Don't continue with an autoconnect-activation if a more important activation - * already exists. - * We also check this earlier, but there we may fail to detect a duplicate - * if the existing active connection was undergoing authorization. - */ - if (NM_IN_SET(nm_active_connection_get_activation_reason(active), - NM_ACTIVATION_REASON_EXTERNAL, - NM_ACTIVATION_REASON_ASSUME, - NM_ACTIVATION_REASON_AUTOCONNECT)) { - c_list_for_each_entry (ac, &priv->active_connections_lst_head, active_connections_lst) { - if (nm_active_connection_get_device(ac) == nm_active_connection_get_device(active) - && nm_active_connection_get_settings_connection(ac) - == nm_active_connection_get_settings_connection(active) - && nm_active_connection_get_state(ac) <= NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { - g_set_error(&error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_CONNECTION_ALREADY_ACTIVE, - "Connection '%s' is already active", - nm_active_connection_get_settings_connection_id(active)); - goto fail; - } - } - } - - if (_internal_activate_generic(self, active, &error)) - return; - -fail: - nm_assert(error_desc || error); - nm_active_connection_set_state_fail(active, - NM_ACTIVE_CONNECTION_STATE_REASON_UNKNOWN, - error_desc ?: error->message); -} - -/** - * nm_manager_activate_connection(): - * @self: the #NMManager - * @sett_conn: the #NMSettingsConnection to activate on @device - * @applied: (allow-none): the applied connection to activate on @device - * @specific_object: the specific object path, if any, for the activation - * @device: the #NMDevice to activate @sett_conn on. Can be %NULL for VPNs. - * @subject: the subject which requested activation - * @activation_type: whether to assume the connection. That is, take over gracefully, - * non-destructible. - * @activation_reason: the reason for activation - * @initial_state_flags: the initial state flags for the activation. - * @error: return location for an error - * - * Begins a new internally-initiated activation of @sett_conn on @device. - * @subject should be the subject of the activation that triggered this - * one, or if this is an autoconnect request, a new internal subject. - * The returned #NMActiveConnection is owned by the Manager and should be - * referenced by the caller if the caller continues to use it. If @applied - * is supplied, it shall not be modified by the caller afterwards. - * - * Returns: (transfer none): the new #NMActiveConnection that tracks - * activation of @sett_conn on @device - */ -NMActiveConnection * -nm_manager_activate_connection(NMManager * self, - NMSettingsConnection * sett_conn, - NMConnection * applied, - const char * specific_object, - NMDevice * device, - NMAuthSubject * subject, - NMActivationType activation_type, - NMActivationReason activation_reason, - NMActivationStateFlags initial_state_flags, - GError ** error) -{ - NMManagerPrivate * priv; - NMActiveConnection *active; - AsyncOpData * async_op_data; - gboolean is_vpn; - - g_return_val_if_fail(NM_IS_MANAGER(self), NULL); - g_return_val_if_fail(NM_IS_SETTINGS_CONNECTION(sett_conn), NULL); - is_vpn = _connection_is_vpn(nm_settings_connection_get_connection(sett_conn)); - g_return_val_if_fail(is_vpn || NM_IS_DEVICE(device), NULL); - g_return_val_if_fail(!error || !*error, NULL); - nm_assert(!nm_streq0(specific_object, "/")); - - priv = NM_MANAGER_GET_PRIVATE(self); - - if (!nm_auth_is_subject_in_acl_set_error(nm_settings_connection_get_connection(sett_conn), - subject, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - error)) - return NULL; - - /* Look for a active connection that's equivalent and is already pending authorization - * and eventual activation. This is used to de-duplicate concurrent activations which would - * otherwise race and cause the device to disconnect and reconnect repeatedly. - * In particular, this allows the master and multiple slaves to concurrently auto-activate - * while all the slaves would use the same active-connection. */ - c_list_for_each_entry (async_op_data, &priv->async_op_lst_head, async_op_lst) { - if (async_op_data->async_op_type != ASYNC_OP_TYPE_AC_AUTH_ACTIVATE_INTERNAL) - continue; - - active = async_op_data->ac_auth.active; - if (sett_conn == nm_active_connection_get_settings_connection(active) - && nm_streq0(nm_active_connection_get_specific_object(active), specific_object) - && (!device || nm_active_connection_get_device(active) == device) - && nm_auth_subject_get_subject_type(nm_active_connection_get_subject(active)) - == NM_AUTH_SUBJECT_TYPE_INTERNAL - && nm_auth_subject_get_subject_type(subject) == NM_AUTH_SUBJECT_TYPE_INTERNAL - && nm_active_connection_get_activation_reason(active) == activation_reason) - return active; - } - - active = _new_active_connection(self, - is_vpn, - sett_conn, - NULL, - applied, - specific_object, - device, - subject, - activation_type, - activation_reason, - initial_state_flags, - error); - if (!active) - return NULL; - - nm_active_connection_authorize(active, - NULL, - _async_op_complete_ac_auth_cb, - _async_op_data_new_authorize_activate_internal(self, active)); - return active; -} - -/** - * validate_activation_request: - * @self: the #NMManager - * @context: the D-Bus context of the requestor - * @sett_conn: the #NMSettingsConnection to be activated, or %NULL if there - * is only a partial activation. - * @connection: the partial #NMConnection to be activated (if @sett_conn is unspecified) - * @device_path: the object path of the device to be activated, or NULL - * @out_device: on successful return, the #NMDevice to be activated with @connection - * The caller may pass in a device which shortcuts the lookup by path. - * In this case, the passed in device must have the matching @device_path - * already. - * @out_is_vpn: on successful return, %TRUE if @connection is a VPN connection - * @error: location to store an error on failure - * - * Performs basic validation on an activation request, including ensuring that - * the requestor is a valid Unix process, is not disallowed in @connection - * permissions, and that a device exists that can activate @connection. - * - * Returns: on success, the #NMAuthSubject representing the requestor, or - * %NULL on error - */ -static NMAuthSubject * -validate_activation_request(NMManager * self, - GDBusMethodInvocation *context, - NMSettingsConnection * sett_conn, - NMConnection * connection, - const char * device_path, - NMDevice ** out_device, - gboolean * out_is_vpn, - GError ** error) -{ - NMDevice * device = NULL; - gboolean is_vpn = FALSE; - gs_unref_object NMAuthSubject *subject = NULL; - - nm_assert(!sett_conn || NM_IS_SETTINGS_CONNECTION(sett_conn)); - nm_assert(!connection || NM_IS_CONNECTION(connection)); - nm_assert(sett_conn || connection); - nm_assert(!connection || !sett_conn - || connection == nm_settings_connection_get_connection(sett_conn)); - nm_assert(out_device); - nm_assert(out_is_vpn); - - if (!connection) - connection = nm_settings_connection_get_connection(sett_conn); - - /* Validate the caller */ - subject = nm_dbus_manager_new_auth_subject_from_context(context); - if (!subject) { - g_set_error_literal(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - NM_UTILS_ERROR_MSG_REQ_UID_UKNOWN); - return NULL; - } - - if (!nm_auth_is_subject_in_acl_set_error(connection, - subject, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - error)) - return NULL; - - is_vpn = _connection_is_vpn(connection); - - if (*out_device) { - device = *out_device; - nm_assert(NM_IS_DEVICE(device)); - nm_assert(device_path); - nm_assert(nm_streq0(device_path, nm_dbus_object_get_path(NM_DBUS_OBJECT(device)))); - nm_assert(device == nm_manager_get_device_by_path(self, device_path)); - } else if (device_path) { - device = nm_manager_get_device_by_path(self, device_path); - if (!device) { - g_set_error_literal(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "Device not found"); - return NULL; - } - } else if (!is_vpn) { - gs_free_error GError *local = NULL; - - device = nm_manager_get_best_device_for_connection(self, - sett_conn, - connection, - TRUE, - NULL, - &local); - if (!device) { - gs_free char *iface = NULL; - - /* VPN and software-device connections don't need a device yet, - * but non-virtual connections do ... */ - if (!nm_connection_is_virtual(connection)) { - g_set_error(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "No suitable device found for this connection (%s).", - local->message); - return NULL; - } - - /* Look for an existing device with the connection's interface name */ - iface = nm_manager_get_connection_iface(self, connection, NULL, NULL, error); - if (!iface) - return NULL; - - device = find_device_by_iface(self, iface, connection, NULL); - if (!device) { - g_set_error_literal(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "Failed to find a compatible device for this connection"); - return NULL; - } - } - } - - nm_assert(is_vpn || NM_IS_DEVICE(device)); - - *out_device = device; - *out_is_vpn = is_vpn; - return g_steal_pointer(&subject); -} - -/*****************************************************************************/ - -static void -_activation_auth_done(NMManager * self, - NMActiveConnection * active, - GDBusMethodInvocation *invocation, - gboolean success, - const char * error_desc) -{ - GError * error = NULL; - NMAuthSubject * subject; - NMSettingsConnection *connection; - - subject = nm_active_connection_get_subject(active); - connection = nm_active_connection_get_settings_connection(active); - - if (!success) { - error = - g_error_new_literal(NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, error_desc); - goto fail; - } - - if (!_internal_activate_generic(self, active, &error)) - goto fail; - - nm_settings_connection_autoconnect_blocked_reason_set( - connection, - NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_USER_REQUEST, - FALSE); - g_dbus_method_invocation_return_value( - invocation, - g_variant_new("(o)", nm_dbus_object_get_path(NM_DBUS_OBJECT(active)))); - nm_audit_log_connection_op(NM_AUDIT_OP_CONN_ACTIVATE, connection, TRUE, NULL, subject, NULL); - return; - -fail: - nm_audit_log_connection_op(NM_AUDIT_OP_CONN_ACTIVATE, - connection, - FALSE, - NULL, - subject, - error->message); - nm_active_connection_set_state_fail(active, - NM_ACTIVE_CONNECTION_STATE_REASON_UNKNOWN, - error->message); - - g_dbus_method_invocation_take_error(invocation, error); -} - -static void -impl_manager_activate_connection(NMDBusObject * obj, - const NMDBusInterfaceInfoExtended *interface_info, - const NMDBusMethodInfoExtended * method_info, - GDBusConnection * dbus_connection, - const char * sender, - GDBusMethodInvocation * invocation, - GVariant * parameters) -{ - NMManager * self = NM_MANAGER(obj); - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - gs_unref_object NMActiveConnection *active = NULL; - gs_unref_object NMAuthSubject *subject = NULL; - NMSettingsConnection * sett_conn = NULL; - NMDevice * device = NULL; - gboolean is_vpn = FALSE; - GError * error = NULL; - const char * connection_path; - const char * device_path; - const char * specific_object_path; - - g_variant_get(parameters, "(&o&o&o)", &connection_path, &device_path, &specific_object_path); - - connection_path = nm_dbus_path_not_empty(connection_path); - specific_object_path = nm_dbus_path_not_empty(specific_object_path); - device_path = nm_dbus_path_not_empty(device_path); - - /* If the connection path is given and valid, that connection is activated. - * Otherwise, the "best" connection for the device is chosen and activated, - * regardless of whether that connection is autoconnect-enabled or not - * (since this is an explicit request, not an auto-activation request). - */ - if (connection_path) { - sett_conn = nm_settings_get_connection_by_path(priv->settings, connection_path); - if (!sett_conn) { - error = g_error_new_literal(NM_MANAGER_ERROR, - NM_MANAGER_ERROR_UNKNOWN_CONNECTION, - "Connection could not be found."); - goto error; - } - } else { - /* If no connection is given, find a suitable connection for the given device path */ - if (!device_path) { - error = g_error_new_literal( - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "Only devices may be activated without a specifying a connection"); - goto error; - } - device = nm_manager_get_device_by_path(self, device_path); - if (!device) { - error = g_error_new(NM_MANAGER_ERROR, - NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "Can not activate an unknown device '%s'", - device_path); - goto error; - } - - sett_conn = nm_device_get_best_connection(device, specific_object_path, &error); - if (!sett_conn) - goto error; - } - - subject = validate_activation_request(self, - invocation, - sett_conn, - NULL, - device_path, - &device, - &is_vpn, - &error); - if (!subject) - goto error; - - active = _new_active_connection(self, - is_vpn, - sett_conn, - NULL, - NULL, - specific_object_path, - device, - subject, - NM_ACTIVATION_TYPE_MANAGED, - NM_ACTIVATION_REASON_USER_REQUEST, - _activation_bind_lifetime_to_profile_visibility(subject), - &error); - if (!active) - goto error; - - nm_active_connection_authorize( - active, - NULL, - _async_op_complete_ac_auth_cb, - _async_op_data_new_ac_auth_activate_user(self, active, invocation)); - - /* we passed the pointer on to _async_op_data_new_ac_auth_activate_user() */ - g_steal_pointer(&active); - - return; - -error: - if (sett_conn) { - nm_audit_log_connection_op(NM_AUDIT_OP_CONN_ACTIVATE, - sett_conn, - FALSE, - NULL, - subject, - error->message); - } - g_dbus_method_invocation_take_error(invocation, error); -} - -/*****************************************************************************/ - -static void -activation_add_done(NMSettings * settings, - NMSettingsConnection * new_connection, - GError * error, - GDBusMethodInvocation *context, - NMAuthSubject * subject, - gpointer user_data) -{ - NMManager * self; - gs_unref_object NMActiveConnection *active = NULL; - gs_free_error GError *local = NULL; - gpointer async_op_type_ptr; - AsyncOpType async_op_type; - GVariant * result_floating; - - nm_utils_user_data_unpack(user_data, &self, &active, &async_op_type_ptr); - async_op_type = GPOINTER_TO_INT(async_op_type_ptr); - - if (error) - goto fail; - - nm_active_connection_set_settings_connection(active, new_connection); - - if (!_internal_activate_generic(self, active, &local)) - goto fail; - - if (async_op_type == ASYNC_OP_TYPE_AC_AUTH_ADD_AND_ACTIVATE) { - result_floating = g_variant_new("(oo)", - nm_dbus_object_get_path(NM_DBUS_OBJECT(new_connection)), - nm_dbus_object_get_path(NM_DBUS_OBJECT(active))); - } else { - result_floating = g_variant_new("(oo@a{sv})", - nm_dbus_object_get_path(NM_DBUS_OBJECT(new_connection)), - nm_dbus_object_get_path(NM_DBUS_OBJECT(active)), - g_variant_new_array(G_VARIANT_TYPE("{sv}"), NULL, 0)); - } - g_dbus_method_invocation_return_value(context, result_floating); - - nm_audit_log_connection_op(NM_AUDIT_OP_CONN_ADD_ACTIVATE, - nm_active_connection_get_settings_connection(active), - TRUE, - NULL, - nm_active_connection_get_subject(active), - NULL); - return; - -fail: - if (local) { - nm_assert(!error); - error = local; - } else - nm_assert(error); - - nm_active_connection_set_state_fail(active, - NM_ACTIVE_CONNECTION_STATE_REASON_UNKNOWN, - error->message); - if (new_connection) - nm_settings_connection_delete(new_connection, FALSE); - g_dbus_method_invocation_return_gerror(context, error); - nm_audit_log_connection_op(NM_AUDIT_OP_CONN_ADD_ACTIVATE, - NULL, - FALSE, - NULL, - nm_active_connection_get_subject(active), - error->message); -} - -static void -_add_and_activate_auth_done(NMManager * self, - AsyncOpType async_op_type, - NMActiveConnection * active, - NMConnection * connection, - GDBusMethodInvocation * invocation, - NMSettingsConnectionPersistMode persist_mode, - gboolean is_volatile, - gboolean success, - const char * error_desc) -{ - NMManagerPrivate *priv; - GError * error = NULL; - - if (!success) { - error = - g_error_new_literal(NM_MANAGER_ERROR, NM_MANAGER_ERROR_PERMISSION_DENIED, error_desc); - nm_audit_log_connection_op(NM_AUDIT_OP_CONN_ADD_ACTIVATE, - NULL, - FALSE, - NULL, - nm_active_connection_get_subject(active), - error->message); - g_dbus_method_invocation_take_error(invocation, error); - return; - } - - priv = NM_MANAGER_GET_PRIVATE(self); - - /* FIXME(shutdown): nm_settings_add_connection_dbus() cannot be cancelled. It should be made - * cancellable and tracked via AsyncOpData to be able to do a clean - * shutdown. */ - nm_settings_add_connection_dbus( - priv->settings, - connection, - persist_mode, - NM_SETTINGS_CONNECTION_ADD_REASON_NONE, - (is_volatile ? NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE - : NM_SETTINGS_CONNECTION_INT_FLAGS_NONE), - nm_active_connection_get_subject(active), - invocation, - activation_add_done, - nm_utils_user_data_pack(self, g_object_ref(active), GINT_TO_POINTER(async_op_type))); -} - -static void -impl_manager_add_and_activate_connection(NMDBusObject * obj, - const NMDBusInterfaceInfoExtended *interface_info, - const NMDBusMethodInfoExtended * method_info, - GDBusConnection * dbus_connection, - const char * sender, - GDBusMethodInvocation * invocation, - GVariant * parameters) -{ - NMManager * self = NM_MANAGER(obj); - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - gs_unref_object NMConnection *incompl_conn = NULL; - gs_unref_object NMActiveConnection *active = NULL; - gs_unref_object NMAuthSubject *subject = NULL; - GError * error = NULL; - NMDevice * device = NULL; - gboolean is_vpn = FALSE; - gs_unref_variant GVariant *settings = NULL; - gs_unref_variant GVariant *options = NULL; - const char * device_path; - const char * specific_object_path; - gs_free NMConnection ** conns = NULL; - NMSettingsConnectionPersistMode persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK; - gboolean is_volatile = FALSE; - gboolean bind_dbus_client = FALSE; - AsyncOpType async_op_type; - - if (nm_streq(method_info->parent.name, "AddAndActivateConnection2")) { - async_op_type = ASYNC_OP_TYPE_AC_AUTH_ADD_AND_ACTIVATE2; - g_variant_get(parameters, - "(@a{sa{sv}}&o&o@a{sv})", - &settings, - &device_path, - &specific_object_path, - &options); - } else { - nm_assert(nm_streq(method_info->parent.name, "AddAndActivateConnection")); - async_op_type = ASYNC_OP_TYPE_AC_AUTH_ADD_AND_ACTIVATE; - g_variant_get(parameters, - "(@a{sa{sv}}&o&o)", - &settings, - &device_path, - &specific_object_path); - } - - if (options) { - GVariantIter iter; - const char * option_name; - GVariant * option_value; - - g_variant_iter_init(&iter, options); - while (g_variant_iter_next(&iter, "{&sv}", &option_name, &option_value)) { - gs_unref_variant GVariant *option_value_free = NULL; - const char * s; - - option_value_free = option_value; - - if (nm_streq(option_name, "persist") - && g_variant_is_of_type(option_value, G_VARIANT_TYPE_STRING)) { - s = g_variant_get_string(option_value, NULL); - - is_volatile = FALSE; - persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK; - - if (nm_streq(s, "volatile")) { - persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY; - is_volatile = TRUE; - } else if (nm_streq(s, "memory")) - persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY; - else if (nm_streq(s, "disk")) { - /* pass */ - } else { - error = g_error_new_literal( - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_INVALID_ARGUMENTS, - "Option \"persist\" must be one of \"volatile\", \"memory\" or \"disk\""); - goto error; - } - } else if (nm_streq(option_name, "bind-activation") - && g_variant_is_of_type(option_value, G_VARIANT_TYPE_STRING)) { - s = g_variant_get_string(option_value, NULL); - - if (nm_streq(s, "dbus-client")) - bind_dbus_client = TRUE; - else if (nm_streq(s, "none")) - bind_dbus_client = FALSE; - else { - error = g_error_new_literal( - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_INVALID_ARGUMENTS, - "Option \"bind-activation\" must be one of \"dbus-client\" or \"none\""); - goto error; - } - } else { - error = g_error_new_literal(NM_MANAGER_ERROR, - NM_MANAGER_ERROR_INVALID_ARGUMENTS, - "Unknown extra option passed"); - goto error; - } - } - } - - specific_object_path = nm_dbus_path_not_empty(specific_object_path); - device_path = nm_dbus_path_not_empty(device_path); - - /* Try to create a new connection with the given settings. - * We allow empty settings for AddAndActivateConnection(). In that case, - * the connection will be completed in nm_utils_complete_generic() or - * nm_device_complete_connection() below. Just make sure we don't expect - * specific data being in the connection till then (especially in - * validate_activation_request()). - */ - incompl_conn = nm_simple_connection_new(); - if (settings && g_variant_n_children(settings)) - _nm_connection_replace_settings(incompl_conn, - settings, - NM_SETTING_PARSE_FLAGS_STRICT, - NULL); - - subject = validate_activation_request(self, - invocation, - NULL, - incompl_conn, - device_path, - &device, - &is_vpn, - &error); - if (!subject) - goto error; - - if (is_vpn) { - /* Try to fill the VPN's connection setting and name at least */ - if (!nm_connection_get_setting_vpn(incompl_conn)) { - error = g_error_new_literal(NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_MISSING_SETTING, - "VPN connections require a 'vpn' setting"); - g_prefix_error(&error, "%s: ", NM_SETTING_VPN_SETTING_NAME); - goto error; - } - - conns = nm_settings_connections_array_to_connections( - nm_settings_get_connections(priv->settings, NULL), - -1); - - nm_utils_complete_generic(priv->platform, - incompl_conn, - NM_SETTING_VPN_SETTING_NAME, - conns, - NULL, - _("VPN connection"), - NULL, - NULL, - FALSE); /* No IPv6 by default for now */ - } else { - conns = nm_settings_connections_array_to_connections( - nm_settings_get_connections(priv->settings, NULL), - -1); - /* Let each device subclass complete the connection */ - if (!nm_device_complete_connection(device, - incompl_conn, - specific_object_path, - conns, - &error)) - goto error; - } - - nm_assert(_nm_connection_verify(incompl_conn, NULL) == NM_SETTING_VERIFY_SUCCESS); - - active = _new_active_connection(self, - is_vpn, - NULL, - incompl_conn, - NULL, - specific_object_path, - device, - subject, - NM_ACTIVATION_TYPE_MANAGED, - NM_ACTIVATION_REASON_USER_REQUEST, - _activation_bind_lifetime_to_profile_visibility(subject), - &error); - if (!active) - goto error; - - if (bind_dbus_client) { - NMKeepAlive *keep_alive; - - keep_alive = nm_active_connection_get_keep_alive(active); - nm_keep_alive_set_dbus_client_watch(keep_alive, dbus_connection, sender); - nm_keep_alive_arm(keep_alive); - } - - nm_active_connection_authorize(active, - incompl_conn, - _async_op_complete_ac_auth_cb, - _async_op_data_new_ac_auth_add_and_activate(self, - async_op_type, - active, - invocation, - incompl_conn, - persist_mode, - is_volatile)); - - /* we passed the pointers on to _async_op_data_new_ac_auth_add_and_activate() */ - g_steal_pointer(&incompl_conn); - g_steal_pointer(&active); - return; - -error: - nm_audit_log_connection_op(NM_AUDIT_OP_CONN_ADD_ACTIVATE, - NULL, - FALSE, - NULL, - subject, - error->message); - g_dbus_method_invocation_take_error(invocation, error); -} - -/*****************************************************************************/ - -gboolean -nm_manager_deactivate_connection(NMManager * manager, - NMActiveConnection *active, - NMDeviceStateReason reason, - GError ** error) -{ - if (NM_IS_VPN_CONNECTION(active)) { - NMActiveConnectionStateReason vpn_reason = - NM_ACTIVE_CONNECTION_STATE_REASON_USER_DISCONNECTED; - - if (nm_device_state_reason_check(reason) == NM_DEVICE_STATE_REASON_CONNECTION_REMOVED) - vpn_reason = NM_ACTIVE_CONNECTION_STATE_REASON_CONNECTION_REMOVED; - - if (!nm_vpn_connection_deactivate(NM_VPN_CONNECTION(active), vpn_reason, FALSE)) { - g_set_error_literal(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE, - "The VPN connection was not active."); - return FALSE; - } - } else { - nm_assert(NM_IS_ACT_REQUEST(active)); - nm_device_disconnect_active_connection(active, - reason, - NM_ACTIVE_CONNECTION_STATE_REASON_UNKNOWN); - } - - _notify(manager, PROP_ACTIVE_CONNECTIONS); - return TRUE; -} - -static void -deactivate_net_auth_done_cb(NMAuthChain *chain, GDBusMethodInvocation *context, gpointer user_data) -{ - NMManager * self = NM_MANAGER(user_data); - GError * error = NULL; - NMAuthCallResult result; - NMActiveConnection *active; - char * path; - - nm_assert(G_IS_DBUS_METHOD_INVOCATION(context)); - - c_list_unlink(nm_auth_chain_parent_lst_list(chain)); - - path = nm_auth_chain_get_data(chain, "path"); - result = nm_auth_chain_get_result(chain, NM_AUTH_PERMISSION_NETWORK_CONTROL); - active = active_connection_get_by_path(self, path); - - if (result != NM_AUTH_CALL_RESULT_YES) { - error = g_error_new_literal(NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - "Not authorized to deactivate connections"); - } else if (!active) { - error = g_error_new_literal(NM_MANAGER_ERROR, - NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE, - "The connection was not active."); - } else { - /* success; deactivation allowed */ - if (!nm_manager_deactivate_connection(self, - active, - NM_DEVICE_STATE_REASON_USER_REQUESTED, - &error)) - nm_assert(error); - } - - if (active) { - nm_audit_log_connection_op(NM_AUDIT_OP_CONN_DEACTIVATE, - nm_active_connection_get_settings_connection(active), - !error, - NULL, - nm_auth_chain_get_subject(chain), - error ? error->message : NULL); - } - - if (error) - g_dbus_method_invocation_take_error(context, error); - else - g_dbus_method_invocation_return_value(context, NULL); -} - -static void -impl_manager_deactivate_connection(NMDBusObject * obj, - const NMDBusInterfaceInfoExtended *interface_info, - const NMDBusMethodInfoExtended * method_info, - GDBusConnection * dbus_connection, - const char * sender, - GDBusMethodInvocation * invocation, - GVariant * parameters) -{ - NMManager * self = NM_MANAGER(obj); - NMManagerPrivate * priv = NM_MANAGER_GET_PRIVATE(self); - NMActiveConnection * ac; - NMSettingsConnection *sett_conn = NULL; - GError * error = NULL; - NMAuthSubject * subject = NULL; - NMAuthChain * chain; - const char * active_path; - - g_variant_get(parameters, "(&o)", &active_path); - - /* Find the connection by its object path */ - ac = active_connection_get_by_path(self, active_path); - if (ac) - sett_conn = nm_active_connection_get_settings_connection(ac); - - if (!sett_conn) { - error = g_error_new_literal(NM_MANAGER_ERROR, - NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE, - "The connection was not active."); - goto done; - } - - /* Validate the caller */ - subject = nm_dbus_manager_new_auth_subject_from_context(invocation); - if (!subject) { - error = g_error_new_literal(NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - NM_UTILS_ERROR_MSG_REQ_UID_UKNOWN); - goto done; - } - - if (!nm_auth_is_subject_in_acl_set_error(nm_settings_connection_get_connection(sett_conn), - subject, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - &error)) - goto done; - - /* Validate the user request */ - chain = nm_auth_chain_new_subject(subject, invocation, deactivate_net_auth_done_cb, self); - if (!chain) { - error = g_error_new_literal(NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - NM_UTILS_ERROR_MSG_REQ_AUTH_FAILED); - goto done; - } - - c_list_link_tail(&priv->auth_lst_head, nm_auth_chain_parent_lst_list(chain)); - nm_auth_chain_set_data(chain, "path", g_strdup(active_path), g_free); - nm_auth_chain_add_call(chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE); - -done: - if (error) { - if (sett_conn) { - nm_audit_log_connection_op(NM_AUDIT_OP_CONN_DEACTIVATE, - sett_conn, - FALSE, - NULL, - subject, - error->message); - } - g_dbus_method_invocation_take_error(invocation, error); - } - g_clear_object(&subject); -} - -static gboolean -sleep_devices_add(NMManager *self, NMDevice *device, gboolean suspending) -{ - NMManagerPrivate * priv = NM_MANAGER_GET_PRIVATE(self); - NMSleepMonitorInhibitorHandle *handle = NULL; - - if (g_hash_table_lookup_extended(priv->sleep_devices, device, NULL, (gpointer *) &handle)) { - if (suspending) { - /* if we are suspending, always insert a new handle in sleep_devices. - * Even if we had an old handle, it might be stale by now. */ - g_hash_table_insert(priv->sleep_devices, - device, - nm_sleep_monitor_inhibit_take(priv->sleep_monitor)); - if (handle) - nm_sleep_monitor_inhibit_release(priv->sleep_monitor, handle); - } - return FALSE; - } - - g_hash_table_insert(priv->sleep_devices, - g_object_ref(device), - suspending ? nm_sleep_monitor_inhibit_take(priv->sleep_monitor) : NULL); - g_signal_connect(device, "notify::" NM_DEVICE_STATE, (GCallback) device_sleep_cb, self); - return TRUE; -} - -static gboolean -sleep_devices_remove(NMManager *self, NMDevice *device) -{ - NMManagerPrivate * priv = NM_MANAGER_GET_PRIVATE(self); - NMSleepMonitorInhibitorHandle *handle; - - if (!g_hash_table_lookup_extended(priv->sleep_devices, device, NULL, (gpointer *) &handle)) - return FALSE; - - if (handle) - nm_sleep_monitor_inhibit_release(priv->sleep_monitor, handle); - - /* Remove device from hash */ - g_signal_handlers_disconnect_by_func(device, device_sleep_cb, self); - g_hash_table_remove(priv->sleep_devices, device); - g_object_unref(device); - return TRUE; -} - -static void -sleep_devices_clear(NMManager *self) -{ - NMManagerPrivate * priv = NM_MANAGER_GET_PRIVATE(self); - NMDevice * device; - NMSleepMonitorInhibitorHandle *handle; - GHashTableIter iter; - - if (!priv->sleep_devices) - return; - - g_hash_table_iter_init(&iter, priv->sleep_devices); - while (g_hash_table_iter_next(&iter, (gpointer *) &device, (gpointer *) &handle)) { - g_signal_handlers_disconnect_by_func(device, device_sleep_cb, self); - if (handle) - nm_sleep_monitor_inhibit_release(priv->sleep_monitor, handle); - g_object_unref(device); - g_hash_table_iter_remove(&iter); - } -} - -static void -device_sleep_cb(NMDevice *device, GParamSpec *pspec, NMManager *self) -{ - switch (nm_device_get_state(device)) { - case NM_DEVICE_STATE_DISCONNECTED: - _LOGD(LOGD_SUSPEND, "sleep: unmanaging device %s", nm_device_get_ip_iface(device)); - nm_device_set_unmanaged_by_flags_queue(device, - NM_UNMANAGED_SLEEPING, - TRUE, - NM_DEVICE_STATE_REASON_SLEEPING); - break; - case NM_DEVICE_STATE_UNMANAGED: - _LOGD(LOGD_SUSPEND, "sleep: device %s is ready", nm_device_get_ip_iface(device)); - - if (!sleep_devices_remove(self, device)) - g_return_if_reached(); - - break; - default: - return; - } -} - -static void -do_sleep_wake(NMManager *self, gboolean sleeping_changed) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - gboolean suspending, waking_from_suspend; - NMDevice * device; - - suspending = sleeping_changed && priv->sleeping; - waking_from_suspend = sleeping_changed && !priv->sleeping; - - if (manager_sleeping(self)) { - _LOGD(LOGD_SUSPEND, "sleep: %s...", suspending ? "sleeping" : "disabling"); - - /* FIXME: are there still hardware devices that need to be disabled around - * suspend/resume? - */ - c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { - if (nm_device_is_software(device)) { - /* If a user disables networking we consider that as an - * indication that also software devices must be disconnected. - * But we don't want to destroy them for external events as - * a system suspend. - */ - if (suspending) - continue; - } - /* Wake-on-LAN devices will be taken down post-suspend rather than pre- */ - if (suspending && device_is_wake_on_lan(priv->platform, device)) { - _LOGD(LOGD_SUSPEND, - "sleep: device %s has wake-on-lan, skipping", - nm_device_get_ip_iface(device)); - continue; - } - - if (nm_device_is_activating(device) - || nm_device_get_state(device) == NM_DEVICE_STATE_ACTIVATED) { - _LOGD(LOGD_SUSPEND, - "sleep: wait disconnection of device %s", - nm_device_get_ip_iface(device)); - - if (sleep_devices_add(self, device, suspending)) - nm_device_queue_state(device, - NM_DEVICE_STATE_DEACTIVATING, - NM_DEVICE_STATE_REASON_SLEEPING); - } else { - nm_device_set_unmanaged_by_flags(device, - NM_UNMANAGED_SLEEPING, - TRUE, - NM_DEVICE_STATE_REASON_SLEEPING); - } - } - } else { - _LOGD(LOGD_SUSPEND, "sleep: %s...", waking_from_suspend ? "waking up" : "re-enabling"); - - sleep_devices_clear(self); - - if (waking_from_suspend) { - c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { - if (nm_device_is_software(device)) - continue; - - /* Belatedly take down Wake-on-LAN devices; ideally we wouldn't have to do this - * but for now it's the only way to make sure we re-check their connectivity. - */ - if (device_is_wake_on_lan(priv->platform, device)) - nm_device_set_unmanaged_by_flags(device, - NM_UNMANAGED_SLEEPING, - TRUE, - NM_DEVICE_STATE_REASON_SLEEPING); - - /* Check if the device is unmanaged but the state transition is still pending. - * If so, change state now so that later we re-manage the device forcing a - * re-check of available connections. - */ - if (!nm_device_get_managed(device, FALSE) - && nm_device_get_state(device) != NM_DEVICE_STATE_UNMANAGED) { - nm_device_state_changed(device, - NM_DEVICE_STATE_UNMANAGED, - NM_DEVICE_STATE_REASON_SLEEPING); - } - } - } - - /* Ensure rfkill state is up-to-date since we don't respond to state - * changes during sleep. - */ - nm_manager_rfkill_update(self, RFKILL_TYPE_UNKNOWN); - - /* Re-manage managed devices */ - c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { - guint i; - - if (nm_device_is_software(device) - && !nm_device_get_unmanaged_flags(device, NM_UNMANAGED_SLEEPING)) { - /* DHCP leases of software devices could have gone stale - * so we need to renew them. */ - nm_device_update_dynamic_ip_setup(device); - continue; - } - - /* enable/disable wireless devices since that we don't respond - * to killswitch changes during sleep. - */ - for (i = 0; i < RFKILL_TYPE_MAX; i++) { - RadioState *rstate = &priv->radio_states[i]; - gboolean enabled = radio_enabled_for_rstate(rstate, TRUE); - - if (rstate->desc) { - _LOGD(LOGD_RFKILL, - "rfkill: %s %s devices (hw_enabled %d, sw_enabled %d, user_enabled %d)", - enabled ? "enabling" : "disabling", - rstate->desc, - rstate->hw_enabled, - rstate->sw_enabled, - rstate->user_enabled); - } - if (nm_device_get_rfkill_type(device) == rstate->rtype) - nm_device_set_enabled(device, enabled); - } - - nm_device_set_unmanaged_by_flags(device, - NM_UNMANAGED_SLEEPING, - FALSE, - NM_DEVICE_STATE_REASON_NOW_MANAGED); - } - } - - nm_manager_update_state(self); -} - -static void -_internal_sleep(NMManager *self, gboolean do_sleep) -{ - NMManagerPrivate *priv; - - g_return_if_fail(NM_IS_MANAGER(self)); - - priv = NM_MANAGER_GET_PRIVATE(self); - - if (priv->sleeping == do_sleep) - return; - - _LOGI(LOGD_SUSPEND, - "sleep: %s requested (sleeping: %s enabled: %s)", - do_sleep ? "sleep" : "wake", - priv->sleeping ? "yes" : "no", - priv->net_enabled ? "yes" : "no"); - - priv->sleeping = do_sleep; - - do_sleep_wake(self, TRUE); - - _notify(self, PROP_SLEEPING); -} - -static void -impl_manager_sleep(NMDBusObject * obj, - const NMDBusInterfaceInfoExtended *interface_info, - const NMDBusMethodInfoExtended * method_info, - GDBusConnection * connection, - const char * sender, - GDBusMethodInvocation * invocation, - GVariant * parameters) -{ - NMManager * self = NM_MANAGER(obj); - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - GError * error = NULL; - gs_unref_object NMAuthSubject *subject = NULL; - gboolean do_sleep; - - g_variant_get(parameters, "(b)", &do_sleep); - - subject = nm_dbus_manager_new_auth_subject_from_context(invocation); - - if (priv->sleeping == do_sleep) { - error = g_error_new(NM_MANAGER_ERROR, - NM_MANAGER_ERROR_ALREADY_ASLEEP_OR_AWAKE, - "Already %s", - do_sleep ? "asleep" : "awake"); - nm_audit_log_control_op(NM_AUDIT_OP_SLEEP_CONTROL, - do_sleep ? "on" : "off", - FALSE, - subject, - error->message); - g_dbus_method_invocation_take_error(invocation, error); - return; - } - - /* Unconditionally allow the request. Previously it was polkit protected - * but unfortunately that doesn't work for short-lived processes like - * pm-utils. It uses dbus-send without --print-reply, which quits - * immediately after sending the request, and NM is unable to obtain the - * sender's UID as dbus-send has already dropped off the bus. Thus NM - * fails the request. Instead, don't validate the request, but rely on - * D-Bus permissions to restrict the call to root. - */ - _internal_sleep(self, do_sleep); - nm_audit_log_control_op(NM_AUDIT_OP_SLEEP_CONTROL, - do_sleep ? "on" : "off", - TRUE, - subject, - NULL); - g_dbus_method_invocation_return_value(invocation, NULL); - return; -} - -static void -sleeping_cb(NMSleepMonitor *monitor, gboolean is_about_to_suspend, gpointer user_data) -{ - NMManager *self = user_data; - - _LOGT(LOGD_SUSPEND, "sleep: received %s signal", is_about_to_suspend ? "sleeping" : "resuming"); - _internal_sleep(self, is_about_to_suspend); -} - -static void -_internal_enable(NMManager *self, gboolean enable) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - - nm_config_state_set(priv->config, - TRUE, - FALSE, - NM_CONFIG_STATE_PROPERTY_NETWORKING_ENABLED, - enable); - - _LOGI(LOGD_SUSPEND, - "%s requested (sleeping: %s enabled: %s)", - enable ? "enable" : "disable", - priv->sleeping ? "yes" : "no", - priv->net_enabled ? "yes" : "no"); - - priv->net_enabled = enable; - - do_sleep_wake(self, FALSE); - - _notify(self, PROP_NETWORKING_ENABLED); -} - -static void -enable_net_done_cb(NMAuthChain *chain, GDBusMethodInvocation *context, gpointer user_data) -{ - NMManager * self = NM_MANAGER(user_data); - NMAuthCallResult result; - gboolean enable; - NMAuthSubject * subject; - - nm_assert(G_IS_DBUS_METHOD_INVOCATION(context)); - - c_list_unlink(nm_auth_chain_parent_lst_list(chain)); - enable = GPOINTER_TO_UINT(nm_auth_chain_get_data(chain, "enable")); - subject = nm_auth_chain_get_subject(chain); - - result = nm_auth_chain_get_result(chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK); - if (result != NM_AUTH_CALL_RESULT_YES) { - GError *ret_error; - - ret_error = g_error_new_literal(NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - "Not authorized to enable/disable networking"); - nm_audit_log_control_op(NM_AUDIT_OP_NET_CONTROL, - enable ? "on" : "off", - FALSE, - subject, - ret_error->message); - g_dbus_method_invocation_take_error(context, ret_error); - return; - } - - _internal_enable(self, enable); - g_dbus_method_invocation_return_value(context, NULL); - nm_audit_log_control_op(NM_AUDIT_OP_NET_CONTROL, enable ? "on" : "off", TRUE, subject, NULL); -} - -static void -impl_manager_enable(NMDBusObject * obj, - const NMDBusInterfaceInfoExtended *interface_info, - const NMDBusMethodInfoExtended * method_info, - GDBusConnection * connection, - const char * sender, - GDBusMethodInvocation * invocation, - GVariant * parameters) -{ - NMManager * self = NM_MANAGER(obj); - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - NMAuthChain * chain; - GError * error = NULL; - gboolean enable; - - g_variant_get(parameters, "(b)", &enable); - - if (priv->net_enabled == enable) { - error = g_error_new(NM_MANAGER_ERROR, - NM_MANAGER_ERROR_ALREADY_ENABLED_OR_DISABLED, - "Already %s", - enable ? "enabled" : "disabled"); - goto done; - } - - chain = nm_auth_chain_new_context(invocation, enable_net_done_cb, self); - if (!chain) { - error = g_error_new_literal(NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - NM_UTILS_ERROR_MSG_REQ_AUTH_FAILED); - goto done; - } - - c_list_link_tail(&priv->auth_lst_head, nm_auth_chain_parent_lst_list(chain)); - nm_auth_chain_set_data(chain, "enable", GUINT_TO_POINTER(enable), NULL); - nm_auth_chain_add_call(chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK, TRUE); - -done: - if (error) - g_dbus_method_invocation_take_error(invocation, error); -} - -/* Permissions */ - -static void -get_permissions_done_cb(NMAuthChain *chain, GDBusMethodInvocation *context, gpointer user_data) -{ - GVariantBuilder results; - int i; - - nm_assert(G_IS_DBUS_METHOD_INVOCATION(context)); - - c_list_unlink(nm_auth_chain_parent_lst_list(chain)); - - g_variant_builder_init(&results, G_VARIANT_TYPE("a{ss}")); - - for (i = 0; i < (int) G_N_ELEMENTS(nm_auth_permission_sorted); i++) { - const char *permission = nm_auth_permission_names_by_idx[nm_auth_permission_sorted[i] - 1]; - NMAuthCallResult result; - const char * result_str; - - result = nm_auth_chain_get_result(chain, permission); - result_str = nm_client_permission_result_to_string(nm_auth_call_result_to_client(result)); - g_variant_builder_add(&results, "{ss}", permission, result_str); - } - - g_dbus_method_invocation_return_value(context, g_variant_new("(a{ss})", &results)); -} - -static void -impl_manager_get_permissions(NMDBusObject * obj, - const NMDBusInterfaceInfoExtended *interface_info, - const NMDBusMethodInfoExtended * method_info, - GDBusConnection * connection, - const char * sender, - GDBusMethodInvocation * invocation, - GVariant * parameters) -{ - NMManager * self = NM_MANAGER(obj); - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - NMAuthChain * chain; - int i; - - chain = nm_auth_chain_new_context(invocation, get_permissions_done_cb, self); - if (!chain) { - g_dbus_method_invocation_return_error_literal(invocation, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - NM_UTILS_ERROR_MSG_REQ_AUTH_FAILED); - return; - } - - c_list_link_tail(&priv->auth_lst_head, nm_auth_chain_parent_lst_list(chain)); - - for (i = 0; i < (int) G_N_ELEMENTS(nm_auth_permission_sorted); i++) { - const char *permission = nm_auth_permission_names_by_idx[nm_auth_permission_sorted[i] - 1]; - - nm_auth_chain_add_call_unsafe(chain, permission, FALSE); - } -} - -static void -impl_manager_state(NMDBusObject * obj, - const NMDBusInterfaceInfoExtended *interface_info, - const NMDBusMethodInfoExtended * method_info, - GDBusConnection * connection, - const char * sender, - GDBusMethodInvocation * invocation, - GVariant * parameters) -{ - NMManager *self = NM_MANAGER(obj); - - nm_manager_update_state(self); - g_dbus_method_invocation_return_value( - invocation, - g_variant_new("(u)", NM_MANAGER_GET_PRIVATE(self)->state)); -} - -static void -impl_manager_set_logging(NMDBusObject * obj, - const NMDBusInterfaceInfoExtended *interface_info, - const NMDBusMethodInfoExtended * method_info, - GDBusConnection * connection, - const char * sender, - GDBusMethodInvocation * invocation, - GVariant * parameters) -{ - NMManager * self = NM_MANAGER(obj); - GError * error = NULL; - const char *level; - const char *domains; - - /* The permission is already enforced by the D-Bus daemon, but we ensure - * that the caller is still alive so that clients are forced to wait and - * we'll be able to switch to polkit without breaking behavior. - */ - if (!nm_dbus_manager_ensure_uid(nm_dbus_object_get_manager(NM_DBUS_OBJECT(self)), - invocation, - G_MAXULONG, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED)) - return; - - g_variant_get(parameters, "(&s&s)", &level, &domains); - - if (nm_logging_setup(level, domains, NULL, &error)) { - _LOGI(LOGD_CORE, - "logging: level '%s' domains '%s'", - nm_logging_level_to_string(), - nm_logging_domains_to_string()); - } - - if (error) - g_dbus_method_invocation_take_error(invocation, error); - else - g_dbus_method_invocation_return_value(invocation, NULL); -} - -static void -impl_manager_get_logging(NMDBusObject * obj, - const NMDBusInterfaceInfoExtended *interface_info, - const NMDBusMethodInfoExtended * method_info, - GDBusConnection * connection, - const char * sender, - GDBusMethodInvocation * invocation, - GVariant * parameters) -{ - g_dbus_method_invocation_return_value( - invocation, - g_variant_new("(ss)", nm_logging_level_to_string(), nm_logging_domains_to_string())); -} - -typedef struct { - NMManager * self; - GDBusMethodInvocation *context; - guint remaining; -} ConnectivityCheckData; - -static void -device_connectivity_done(NMDevice * device, - NMDeviceConnectivityHandle *handle, - NMConnectivityState state, - GError * error, - gpointer user_data) -{ - ConnectivityCheckData *data = user_data; - NMManager * self; - NMManagerPrivate * priv; - - nm_assert(data); - nm_assert(data->remaining > 0); - nm_assert(NM_IS_MANAGER(data->self)); - - data->remaining--; - - self = data->self; - priv = NM_MANAGER_GET_PRIVATE(self); - - if (data->context - && (data->remaining == 0 - || (state == NM_CONNECTIVITY_FULL - && priv->connectivity_state == NM_CONNECTIVITY_FULL))) { - /* despite having a @handle and @state returned by the requests, we always - * return the current connectivity_state. That is, because the connectivity_state - * and the answer to the connectivity check shall agree. - * - * However, if one of the requests (early) returns full connectivity and agrees with - * the accumulated connectivity state, we no longer have to wait. The result is set. - * - * This also works well, because NMDevice first emits change signals to its own - * connectivity state, which is then taken into account for the accumulated global - * state. All this happens, before the callback is invoked. */ - g_dbus_method_invocation_return_value( - g_steal_pointer(&data->context), - g_variant_new("(u)", (guint) priv->connectivity_state)); - } - - if (data->remaining == 0) { - g_object_unref(self); - g_slice_free(ConnectivityCheckData, data); - } -} - -static void -check_connectivity_auth_done_cb(NMAuthChain * chain, - GDBusMethodInvocation *context, - gpointer user_data) -{ - NMManager * self = NM_MANAGER(user_data); - NMManagerPrivate * priv = NM_MANAGER_GET_PRIVATE(self); - GError * error = NULL; - NMAuthCallResult result; - ConnectivityCheckData *data; - NMDevice * device; - - c_list_unlink(nm_auth_chain_parent_lst_list(chain)); - - result = nm_auth_chain_get_result(chain, NM_AUTH_PERMISSION_NETWORK_CONTROL); - - if (result != NM_AUTH_CALL_RESULT_YES) { - error = g_error_new_literal(NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - "Not authorized to recheck connectivity"); - } - if (error) { - g_dbus_method_invocation_take_error(context, error); - return; - } - - data = g_slice_new(ConnectivityCheckData); - data->self = g_object_ref(self); - data->context = context; - data->remaining = 0; - - c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { - if (nm_device_check_connectivity(device, AF_INET, device_connectivity_done, data)) - data->remaining++; - if (nm_device_check_connectivity(device, AF_INET6, device_connectivity_done, data)) - data->remaining++; - } - - if (data->remaining == 0) { - /* call the handler at least once. */ - data->remaining = 1; - device_connectivity_done(NULL, NULL, NM_CONNECTIVITY_UNKNOWN, NULL, data); - /* @data got destroyed. */ - } -} - -static void -impl_manager_check_connectivity(NMDBusObject * obj, - const NMDBusInterfaceInfoExtended *interface_info, - const NMDBusMethodInfoExtended * method_info, - GDBusConnection * connection, - const char * sender, - GDBusMethodInvocation * invocation, - GVariant * parameters) -{ - NMManager * self = NM_MANAGER(obj); - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - NMAuthChain * chain; - - chain = nm_auth_chain_new_context(invocation, check_connectivity_auth_done_cb, self); - if (!chain) { - g_dbus_method_invocation_return_error_literal(invocation, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - NM_UTILS_ERROR_MSG_REQ_AUTH_FAILED); - return; - } - - c_list_link_tail(&priv->auth_lst_head, nm_auth_chain_parent_lst_list(chain)); - nm_auth_chain_add_call(chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE); -} - -static void -start_factory(NMDeviceFactory *factory, gpointer user_data) -{ - nm_device_factory_start(factory); -} - -gboolean -nm_manager_write_device_state(NMManager *self, NMDevice *device, int *out_ifindex) -{ - NMManagerPrivate * priv = NM_MANAGER_GET_PRIVATE(self); - int ifindex; - gboolean managed; - NMConfigDeviceStateManagedType managed_type; - const char * uuid = NULL; - const char * perm_hw_addr_fake = NULL; - gboolean perm_hw_addr_is_fake; - guint32 route_metric_default_aspired; - guint32 route_metric_default_effective; - NMTernary nm_owned; - NMDhcpConfig * dhcp_config; - const char * next_server = NULL; - const char * root_path = NULL; - - NM_SET_OUT(out_ifindex, 0); - - ifindex = nm_device_get_ip_ifindex(device); - if (ifindex <= 0) - return FALSE; - if (ifindex == 1) { - /* ignore loopback */ - return FALSE; - } - - if (!nm_platform_link_get(priv->platform, ifindex)) - return FALSE; - - managed = nm_device_get_managed(device, FALSE); - if (managed) { - NMSettingsConnection *sett_conn = NULL; - - if (nm_device_get_state(device) <= NM_DEVICE_STATE_ACTIVATED) - sett_conn = nm_device_get_settings_connection(device); - if (sett_conn) - uuid = nm_settings_connection_get_uuid(sett_conn); - managed_type = NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_MANAGED; - } else if (nm_device_get_unmanaged_flags(device, NM_UNMANAGED_USER_EXPLICIT)) - managed_type = NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_UNMANAGED; - else - managed_type = NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_UNKNOWN; - - perm_hw_addr_fake = - nm_device_get_permanent_hw_address_full(device, FALSE, &perm_hw_addr_is_fake); - if (perm_hw_addr_fake && !perm_hw_addr_is_fake) - perm_hw_addr_fake = NULL; - - nm_owned = nm_device_is_software(device) ? nm_device_is_nm_owned(device) : NM_TERNARY_DEFAULT; - - route_metric_default_effective = _device_route_metric_get(self, - ifindex, - NM_DEVICE_TYPE_UNKNOWN, - TRUE, - &route_metric_default_aspired); - - dhcp_config = nm_device_get_dhcp_config(device, AF_INET); - if (dhcp_config) { - root_path = nm_dhcp_config_get_option(dhcp_config, "root_path"); - next_server = nm_dhcp_config_get_option(dhcp_config, "next_server"); - } - - if (!nm_config_device_state_write(ifindex, - managed_type, - perm_hw_addr_fake, - uuid, - nm_owned, - route_metric_default_aspired, - route_metric_default_effective, - next_server, - root_path)) - return FALSE; - - NM_SET_OUT(out_ifindex, ifindex); - return TRUE; -} - -void -nm_manager_write_device_state_all(NMManager *self) -{ - NMManagerPrivate * priv = NM_MANAGER_GET_PRIVATE(self); - gs_unref_hashtable GHashTable *preserve_ifindexes = NULL; - NMDevice * device; - - preserve_ifindexes = g_hash_table_new(nm_direct_hash, NULL); - - c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { - int ifindex; - - if (nm_manager_write_device_state(self, device, &ifindex)) { - g_hash_table_add(preserve_ifindexes, GINT_TO_POINTER(ifindex)); - } - } - - nm_config_device_state_prune_stale(preserve_ifindexes, NULL); -} - -static gboolean -devices_inited_cb(gpointer user_data) -{ - NMManager * self = user_data; - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - - priv->devices_inited_id = 0; - priv->devices_inited = TRUE; - check_if_startup_complete(self); - return G_SOURCE_REMOVE; -} - -gboolean -nm_manager_start(NMManager *self, GError **error) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - gs_free NMSettingsConnection **connections = NULL; - guint i; - - nm_device_factory_manager_load_factories(_register_device_factory, self); - - nm_device_factory_manager_for_each_factory(start_factory, NULL); - - /* Set initial radio enabled/disabled state */ - for (i = 0; i < RFKILL_TYPE_MAX; i++) { - RadioState *rstate = &priv->radio_states[i]; - gboolean enabled; - - if (!rstate->desc) - continue; - - /* recheck kernel rfkill state */ - update_rstate_from_rfkill(priv->rfkill_mgr, rstate); - - if (rstate->desc) { - _LOGI(LOGD_RFKILL, - "rfkill: %s %s by radio killswitch; %s by state file", - rstate->desc, - (rstate->hw_enabled && rstate->sw_enabled) ? "enabled" : "disabled", - rstate->user_enabled ? "enabled" : "disabled"); - } - enabled = radio_enabled_for_rstate(rstate, TRUE); - manager_update_radio_enabled(self, rstate, enabled); - } - - _LOGI(LOGD_CORE, "Networking is %s by state file", priv->net_enabled ? "enabled" : "disabled"); - - system_unmanaged_devices_changed_cb(priv->settings, NULL, self); - - hostname_changed_cb(priv->hostname_manager, NULL, self); - - if (!nm_settings_start(priv->settings, error)) - return FALSE; - - nm_platform_process_events(priv->platform); - - g_signal_connect(priv->platform, - NM_PLATFORM_SIGNAL_LINK_CHANGED, - G_CALLBACK(platform_link_cb), - self); - - platform_query_devices(self); - - /* Load VPN plugins */ - priv->vpn_manager = g_object_ref(nm_vpn_manager_get()); - - _LOGD(LOGD_CORE, "creating virtual devices..."); - g_signal_connect(priv->settings, - NM_SETTINGS_SIGNAL_CONNECTION_ADDED, - G_CALLBACK(connection_added_cb), - self); - g_signal_connect(priv->settings, - NM_SETTINGS_SIGNAL_CONNECTION_UPDATED, - G_CALLBACK(connection_updated_cb), - self); - connections = nm_settings_get_connections_clone( - priv->settings, - NULL, - NULL, - NULL, - nm_settings_connection_cmp_autoconnect_priority_p_with_data, - NULL); - for (i = 0; connections[i]; i++) - connection_changed(self, connections[i]); - - nm_clear_g_source(&priv->devices_inited_id); - priv->devices_inited_id = g_idle_add_full(G_PRIORITY_LOW + 10, devices_inited_cb, self, NULL); - - return TRUE; -} - -void -nm_manager_stop(NMManager *self) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - NMDevice * device; - - /* FIXME(shutdown): we don't do a proper shutdown yet: - * - need to ensure that all pending async operations are cancelled - * - e.g. operations in priv->async_op_lst_head - * - need to ensure that no more asynchronous requests are started, - * or that they complete quickly, or that they fail quickly. - * - note that cancelling some operations is not possible synchronously. - * Hence, stop() only prepares shutdown and tells everybody to not - * accept new work, and to complete in a timely manner. - * We need to still iterate the mainloop for a bit, to give everybody - * the chance to complete. - * - e.g. see comment at nm_auth_manager_force_shutdown() - */ - - nm_dbus_manager_stop(nm_dbus_object_get_manager(NM_DBUS_OBJECT(self))); - - while ((device = c_list_first_entry(&priv->devices_lst_head, NMDevice, devices_lst))) - remove_device(self, device, TRUE); - - _active_connection_cleanup(self); - - nm_clear_g_source(&priv->devices_inited_id); -} - -static gboolean -handle_firmware_changed(gpointer user_data) -{ - NMManager * self = NM_MANAGER(user_data); - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - NMDevice * device; - - priv->fw_changed_id = 0; - - /* Try to re-enable devices with missing firmware */ - c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { - NMDeviceState state = nm_device_get_state(device); - - if (nm_device_get_firmware_missing(device) && (state == NM_DEVICE_STATE_UNAVAILABLE)) { - _LOG2I(LOGD_CORE, device, "firmware may now be available"); - - /* Re-set unavailable state to try bringing the device up again */ - nm_device_state_changed(device, - NM_DEVICE_STATE_UNAVAILABLE, - NM_DEVICE_STATE_REASON_NONE); - } - } - - return FALSE; -} - -static void -firmware_dir_changed(GFileMonitor * monitor, - GFile * file, - GFile * other_file, - GFileMonitorEvent event_type, - gpointer user_data) -{ - NMManager * self = NM_MANAGER(user_data); - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - - switch (event_type) { - case G_FILE_MONITOR_EVENT_CREATED: - case G_FILE_MONITOR_EVENT_CHANGED: - case G_FILE_MONITOR_EVENT_MOVED: - case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED: - case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT: - if (!priv->fw_changed_id) { - priv->fw_changed_id = g_timeout_add_seconds(4, handle_firmware_changed, self); - _LOGI(LOGD_CORE, "kernel firmware directory '%s' changed", KERNEL_FIRMWARE_DIR); - } - break; - default: - break; - } -} - -static void -connection_metered_changed(GObject *object, NMMetered metered, gpointer user_data) -{ - nm_manager_update_metered(NM_MANAGER(user_data)); -} - -static void -policy_default_ac_changed(GObject *object, GParamSpec *pspec, gpointer user_data) -{ - NMManager * self = NM_MANAGER(user_data); - NMManagerPrivate * priv = NM_MANAGER_GET_PRIVATE(self); - NMActiveConnection *ac; - - /* Note: this assumes that it's not possible for the IP4 default - * route to be going over the default-ip6-device. If that changes, - * we need something more complicated here. - */ - ac = nm_policy_get_default_ip4_ac(priv->policy); - if (!ac) - ac = nm_policy_get_default_ip6_ac(priv->policy); - - if (ac != priv->primary_connection) { - if (priv->primary_connection) { - g_signal_handlers_disconnect_by_func(priv->primary_connection, - G_CALLBACK(connection_metered_changed), - self); - g_clear_object(&priv->primary_connection); - } - - priv->primary_connection = ac ? g_object_ref(ac) : NULL; - - if (priv->primary_connection) { - g_signal_connect(priv->primary_connection, - NM_ACTIVE_CONNECTION_DEVICE_METERED_CHANGED, - G_CALLBACK(connection_metered_changed), - self); - } - _LOGD(LOGD_CORE, - "PrimaryConnection now %s", - ac ? nm_active_connection_get_settings_connection_id(ac) : "(none)"); - _notify(self, PROP_PRIMARY_CONNECTION); - _notify(self, PROP_PRIMARY_CONNECTION_TYPE); - nm_manager_update_metered(self); - } -} - -static void -policy_activating_ac_changed(GObject *object, GParamSpec *pspec, gpointer user_data) -{ - NMManager * self = NM_MANAGER(user_data); - NMManagerPrivate * priv = NM_MANAGER_GET_PRIVATE(self); - NMActiveConnection *activating, *best; - - /* We only look at activating-ip6-ac if activating-ip4-ac - * AND default-ip4-ac are NULL; if default-ip4-ac is - * non-NULL, then activating-ip6-ac is irrelevant, since while - * that AC might become the new default-ip6-ac, it can't - * become primary-connection while default-ip4-ac is set to - * something else. - */ - activating = nm_policy_get_activating_ip4_ac(priv->policy); - best = nm_policy_get_default_ip4_ac(priv->policy); - if (!activating && !best) - activating = nm_policy_get_activating_ip6_ac(priv->policy); - - if (nm_g_object_ref_set(&priv->activating_connection, activating)) { - _LOGD(LOGD_CORE, - "ActivatingConnection now %s", - activating ? nm_active_connection_get_settings_connection_id(activating) : "(none)"); - _notify(self, PROP_ACTIVATING_CONNECTION); - } -} - -/*****************************************************************************/ - -typedef struct { - NMManager * self; - NMDBusObject * obj; - const NMDBusInterfaceInfoExtended *interface_info; - const NMDBusPropertyInfoExtended * property_info; - GVariant * value; - guint64 export_version_id; -} DBusSetPropertyHandle; - -#define NM_PERM_DENIED_ERROR "org.freedesktop.NetworkManager.PermissionDenied" - -static void -_dbus_set_property_auth_cb(NMAuthChain * chain, - GDBusMethodInvocation *invocation, - gpointer user_data) -{ - DBusSetPropertyHandle *handle_data = user_data; - gs_unref_object NMDBusObject * obj = handle_data->obj; - const NMDBusInterfaceInfoExtended *interface_info = handle_data->interface_info; - const NMDBusPropertyInfoExtended * property_info = handle_data->property_info; - gs_unref_variant GVariant *value = handle_data->value; - guint64 export_version_id = handle_data->export_version_id; - gs_unref_object NMManager *self = handle_data->self; - NMManagerPrivate * priv = NM_MANAGER_GET_PRIVATE(self); - NMAuthCallResult result; - gs_free_error GError *local = NULL; - const char * error_name = NULL; - const char * error_message = NULL; - GValue gvalue; - - g_slice_free(DBusSetPropertyHandle, handle_data); - - c_list_unlink(nm_auth_chain_parent_lst_list(chain)); - result = nm_auth_chain_get_result(chain, property_info->writable.permission); - - if (result != NM_AUTH_CALL_RESULT_YES) { - error_name = NM_PERM_DENIED_ERROR; - error_message = "Not authorized to perform this operation"; - goto out; - } - - if (export_version_id != nm_dbus_object_get_export_version_id(obj)) { - error_name = "org.freedesktop.DBus.Error.UnknownObject"; - error_message = "Object was deleted while authenticating"; - goto out; - } - - /* Handle some properties specially *sigh* */ - if (interface_info == &interface_info_manager - && nm_streq(property_info->property_name, NM_MANAGER_GLOBAL_DNS_CONFIGURATION)) { - const NMGlobalDnsConfig *global_dns; - - global_dns = nm_config_data_get_global_dns_config(nm_config_get_data(priv->config)); - if (global_dns && !nm_global_dns_config_is_internal(global_dns)) { - error_name = NM_PERM_DENIED_ERROR; - error_message = "Global DNS configuration already set via configuration file"; - goto out; - } - } - - g_dbus_gvariant_to_gvalue(value, &gvalue); - if (!nm_g_object_set_property(G_OBJECT(obj), property_info->property_name, &gvalue, &local)) { - error_name = "org.freedesktop.DBus.Error.InvalidArgs"; - error_message = local->message; - } - g_value_unset(&gvalue); - -out: - nm_audit_log_control_op(property_info->writable.audit_op, - property_info->property_name, - !error_message, - nm_auth_chain_get_subject(chain), - error_message); - if (error_message) - g_dbus_method_invocation_return_dbus_error(invocation, error_name, error_message); - else - g_dbus_method_invocation_return_value(invocation, NULL); -} - -void -nm_manager_dbus_set_property_handle(NMDBusObject * obj, - const NMDBusInterfaceInfoExtended *interface_info, - const NMDBusPropertyInfoExtended * property_info, - GDBusConnection * connection, - const char * sender, - GDBusMethodInvocation * invocation, - GVariant * value, - gpointer user_data) -{ - NMManager * self = user_data; - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - NMAuthChain * chain; - const char * error_message = NULL; - gs_unref_object NMAuthSubject *subject = NULL; - DBusSetPropertyHandle * handle_data; - - subject = nm_dbus_manager_new_auth_subject_from_context(invocation); - if (!subject) { - error_message = NM_UTILS_ERROR_MSG_REQ_UID_UKNOWN; - goto err; - } - - handle_data = g_slice_new0(DBusSetPropertyHandle); - handle_data->self = g_object_ref(self); - handle_data->obj = g_object_ref(obj); - handle_data->interface_info = interface_info; - handle_data->property_info = property_info; - handle_data->value = g_variant_ref(value); - handle_data->export_version_id = nm_dbus_object_get_export_version_id(obj); - - chain = nm_auth_chain_new_subject(subject, invocation, _dbus_set_property_auth_cb, handle_data); - c_list_link_tail(&priv->auth_lst_head, nm_auth_chain_parent_lst_list(chain)); - nm_auth_chain_add_call_unsafe(chain, property_info->writable.permission, TRUE); - return; - -err: - nm_audit_log_control_op(property_info->writable.audit_op, - property_info->property_name, - FALSE, - invocation, - error_message); - g_dbus_method_invocation_return_error_literal(invocation, - G_DBUS_ERROR, - G_DBUS_ERROR_AUTH_FAILED, - error_message); -} - -/*****************************************************************************/ - -static NMCheckpointManager * -_checkpoint_mgr_get(NMManager *self, gboolean create_as_needed) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - - if (G_UNLIKELY(!priv->checkpoint_mgr) && create_as_needed) - priv->checkpoint_mgr = nm_checkpoint_manager_new(self, obj_properties[PROP_CHECKPOINTS]); - return priv->checkpoint_mgr; -} - -static void -checkpoint_auth_done_cb(NMAuthChain *chain, GDBusMethodInvocation *context, gpointer user_data) -{ - NMManager * self = NM_MANAGER(user_data); - char * op; - char * checkpoint_path = NULL; - char ** devices; - NMCheckpoint * checkpoint; - NMAuthCallResult result; - guint32 timeout, flags; - GVariant * variant = NULL; - GError * error = NULL; - const char * arg = NULL; - guint32 add_timeout; - - op = nm_auth_chain_get_data(chain, "audit-op"); - c_list_unlink(nm_auth_chain_parent_lst_list(chain)); - result = nm_auth_chain_get_result(chain, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK); - - if (NM_IN_STRSET(op, - NM_AUDIT_OP_CHECKPOINT_DESTROY, - NM_AUDIT_OP_CHECKPOINT_ROLLBACK, - NM_AUDIT_OP_CHECKPOINT_ADJUST_ROLLBACK_TIMEOUT)) - arg = checkpoint_path = nm_auth_chain_get_data(chain, "checkpoint_path"); - - if (result != NM_AUTH_CALL_RESULT_YES) { - error = g_error_new_literal(NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - "Not authorized to checkpoint/rollback"); - } else { - if (nm_streq0(op, NM_AUDIT_OP_CHECKPOINT_CREATE)) { - timeout = GPOINTER_TO_UINT(nm_auth_chain_get_data(chain, "timeout")); - flags = GPOINTER_TO_UINT(nm_auth_chain_get_data(chain, "flags")); - devices = nm_auth_chain_get_data(chain, "devices"); - - checkpoint = nm_checkpoint_manager_create(_checkpoint_mgr_get(self, TRUE), - (const char *const *) devices, - timeout, - (NMCheckpointCreateFlags) flags, - &error); - if (checkpoint) { - arg = nm_dbus_object_get_path(NM_DBUS_OBJECT(checkpoint)); - variant = g_variant_new("(o)", arg); - } - } else if (nm_streq0(op, NM_AUDIT_OP_CHECKPOINT_DESTROY)) { - nm_checkpoint_manager_destroy(_checkpoint_mgr_get(self, TRUE), checkpoint_path, &error); - } else if (nm_streq0(op, NM_AUDIT_OP_CHECKPOINT_ROLLBACK)) { - nm_checkpoint_manager_rollback(_checkpoint_mgr_get(self, TRUE), - checkpoint_path, - &variant, - &error); - } else if (nm_streq0(op, NM_AUDIT_OP_CHECKPOINT_ADJUST_ROLLBACK_TIMEOUT)) { - add_timeout = GPOINTER_TO_UINT(nm_auth_chain_get_data(chain, "add_timeout")); - nm_checkpoint_manager_adjust_rollback_timeout(_checkpoint_mgr_get(self, TRUE), - checkpoint_path, - add_timeout, - &error); - } else - g_return_if_reached(); - } - - nm_audit_log_checkpoint_op(op, - arg ?: "", - !error, - nm_auth_chain_get_subject(chain), - error ? error->message : NULL); - - if (error) - g_dbus_method_invocation_take_error(context, error); - else - g_dbus_method_invocation_return_value(context, variant); -} - -static void -impl_manager_checkpoint_create(NMDBusObject * obj, - const NMDBusInterfaceInfoExtended *interface_info, - const NMDBusMethodInfoExtended * method_info, - GDBusConnection * connection, - const char * sender, - GDBusMethodInvocation * invocation, - GVariant * parameters) -{ - NMManager * self = NM_MANAGER(obj); - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - NMAuthChain * chain; - char ** devices; - guint32 rollback_timeout; - guint32 flags; - - G_STATIC_ASSERT_EXPR(sizeof(flags) <= sizeof(NMCheckpointCreateFlags)); - - chain = nm_auth_chain_new_context(invocation, checkpoint_auth_done_cb, self); - if (!chain) { - g_dbus_method_invocation_return_error_literal(invocation, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - NM_UTILS_ERROR_MSG_REQ_AUTH_FAILED); - return; - } - - g_variant_get(parameters, "(^aouu)", &devices, &rollback_timeout, &flags); - - c_list_link_tail(&priv->auth_lst_head, nm_auth_chain_parent_lst_list(chain)); - nm_auth_chain_set_data(chain, "audit-op", NM_AUDIT_OP_CHECKPOINT_CREATE, NULL); - nm_auth_chain_set_data(chain, "devices", devices, (GDestroyNotify) g_strfreev); - nm_auth_chain_set_data(chain, "flags", GUINT_TO_POINTER(flags), NULL); - nm_auth_chain_set_data(chain, "timeout", GUINT_TO_POINTER(rollback_timeout), NULL); - nm_auth_chain_add_call(chain, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK, TRUE); -} - -static void -impl_manager_checkpoint_destroy(NMDBusObject * obj, - const NMDBusInterfaceInfoExtended *interface_info, - const NMDBusMethodInfoExtended * method_info, - GDBusConnection * connection, - const char * sender, - GDBusMethodInvocation * invocation, - GVariant * parameters) -{ - NMManager * self = NM_MANAGER(obj); - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - NMAuthChain * chain; - const char * checkpoint_path; - - chain = nm_auth_chain_new_context(invocation, checkpoint_auth_done_cb, self); - if (!chain) { - g_dbus_method_invocation_return_error_literal(invocation, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - NM_UTILS_ERROR_MSG_REQ_AUTH_FAILED); - return; - } - - g_variant_get(parameters, "(&o)", &checkpoint_path); - - c_list_link_tail(&priv->auth_lst_head, nm_auth_chain_parent_lst_list(chain)); - nm_auth_chain_set_data(chain, "audit-op", NM_AUDIT_OP_CHECKPOINT_DESTROY, NULL); - nm_auth_chain_set_data(chain, "checkpoint_path", g_strdup(checkpoint_path), g_free); - nm_auth_chain_add_call(chain, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK, TRUE); -} - -static void -impl_manager_checkpoint_rollback(NMDBusObject * obj, - const NMDBusInterfaceInfoExtended *interface_info, - const NMDBusMethodInfoExtended * method_info, - GDBusConnection * connection, - const char * sender, - GDBusMethodInvocation * invocation, - GVariant * parameters) -{ - NMManager * self = NM_MANAGER(obj); - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - NMAuthChain * chain; - const char * checkpoint_path; - - chain = nm_auth_chain_new_context(invocation, checkpoint_auth_done_cb, self); - if (!chain) { - g_dbus_method_invocation_return_error_literal(invocation, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - NM_UTILS_ERROR_MSG_REQ_AUTH_FAILED); - return; - } - - g_variant_get(parameters, "(&o)", &checkpoint_path); - - c_list_link_tail(&priv->auth_lst_head, nm_auth_chain_parent_lst_list(chain)); - nm_auth_chain_set_data(chain, "audit-op", NM_AUDIT_OP_CHECKPOINT_ROLLBACK, NULL); - nm_auth_chain_set_data(chain, "checkpoint_path", g_strdup(checkpoint_path), g_free); - nm_auth_chain_add_call(chain, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK, TRUE); -} - -static void -impl_manager_checkpoint_adjust_rollback_timeout(NMDBusObject * obj, - const NMDBusInterfaceInfoExtended *interface_info, - const NMDBusMethodInfoExtended * method_info, - GDBusConnection * connection, - const char * sender, - GDBusMethodInvocation * invocation, - GVariant * parameters) -{ - NMManager * self = NM_MANAGER(obj); - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - NMAuthChain * chain; - const char * checkpoint_path; - guint32 add_timeout; - - chain = nm_auth_chain_new_context(invocation, checkpoint_auth_done_cb, self); - if (!chain) { - g_dbus_method_invocation_return_error_literal(invocation, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_PERMISSION_DENIED, - NM_UTILS_ERROR_MSG_REQ_AUTH_FAILED); - return; - } - - g_variant_get(parameters, "(&ou)", &checkpoint_path, &add_timeout); - - c_list_link_tail(&priv->auth_lst_head, nm_auth_chain_parent_lst_list(chain)); - nm_auth_chain_set_data(chain, "audit-op", NM_AUDIT_OP_CHECKPOINT_ADJUST_ROLLBACK_TIMEOUT, NULL); - nm_auth_chain_set_data(chain, "checkpoint_path", g_strdup(checkpoint_path), g_free); - nm_auth_chain_set_data(chain, "add_timeout", GUINT_TO_POINTER(add_timeout), NULL); - nm_auth_chain_add_call(chain, NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK, TRUE); -} - -/*****************************************************************************/ - -static void -auth_mgr_changed(NMAuthManager *auth_manager, gpointer user_data) -{ - /* Let clients know they should re-check their authorization */ - nm_dbus_object_emit_signal(user_data, - &interface_info_manager, - &signal_info_check_permissions, - "()"); -} - -#define KERN_RFKILL_OP_CHANGE_ALL 3 -#define KERN_RFKILL_TYPE_WLAN 1 -#define KERN_RFKILL_TYPE_WWAN 5 -struct rfkill_event { - __u32 idx; - __u8 type; - __u8 op; - __u8 soft, hard; -} _nm_packed; - -static void -rfkill_change(NMManager *self, const char *desc, RfKillType rtype, gboolean enabled) -{ - int fd; - struct rfkill_event event; - ssize_t len; - int errsv; - - g_return_if_fail(rtype == RFKILL_TYPE_WLAN || rtype == RFKILL_TYPE_WWAN); - - fd = open("/dev/rfkill", O_RDWR | O_CLOEXEC); - if (fd < 0) { - if (errno == EACCES) - _LOGW(LOGD_RFKILL, "rfkill: (%s): failed to open killswitch device", desc); - return; - } - - if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) { - _LOGW(LOGD_RFKILL, - "rfkill: (%s): failed to set killswitch device for " - "non-blocking operation", - desc); - nm_close(fd); - return; - } - - memset(&event, 0, sizeof(event)); - event.op = KERN_RFKILL_OP_CHANGE_ALL; - switch (rtype) { - case RFKILL_TYPE_WLAN: - event.type = KERN_RFKILL_TYPE_WLAN; - break; - case RFKILL_TYPE_WWAN: - event.type = KERN_RFKILL_TYPE_WWAN; - break; - default: - g_assert_not_reached(); - } - event.soft = enabled ? 0 : 1; - - len = write(fd, &event, sizeof(event)); - if (len < 0) { - errsv = errno; - _LOGW(LOGD_RFKILL, - "rfkill: (%s): failed to change Wi-Fi killswitch state: (%d) %s", - desc, - errsv, - nm_strerror_native(errsv)); - } else if (len == sizeof(event)) { - _LOGI(LOGD_RFKILL, - "rfkill: %s hardware radio set %s", - desc, - enabled ? "enabled" : "disabled"); - } else { - /* Failed to write full structure */ - _LOGW(LOGD_RFKILL, "rfkill: (%s): failed to change Wi-Fi killswitch state", desc); - } - - nm_close(fd); -} - -static void -manager_radio_user_toggled(NMManager *self, RadioState *rstate, gboolean enabled) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - gboolean old_enabled, new_enabled; - - /* Don't touch devices if asleep/networking disabled */ - if (manager_sleeping(self)) - return; - - if (rstate->desc) { - _LOGD(LOGD_RFKILL, - "rfkill: (%s): setting radio %s by user", - rstate->desc, - enabled ? "enabled" : "disabled"); - } - - /* Update enabled key in state file */ - nm_config_state_set(priv->config, TRUE, FALSE, rstate->key, enabled); - - /* 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, FALSE); - if (new_enabled != old_enabled) { - /* Try to change the kernel rfkill state */ - if (rstate->rtype == RFKILL_TYPE_WLAN || rstate->rtype == RFKILL_TYPE_WWAN) - rfkill_change(self, rstate->desc, rstate->rtype, new_enabled); - - manager_update_radio_enabled(self, rstate, new_enabled); - } -} - -static gboolean -periodic_update_active_connection_timestamps(gpointer user_data) -{ - NMManager * manager = NM_MANAGER(user_data); - NMManagerPrivate * priv = NM_MANAGER_GET_PRIVATE(manager); - NMActiveConnection *ac; - gboolean has_time = FALSE; - guint64 t = 0; - - c_list_for_each_entry (ac, &priv->active_connections_lst_head, active_connections_lst) { - if (nm_active_connection_get_state(ac) != NM_ACTIVE_CONNECTION_STATE_ACTIVATED) - continue; - - if (!has_time) { - t = time(NULL); - has_time = TRUE; - } - nm_settings_connection_update_timestamp(nm_active_connection_get_settings_connection(ac), - t); - } - return G_SOURCE_CONTINUE; -} - -/*****************************************************************************/ - -void -nm_manager_set_capability(NMManager *self, NMCapability cap) -{ - NMManagerPrivate *priv; - guint32 cap_i; - gssize idx; - - g_return_if_fail(NM_IS_MANAGER(self)); - if (cap < 1 || cap > _NM_CAPABILITY_MAX) - g_return_if_reached(); - - cap_i = (guint32) cap; - - priv = NM_MANAGER_GET_PRIVATE(self); - - idx = nm_utils_array_find_binary_search(&g_array_index(priv->capabilities, guint32, 0), - sizeof(guint32), - priv->capabilities->len, - &cap_i, - nm_cmp_uint32_p_with_data, - NULL); - if (idx >= 0) - return; - - nm_assert((~idx) <= (gssize) priv->capabilities->len); - - g_array_insert_val(priv->capabilities, ~idx, cap_i); - _notify(self, PROP_CAPABILITIES); -} - -/*****************************************************************************/ - -NM_DEFINE_SINGLETON_REGISTER(NMManager); - -NMManager * -nm_manager_get(void) -{ - g_return_val_if_fail(singleton_instance, NULL); - return singleton_instance; -} - -NMSettings * -nm_settings_get(void) -{ - g_return_val_if_fail(singleton_instance, NULL); - - return NM_MANAGER_GET_PRIVATE(singleton_instance)->settings; -} - -NMManager * -nm_manager_setup(void) -{ - NMManager *self; - - g_return_val_if_fail(!singleton_instance, singleton_instance); - - self = g_object_new(NM_TYPE_MANAGER, NULL); - nm_assert(NM_IS_MANAGER(self)); - singleton_instance = self; - - nm_singleton_instance_register(); - nm_log_dbg(LOGD_CORE, - "setup %s singleton (" NM_HASH_OBFUSCATE_PTR_FMT ")", - "NMManager", - NM_HASH_OBFUSCATE_PTR(singleton_instance)); - - nm_dbus_object_export(NM_DBUS_OBJECT(self)); - return self; -} - -static void -constructed(GObject *object) -{ - NMManager * self = NM_MANAGER(object); - NMManagerPrivate * priv = NM_MANAGER_GET_PRIVATE(self); - const NMConfigState *state; - - G_OBJECT_CLASS(nm_manager_parent_class)->constructed(object); - - priv->settings = nm_settings_new(self); - - nm_dbus_object_export(NM_DBUS_OBJECT(priv->settings)); - - g_signal_connect(priv->settings, - "notify::" NM_SETTINGS_STARTUP_COMPLETE, - G_CALLBACK(settings_startup_complete_changed), - self); - g_signal_connect(priv->settings, - "notify::" NM_SETTINGS_UNMANAGED_SPECS, - G_CALLBACK(system_unmanaged_devices_changed_cb), - self); - g_signal_connect(priv->settings, - NM_SETTINGS_SIGNAL_CONNECTION_FLAGS_CHANGED, - G_CALLBACK(connection_flags_changed), - self); - - priv->hostname_manager = g_object_ref(nm_hostname_manager_get()); - g_signal_connect(priv->hostname_manager, - "notify::" NM_HOSTNAME_MANAGER_HOSTNAME, - G_CALLBACK(hostname_changed_cb), - self); - - /* - * Do not delete existing virtual devices to keep connectivity up. - * Virtual devices are reused when NetworkManager is restarted. - * Hence, don't react on NM_SETTINGS_SIGNAL_CONNECTION_REMOVED. - */ - - priv->policy = nm_policy_new(self, priv->settings); - g_signal_connect(priv->policy, - "notify::" NM_POLICY_DEFAULT_IP4_AC, - G_CALLBACK(policy_default_ac_changed), - self); - g_signal_connect(priv->policy, - "notify::" NM_POLICY_DEFAULT_IP6_AC, - G_CALLBACK(policy_default_ac_changed), - self); - g_signal_connect(priv->policy, - "notify::" NM_POLICY_ACTIVATING_IP4_AC, - G_CALLBACK(policy_activating_ac_changed), - self); - g_signal_connect(priv->policy, - "notify::" NM_POLICY_ACTIVATING_IP6_AC, - G_CALLBACK(policy_activating_ac_changed), - self); - - priv->config = g_object_ref(nm_config_get()); - g_signal_connect(G_OBJECT(priv->config), - NM_CONFIG_SIGNAL_CONFIG_CHANGED, - G_CALLBACK(_config_changed_cb), - self); - - state = nm_config_state_get(priv->config); - - priv->net_enabled = state->net_enabled; - - priv->radio_states[RFKILL_TYPE_WLAN].user_enabled = state->wifi_enabled; - priv->radio_states[RFKILL_TYPE_WWAN].user_enabled = state->wwan_enabled; - - priv->rfkill_mgr = nm_rfkill_manager_new(); - g_signal_connect(priv->rfkill_mgr, - NM_RFKILL_MANAGER_SIGNAL_RFKILL_CHANGED, - G_CALLBACK(rfkill_manager_rfkill_changed_cb), - self); - - /* Force kernel Wi-Fi/WWAN rfkill state to follow NM saved Wi-Fi/WWAN state - * in case the BIOS doesn't save rfkill state, and to be consistent with user - * changes to the WirelessEnabled/WWANEnabled properties which toggle kernel - * rfkill. - */ - rfkill_change(self, - priv->radio_states[RFKILL_TYPE_WLAN].desc, - RFKILL_TYPE_WLAN, - priv->radio_states[RFKILL_TYPE_WLAN].user_enabled); - rfkill_change(self, - priv->radio_states[RFKILL_TYPE_WWAN].desc, - RFKILL_TYPE_WWAN, - priv->radio_states[RFKILL_TYPE_WWAN].user_enabled); -} - -static void -nm_manager_init(NMManager *self) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - guint i; - GFile * file; - - c_list_init(&priv->auth_lst_head); - c_list_init(&priv->link_cb_lst); - c_list_init(&priv->devices_lst_head); - c_list_init(&priv->active_connections_lst_head); - c_list_init(&priv->async_op_lst_head); - c_list_init(&priv->delete_volatile_connection_lst_head); - - priv->platform = g_object_ref(NM_PLATFORM_GET); - - priv->capabilities = g_array_new(FALSE, FALSE, sizeof(guint32)); - - /* Initialize rfkill structures and states */ - memset(priv->radio_states, 0, sizeof(priv->radio_states)); - - priv->radio_states[RFKILL_TYPE_WLAN].user_enabled = TRUE; - priv->radio_states[RFKILL_TYPE_WLAN].key = NM_CONFIG_STATE_PROPERTY_WIFI_ENABLED; - priv->radio_states[RFKILL_TYPE_WLAN].prop = NM_MANAGER_WIRELESS_ENABLED; - priv->radio_states[RFKILL_TYPE_WLAN].hw_prop = NM_MANAGER_WIRELESS_HARDWARE_ENABLED; - priv->radio_states[RFKILL_TYPE_WLAN].desc = "Wi-Fi"; - priv->radio_states[RFKILL_TYPE_WLAN].rtype = RFKILL_TYPE_WLAN; - - priv->radio_states[RFKILL_TYPE_WWAN].user_enabled = TRUE; - priv->radio_states[RFKILL_TYPE_WWAN].key = NM_CONFIG_STATE_PROPERTY_WWAN_ENABLED; - 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].rtype = RFKILL_TYPE_WWAN; - - for (i = 0; i < RFKILL_TYPE_MAX; i++) - priv->radio_states[i].hw_enabled = TRUE; - - priv->sleeping = FALSE; - priv->state = NM_STATE_DISCONNECTED; - priv->startup = TRUE; - - /* sleep/wake handling */ - priv->sleep_monitor = nm_sleep_monitor_new(); - g_signal_connect(priv->sleep_monitor, NM_SLEEP_MONITOR_SLEEPING, G_CALLBACK(sleeping_cb), self); - - /* Listen for authorization changes */ - priv->auth_mgr = g_object_ref(nm_auth_manager_get()); - g_signal_connect(priv->auth_mgr, - NM_AUTH_MANAGER_SIGNAL_CHANGED, - G_CALLBACK(auth_mgr_changed), - self); - - /* Monitor the firmware directory */ - if (strlen(KERNEL_FIRMWARE_DIR)) { - file = g_file_new_for_path(KERNEL_FIRMWARE_DIR "/"); - priv->fw_monitor = g_file_monitor_directory(file, G_FILE_MONITOR_NONE, NULL, NULL); - g_object_unref(file); - } - - if (priv->fw_monitor) { - g_signal_connect(priv->fw_monitor, "changed", G_CALLBACK(firmware_dir_changed), self); - _LOGI(LOGD_CORE, "monitoring kernel firmware directory '%s'.", KERNEL_FIRMWARE_DIR); - } else { - _LOGW(LOGD_CORE, "failed to monitor kernel firmware directory '%s'.", KERNEL_FIRMWARE_DIR); - } - - /* Update timestamps in active connections */ - priv->timestamp_update_id = - g_timeout_add_seconds(300, - (GSourceFunc) periodic_update_active_connection_timestamps, - self); - - priv->metered = NM_METERED_UNKNOWN; - priv->sleep_devices = g_hash_table_new(nm_direct_hash, NULL); -} - -static void -get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) -{ - NMManager * self = NM_MANAGER(object); - NMManagerPrivate * priv = NM_MANAGER_GET_PRIVATE(self); - NMConfigData * config_data; - const NMGlobalDnsConfig *dns_config; - const char * type; - const char * path; - NMActiveConnection * ac; - GPtrArray * ptrarr; - - switch (prop_id) { - case PROP_VERSION: - g_value_set_string(value, VERSION); - break; - case PROP_CAPABILITIES: - g_value_set_variant(value, - g_variant_new_fixed_array(G_VARIANT_TYPE("u"), - priv->capabilities->data, - priv->capabilities->len, - sizeof(guint32))); - break; - case PROP_STATE: - g_value_set_uint(value, priv->state); - break; - case PROP_STARTUP: - g_value_set_boolean(value, priv->startup); - break; - case PROP_NETWORKING_ENABLED: - g_value_set_boolean(value, priv->net_enabled); - break; - case PROP_WIRELESS_ENABLED: - g_value_set_boolean(value, radio_enabled_for_type(self, RFKILL_TYPE_WLAN, TRUE)); - break; - case PROP_WIRELESS_HARDWARE_ENABLED: - g_value_set_boolean(value, priv->radio_states[RFKILL_TYPE_WLAN].hw_enabled); - break; - case PROP_WWAN_ENABLED: - g_value_set_boolean(value, radio_enabled_for_type(self, RFKILL_TYPE_WWAN, TRUE)); - break; - case PROP_WWAN_HARDWARE_ENABLED: - g_value_set_boolean(value, priv->radio_states[RFKILL_TYPE_WWAN].hw_enabled); - break; - case PROP_WIMAX_ENABLED: - g_value_set_boolean(value, FALSE); - break; - case PROP_WIMAX_HARDWARE_ENABLED: - g_value_set_boolean(value, FALSE); - break; - case PROP_ACTIVE_CONNECTIONS: - ptrarr = g_ptr_array_new(); - c_list_for_each_entry (ac, &priv->active_connections_lst_head, active_connections_lst) { - path = nm_dbus_object_get_path(NM_DBUS_OBJECT(ac)); - if (path) - g_ptr_array_add(ptrarr, g_strdup(path)); - } - g_ptr_array_add(ptrarr, NULL); - g_value_take_boxed(value, g_ptr_array_free(ptrarr, FALSE)); - break; - case PROP_CONNECTIVITY: - g_value_set_uint(value, priv->connectivity_state); - break; - case PROP_CONNECTIVITY_CHECK_AVAILABLE: - config_data = nm_config_get_data(priv->config); - g_value_set_boolean(value, nm_config_data_get_connectivity_uri(config_data) != NULL); - break; - case PROP_CONNECTIVITY_CHECK_ENABLED: - g_value_set_boolean(value, concheck_enabled(self, NULL)); - break; - case PROP_CONNECTIVITY_CHECK_URI: - config_data = nm_config_get_data(priv->config); - g_value_set_string(value, nm_config_data_get_connectivity_uri(config_data)); - break; - case PROP_PRIMARY_CONNECTION: - nm_dbus_utils_g_value_set_object_path(value, priv->primary_connection); - break; - case PROP_PRIMARY_CONNECTION_TYPE: - type = NULL; - if (priv->primary_connection) { - NMConnection *con; - - con = nm_active_connection_get_applied_connection(priv->primary_connection); - if (con) - type = nm_connection_get_connection_type(con); - } - g_value_set_string(value, type ?: ""); - break; - case PROP_ACTIVATING_CONNECTION: - nm_dbus_utils_g_value_set_object_path(value, priv->activating_connection); - break; - case PROP_SLEEPING: - g_value_set_boolean(value, priv->sleeping); - break; - case PROP_DEVICES: - g_value_take_boxed(value, nm_utils_strv_make_deep_copied(_get_devices_paths(self, FALSE))); - break; - case PROP_METERED: - g_value_set_uint(value, priv->metered); - break; - case PROP_GLOBAL_DNS_CONFIGURATION: - config_data = nm_config_get_data(priv->config); - dns_config = nm_config_data_get_global_dns_config(config_data); - nm_global_dns_config_to_dbus(dns_config, value); - break; - case PROP_ALL_DEVICES: - g_value_take_boxed(value, nm_utils_strv_make_deep_copied(_get_devices_paths(self, TRUE))); - break; - case PROP_CHECKPOINTS: - g_value_take_boxed( - value, - priv->checkpoint_mgr ? nm_utils_strv_make_deep_copied( - nm_checkpoint_manager_get_checkpoint_paths(priv->checkpoint_mgr, NULL)) - : NULL); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } -} - -static void -set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) -{ - NMManager * self = NM_MANAGER(object); - NMManagerPrivate * priv = NM_MANAGER_GET_PRIVATE(self); - NMGlobalDnsConfig *dns_config; - GError * error = NULL; - - switch (prop_id) { - case PROP_WIRELESS_ENABLED: - manager_radio_user_toggled(NM_MANAGER(object), - &priv->radio_states[RFKILL_TYPE_WLAN], - g_value_get_boolean(value)); - break; - case PROP_WWAN_ENABLED: - manager_radio_user_toggled(NM_MANAGER(object), - &priv->radio_states[RFKILL_TYPE_WWAN], - g_value_get_boolean(value)); - break; - case PROP_WIMAX_ENABLED: - /* WIMAX is deprecated. This does nothing. */ - break; - case PROP_CONNECTIVITY_CHECK_ENABLED: - nm_config_set_connectivity_check_enabled(priv->config, g_value_get_boolean(value)); - break; - case PROP_GLOBAL_DNS_CONFIGURATION: - dns_config = nm_global_dns_config_from_dbus(value, &error); - if (!error) - nm_config_set_global_dns(priv->config, dns_config, &error); - - nm_global_dns_config_free(dns_config); - - if (error) { - _LOGD(LOGD_CORE, "set global DNS failed with error: %s", error->message); - g_error_free(error); - } - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } -} - -static void -_deinit_device_factory(NMDeviceFactory *factory, gpointer user_data) -{ - g_signal_handlers_disconnect_matched(factory, - G_SIGNAL_MATCH_DATA, - 0, - 0, - NULL, - NULL, - NM_MANAGER(user_data)); -} - -static void -dispose(GObject *object) -{ - NMManager * self = NM_MANAGER(object); - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); - CList * iter; - - nm_assert(c_list_is_empty(&priv->async_op_lst_head)); - - g_signal_handlers_disconnect_by_func(priv->platform, G_CALLBACK(platform_link_cb), self); - while ((iter = c_list_first(&priv->link_cb_lst))) { - PlatformLinkCbData *data = c_list_entry(iter, PlatformLinkCbData, lst); - - g_source_remove(data->idle_id); - c_list_unlink_stale(&data->lst); - g_slice_free(PlatformLinkCbData, data); - } - - while ((iter = c_list_first(&priv->auth_lst_head))) - nm_auth_chain_destroy(nm_auth_chain_parent_lst_entry(iter)); - - nm_clear_g_source(&priv->devices_inited_id); - - nm_clear_pointer(&priv->checkpoint_mgr, nm_checkpoint_manager_free); - - if (priv->concheck_mgr) { - g_signal_handlers_disconnect_by_func(priv->concheck_mgr, - G_CALLBACK(concheck_config_changed_cb), - self); - g_clear_object(&priv->concheck_mgr); - } - - if (priv->auth_mgr) { - g_signal_handlers_disconnect_by_func(priv->auth_mgr, G_CALLBACK(auth_mgr_changed), self); - g_clear_object(&priv->auth_mgr); - } - - nm_assert(c_list_is_empty(&priv->devices_lst_head)); - - nm_clear_g_source(&priv->ac_cleanup_id); - - while ((iter = c_list_first(&priv->active_connections_lst_head))) - active_connection_remove(self, - c_list_entry(iter, NMActiveConnection, active_connections_lst)); - - nm_assert(c_list_is_empty(&priv->active_connections_lst_head)); - g_clear_object(&priv->primary_connection); - g_clear_object(&priv->activating_connection); - - if (priv->config) { - g_signal_handlers_disconnect_by_func(priv->config, _config_changed_cb, self); - g_clear_object(&priv->config); - } - - if (priv->policy) { - g_signal_handlers_disconnect_by_func(priv->policy, policy_default_ac_changed, self); - g_signal_handlers_disconnect_by_func(priv->policy, policy_activating_ac_changed, self); - g_clear_object(&priv->policy); - } - - if (priv->settings) { - g_signal_handlers_disconnect_by_func(priv->settings, - settings_startup_complete_changed, - self); - g_signal_handlers_disconnect_by_func(priv->settings, - system_unmanaged_devices_changed_cb, - self); - g_signal_handlers_disconnect_by_func(priv->settings, connection_added_cb, self); - g_signal_handlers_disconnect_by_func(priv->settings, connection_updated_cb, self); - g_signal_handlers_disconnect_by_func(priv->settings, connection_flags_changed, self); - g_clear_object(&priv->settings); - } - - if (priv->hostname_manager) { - g_signal_handlers_disconnect_by_func(priv->hostname_manager, hostname_changed_cb, self); - g_clear_object(&priv->hostname_manager); - } - - g_clear_object(&priv->vpn_manager); - - sleep_devices_clear(self); - nm_clear_pointer(&priv->sleep_devices, g_hash_table_unref); - - if (priv->sleep_monitor) { - g_signal_handlers_disconnect_by_func(priv->sleep_monitor, sleeping_cb, self); - g_clear_object(&priv->sleep_monitor); - } - - if (priv->fw_monitor) { - g_signal_handlers_disconnect_by_func(priv->fw_monitor, firmware_dir_changed, self); - - nm_clear_g_source(&priv->fw_changed_id); - - g_file_monitor_cancel(priv->fw_monitor); - g_clear_object(&priv->fw_monitor); - } - - if (priv->rfkill_mgr) { - g_signal_handlers_disconnect_by_func(priv->rfkill_mgr, - rfkill_manager_rfkill_changed_cb, - self); - g_clear_object(&priv->rfkill_mgr); - } - - nm_clear_g_source(&priv->delete_volatile_connection_idle_id); - _delete_volatile_connection_all(self, FALSE); - nm_assert(!priv->delete_volatile_connection_idle_id); - nm_assert(c_list_is_empty(&priv->delete_volatile_connection_lst_head)); - - nm_device_factory_manager_for_each_factory(_deinit_device_factory, self); - - nm_clear_g_source(&priv->timestamp_update_id); - - nm_clear_pointer(&priv->device_route_metrics, g_hash_table_destroy); - - G_OBJECT_CLASS(nm_manager_parent_class)->dispose(object); -} - -static void -finalize(GObject *object) -{ - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(object); - - g_array_free(priv->capabilities, TRUE); - - G_OBJECT_CLASS(nm_manager_parent_class)->finalize(object); - - g_object_unref(priv->platform); -} - -static const GDBusSignalInfo signal_info_check_permissions = - NM_DEFINE_GDBUS_SIGNAL_INFO_INIT("CheckPermissions", ); - -static const GDBusSignalInfo signal_info_state_changed = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT( - "StateChanged", - .args = NM_DEFINE_GDBUS_ARG_INFOS(NM_DEFINE_GDBUS_ARG_INFO("state", "u"), ), ); - -static const GDBusSignalInfo signal_info_device_added = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT( - "DeviceAdded", - .args = NM_DEFINE_GDBUS_ARG_INFOS(NM_DEFINE_GDBUS_ARG_INFO("device_path", "o"), ), ); - -static const GDBusSignalInfo signal_info_device_removed = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT( - "DeviceRemoved", - .args = NM_DEFINE_GDBUS_ARG_INFOS(NM_DEFINE_GDBUS_ARG_INFO("device_path", "o"), ), ); - -static const NMDBusInterfaceInfoExtended interface_info_manager = { - .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT( - NM_DBUS_INTERFACE, - .methods = NM_DEFINE_GDBUS_METHOD_INFOS( - NM_DEFINE_DBUS_METHOD_INFO_EXTENDED( - NM_DEFINE_GDBUS_METHOD_INFO_INIT("Reload", - .in_args = NM_DEFINE_GDBUS_ARG_INFOS( - NM_DEFINE_GDBUS_ARG_INFO("flags", "u"), ), ), - .handle = impl_manager_reload, ), - NM_DEFINE_DBUS_METHOD_INFO_EXTENDED( - NM_DEFINE_GDBUS_METHOD_INFO_INIT( - "GetDevices", - .out_args = - NM_DEFINE_GDBUS_ARG_INFOS(NM_DEFINE_GDBUS_ARG_INFO("devices", "ao"), ), ), - .handle = impl_manager_get_devices, ), - NM_DEFINE_DBUS_METHOD_INFO_EXTENDED( - NM_DEFINE_GDBUS_METHOD_INFO_INIT( - "GetAllDevices", - .out_args = - NM_DEFINE_GDBUS_ARG_INFOS(NM_DEFINE_GDBUS_ARG_INFO("devices", "ao"), ), ), - .handle = impl_manager_get_all_devices, ), - NM_DEFINE_DBUS_METHOD_INFO_EXTENDED( - NM_DEFINE_GDBUS_METHOD_INFO_INIT( - "GetDeviceByIpIface", - .in_args = NM_DEFINE_GDBUS_ARG_INFOS(NM_DEFINE_GDBUS_ARG_INFO("iface", "s"), ), - .out_args = - NM_DEFINE_GDBUS_ARG_INFOS(NM_DEFINE_GDBUS_ARG_INFO("device", "o"), ), ), - .handle = impl_manager_get_device_by_ip_iface, ), - NM_DEFINE_DBUS_METHOD_INFO_EXTENDED( - NM_DEFINE_GDBUS_METHOD_INFO_INIT( - "ActivateConnection", - .in_args = NM_DEFINE_GDBUS_ARG_INFOS( - NM_DEFINE_GDBUS_ARG_INFO("connection", "o"), - NM_DEFINE_GDBUS_ARG_INFO("device", "o"), - NM_DEFINE_GDBUS_ARG_INFO("specific_object", "o"), ), - .out_args = NM_DEFINE_GDBUS_ARG_INFOS( - NM_DEFINE_GDBUS_ARG_INFO("active_connection", "o"), ), ), - .handle = impl_manager_activate_connection, ), - NM_DEFINE_DBUS_METHOD_INFO_EXTENDED( - NM_DEFINE_GDBUS_METHOD_INFO_INIT( - "AddAndActivateConnection", - .in_args = NM_DEFINE_GDBUS_ARG_INFOS( - NM_DEFINE_GDBUS_ARG_INFO("connection", "a{sa{sv}}"), - NM_DEFINE_GDBUS_ARG_INFO("device", "o"), - NM_DEFINE_GDBUS_ARG_INFO("specific_object", "o"), ), - .out_args = NM_DEFINE_GDBUS_ARG_INFOS( - NM_DEFINE_GDBUS_ARG_INFO("path", "o"), - NM_DEFINE_GDBUS_ARG_INFO("active_connection", "o"), ), ), - .handle = impl_manager_add_and_activate_connection, ), - NM_DEFINE_DBUS_METHOD_INFO_EXTENDED( - NM_DEFINE_GDBUS_METHOD_INFO_INIT( - "AddAndActivateConnection2", - .in_args = NM_DEFINE_GDBUS_ARG_INFOS( - NM_DEFINE_GDBUS_ARG_INFO("connection", "a{sa{sv}}"), - NM_DEFINE_GDBUS_ARG_INFO("device", "o"), - NM_DEFINE_GDBUS_ARG_INFO("specific_object", "o"), - NM_DEFINE_GDBUS_ARG_INFO("options", "a{sv}"), ), - .out_args = NM_DEFINE_GDBUS_ARG_INFOS( - NM_DEFINE_GDBUS_ARG_INFO("path", "o"), - NM_DEFINE_GDBUS_ARG_INFO("active_connection", "o"), - NM_DEFINE_GDBUS_ARG_INFO("result", "a{sv}"), ), ), - .handle = impl_manager_add_and_activate_connection, ), - NM_DEFINE_DBUS_METHOD_INFO_EXTENDED( - NM_DEFINE_GDBUS_METHOD_INFO_INIT( - "DeactivateConnection", - .in_args = NM_DEFINE_GDBUS_ARG_INFOS( - NM_DEFINE_GDBUS_ARG_INFO("active_connection", "o"), ), ), - .handle = impl_manager_deactivate_connection, ), - NM_DEFINE_DBUS_METHOD_INFO_EXTENDED( - NM_DEFINE_GDBUS_METHOD_INFO_INIT("Sleep", - .in_args = NM_DEFINE_GDBUS_ARG_INFOS( - NM_DEFINE_GDBUS_ARG_INFO("sleep", "b"), ), ), - .handle = impl_manager_sleep, ), - NM_DEFINE_DBUS_METHOD_INFO_EXTENDED( - NM_DEFINE_GDBUS_METHOD_INFO_INIT("Enable", - .in_args = NM_DEFINE_GDBUS_ARG_INFOS( - NM_DEFINE_GDBUS_ARG_INFO("enable", "b"), ), ), - .handle = impl_manager_enable, ), - NM_DEFINE_DBUS_METHOD_INFO_EXTENDED( - NM_DEFINE_GDBUS_METHOD_INFO_INIT( - "GetPermissions", - .out_args = NM_DEFINE_GDBUS_ARG_INFOS( - NM_DEFINE_GDBUS_ARG_INFO("permissions", "a{ss}"), ), ), - .handle = impl_manager_get_permissions, ), - NM_DEFINE_DBUS_METHOD_INFO_EXTENDED( - NM_DEFINE_GDBUS_METHOD_INFO_INIT("SetLogging", - .in_args = NM_DEFINE_GDBUS_ARG_INFOS( - NM_DEFINE_GDBUS_ARG_INFO("level", "s"), - NM_DEFINE_GDBUS_ARG_INFO("domains", "s"), ), ), - .handle = impl_manager_set_logging, ), - NM_DEFINE_DBUS_METHOD_INFO_EXTENDED( - NM_DEFINE_GDBUS_METHOD_INFO_INIT("GetLogging", - .out_args = NM_DEFINE_GDBUS_ARG_INFOS( - NM_DEFINE_GDBUS_ARG_INFO("level", "s"), - NM_DEFINE_GDBUS_ARG_INFO("domains", "s"), ), ), - .handle = impl_manager_get_logging, ), - NM_DEFINE_DBUS_METHOD_INFO_EXTENDED( - NM_DEFINE_GDBUS_METHOD_INFO_INIT( - "CheckConnectivity", - .out_args = NM_DEFINE_GDBUS_ARG_INFOS( - NM_DEFINE_GDBUS_ARG_INFO("connectivity", "u"), ), ), - .handle = impl_manager_check_connectivity, ), - NM_DEFINE_DBUS_METHOD_INFO_EXTENDED( - NM_DEFINE_GDBUS_METHOD_INFO_INIT("state", - .out_args = NM_DEFINE_GDBUS_ARG_INFOS( - NM_DEFINE_GDBUS_ARG_INFO("state", "u"), ), ), - .handle = impl_manager_state, ), - NM_DEFINE_DBUS_METHOD_INFO_EXTENDED( - NM_DEFINE_GDBUS_METHOD_INFO_INIT( - "CheckpointCreate", - .in_args = - NM_DEFINE_GDBUS_ARG_INFOS(NM_DEFINE_GDBUS_ARG_INFO("devices", "ao"), - NM_DEFINE_GDBUS_ARG_INFO("rollback_timeout", "u"), - NM_DEFINE_GDBUS_ARG_INFO("flags", "u"), ), - .out_args = - NM_DEFINE_GDBUS_ARG_INFOS(NM_DEFINE_GDBUS_ARG_INFO("checkpoint", "o"), ), ), - .handle = impl_manager_checkpoint_create, ), - NM_DEFINE_DBUS_METHOD_INFO_EXTENDED( - NM_DEFINE_GDBUS_METHOD_INFO_INIT( - "CheckpointDestroy", - .in_args = - NM_DEFINE_GDBUS_ARG_INFOS(NM_DEFINE_GDBUS_ARG_INFO("checkpoint", "o"), ), ), - .handle = impl_manager_checkpoint_destroy, ), - NM_DEFINE_DBUS_METHOD_INFO_EXTENDED( - NM_DEFINE_GDBUS_METHOD_INFO_INIT( - "CheckpointRollback", - .in_args = - NM_DEFINE_GDBUS_ARG_INFOS(NM_DEFINE_GDBUS_ARG_INFO("checkpoint", "o"), ), - .out_args = - NM_DEFINE_GDBUS_ARG_INFOS(NM_DEFINE_GDBUS_ARG_INFO("result", "a{su}"), ), ), - .handle = impl_manager_checkpoint_rollback, ), - NM_DEFINE_DBUS_METHOD_INFO_EXTENDED( - NM_DEFINE_GDBUS_METHOD_INFO_INIT( - "CheckpointAdjustRollbackTimeout", - .in_args = NM_DEFINE_GDBUS_ARG_INFOS( - NM_DEFINE_GDBUS_ARG_INFO("checkpoint", "o"), - NM_DEFINE_GDBUS_ARG_INFO("add_timeout", "u"), ), ), - .handle = impl_manager_checkpoint_adjust_rollback_timeout, ), ), - .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS(&nm_signal_info_property_changed_legacy, - &signal_info_check_permissions, - &signal_info_state_changed, - &signal_info_device_added, - &signal_info_device_removed, ), - .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS( - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Devices", "ao", NM_MANAGER_DEVICES), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("AllDevices", - "ao", - NM_MANAGER_ALL_DEVICES), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Checkpoints", - "ao", - NM_MANAGER_CHECKPOINTS), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("NetworkingEnabled", - "b", - NM_MANAGER_NETWORKING_ENABLED), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_L( - "WirelessEnabled", - "b", - NM_MANAGER_WIRELESS_ENABLED, - NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI, - NM_AUDIT_OP_RADIO_CONTROL), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("WirelessHardwareEnabled", - "b", - NM_MANAGER_WIRELESS_HARDWARE_ENABLED), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_L( - "WwanEnabled", - "b", - NM_MANAGER_WWAN_ENABLED, - NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN, - NM_AUDIT_OP_RADIO_CONTROL), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("WwanHardwareEnabled", - "b", - NM_MANAGER_WWAN_HARDWARE_ENABLED), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_L( - "WimaxEnabled", - "b", - NM_MANAGER_WIMAX_ENABLED, - NM_AUTH_PERMISSION_ENABLE_DISABLE_WIMAX, - NM_AUDIT_OP_RADIO_CONTROL), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("WimaxHardwareEnabled", - "b", - NM_MANAGER_WIMAX_HARDWARE_ENABLED), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("ActiveConnections", - "ao", - NM_MANAGER_ACTIVE_CONNECTIONS), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("PrimaryConnection", - "o", - NM_MANAGER_PRIMARY_CONNECTION), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("PrimaryConnectionType", - "s", - NM_MANAGER_PRIMARY_CONNECTION_TYPE), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Metered", "u", NM_MANAGER_METERED), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("ActivatingConnection", - "o", - NM_MANAGER_ACTIVATING_CONNECTION), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Startup", "b", NM_MANAGER_STARTUP), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Version", "s", NM_MANAGER_VERSION), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Capabilities", - "au", - NM_MANAGER_CAPABILITIES), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("State", "u", NM_MANAGER_STATE), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L("Connectivity", - "u", - NM_MANAGER_CONNECTIVITY), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L( - "ConnectivityCheckAvailable", - "b", - NM_MANAGER_CONNECTIVITY_CHECK_AVAILABLE), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_L( - "ConnectivityCheckEnabled", - "b", - NM_MANAGER_CONNECTIVITY_CHECK_ENABLED, - NM_AUTH_PERMISSION_ENABLE_DISABLE_CONNECTIVITY_CHECK, - NM_AUDIT_OP_NET_CONTROL), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("ConnectivityCheckUri", - "s", - NM_MANAGER_CONNECTIVITY_CHECK_URI), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READWRITABLE_L( - "GlobalDnsConfiguration", - "a{sv}", - NM_MANAGER_GLOBAL_DNS_CONFIGURATION, - NM_AUTH_PERMISSION_SETTINGS_MODIFY_GLOBAL_DNS, - NM_AUDIT_OP_NET_CONTROL), ), ), - .legacy_property_changed = TRUE, -}; - -static void -nm_manager_class_init(NMManagerClass *manager_class) -{ - GObjectClass * object_class = G_OBJECT_CLASS(manager_class); - NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS(manager_class); - - dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_STATIC(NM_DBUS_PATH); - dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS(&interface_info_manager); - - object_class->constructed = constructed; - object_class->set_property = set_property; - object_class->get_property = get_property; - object_class->dispose = dispose; - object_class->finalize = finalize; - - obj_properties[PROP_VERSION] = g_param_spec_string(NM_MANAGER_VERSION, - "", - "", - NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_CAPABILITIES] = - g_param_spec_variant(NM_MANAGER_CAPABILITIES, - "", - "", - G_VARIANT_TYPE("au"), - NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_STATE] = g_param_spec_uint(NM_MANAGER_STATE, - "", - "", - 0, - NM_STATE_DISCONNECTED, - 0, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_STARTUP] = g_param_spec_boolean(NM_MANAGER_STARTUP, - "", - "", - TRUE, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_NETWORKING_ENABLED] = - g_param_spec_boolean(NM_MANAGER_NETWORKING_ENABLED, - "", - "", - TRUE, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_WIRELESS_ENABLED] = - g_param_spec_boolean(NM_MANAGER_WIRELESS_ENABLED, - "", - "", - TRUE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_WIRELESS_HARDWARE_ENABLED] = - g_param_spec_boolean(NM_MANAGER_WIRELESS_HARDWARE_ENABLED, - "", - "", - TRUE, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_WWAN_ENABLED] = - g_param_spec_boolean(NM_MANAGER_WWAN_ENABLED, - "", - "", - TRUE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_WWAN_HARDWARE_ENABLED] = - g_param_spec_boolean(NM_MANAGER_WWAN_HARDWARE_ENABLED, - "", - "", - TRUE, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_WIMAX_ENABLED] = - g_param_spec_boolean(NM_MANAGER_WIMAX_ENABLED, - "", - "", - TRUE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_WIMAX_HARDWARE_ENABLED] = - g_param_spec_boolean(NM_MANAGER_WIMAX_HARDWARE_ENABLED, - "", - "", - TRUE, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_ACTIVE_CONNECTIONS] = - g_param_spec_boxed(NM_MANAGER_ACTIVE_CONNECTIONS, - "", - "", - G_TYPE_STRV, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_CONNECTIVITY] = - g_param_spec_uint(NM_MANAGER_CONNECTIVITY, - "", - "", - NM_CONNECTIVITY_UNKNOWN, - NM_CONNECTIVITY_FULL, - NM_CONNECTIVITY_UNKNOWN, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_CONNECTIVITY_CHECK_AVAILABLE] = - g_param_spec_boolean(NM_MANAGER_CONNECTIVITY_CHECK_AVAILABLE, - "", - "", - TRUE, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_CONNECTIVITY_CHECK_ENABLED] = - g_param_spec_boolean(NM_MANAGER_CONNECTIVITY_CHECK_ENABLED, - "", - "", - TRUE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_CONNECTIVITY_CHECK_URI] = - g_param_spec_string(NM_MANAGER_CONNECTIVITY_CHECK_URI, - "", - "", - NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_PRIMARY_CONNECTION] = - g_param_spec_string(NM_MANAGER_PRIMARY_CONNECTION, - "", - "", - NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_PRIMARY_CONNECTION_TYPE] = - g_param_spec_string(NM_MANAGER_PRIMARY_CONNECTION_TYPE, - "", - "", - NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_ACTIVATING_CONNECTION] = - g_param_spec_string(NM_MANAGER_ACTIVATING_CONNECTION, - "", - "", - NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - - /* Sleeping is not exported over D-Bus */ - obj_properties[PROP_SLEEPING] = g_param_spec_boolean(NM_MANAGER_SLEEPING, - "", - "", - FALSE, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_DEVICES] = g_param_spec_boxed(NM_MANAGER_DEVICES, - "", - "", - G_TYPE_STRV, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - - /** - * NMManager:metered: - * - * Whether the connectivity is metered. - * - * Since: 1.2 - **/ - obj_properties[PROP_METERED] = g_param_spec_uint(NM_MANAGER_METERED, - "", - "", - 0, - G_MAXUINT32, - NM_METERED_UNKNOWN, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - - /** - * NMManager:global-dns-configuration: - * - * The global DNS configuration. - * - * Since: 1.2 - **/ - obj_properties[PROP_GLOBAL_DNS_CONFIGURATION] = - g_param_spec_variant(NM_MANAGER_GLOBAL_DNS_CONFIGURATION, - "", - "", - G_VARIANT_TYPE("a{sv}"), - NULL, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - - /** - * NMManager:all-devices: - * - * All devices, including those that are not realized. - * - * Since: 1.2 - **/ - obj_properties[PROP_ALL_DEVICES] = - g_param_spec_boxed(NM_MANAGER_ALL_DEVICES, - "", - "", - G_TYPE_STRV, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_CHECKPOINTS] = - g_param_spec_boxed(NM_MANAGER_CHECKPOINTS, - "", - "", - G_TYPE_STRV, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); - - /* signals */ - - /* emitted only for realized devices */ - signals[DEVICE_ADDED] = g_signal_new(NM_MANAGER_DEVICE_ADDED, - G_OBJECT_CLASS_TYPE(object_class), - G_SIGNAL_RUN_FIRST, - 0, - NULL, - NULL, - NULL, - G_TYPE_NONE, - 1, - NM_TYPE_DEVICE); - - /* Emitted for both realized devices and placeholder devices */ - signals[INTERNAL_DEVICE_ADDED] = g_signal_new(NM_MANAGER_INTERNAL_DEVICE_ADDED, - G_OBJECT_CLASS_TYPE(object_class), - G_SIGNAL_RUN_FIRST, - 0, - NULL, - NULL, - NULL, - G_TYPE_NONE, - 1, - G_TYPE_OBJECT); - - /* emitted only for realized devices when a device - * becomes unrealized or removed */ - signals[DEVICE_REMOVED] = g_signal_new(NM_MANAGER_DEVICE_REMOVED, - G_OBJECT_CLASS_TYPE(object_class), - G_SIGNAL_RUN_FIRST, - 0, - NULL, - NULL, - NULL, - G_TYPE_NONE, - 1, - NM_TYPE_DEVICE); - - /* Emitted for both realized devices and placeholder devices */ - signals[INTERNAL_DEVICE_REMOVED] = g_signal_new(NM_MANAGER_INTERNAL_DEVICE_REMOVED, - G_OBJECT_CLASS_TYPE(object_class), - G_SIGNAL_RUN_FIRST, - 0, - NULL, - NULL, - NULL, - G_TYPE_NONE, - 1, - G_TYPE_OBJECT); - - signals[ACTIVE_CONNECTION_ADDED] = g_signal_new(NM_MANAGER_ACTIVE_CONNECTION_ADDED, - G_OBJECT_CLASS_TYPE(object_class), - G_SIGNAL_RUN_FIRST, - 0, - NULL, - NULL, - NULL, - G_TYPE_NONE, - 1, - NM_TYPE_ACTIVE_CONNECTION); - - signals[ACTIVE_CONNECTION_REMOVED] = g_signal_new(NM_MANAGER_ACTIVE_CONNECTION_REMOVED, - G_OBJECT_CLASS_TYPE(object_class), - G_SIGNAL_RUN_FIRST, - 0, - NULL, - NULL, - NULL, - G_TYPE_NONE, - 1, - NM_TYPE_ACTIVE_CONNECTION); - - signals[CONFIGURE_QUIT] = g_signal_new(NM_MANAGER_CONFIGURE_QUIT, - G_OBJECT_CLASS_TYPE(object_class), - G_SIGNAL_RUN_FIRST, - 0, - NULL, - NULL, - NULL, - G_TYPE_NONE, - 0); -} |