diff options
Diffstat (limited to 'src/nm-netlink-monitor.c')
-rw-r--r-- | src/nm-netlink-monitor.c | 943 |
1 files changed, 0 insertions, 943 deletions
diff --git a/src/nm-netlink-monitor.c b/src/nm-netlink-monitor.c deleted file mode 100644 index ba8053e93..000000000 --- a/src/nm-netlink-monitor.c +++ /dev/null @@ -1,943 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 2005 - 2010 Red Hat, Inc. - * Copyright (C) 2005 - 2008 Novell, Inc. - * Copyright (C) 2005 Ray Strode - * - * Some code borrowed from HAL: - * - * Copyright (C) 2003 David Zeuthen, <david@fubar.dk> - * Copyright (C) 2004 Novell, Inc. - */ - -/* for struct ucred and LIBNL_NEEDS_ADDR_CACHING_WORKAROUND */ -#include <config.h> - -#include <errno.h> -#include <string.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <linux/types.h> -#include <linux/netlink.h> -#include <linux/rtnetlink.h> -#include <linux/if.h> -#include <linux/unistd.h> -#include <unistd.h> -#include <netlink/netlink.h> -#include <netlink/object.h> -#include <netlink/route/addr.h> -#include <netlink/route/rtnl.h> - -#include <glib.h> -#include <glib/gi18n.h> - -#include "nm-netlink-compat.h" -#include "nm-netlink-monitor.h" -#include "nm-logging.h" - -#define EVENT_CONDITIONS ((GIOCondition) (G_IO_IN | G_IO_PRI)) -#define ERROR_CONDITIONS ((GIOCondition) (G_IO_ERR | G_IO_NVAL)) -#define DISCONNECT_CONDITIONS ((GIOCondition) (G_IO_HUP)) - -#define NM_NETLINK_MONITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \ - NM_TYPE_NETLINK_MONITOR, \ - NMNetlinkMonitorPrivate)) - -typedef struct { - /* Async event listener connection */ - struct nl_sock *nlh_event; - GIOChannel * io_channel; - guint event_id; - - /* Sync/blocking request/response connection */ - struct nl_sock *nlh_sync; - struct nl_cache * link_cache; - - guint request_status_id; - - GHashTable *subscriptions; -} NMNetlinkMonitorPrivate; - -enum { - NOTIFICATION = 0, - CARRIER_ON, - CARRIER_OFF, - LAST_SIGNAL -}; -static guint signals[LAST_SIGNAL] = { 0 }; - - -G_DEFINE_TYPE (NMNetlinkMonitor, nm_netlink_monitor, G_TYPE_OBJECT); - -/****************************************************************/ - -static gboolean -detach_monitor (gpointer data) -{ - nm_log_warn (LOGD_HW, "detaching netlink event monitor"); - nm_netlink_monitor_detach (NM_NETLINK_MONITOR (data)); - return FALSE; -} - -static void -log_error_limited (NMNetlinkMonitor *monitor, guint32 code, const char *fmt, ...) -{ - static time_t rl_time = 0; - static guint32 rl_code = 0; - static guint32 rl_count = 0; - va_list args; - char *msg; - time_t now; - - g_return_if_fail (monitor != NULL); - - now = time (NULL); - - if ((code != rl_code) || (now > rl_time + 10)) { - va_start (args, fmt); - msg = g_strdup_vprintf (fmt, args); - va_end (args); - - nm_log_warn (LOGD_HW, "error monitoring device for netlink events: %s\n", msg); - g_free (msg); - - rl_time = now; - rl_code = code; - rl_count = 0; - } - - rl_count++; - if (rl_count > 100) { - /* Broken drivers will sometimes cause a flood of netlink errors. - * rh #459205, novell #443429, lp #284507 - */ - nm_log_warn (LOGD_HW, "excessive netlink errors ocurred, disabling netlink monitor."); - nm_log_warn (LOGD_HW, "link change events will not be processed."); - g_idle_add_full (G_PRIORITY_HIGH, detach_monitor, monitor, NULL); - } -} - -/****************************************************************/ - -static void -link_msg_handler (struct nl_object *obj, void *arg) -{ - NMNetlinkMonitor *self = NM_NETLINK_MONITOR (arg); - struct rtnl_link *filter; - struct rtnl_link *link_obj; - guint flags; - guint ifidx; - - filter = rtnl_link_alloc (); - if (!filter) { - log_error_limited (self, NM_NETLINK_MONITOR_ERROR_BAD_ALLOC, - _("error processing netlink message: %s"), - nl_geterror (ENOMEM)); - return; - } - - /* Ensure it's a link object */ - if (nl_object_match_filter (obj, OBJ_CAST (filter)) == 0) { - rtnl_link_put (filter); - return; - } - - link_obj = (struct rtnl_link *) obj; - flags = rtnl_link_get_flags (link_obj); - ifidx = rtnl_link_get_ifindex (link_obj); - - nm_log_dbg (LOGD_HW, "netlink link message: iface idx %d flags 0x%X", ifidx, flags); - - /* IFF_LOWER_UP is the indicator of carrier status since kernel commit - * b00055aacdb172c05067612278ba27265fcd05ce in 2.6.17. - */ - if (flags & IFF_LOWER_UP) - g_signal_emit (self, signals[CARRIER_ON], 0, ifidx); - else - g_signal_emit (self, signals[CARRIER_OFF], 0, ifidx); - - rtnl_link_put (filter); -} - -static int -event_msg_recv (struct nl_msg *msg, void *arg) -{ - struct nl_sock *nlh = arg; - struct nlmsghdr *hdr = nlmsg_hdr (msg); - struct ucred *creds = nlmsg_get_creds (msg); - const struct sockaddr_nl *snl; - guint32 local_port; - gboolean accept_msg = FALSE; - - /* Only messages sent from the kernel */ - if (!creds || creds->uid != 0) { - nm_log_dbg (LOGD_HW, "ignoring netlink message from UID %d", - creds ? creds->uid : -1); - return NL_SKIP; - } - - snl = nlmsg_get_src (msg); - g_assert (snl); - - /* Accept any messages from the kernel */ - if (hdr->nlmsg_pid == 0 || snl->nl_pid == 0) - accept_msg = TRUE; - - /* And any multicast message directed to our netlink PID, since multicast - * currently requires CAP_ADMIN to use. - */ - local_port = nl_socket_get_local_port (nlh); - if ((hdr->nlmsg_pid == local_port) && snl->nl_groups) - accept_msg = TRUE; - - if (accept_msg == FALSE) { - nm_log_dbg (LOGD_HW, "ignoring netlink message from PID %d (local PID %d, multicast %d)", - hdr->nlmsg_pid, - local_port, - (hdr->nlmsg_flags & NLM_F_MULTI)); - return NL_SKIP; - } - - return NL_OK; -} - -static int -event_msg_ready (struct nl_msg *msg, void *arg) -{ - NMNetlinkMonitor *self = NM_NETLINK_MONITOR (arg); - - /* By the time the message gets here we've already checked the sender - * and we're sure it's safe to parse this message. - */ - - /* Let clients handle generic messages */ - g_signal_emit (self, signals[NOTIFICATION], 0, msg); - - /* Parse carrier messages */ - nl_msg_parse (msg, &link_msg_handler, self); - - return NL_OK; -} - -static gboolean -event_handler (GIOChannel *channel, - GIOCondition io_condition, - gpointer user_data) -{ - NMNetlinkMonitor *self = (NMNetlinkMonitor *) user_data; - NMNetlinkMonitorPrivate *priv; - int err; - - g_return_val_if_fail (NM_IS_NETLINK_MONITOR (self), TRUE); - - priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); - g_return_val_if_fail (priv->event_id > 0, TRUE); - - if (io_condition & ERROR_CONDITIONS) { - const char *err_msg = _("error occurred while waiting for data on socket"); - int err_code = 0; - socklen_t err_len = sizeof (err_code); - - /* Grab error information */ - if (getsockopt (g_io_channel_unix_get_fd (channel), - SOL_SOCKET, SO_ERROR, - (void *) &err_code, &err_len) == 0) - err_msg = strerror (err_code); - - log_error_limited (self, NM_NETLINK_MONITOR_ERROR_WAITING_FOR_SOCKET_DATA, "%s", err_msg); - return TRUE; - } else if (io_condition & DISCONNECT_CONDITIONS) - return FALSE; - - g_return_val_if_fail (!(io_condition & ~EVENT_CONDITIONS), FALSE); - - /* Process the netlink messages */ - err = nl_recvmsgs_default (priv->nlh_event); - if (err < 0) { - log_error_limited (self, NM_NETLINK_MONITOR_ERROR_PROCESSING_MESSAGE, - _("error processing netlink message: %s"), - nl_geterror (err)); - } - - return TRUE; -} - -static gboolean -nlh_setup (struct nl_sock *nlh, - nl_recvmsg_msg_cb_t valid_func, - gpointer cb_data, - GError **error) -{ - int err; - - nl_socket_modify_cb (nlh, NL_CB_MSG_IN, NL_CB_CUSTOM, event_msg_recv, cb_data); - - if (valid_func) - nl_socket_modify_cb (nlh, NL_CB_VALID, NL_CB_CUSTOM, valid_func, cb_data); - - err = nl_connect (nlh, NETLINK_ROUTE); - if (err < 0) { - g_set_error (error, NM_NETLINK_MONITOR_ERROR, - NM_NETLINK_MONITOR_ERROR_NETLINK_CONNECT, - _("unable to connect to netlink for monitoring link status: %s"), - nl_geterror (err)); - return FALSE; - } - - /* Enable unix socket peer credentials which we use for verifying that the - * sender of the message is actually the kernel. - */ - if (nl_socket_set_passcred (nlh, 1) < 0) { - g_set_error (error, NM_NETLINK_MONITOR_ERROR, - NM_NETLINK_MONITOR_ERROR_NETLINK_CONNECT, - _("unable to enable netlink handle credential passing: %s"), - nl_geterror (err)); - return FALSE; - } - - return TRUE; -} - -static gboolean -event_connection_setup (NMNetlinkMonitor *self, GError **error) -{ - NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); - GError *channel_error = NULL; - GIOFlags channel_flags; - int fd; - - g_return_val_if_fail (priv->io_channel == NULL, FALSE); - - /* Set up the event listener connection */ - priv->nlh_event = nl_socket_alloc (); - if (!priv->nlh_event) { - g_set_error (error, NM_NETLINK_MONITOR_ERROR, - NM_NETLINK_MONITOR_ERROR_NETLINK_ALLOC_HANDLE, - _("unable to allocate netlink handle for monitoring link status: %s"), - nl_geterror (ENOMEM)); - goto error; - } - - if (!nlh_setup (priv->nlh_event, event_msg_ready, self, error)) - goto error; - - nl_socket_disable_seq_check (priv->nlh_event); - - /* Subscribe to the LINK group for internal carrier signals */ - if (!nm_netlink_monitor_subscribe (self, RTNLGRP_LINK, error)) - goto error; - - fd = nl_socket_get_fd (priv->nlh_event); - priv->io_channel = g_io_channel_unix_new (fd); - - g_io_channel_set_encoding (priv->io_channel, NULL, &channel_error); - /* Encoding is NULL, so no conversion error can possibly occur */ - g_assert (channel_error == NULL); - - g_io_channel_set_close_on_unref (priv->io_channel, TRUE); - channel_flags = g_io_channel_get_flags (priv->io_channel); - channel_error = NULL; - g_io_channel_set_flags (priv->io_channel, - channel_flags | G_IO_FLAG_NONBLOCK, - &channel_error); - if (channel_error != NULL) { - g_propagate_error (error, channel_error); - goto error; - } - - return TRUE; - -error: - if (priv->io_channel) - nm_netlink_monitor_close_connection (self); - - if (priv->nlh_event) { - nl_socket_free (priv->nlh_event); - priv->nlh_event = NULL; - } - - return FALSE; -} - -static gboolean -sync_connection_setup (NMNetlinkMonitor *self, GError **error) -{ - NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); -#ifdef LIBNL_NEEDS_ADDR_CACHING_WORKAROUND - struct nl_cache *addr_cache; -#endif - int err; - - /* Set up the event listener connection */ - priv->nlh_sync = nl_socket_alloc (); - if (!priv->nlh_sync) { - g_set_error (error, NM_NETLINK_MONITOR_ERROR, - NM_NETLINK_MONITOR_ERROR_NETLINK_ALLOC_HANDLE, - _("unable to allocate netlink handle for monitoring link status: %s"), - nl_geterror (ENOMEM)); - goto error; - } - - if (!nlh_setup (priv->nlh_sync, NULL, self, error)) - goto error; - -#ifdef LIBNL_NEEDS_ADDR_CACHING_WORKAROUND - /* Work around apparent libnl bug; rtnl_addr requires that all - * addresses have the "peer" attribute set in order to be compared - * for equality, but this attribute is not normally set. As a - * result, most addresses will not compare as equal even to - * themselves, busting caching. - */ - rtnl_addr_alloc_cache (priv->nlh_sync, &addr_cache); - g_warn_if_fail (addr_cache != NULL); - nl_cache_get_ops (addr_cache)->co_obj_ops->oo_id_attrs &= ~0x80; - nl_cache_free (addr_cache); -#endif - - err = rtnl_link_alloc_cache (priv->nlh_sync, AF_UNSPEC, &priv->link_cache); - if (err < 0) { - g_set_error (error, NM_NETLINK_MONITOR_ERROR, - NM_NETLINK_MONITOR_ERROR_NETLINK_ALLOC_LINK_CACHE, - _("unable to allocate netlink link cache for monitoring link status: %s"), - nl_geterror (err)); - goto error; - } - nl_cache_mngt_provide (priv->link_cache); - - return TRUE; - -error: - if (priv->link_cache) { - nl_cache_free (priv->link_cache); - priv->link_cache = NULL; - } - - if (priv->nlh_sync) { - nl_socket_free (priv->nlh_sync); - priv->nlh_sync = NULL; - } - - return FALSE; -} - -gboolean -nm_netlink_monitor_open_connection (NMNetlinkMonitor *self, GError **error) -{ - g_return_val_if_fail (self != NULL, FALSE); - g_return_val_if_fail (NM_IS_NETLINK_MONITOR (self), FALSE); - - if (!event_connection_setup (self, error)) - return FALSE; - - if (!sync_connection_setup (self, error)) - return FALSE; - - return TRUE; -} - -void -nm_netlink_monitor_close_connection (NMNetlinkMonitor *self) -{ - NMNetlinkMonitorPrivate *priv; - - g_return_if_fail (NM_IS_NETLINK_MONITOR (self)); - - priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); - g_return_if_fail (priv->io_channel != NULL); - - if (priv->event_id) - nm_netlink_monitor_detach (self); - - g_io_channel_shutdown (priv->io_channel, - TRUE /* flush pending data */, - NULL); - g_io_channel_unref (priv->io_channel); - priv->io_channel = NULL; -} - -void -nm_netlink_monitor_attach (NMNetlinkMonitor *self) -{ - NMNetlinkMonitorPrivate *priv; - - g_return_if_fail (NM_IS_NETLINK_MONITOR (self)); - - priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); - g_return_if_fail (priv->nlh_event != NULL); - g_return_if_fail (priv->event_id == 0); - - priv->event_id = g_io_add_watch (priv->io_channel, - (EVENT_CONDITIONS | ERROR_CONDITIONS | DISCONNECT_CONDITIONS), - event_handler, self); -} - -void -nm_netlink_monitor_detach (NMNetlinkMonitor *self) -{ - NMNetlinkMonitorPrivate *priv; - - g_return_if_fail (NM_IS_NETLINK_MONITOR (self)); - - priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); - g_return_if_fail (priv->event_id > 0); - - g_source_remove (priv->event_id); - priv->event_id = 0; -} - -static int -get_subs (NMNetlinkMonitor *self, int group) -{ - NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); - - return GPOINTER_TO_INT (g_hash_table_lookup (priv->subscriptions, - GINT_TO_POINTER (group))); -} - -static void -set_subs (NMNetlinkMonitor *self, int group, int new_subs) -{ - NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); - - g_hash_table_insert (priv->subscriptions, - GINT_TO_POINTER (group), - GINT_TO_POINTER (new_subs)); -} - -gboolean -nm_netlink_monitor_subscribe (NMNetlinkMonitor *self, int group, GError **error) -{ - NMNetlinkMonitorPrivate *priv; - int subs, err; - - g_return_val_if_fail (NM_IS_NETLINK_MONITOR (self), FALSE); - - priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); - - if (!priv->nlh_event) { - if (!nm_netlink_monitor_open_connection (self, error)) - return FALSE; - } - - subs = get_subs (self, group) + 1; - if (subs == 1) { - err = nl_socket_add_membership (priv->nlh_event, group); - if (err < 0) { - g_set_error (error, NM_NETLINK_MONITOR_ERROR, - NM_NETLINK_MONITOR_ERROR_NETLINK_JOIN_GROUP, - _("unable to join netlink group: %s"), - nl_geterror (err)); - return FALSE; - } - } - - /* Update # of subscriptions for this group */ - set_subs (self, group, subs); - - return TRUE; -} - -void -nm_netlink_monitor_unsubscribe (NMNetlinkMonitor *self, int group) -{ - NMNetlinkMonitorPrivate *priv; - int subs; - - g_return_if_fail (self != NULL); - g_return_if_fail (NM_IS_NETLINK_MONITOR (self)); - - priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); - g_return_if_fail (priv->nlh_event != NULL); - - subs = get_subs (self, group) - 1; - if (subs == 0) - nl_socket_drop_membership (priv->nlh_event, group); - - /* Update # of subscriptions for this group */ - set_subs (self, group, subs); -} - -/***************************************************************/ - -gboolean -nm_netlink_monitor_request_ip6_info (NMNetlinkMonitor *self, GError **error) -{ - NMNetlinkMonitorPrivate *priv; - - g_return_val_if_fail (self != NULL, FALSE); - g_return_val_if_fail (NM_IS_NETLINK_MONITOR (self), FALSE); - - priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); - - /* FIXME: nl_rtgen_request() gets the return value screwed up with - * libnl-1.1; revisit this and return a proper error when we port to - * a later libnl. - */ - nl_rtgen_request (priv->nlh_event, RTM_GETLINK, AF_INET6, NLM_F_DUMP); - - return TRUE; -} - -gboolean -nm_netlink_monitor_request_bridge_info (NMNetlinkMonitor *self, GError **error) -{ - NMNetlinkMonitorPrivate *priv; - - g_return_val_if_fail (self != NULL, FALSE); - g_return_val_if_fail (NM_IS_NETLINK_MONITOR (self), FALSE); - - priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); - - /* FIXME: nl_rtgen_request() gets the return value screwed up with - * libnl-1.1; revisit this and return a proper error when we port to - * a later libnl. - */ - nl_rtgen_request (priv->nlh_event, RTM_GETLINK, AF_BRIDGE, NLM_F_DUMP); - - return TRUE; -} - -static gboolean -deferred_emit_carrier_state (gpointer user_data) -{ - NMNetlinkMonitor *self = NM_NETLINK_MONITOR (user_data); - NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); - int err; - - priv->request_status_id = 0; - - /* Update the link cache with latest state, and if there are no errors - * emit the link states for all the interfaces in the cache. - */ - err = nl_cache_refill (priv->nlh_sync, priv->link_cache); - if (err < 0) - nm_log_err (LOGD_HW, "error updating link cache: %s", nl_geterror (err)); - else - nl_cache_foreach_filter (priv->link_cache, NULL, link_msg_handler, self); - - return FALSE; -} - -void -nm_netlink_monitor_request_status (NMNetlinkMonitor *self) -{ - NMNetlinkMonitorPrivate *priv; - - g_return_if_fail (NM_IS_NETLINK_MONITOR (self)); - - priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); - - /* Schedule the carrier state emission */ - if (!priv->request_status_id) - priv->request_status_id = g_idle_add (deferred_emit_carrier_state, self); -} - -typedef struct { - NMNetlinkMonitor *self; - struct rtnl_link *filter; - GError *error; - guint32 flags; -} GetFlagsInfo; - -static void -get_flags_sync_cb (struct nl_object *obj, void *arg) -{ - GetFlagsInfo *info = arg; - - /* Ensure this cache item matches our filter */ - if (nl_object_match_filter (obj, OBJ_CAST (info->filter)) != 0) - info->flags = rtnl_link_get_flags ((struct rtnl_link *) obj); -} - -gboolean -nm_netlink_monitor_get_flags_sync (NMNetlinkMonitor *self, - guint32 ifindex, - guint32 *ifflags, - GError **error) -{ - NMNetlinkMonitorPrivate *priv; - GetFlagsInfo info; - struct rtnl_link *filter; - int err; - - g_return_val_if_fail (self != NULL, FALSE); - g_return_val_if_fail (NM_IS_NETLINK_MONITOR (self), FALSE); - g_return_val_if_fail (ifflags != NULL, FALSE); - - priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); - - /* Update the link cache with the latest information */ - err = nl_cache_refill (priv->nlh_sync, priv->link_cache); - if (err < 0) { - g_set_error (error, - NM_NETLINK_MONITOR_ERROR, - NM_NETLINK_MONITOR_ERROR_LINK_CACHE_UPDATE, - _("error updating link cache: %s"), - nl_geterror (err)); - return FALSE; - } - - /* HACK: Apparently to get it working we have to refill the cache twice; - * otherwise some kernels (or maybe libnl?) only send a few of the - * interfaces in the refill request. - */ - if (nl_cache_refill (priv->nlh_sync, priv->link_cache)) { - g_set_error (error, - NM_NETLINK_MONITOR_ERROR, - NM_NETLINK_MONITOR_ERROR_LINK_CACHE_UPDATE, - _("error updating link cache: %s"), - nl_geterror (err)); - return FALSE; - } - - /* Set up the filter */ - filter = rtnl_link_alloc (); - if (!filter) { - g_set_error (error, - NM_NETLINK_MONITOR_ERROR, - NM_NETLINK_MONITOR_ERROR_BAD_ALLOC, - _("error processing netlink message: %s"), - nl_geterror (err)); - return FALSE; - } - rtnl_link_set_ifindex (filter, ifindex); - - memset (&info, 0, sizeof (info)); - info.self = self; - info.filter = filter; - info.error = NULL; - nl_cache_foreach_filter (priv->link_cache, NULL, get_flags_sync_cb, &info); - - rtnl_link_put (filter); - - if (info.error) { - if (error) - *error = info.error; - else - g_error_free (info.error); - return FALSE; - } else - *ifflags = info.flags; - - return TRUE; /* success */ -} - -/***************************************************************/ - -struct nl_sock * -nm_netlink_get_default_handle (void) -{ - NMNetlinkMonitor *self; - struct nl_sock *nlh; - - self = nm_netlink_monitor_get (); - nlh = NM_NETLINK_MONITOR_GET_PRIVATE (self)->nlh_sync; - g_object_unref (self); - - return nlh; -} - -int -nm_netlink_iface_to_index (const char *iface) -{ - NMNetlinkMonitor *self; - NMNetlinkMonitorPrivate *priv; - int idx; - - g_return_val_if_fail (iface != NULL, -1); - - self = nm_netlink_monitor_get (); - priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); - - nl_cache_refill (priv->nlh_sync, priv->link_cache); - idx = rtnl_link_name2i (priv->link_cache, iface); - g_object_unref (self); - - return idx; -} - -/** - * nm_netlink_index_to_iface: - * @idx: kernel interface index - * - * Returns: the device name corresponding to the kernel interface index; caller - * owns returned value and must free it when it is no longer required - **/ -#define MAX_IFACE_LEN 33 -char * -nm_netlink_index_to_iface (int idx) -{ - NMNetlinkMonitor *self; - NMNetlinkMonitorPrivate *priv; - char *buf = NULL; - - g_return_val_if_fail (idx >= 0, NULL); - - self = nm_netlink_monitor_get (); - priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); - - buf = g_malloc0 (MAX_IFACE_LEN); - g_assert (buf); - - nl_cache_refill (priv->nlh_sync, priv->link_cache); - if (!rtnl_link_i2name (priv->link_cache, idx, buf, MAX_IFACE_LEN - 1)) { - nm_log_warn (LOGD_HW, "(%d) failed to find interface name for index", idx); - g_free (buf); - buf = NULL; - } - - g_object_unref (self); - return buf; -} - -struct rtnl_link * -nm_netlink_index_to_rtnl_link (int idx) -{ - NMNetlinkMonitor *self; - NMNetlinkMonitorPrivate *priv; - struct rtnl_link *ret = NULL; - - if (idx <= 0) - return NULL; - - self = nm_netlink_monitor_get (); - priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); - - nl_cache_refill (priv->nlh_sync, priv->link_cache); - ret = rtnl_link_get (priv->link_cache, idx); - g_object_unref (self); - - return ret; -} - -/***************************************************************/ - -NMNetlinkMonitor * -nm_netlink_monitor_get (void) -{ - static NMNetlinkMonitor *singleton = NULL; - GError *error = NULL; - - if (!singleton) { - singleton = (NMNetlinkMonitor *) g_object_new (NM_TYPE_NETLINK_MONITOR, NULL); - g_return_val_if_fail (singleton != NULL, NULL); - - if (nm_netlink_monitor_open_connection (singleton, &error)) { - nm_netlink_monitor_attach (singleton); - /* Request initial status of cards */ - nm_netlink_monitor_request_status (singleton); - } else { - nm_log_warn (LOGD_HW, "Failed to connect to netlink: (%d) %s", - error ? error->code : 0, - (error && error->message) ? error->message : "(unknown)"); - g_clear_error (&error); - - g_object_unref (singleton); - singleton = NULL; - } - } else - g_object_ref (singleton); - - return singleton; -} - -static void -nm_netlink_monitor_init (NMNetlinkMonitor *self) -{ - NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (self); - - priv->subscriptions = g_hash_table_new (g_direct_hash, g_direct_equal); -} - -static void -finalize (GObject *object) -{ - NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (object); - - if (priv->request_status_id) - g_source_remove (priv->request_status_id); - - if (priv->io_channel) - nm_netlink_monitor_close_connection (NM_NETLINK_MONITOR (object)); - - if (priv->link_cache) { - nl_cache_free (priv->link_cache); - priv->link_cache = NULL; - } - - if (priv->nlh_event) { - nl_socket_free (priv->nlh_event); - priv->nlh_event = NULL; - } - - if (priv->nlh_sync) { - nl_socket_free (priv->nlh_sync); - priv->nlh_sync = NULL; - } - - g_hash_table_destroy (priv->subscriptions); - - G_OBJECT_CLASS (nm_netlink_monitor_parent_class)->finalize (object); -} - -static void -nm_netlink_monitor_class_init (NMNetlinkMonitorClass *monitor_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (monitor_class); - - g_type_class_add_private (monitor_class, sizeof (NMNetlinkMonitorPrivate)); - - /* Virtual methods */ - object_class->finalize = finalize; - - /* Signals */ - signals[NOTIFICATION] = - g_signal_new ("notification", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (NMNetlinkMonitorClass, notification), - NULL, NULL, g_cclosure_marshal_VOID__POINTER, - G_TYPE_NONE, 1, G_TYPE_POINTER); - - signals[CARRIER_ON] = - g_signal_new ("carrier-on", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (NMNetlinkMonitorClass, carrier_on), - NULL, NULL, g_cclosure_marshal_VOID__INT, - G_TYPE_NONE, 1, G_TYPE_INT); - - signals[CARRIER_OFF] = - g_signal_new ("carrier-off", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (NMNetlinkMonitorClass, carrier_off), - NULL, NULL, g_cclosure_marshal_VOID__INT, - G_TYPE_NONE, 1, G_TYPE_INT); -} - -GQuark -nm_netlink_monitor_error_quark (void) -{ - static GQuark error_quark = 0; - - if (G_UNLIKELY (error_quark == 0)) - error_quark = g_quark_from_static_string ("nm-netlink-monitor-error-quark"); - return error_quark; -} - |