2008-11-03 04:13:42 +00:00
|
|
|
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
|
2004-06-24 14:18:37 +00:00
|
|
|
/* 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.
|
|
|
|
|
*
|
2008-06-26 18:31:52 +00:00
|
|
|
* 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.
|
2004-06-24 14:18:37 +00:00
|
|
|
*
|
2014-09-05 10:55:37 +02:00
|
|
|
* Copyright 2004 - 2014 Red Hat, Inc.
|
|
|
|
|
* Copyright 2005 - 2008 Novell, Inc.
|
2004-06-24 14:18:37 +00:00
|
|
|
*/
|
|
|
|
|
|
2014-08-25 16:21:59 +02:00
|
|
|
#include "config.h"
|
|
|
|
|
|
2004-06-24 14:18:37 +00:00
|
|
|
#include <glib.h>
|
2014-08-25 16:21:59 +02:00
|
|
|
#include <glib/gi18n.h>
|
2009-07-29 12:12:41 -04:00
|
|
|
#include <errno.h>
|
|
|
|
|
#include <fcntl.h>
|
2005-03-26 03:42:05 +00:00
|
|
|
#include <string.h>
|
2009-07-29 12:12:41 -04:00
|
|
|
#include <unistd.h>
|
2011-07-22 12:50:54 -05:00
|
|
|
#include <stdlib.h>
|
2013-11-19 22:28:36 -06:00
|
|
|
#include <resolv.h>
|
2014-02-14 12:54:36 +01:00
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <sys/wait.h>
|
2014-07-17 17:06:44 -04:00
|
|
|
#include <linux/if.h>
|
2014-04-10 15:29:45 -05:00
|
|
|
#include <linux/if_infiniband.h>
|
2004-06-24 14:18:37 +00:00
|
|
|
|
|
|
|
|
#include "NetworkManagerUtils.h"
|
2005-03-14 Ray Strode <rstrode@redhat.com>
Fourth (probably working) cut at porting to
dbus 0.30 api and new hal. This cut adds
some new logging macros to make debugging
easier.
* dispatcher-daemon/NetworkManagerDispatcher.c:
* info-daemon/NetworkmanagerInfo.c:
* info-daemon/NetworkManagerInfoPassphraseDialog.c:
* info-daemon/NetworkManagerInfoVPN.c:
* src/NetworkManager.c:
* src/NetworkManagerAP.c:
* src/NetworkManagerAPList.c:
* src/NetworkManagerDHCP.c:
* src/NetworkManagerDbus.c:
* src/NetworkManagerDevice.c:
* src/NetworkManagerPolicy.c:
* src/NetworkManagerSystem.c:
* src/NetworkManagerUtils.c:
* src/NetworkManagerWireless.c:
* src/autoip.c:
* src/nm-dbus-nm.c:
* src/backends/NetworkManagerDebian.c:
* src/backends/NetworkManagerGentoo.c:
* src/backends/NetworkManagerRedHat.c:
* src/backends/NetworkManagerSlackware.c:
use new logging macros.
* dispatcher-daemon/NetworkManagerDispatcher.c:
(nmd_dbus_filter): s/dbus_free/g_free/
* info-daemon/Makefile.am: link in utils library.
* info-daemon/NetworkmanagerInfo.c: use new logging
macros.
(nmi_dbus_get_network): don't assume enumerations
are 32-bit.
(nmi_dbus_nmi_message_handler): don't free what
doesn't belong to us.
* libnm_glib/libnm_glib.c:
(libnm_glib_get_nm_status):
(libnm_glib_init): don't free what doesn't
belong to us.
(libnm_glib_dbus): strdup result, so it doesn't get
lost when message is unref'd.
* panel-applet/NMWirelessAppletDbus.c:
(nmwa_dbus_update_devices): s/dbus_free/g_free/
* src/NetworkManager.c:
(nm_monitor_wired_link_state): request initial status
dump of all cards when we start up, instead of relying
on /sys/.../carrier.
(nm_info_handler), (nm_set_up_log_handlers):
log handlers to specify what syslog priorites
the logging macros default to.
* src/NetworkManagerAPList.c:
(nm_ap_list_populate_from_nmi):
s/dbus_free_string_array/g_strfreev/
* src/NetworkManagerDbus.c:
(nm_dbus_get_network_object):
validate d-bus message argument types.
Advance message iterator after reading argument,
prepend instead of append to GSList.
* src/NetworkManagerDevice.c:
(nm_device_probe_wired_link_status):
remove redundant /sys in /sys path. remove wrong
contents == NULL means has carrier assumption.
* src/nm-netlink-monitor.c
(nm_netlink_monitor_request_status): implement
function to ask kernel to dump interface link
status over netlink socket.
* test/*.c: s/dbus_free/g_free/
* utils/nm-utils.h:
(nm_print_backtrace): new macro to print backtrace.
(nm_get_timestamp): new macro to get sub-second precise
unix timestamp.
(nm_info), (nm_debug), (nm_warning), (nm_error):
new logging functions. nm_info just prints,
nm_debug includes timestamp and function,
nm_warning includes function, nm_error includes
backtrace and sigtrap.
git-svn-id: http://svn-archive.gnome.org/svn/NetworkManager/trunk@497 4912f4e0-d625-0410-9fb7-b9a5a253dbdc
2005-03-15 05:30:15 +00:00
|
|
|
#include "nm-utils.h"
|
2014-08-11 18:10:43 +02:00
|
|
|
#include "nm-core-internal.h"
|
2010-04-07 12:28:57 -07:00
|
|
|
#include "nm-logging.h"
|
2005-12-31 08:21:24 +00:00
|
|
|
#include "nm-device.h"
|
2011-01-13 13:28:52 -06:00
|
|
|
#include "nm-setting-connection.h"
|
|
|
|
|
#include "nm-setting-ip4-config.h"
|
|
|
|
|
#include "nm-setting-ip6-config.h"
|
2011-07-01 14:56:07 -05:00
|
|
|
#include "nm-setting-wireless.h"
|
|
|
|
|
#include "nm-setting-wireless-security.h"
|
|
|
|
|
#include "nm-manager-auth.h"
|
2012-05-21 14:10:05 +02:00
|
|
|
#include "nm-posix-signals.h"
|
2004-06-24 14:18:37 +00:00
|
|
|
|
2014-08-11 09:37:05 +02:00
|
|
|
/*
|
|
|
|
|
* Some toolchains (E.G. uClibc 0.9.33 and earlier) don't export
|
|
|
|
|
* CLOCK_BOOTTIME even though the kernel supports it, so provide a
|
|
|
|
|
* local definition
|
|
|
|
|
*/
|
|
|
|
|
#ifndef CLOCK_BOOTTIME
|
|
|
|
|
#define CLOCK_BOOTTIME 7
|
|
|
|
|
#endif
|
|
|
|
|
|
2004-07-06 01:34:10 +00:00
|
|
|
/*
|
2014-09-05 10:55:37 +02:00
|
|
|
* nm_ethernet_address_is_valid:
|
|
|
|
|
* @addr: pointer to a binary or ASCII Ethernet address
|
|
|
|
|
* @len: length of @addr, or -1 if @addr is ASCII
|
2004-07-06 01:34:10 +00:00
|
|
|
*
|
2006-05-17 15:02:57 +00:00
|
|
|
* Compares an Ethernet address against known invalid addresses.
|
2014-09-05 10:55:37 +02:00
|
|
|
|
|
|
|
|
* Returns: %TRUE if @addr is a valid Ethernet address, %FALSE if it is not.
|
2004-07-06 01:34:10 +00:00
|
|
|
*/
|
2007-11-15 18:33:17 +00:00
|
|
|
gboolean
|
2014-09-05 10:55:37 +02:00
|
|
|
nm_ethernet_address_is_valid (gconstpointer addr, gssize len)
|
2004-07-06 01:34:10 +00:00
|
|
|
{
|
2007-11-15 18:33:17 +00:00
|
|
|
guint8 invalid_addr1[ETH_ALEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
|
|
|
|
guint8 invalid_addr2[ETH_ALEN] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
|
|
|
|
guint8 invalid_addr3[ETH_ALEN] = {0x44, 0x44, 0x44, 0x44, 0x44, 0x44};
|
|
|
|
|
guint8 invalid_addr4[ETH_ALEN] = {0x00, 0x30, 0xb4, 0x00, 0x00, 0x00}; /* prism54 dummy MAC */
|
2014-07-30 10:57:45 -04:00
|
|
|
guchar first_octet;
|
2004-07-06 01:34:10 +00:00
|
|
|
|
2014-07-30 10:57:45 -04:00
|
|
|
g_return_val_if_fail (addr != NULL, FALSE);
|
|
|
|
|
g_return_val_if_fail (len == ETH_ALEN || len == -1, FALSE);
|
2004-07-06 01:34:10 +00:00
|
|
|
|
|
|
|
|
/* Compare the AP address the card has with invalid ethernet MAC addresses. */
|
2014-07-30 10:57:45 -04:00
|
|
|
if (nm_utils_hwaddr_matches (addr, len, invalid_addr1, ETH_ALEN))
|
2007-11-15 18:33:17 +00:00
|
|
|
return FALSE;
|
|
|
|
|
|
2014-07-30 10:57:45 -04:00
|
|
|
if (nm_utils_hwaddr_matches (addr, len, invalid_addr2, ETH_ALEN))
|
2007-11-15 18:33:17 +00:00
|
|
|
return FALSE;
|
|
|
|
|
|
2014-07-30 10:57:45 -04:00
|
|
|
if (nm_utils_hwaddr_matches (addr, len, invalid_addr3, ETH_ALEN))
|
2007-11-15 18:33:17 +00:00
|
|
|
return FALSE;
|
|
|
|
|
|
2014-07-30 10:57:45 -04:00
|
|
|
if (nm_utils_hwaddr_matches (addr, len, invalid_addr4, ETH_ALEN))
|
2007-11-15 18:33:17 +00:00
|
|
|
return FALSE;
|
|
|
|
|
|
2014-07-30 10:57:45 -04:00
|
|
|
/* Check for multicast address */
|
|
|
|
|
if (len == -1)
|
|
|
|
|
first_octet = strtoul (addr, NULL, 16);
|
|
|
|
|
else
|
|
|
|
|
first_octet = ((guint8 *)addr)[0];
|
|
|
|
|
if (first_octet & 0x01)
|
|
|
|
|
return FALSE;
|
2007-11-15 18:33:17 +00:00
|
|
|
|
|
|
|
|
return TRUE;
|
2004-07-06 01:34:10 +00:00
|
|
|
}
|
|
|
|
|
|
2004-06-24 14:18:37 +00:00
|
|
|
|
2014-02-13 18:12:41 +01:00
|
|
|
/* nm_utils_ip4_address_clear_host_address:
|
|
|
|
|
* @addr: source ip6 address
|
|
|
|
|
* @plen: prefix length of network
|
|
|
|
|
*
|
|
|
|
|
* returns: the input address, with the host address set to 0.
|
|
|
|
|
*/
|
|
|
|
|
in_addr_t
|
|
|
|
|
nm_utils_ip4_address_clear_host_address (in_addr_t addr, guint8 plen)
|
|
|
|
|
{
|
|
|
|
|
return addr & nm_utils_ip4_prefix_to_netmask (plen);
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-16 15:01:32 +01:00
|
|
|
/* nm_utils_ip6_address_clear_host_address:
|
2014-02-13 18:12:41 +01:00
|
|
|
* @dst: destination output buffer, will contain the network part of the @src address
|
|
|
|
|
* @src: source ip6 address
|
|
|
|
|
* @plen: prefix length of network
|
|
|
|
|
*
|
|
|
|
|
* Note: this function is self assignment save, to update @src inplace, set both
|
|
|
|
|
* @dst and @src to the same destination.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
nm_utils_ip6_address_clear_host_address (struct in6_addr *dst, const struct in6_addr *src, guint8 plen)
|
|
|
|
|
{
|
|
|
|
|
g_return_if_fail (plen <= 128);
|
|
|
|
|
g_return_if_fail (src);
|
|
|
|
|
g_return_if_fail (dst);
|
|
|
|
|
|
|
|
|
|
if (plen < 128) {
|
|
|
|
|
guint nbytes = plen / 8;
|
|
|
|
|
guint nbits = plen % 8;
|
|
|
|
|
|
|
|
|
|
if (nbytes && dst != src)
|
|
|
|
|
memcpy (dst, src, nbytes);
|
|
|
|
|
if (nbits) {
|
|
|
|
|
dst->s6_addr[nbytes] = (src->s6_addr[nbytes] & (0xFF << (8 - nbits)));
|
|
|
|
|
nbytes++;
|
|
|
|
|
}
|
|
|
|
|
if (nbytes <= 15)
|
|
|
|
|
memset (&dst->s6_addr[nbytes], 0, 16 - nbytes);
|
|
|
|
|
} else if (src != dst)
|
|
|
|
|
*dst = *src;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2008-04-27 12:23:17 +00:00
|
|
|
int
|
|
|
|
|
nm_spawn_process (const char *args)
|
2004-08-12 Dan Williams <dcbw@redhat.com>
* info-daemon/passphrase.glade
- Set window title to " "
* panel-applet/Makefile.am
panel-applet/keyring.png
- Deliver to correct place
* panel-applet/NMWirelessApplet.[ch]
- Add comments
- Remove applet->have_active_device as its no longer used
- (nmwa_load_theme): load keyring.png too
- (error_dialog): remove
- (show_warning_dialog): subsume functionality of error dialog too
- (nmwa_destroy, nmwa_new): create and dispose of an application-wide GConfClient
- (nmwa_handle_network_choice): add to deal with user clicking on an item from
the networks menu
- (nmwa_menu_item_activated): GtkMenuItem "activate" signal handler
- (nmwa_button_clicked, nmwa_setup_widgets): create and populate the menu on startup
and when we get broadcasts of changed wireless access points only, not when the
user clicks on the button to display the menu (too long of a wait)
- (nmwa_add_menu_item): Make active network bold, and place a keyring icon beside
networks that are encrypted
- (nmwa_dispose_menu, nmwa_menu_item_data_free): dispose of the data we place on each
menu item with g_object_set_data()
* panel-applet/NMWirelessAppletDbus.[ch]
- (nmwa_dbus_get_bool): add method to return boolean value from dbus message
- (nmwa_dbus_get_active_network): add (nmwa_dbus_get_string() wrapper to get active network)
- (nmwa_dbus_add_networks_to_menu): clean up, only show one instance of each ESSID in the menu
- (nmwa_dbus_set_network): force NetworkManager to use a particular network for wireless cards
- (nmwa_dbus_init, nmwa_dbus_filter): Trap network appear/disappear and device
activation/deactivation signals and rebuild the menu when they happen
* src/NetworkManager.c
- (main): use new nm_spawn_process() rather than system()
* src/NetworkManagerDbus.c
- (nm_dbus_devices_handle_request): don't compare AP structure addresses directly, but essids
instead. Since we can now force best_aps to stick around, the AP structure to which
dev->options.wireless.best_ap points to won't necessarily be in the device's device list
if a scan has happened since the best_ap was frozen. Also add "setNetwork" method
to freeze the best_ap.
* src/NetworkManagerDevice.[ch]
- (nm_device_activation_worker): Use new nm_spawn_process() call rather than system()
- (nm_device_*_best_ap): add freeze/unfreeze/get_frozen functions, and don't really update
the best_ap in nm_device_update_best_ap() if the best_ap is frozen AND in the device's
ap list
* src/NetworkManagerUtils.[ch]
- (nm_spawn_process): add replacement for system() usage
git-svn-id: http://svn-archive.gnome.org/svn/NetworkManager/trunk@48 4912f4e0-d625-0410-9fb7-b9a5a253dbdc
2004-08-12 19:58:01 +00:00
|
|
|
{
|
2008-04-27 12:23:17 +00:00
|
|
|
gint num_args;
|
|
|
|
|
char **argv = NULL;
|
|
|
|
|
int status = -1;
|
|
|
|
|
GError *error = NULL;
|
2004-10-28 19:49:55 +00:00
|
|
|
|
2004-08-12 Dan Williams <dcbw@redhat.com>
* info-daemon/passphrase.glade
- Set window title to " "
* panel-applet/Makefile.am
panel-applet/keyring.png
- Deliver to correct place
* panel-applet/NMWirelessApplet.[ch]
- Add comments
- Remove applet->have_active_device as its no longer used
- (nmwa_load_theme): load keyring.png too
- (error_dialog): remove
- (show_warning_dialog): subsume functionality of error dialog too
- (nmwa_destroy, nmwa_new): create and dispose of an application-wide GConfClient
- (nmwa_handle_network_choice): add to deal with user clicking on an item from
the networks menu
- (nmwa_menu_item_activated): GtkMenuItem "activate" signal handler
- (nmwa_button_clicked, nmwa_setup_widgets): create and populate the menu on startup
and when we get broadcasts of changed wireless access points only, not when the
user clicks on the button to display the menu (too long of a wait)
- (nmwa_add_menu_item): Make active network bold, and place a keyring icon beside
networks that are encrypted
- (nmwa_dispose_menu, nmwa_menu_item_data_free): dispose of the data we place on each
menu item with g_object_set_data()
* panel-applet/NMWirelessAppletDbus.[ch]
- (nmwa_dbus_get_bool): add method to return boolean value from dbus message
- (nmwa_dbus_get_active_network): add (nmwa_dbus_get_string() wrapper to get active network)
- (nmwa_dbus_add_networks_to_menu): clean up, only show one instance of each ESSID in the menu
- (nmwa_dbus_set_network): force NetworkManager to use a particular network for wireless cards
- (nmwa_dbus_init, nmwa_dbus_filter): Trap network appear/disappear and device
activation/deactivation signals and rebuild the menu when they happen
* src/NetworkManager.c
- (main): use new nm_spawn_process() rather than system()
* src/NetworkManagerDbus.c
- (nm_dbus_devices_handle_request): don't compare AP structure addresses directly, but essids
instead. Since we can now force best_aps to stick around, the AP structure to which
dev->options.wireless.best_ap points to won't necessarily be in the device's device list
if a scan has happened since the best_ap was frozen. Also add "setNetwork" method
to freeze the best_ap.
* src/NetworkManagerDevice.[ch]
- (nm_device_activation_worker): Use new nm_spawn_process() call rather than system()
- (nm_device_*_best_ap): add freeze/unfreeze/get_frozen functions, and don't really update
the best_ap in nm_device_update_best_ap() if the best_ap is frozen AND in the device's
ap list
* src/NetworkManagerUtils.[ch]
- (nm_spawn_process): add replacement for system() usage
git-svn-id: http://svn-archive.gnome.org/svn/NetworkManager/trunk@48 4912f4e0-d625-0410-9fb7-b9a5a253dbdc
2004-08-12 19:58:01 +00:00
|
|
|
g_return_val_if_fail (args != NULL, -1);
|
|
|
|
|
|
2008-04-27 12:23:17 +00:00
|
|
|
if (!g_shell_parse_argv (args, &num_args, &argv, &error)) {
|
2010-04-07 12:28:57 -07:00
|
|
|
nm_log_warn (LOGD_CORE, "could not parse arguments for '%s': %s", args, error->message);
|
2008-04-27 12:23:17 +00:00
|
|
|
g_error_free (error);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2004-10-28 19:49:55 +00:00
|
|
|
|
2012-05-21 14:10:05 +02:00
|
|
|
if (!g_spawn_sync ("/", argv, NULL, 0, nm_unblock_posix_signals, NULL, NULL, NULL, &status, &error)) {
|
2010-04-07 12:28:57 -07:00
|
|
|
nm_log_warn (LOGD_CORE, "could not spawn process '%s': %s", args, error->message);
|
2008-04-27 12:23:17 +00:00
|
|
|
g_error_free (error);
|
|
|
|
|
}
|
2004-10-28 19:49:55 +00:00
|
|
|
|
2008-04-27 12:23:17 +00:00
|
|
|
g_strfreev (argv);
|
|
|
|
|
return status;
|
2004-08-12 Dan Williams <dcbw@redhat.com>
* info-daemon/passphrase.glade
- Set window title to " "
* panel-applet/Makefile.am
panel-applet/keyring.png
- Deliver to correct place
* panel-applet/NMWirelessApplet.[ch]
- Add comments
- Remove applet->have_active_device as its no longer used
- (nmwa_load_theme): load keyring.png too
- (error_dialog): remove
- (show_warning_dialog): subsume functionality of error dialog too
- (nmwa_destroy, nmwa_new): create and dispose of an application-wide GConfClient
- (nmwa_handle_network_choice): add to deal with user clicking on an item from
the networks menu
- (nmwa_menu_item_activated): GtkMenuItem "activate" signal handler
- (nmwa_button_clicked, nmwa_setup_widgets): create and populate the menu on startup
and when we get broadcasts of changed wireless access points only, not when the
user clicks on the button to display the menu (too long of a wait)
- (nmwa_add_menu_item): Make active network bold, and place a keyring icon beside
networks that are encrypted
- (nmwa_dispose_menu, nmwa_menu_item_data_free): dispose of the data we place on each
menu item with g_object_set_data()
* panel-applet/NMWirelessAppletDbus.[ch]
- (nmwa_dbus_get_bool): add method to return boolean value from dbus message
- (nmwa_dbus_get_active_network): add (nmwa_dbus_get_string() wrapper to get active network)
- (nmwa_dbus_add_networks_to_menu): clean up, only show one instance of each ESSID in the menu
- (nmwa_dbus_set_network): force NetworkManager to use a particular network for wireless cards
- (nmwa_dbus_init, nmwa_dbus_filter): Trap network appear/disappear and device
activation/deactivation signals and rebuild the menu when they happen
* src/NetworkManager.c
- (main): use new nm_spawn_process() rather than system()
* src/NetworkManagerDbus.c
- (nm_dbus_devices_handle_request): don't compare AP structure addresses directly, but essids
instead. Since we can now force best_aps to stick around, the AP structure to which
dev->options.wireless.best_ap points to won't necessarily be in the device's device list
if a scan has happened since the best_ap was frozen. Also add "setNetwork" method
to freeze the best_ap.
* src/NetworkManagerDevice.[ch]
- (nm_device_activation_worker): Use new nm_spawn_process() call rather than system()
- (nm_device_*_best_ap): add freeze/unfreeze/get_frozen functions, and don't really update
the best_ap in nm_device_update_best_ap() if the best_ap is frozen AND in the device's
ap list
* src/NetworkManagerUtils.[ch]
- (nm_spawn_process): add replacement for system() usage
git-svn-id: http://svn-archive.gnome.org/svn/NetworkManager/trunk@48 4912f4e0-d625-0410-9fb7-b9a5a253dbdc
2004-08-12 19:58:01 +00:00
|
|
|
}
|
2004-08-16 19:46:43 +00:00
|
|
|
|
2014-02-14 12:54:36 +01:00
|
|
|
/******************************************************************************************/
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
pid_t pid;
|
|
|
|
|
guint64 log_domain;
|
|
|
|
|
union {
|
|
|
|
|
struct {
|
|
|
|
|
gint64 wait_start_us;
|
|
|
|
|
guint source_timeout_kill_id;
|
|
|
|
|
} async;
|
|
|
|
|
struct {
|
|
|
|
|
gboolean success;
|
|
|
|
|
int child_status;
|
|
|
|
|
} sync;
|
|
|
|
|
};
|
|
|
|
|
NMUtilsKillChildAsyncCb callback;
|
|
|
|
|
void *user_data;
|
|
|
|
|
|
|
|
|
|
char log_name[1]; /* variable-length object, must be last element!! */
|
|
|
|
|
} KillChildAsyncData;
|
|
|
|
|
|
|
|
|
|
#define LOG_NAME_FMT "kill child process '%s' (%ld)"
|
|
|
|
|
#define LOG_NAME_ARGS log_name,(long)pid
|
|
|
|
|
|
|
|
|
|
static KillChildAsyncData *
|
|
|
|
|
_kc_async_data_alloc (pid_t pid, guint64 log_domain, const char *log_name, NMUtilsKillChildAsyncCb callback, void *user_data)
|
|
|
|
|
{
|
|
|
|
|
KillChildAsyncData *data;
|
|
|
|
|
size_t log_name_len;
|
|
|
|
|
|
|
|
|
|
/* append the name at the end of our KillChildAsyncData. */
|
|
|
|
|
log_name_len = strlen (LOG_NAME_FMT) + 20 + strlen (log_name);
|
|
|
|
|
data = g_malloc (sizeof (KillChildAsyncData) - 1 + log_name_len);
|
|
|
|
|
g_snprintf (data->log_name, log_name_len, LOG_NAME_FMT, LOG_NAME_ARGS);
|
|
|
|
|
|
|
|
|
|
data->pid = pid;
|
|
|
|
|
data->user_data = user_data;
|
|
|
|
|
data->callback = callback;
|
|
|
|
|
data->log_domain = log_domain;
|
|
|
|
|
|
|
|
|
|
return data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define KC_EXIT_TO_STRING_BUF_SIZE 128
|
|
|
|
|
static const char *
|
|
|
|
|
_kc_exit_to_string (char *buf, int exit)
|
|
|
|
|
#define _kc_exit_to_string(buf, exit) ( G_STATIC_ASSERT_EXPR(sizeof (buf) == KC_EXIT_TO_STRING_BUF_SIZE && sizeof ((buf)[0]) == 1), _kc_exit_to_string (buf, exit) )
|
|
|
|
|
{
|
|
|
|
|
if (WIFEXITED (exit))
|
|
|
|
|
g_snprintf (buf, KC_EXIT_TO_STRING_BUF_SIZE, "normally with status %d", WEXITSTATUS (exit));
|
|
|
|
|
else if (WIFSIGNALED (exit))
|
|
|
|
|
g_snprintf (buf, KC_EXIT_TO_STRING_BUF_SIZE, "by signal %d", WTERMSIG (exit));
|
|
|
|
|
else
|
|
|
|
|
g_snprintf (buf, KC_EXIT_TO_STRING_BUF_SIZE, "with unexpected status %d", exit);
|
|
|
|
|
return buf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
_kc_signal_to_string (int sig)
|
|
|
|
|
{
|
|
|
|
|
switch (sig) {
|
|
|
|
|
case 0: return "no signal (0)";
|
|
|
|
|
case SIGKILL: return "SIGKILL (" G_STRINGIFY (SIGKILL) ")";
|
|
|
|
|
case SIGTERM: return "SIGTERM (" G_STRINGIFY (SIGTERM) ")";
|
|
|
|
|
default:
|
|
|
|
|
return "Unexpected signal";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define KC_WAITED_TO_STRING 100
|
|
|
|
|
static const char *
|
|
|
|
|
_kc_waited_to_string (char *buf, gint64 wait_start_us)
|
|
|
|
|
#define _kc_waited_to_string(buf, wait_start_us) ( G_STATIC_ASSERT_EXPR(sizeof (buf) == KC_WAITED_TO_STRING && sizeof ((buf)[0]) == 1), _kc_waited_to_string (buf, wait_start_us) )
|
|
|
|
|
{
|
|
|
|
|
g_snprintf (buf, KC_WAITED_TO_STRING, " (%ld usec elapsed)", (long) (nm_utils_get_monotonic_timestamp_us () - wait_start_us));
|
|
|
|
|
return buf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
_kc_cb_watch_child (GPid pid, gint status, gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
KillChildAsyncData *data = user_data;
|
|
|
|
|
char buf_exit[KC_EXIT_TO_STRING_BUF_SIZE], buf_wait[KC_WAITED_TO_STRING];
|
|
|
|
|
|
|
|
|
|
if (data->async.source_timeout_kill_id)
|
|
|
|
|
g_source_remove (data->async.source_timeout_kill_id);
|
|
|
|
|
|
|
|
|
|
nm_log_dbg (data->log_domain, "%s: terminated %s%s",
|
|
|
|
|
data->log_name, _kc_exit_to_string (buf_exit, status),
|
|
|
|
|
_kc_waited_to_string (buf_wait, data->async.wait_start_us));
|
|
|
|
|
|
|
|
|
|
if (data->callback)
|
|
|
|
|
data->callback (pid, TRUE, status, data->user_data);
|
|
|
|
|
|
|
|
|
|
g_free (data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
_kc_cb_timeout_grace_period (void *user_data)
|
|
|
|
|
{
|
|
|
|
|
KillChildAsyncData *data = user_data;
|
|
|
|
|
int ret, errsv;
|
|
|
|
|
|
|
|
|
|
data->async.source_timeout_kill_id = 0;
|
|
|
|
|
|
|
|
|
|
if ((ret = kill (data->pid, SIGKILL)) != 0) {
|
|
|
|
|
errsv = errno;
|
|
|
|
|
/* ESRCH means, process does not exist or is already a zombie. */
|
|
|
|
|
if (errsv != ESRCH) {
|
|
|
|
|
nm_log_err (LOGD_CORE | data->log_domain, "%s: kill(SIGKILL) returned unexpected return value %d: (%s, %d)",
|
|
|
|
|
data->log_name, ret, strerror (errsv), errsv);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
nm_log_dbg (data->log_domain, "%s: process not terminated after %ld usec. Sending SIGKILL signal",
|
|
|
|
|
data->log_name, (long) (nm_utils_get_monotonic_timestamp_us () - data->async.wait_start_us));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return G_SOURCE_REMOVE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
_kc_invoke_callback_idle (gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
KillChildAsyncData *data = user_data;
|
|
|
|
|
|
|
|
|
|
if (data->sync.success) {
|
|
|
|
|
char buf_exit[KC_EXIT_TO_STRING_BUF_SIZE];
|
|
|
|
|
|
|
|
|
|
nm_log_dbg (data->log_domain, "%s: invoke callback: terminated %s",
|
|
|
|
|
data->log_name, _kc_exit_to_string (buf_exit, data->sync.child_status));
|
|
|
|
|
} else
|
|
|
|
|
nm_log_dbg (data->log_domain, "%s: invoke callback: killing child failed", data->log_name);
|
|
|
|
|
|
|
|
|
|
data->callback (data->pid, data->sync.success, data->sync.child_status, data->user_data);
|
|
|
|
|
g_free (data);
|
|
|
|
|
|
|
|
|
|
return G_SOURCE_REMOVE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
_kc_invoke_callback (pid_t pid, guint64 log_domain, const char *log_name, NMUtilsKillChildAsyncCb callback, void *user_data, gboolean success, int child_status)
|
|
|
|
|
{
|
|
|
|
|
KillChildAsyncData *data;
|
|
|
|
|
|
|
|
|
|
if (!callback)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
data = _kc_async_data_alloc (pid, log_domain, log_name, callback, user_data);
|
|
|
|
|
data->sync.success = success;
|
|
|
|
|
data->sync.child_status = child_status;
|
|
|
|
|
|
|
|
|
|
g_idle_add (_kc_invoke_callback_idle, data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* nm_utils_kill_child_async:
|
|
|
|
|
* @pid: the process id of the process to kill
|
|
|
|
|
* @sig: signal to send initially. Set to 0 to send not signal.
|
|
|
|
|
* @log_domain: the logging domain used for logging (LOGD_NONE to suppress logging)
|
|
|
|
|
* @log_name: (allow-none): for logging, the name of the processes to kill
|
|
|
|
|
* @wait_before_kill_msec: Waittime in milliseconds before sending %SIGKILL signal. Set this value
|
|
|
|
|
* to zero, not to send %SIGKILL. If @sig is already %SIGKILL, this parameter is ignored.
|
|
|
|
|
* @callback: (allow-none): callback after the child terminated. This function will always
|
|
|
|
|
* be invoked asynchronously.
|
|
|
|
|
* @user_data: passed on to callback
|
|
|
|
|
*
|
|
|
|
|
* Uses g_child_watch_add(), so note the glib comment: if you obtain pid from g_spawn_async() or
|
|
|
|
|
* g_spawn_async_with_pipes() you will need to pass %G_SPAWN_DO_NOT_REAP_CHILD as flag to the spawn
|
|
|
|
|
* function for the child watching to work.
|
|
|
|
|
* Also note, that you must g_source_remove() any other child watchers for @pid because glib
|
|
|
|
|
* supports only one watcher per child.
|
|
|
|
|
**/
|
|
|
|
|
void
|
|
|
|
|
nm_utils_kill_child_async (pid_t pid, int sig, guint64 log_domain,
|
|
|
|
|
const char *log_name, guint32 wait_before_kill_msec,
|
|
|
|
|
NMUtilsKillChildAsyncCb callback, void *user_data)
|
|
|
|
|
{
|
|
|
|
|
int status = 0, errsv;
|
|
|
|
|
pid_t ret;
|
|
|
|
|
KillChildAsyncData *data;
|
|
|
|
|
char buf_exit[KC_EXIT_TO_STRING_BUF_SIZE];
|
|
|
|
|
|
|
|
|
|
g_return_if_fail (pid > 0);
|
|
|
|
|
g_return_if_fail (log_name != NULL);
|
|
|
|
|
|
|
|
|
|
/* let's see if the child already terminated... */
|
|
|
|
|
ret = waitpid (pid, &status, WNOHANG);
|
|
|
|
|
if (ret > 0) {
|
|
|
|
|
nm_log_dbg (log_domain, LOG_NAME_FMT ": process %ld already terminated %s",
|
|
|
|
|
LOG_NAME_ARGS, (long) ret, _kc_exit_to_string (buf_exit, status));
|
|
|
|
|
_kc_invoke_callback (pid, log_domain, log_name, callback, user_data, TRUE, status);
|
|
|
|
|
return;
|
|
|
|
|
} else if (ret != 0) {
|
|
|
|
|
errsv = errno;
|
|
|
|
|
/* ECHILD means, the process is not a child/does not exist or it has SIGCHILD blocked. */
|
|
|
|
|
if (errsv != ECHILD) {
|
|
|
|
|
nm_log_err (LOGD_CORE | log_domain, LOG_NAME_FMT ": unexpected error while waitpid: %s (%d)",
|
|
|
|
|
LOG_NAME_ARGS, strerror (errsv), errsv);
|
|
|
|
|
_kc_invoke_callback (pid, log_domain, log_name, callback, user_data, FALSE, -1);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* send the first signal. */
|
|
|
|
|
if (kill (pid, sig) != 0) {
|
|
|
|
|
errsv = errno;
|
|
|
|
|
/* ESRCH means, process does not exist or is already a zombie. */
|
|
|
|
|
if (errsv != ESRCH) {
|
|
|
|
|
nm_log_err (LOGD_CORE | log_domain, LOG_NAME_FMT ": unexpected error sending %s: %s (%d)",
|
|
|
|
|
LOG_NAME_ARGS, _kc_signal_to_string (sig), strerror (errsv), errsv);
|
|
|
|
|
_kc_invoke_callback (pid, log_domain, log_name, callback, user_data, FALSE, -1);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* let's try again with waitpid, probably there was a race... */
|
|
|
|
|
ret = waitpid (pid, &status, 0);
|
|
|
|
|
if (ret > 0) {
|
|
|
|
|
nm_log_dbg (log_domain, LOG_NAME_FMT ": process %ld already terminated %s",
|
|
|
|
|
LOG_NAME_ARGS, (long) ret, _kc_exit_to_string (buf_exit, status));
|
|
|
|
|
_kc_invoke_callback (pid, log_domain, log_name, callback, user_data, TRUE, status);
|
|
|
|
|
} else {
|
|
|
|
|
errsv = errno;
|
|
|
|
|
nm_log_err (LOGD_CORE | log_domain, LOG_NAME_FMT ": failed due to unexpected return value %ld by waitpid (%s, %d) after sending %s",
|
|
|
|
|
LOG_NAME_ARGS, (long) ret, strerror (errsv), errsv, _kc_signal_to_string (sig));
|
|
|
|
|
_kc_invoke_callback (pid, log_domain, log_name, callback, user_data, FALSE, -1);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data = _kc_async_data_alloc (pid, log_domain, log_name, callback, user_data);
|
|
|
|
|
data->async.wait_start_us = nm_utils_get_monotonic_timestamp_us ();
|
|
|
|
|
|
|
|
|
|
if (sig != SIGKILL && wait_before_kill_msec > 0) {
|
|
|
|
|
data->async.source_timeout_kill_id = g_timeout_add (wait_before_kill_msec, _kc_cb_timeout_grace_period, data);
|
|
|
|
|
nm_log_dbg (log_domain, "%s: wait for process to terminate after sending %s (send SIGKILL in %ld milliseconds)...",
|
|
|
|
|
data->log_name, _kc_signal_to_string (sig), (long) wait_before_kill_msec);
|
|
|
|
|
} else {
|
|
|
|
|
data->async.source_timeout_kill_id = 0;
|
|
|
|
|
nm_log_dbg (log_domain, "%s: wait for process to terminate after sending %s...",
|
|
|
|
|
data->log_name, _kc_signal_to_string (sig));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_child_watch_add (pid, _kc_cb_watch_child, data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* nm_utils_kill_child_sync:
|
|
|
|
|
* @pid: process id to kill
|
|
|
|
|
* @sig: signal to sent initially. If 0, no signal is sent. If %SIGKILL, the
|
|
|
|
|
* second %SIGKILL signal is not sent after @wait_before_kill_msec milliseconds.
|
|
|
|
|
* @log_domain: log debug information for this domain. Errors and warnings are logged both
|
|
|
|
|
* as %LOGD_CORE and @log_domain.
|
|
|
|
|
* @log_name: (allow-none): name of the process to kill for logging.
|
|
|
|
|
* @child_status: (out) (allow-none): return the exit status of the child, if no error occured.
|
|
|
|
|
* @wait_before_kill_msec: Waittime in milliseconds before sending %SIGKILL signal. Set this value
|
|
|
|
|
* to zero, not to send %SIGKILL. If @sig is already %SIGKILL, this parameter has not effect.
|
|
|
|
|
* @sleep_duration_msec: the synchronous function sleeps repeatedly waiting for the child to terminate.
|
|
|
|
|
* Set to zero, to use the default (meaning 20 wakeups per seconds).
|
|
|
|
|
*
|
|
|
|
|
* Kill a child process synchronously and wait. The function first checks if the child already terminated
|
|
|
|
|
* and if it did, return the exit status. Otherwise send one @sig signal. @sig will always be
|
|
|
|
|
* sent unless the child already exited. If the child does not exit within @wait_before_kill_msec milliseconds,
|
|
|
|
|
* the function will send %SIGKILL and waits for the child indefinitly. If @wait_before_kill_msec is zero, no
|
|
|
|
|
* %SIGKILL signal will be sent.
|
|
|
|
|
**/
|
|
|
|
|
gboolean
|
|
|
|
|
nm_utils_kill_child_sync (pid_t pid, int sig, guint64 log_domain, const char *log_name,
|
|
|
|
|
int *child_status, guint32 wait_before_kill_msec,
|
|
|
|
|
guint32 sleep_duration_msec)
|
|
|
|
|
{
|
|
|
|
|
int status = 0, errsv;
|
|
|
|
|
pid_t ret;
|
|
|
|
|
gboolean success = FALSE;
|
|
|
|
|
gboolean was_waiting = FALSE, send_kill = FALSE;
|
|
|
|
|
char buf_exit[KC_EXIT_TO_STRING_BUF_SIZE];
|
|
|
|
|
char buf_wait[KC_WAITED_TO_STRING];
|
|
|
|
|
gint64 wait_start_us;
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (pid > 0, FALSE);
|
|
|
|
|
g_return_val_if_fail (log_name != NULL, FALSE);
|
|
|
|
|
|
|
|
|
|
/* check if the child process already terminated... */
|
|
|
|
|
ret = waitpid (pid, &status, WNOHANG);
|
|
|
|
|
if (ret > 0) {
|
|
|
|
|
nm_log_dbg (log_domain, LOG_NAME_FMT ": process %ld already terminated %s",
|
|
|
|
|
LOG_NAME_ARGS, (long) ret, _kc_exit_to_string (buf_exit, status));
|
|
|
|
|
success = TRUE;
|
|
|
|
|
goto out;
|
|
|
|
|
} else if (ret != 0) {
|
|
|
|
|
errsv = errno;
|
|
|
|
|
/* ECHILD means, the process is not a child/does not exist or it has SIGCHILD blocked. */
|
|
|
|
|
if (errsv != ECHILD) {
|
|
|
|
|
nm_log_err (LOGD_CORE | log_domain, LOG_NAME_FMT ": unexpected error while waitpid: %s (%d)",
|
|
|
|
|
LOG_NAME_ARGS, strerror (errsv), errsv);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* send first signal @sig */
|
|
|
|
|
if (kill (pid, sig) != 0) {
|
|
|
|
|
errsv = errno;
|
|
|
|
|
/* ESRCH means, process does not exist or is already a zombie. */
|
|
|
|
|
if (errsv != ESRCH) {
|
|
|
|
|
nm_log_err (LOGD_CORE | log_domain, LOG_NAME_FMT ": failed to send %s: %s (%d)",
|
|
|
|
|
LOG_NAME_ARGS, _kc_signal_to_string (sig), strerror (errsv), errsv);
|
|
|
|
|
} else {
|
|
|
|
|
/* let's try again with waitpid, probably there was a race... */
|
|
|
|
|
ret = waitpid (pid, &status, 0);
|
|
|
|
|
if (ret > 0) {
|
|
|
|
|
nm_log_dbg (log_domain, LOG_NAME_FMT ": process %ld already terminated %s",
|
|
|
|
|
LOG_NAME_ARGS, (long) ret, _kc_exit_to_string (buf_exit, status));
|
|
|
|
|
success = TRUE;
|
|
|
|
|
} else {
|
|
|
|
|
errsv = errno;
|
|
|
|
|
nm_log_err (LOGD_CORE | log_domain, LOG_NAME_FMT ": failed due to unexpected return value %ld by waitpid (%s, %d) after sending %s",
|
|
|
|
|
LOG_NAME_ARGS, (long) ret, strerror (errsv), errsv, _kc_signal_to_string (sig));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
wait_start_us = nm_utils_get_monotonic_timestamp_us ();
|
|
|
|
|
|
|
|
|
|
/* wait for the process to terminated... */
|
|
|
|
|
if (sig != SIGKILL) {
|
|
|
|
|
gint64 wait_until, now;
|
|
|
|
|
gulong sleep_time, sleep_duration_usec;
|
|
|
|
|
int loop_count = 0;
|
|
|
|
|
|
|
|
|
|
sleep_duration_usec = (sleep_duration_msec <= 0) ? (G_USEC_PER_SEC / 20) : MIN (G_MAXULONG, ((gint64) sleep_duration_msec) * 1000L);
|
|
|
|
|
wait_until = wait_before_kill_msec <= 0 ? 0 : wait_start_us + (((gint64) wait_before_kill_msec) * 1000L);
|
|
|
|
|
|
|
|
|
|
while (TRUE) {
|
|
|
|
|
ret = waitpid (pid, &status, WNOHANG);
|
|
|
|
|
if (ret > 0) {
|
|
|
|
|
nm_log_dbg (log_domain, LOG_NAME_FMT ": after sending %s, process %ld exited %s%s",
|
|
|
|
|
LOG_NAME_ARGS, _kc_signal_to_string (sig), (long) ret, _kc_exit_to_string (buf_exit, status),
|
|
|
|
|
was_waiting ? _kc_waited_to_string (buf_wait, wait_start_us) : "");
|
|
|
|
|
success = TRUE;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
if (ret == -1) {
|
|
|
|
|
errsv = errno;
|
|
|
|
|
/* ECHILD means, the process is not a child/does not exist or it has SIGCHILD blocked. */
|
|
|
|
|
if (errsv != ECHILD) {
|
|
|
|
|
nm_log_err (LOGD_CORE | log_domain, LOG_NAME_FMT ": after sending %s, waitpid failed with %s (%d)%s",
|
|
|
|
|
LOG_NAME_ARGS, _kc_signal_to_string (sig), strerror (errsv), errsv,
|
|
|
|
|
was_waiting ? _kc_waited_to_string (buf_wait, wait_start_us) : "");
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!wait_until)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
now = nm_utils_get_monotonic_timestamp_us ();
|
|
|
|
|
if (now >= wait_until)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (!was_waiting) {
|
|
|
|
|
nm_log_dbg (log_domain, LOG_NAME_FMT ": waiting up to %ld milliseconds for process to terminate normally after sending %s...",
|
|
|
|
|
LOG_NAME_ARGS, (long) MAX (wait_before_kill_msec, 0), _kc_signal_to_string (sig));
|
|
|
|
|
was_waiting = TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sleep_time = MIN (wait_until - now, sleep_duration_usec);
|
|
|
|
|
if (loop_count < 20) {
|
|
|
|
|
/* At the beginning we expect the process to die fast.
|
|
|
|
|
* Limit the sleep time, the limit doubles with every iteration. */
|
|
|
|
|
sleep_time = MIN (sleep_time, (((guint64) 1) << loop_count++) * G_USEC_PER_SEC / 2000);
|
|
|
|
|
}
|
|
|
|
|
g_usleep (sleep_time);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* send SIGKILL, if called with @wait_before_kill_msec > 0 */
|
|
|
|
|
if (wait_until) {
|
|
|
|
|
nm_log_dbg (log_domain, LOG_NAME_FMT ": sending SIGKILL...", LOG_NAME_ARGS);
|
|
|
|
|
|
|
|
|
|
send_kill = TRUE;
|
|
|
|
|
if (kill (pid, SIGKILL) != 0) {
|
|
|
|
|
errsv = errno;
|
|
|
|
|
/* ESRCH means, process does not exist or is already a zombie. */
|
|
|
|
|
if (errsv != ESRCH) {
|
|
|
|
|
nm_log_err (LOGD_CORE | log_domain, LOG_NAME_FMT ": failed to send SIGKILL (after sending %s), %s (%d)",
|
|
|
|
|
LOG_NAME_ARGS, _kc_signal_to_string (sig), strerror (errsv), errsv);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!was_waiting) {
|
|
|
|
|
nm_log_dbg (log_domain, LOG_NAME_FMT ": waiting for process to terminate after sending %s%s...",
|
|
|
|
|
LOG_NAME_ARGS, _kc_signal_to_string (sig), send_kill ? " and SIGKILL" : "");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* block until the child terminates. */
|
|
|
|
|
while ((ret = waitpid (pid, &status, 0)) <= 0) {
|
|
|
|
|
errsv = errno;
|
|
|
|
|
|
|
|
|
|
if (errsv != EINTR) {
|
|
|
|
|
nm_log_err (LOGD_CORE | log_domain, LOG_NAME_FMT ": after sending %s%s, waitpid failed with %s (%d)%s",
|
|
|
|
|
LOG_NAME_ARGS, _kc_signal_to_string (sig), send_kill ? " and SIGKILL" : "", strerror (errsv), errsv,
|
|
|
|
|
_kc_waited_to_string (buf_wait, wait_start_us));
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nm_log_dbg (log_domain, LOG_NAME_FMT ": after sending %s%s, process %ld exited %s%s",
|
|
|
|
|
LOG_NAME_ARGS, _kc_signal_to_string (sig), send_kill ? " and SIGKILL" : "", (long) ret,
|
|
|
|
|
_kc_exit_to_string (buf_exit, status), _kc_waited_to_string (buf_wait, wait_start_us));
|
|
|
|
|
success = TRUE;
|
|
|
|
|
out:
|
|
|
|
|
if (child_status)
|
|
|
|
|
*child_status = success ? status : -1;
|
|
|
|
|
return success;
|
|
|
|
|
}
|
|
|
|
|
#undef LOG_NAME_FMT
|
|
|
|
|
#undef LOG_NAME_ARGS
|
|
|
|
|
|
2014-07-31 14:02:22 -05:00
|
|
|
/**
|
|
|
|
|
* nm_utils_find_helper:
|
|
|
|
|
* @progname: the helper program name, like "iptables"
|
|
|
|
|
* @try_first: a custom path to try first before searching
|
|
|
|
|
* @error: on failure, a "not found" error using @error_domain and @error_code
|
|
|
|
|
*
|
|
|
|
|
* Searches for the @progname in common system paths.
|
|
|
|
|
*
|
|
|
|
|
* Returns: the full path to the helper, if found, or %NULL if not found.
|
|
|
|
|
*/
|
|
|
|
|
const char *
|
|
|
|
|
nm_utils_find_helper (const char *progname,
|
|
|
|
|
const char *try_first,
|
|
|
|
|
GError **error)
|
|
|
|
|
{
|
|
|
|
|
static const char *paths[] = {
|
|
|
|
|
PREFIX "/sbin/",
|
|
|
|
|
PREFIX "/bin/",
|
|
|
|
|
"/sbin/",
|
|
|
|
|
"/usr/sbin/",
|
|
|
|
|
"/usr/local/sbin/",
|
|
|
|
|
"/usr/bin/",
|
|
|
|
|
"/usr/local/bin/",
|
|
|
|
|
};
|
|
|
|
|
guint i;
|
|
|
|
|
GString *tmp;
|
|
|
|
|
const char *ret;
|
|
|
|
|
|
|
|
|
|
if (error)
|
|
|
|
|
g_return_val_if_fail (*error == NULL, NULL);
|
|
|
|
|
|
|
|
|
|
if (try_first && try_first[0] && g_file_test (try_first, G_FILE_TEST_EXISTS))
|
|
|
|
|
return g_intern_string (try_first);
|
|
|
|
|
|
|
|
|
|
tmp = g_string_sized_new (50);
|
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (paths); i++) {
|
|
|
|
|
g_string_append_printf (tmp, "%s%s", paths[i], progname);
|
|
|
|
|
if (g_file_test (tmp->str, G_FILE_TEST_EXISTS)) {
|
|
|
|
|
ret = g_intern_string (tmp->str);
|
|
|
|
|
g_string_free (tmp, TRUE);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
g_string_set_size (tmp, 0);
|
|
|
|
|
}
|
|
|
|
|
g_string_free (tmp, TRUE);
|
|
|
|
|
|
|
|
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Could not find %s binary", progname);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-14 12:54:36 +01:00
|
|
|
/******************************************************************************************/
|
|
|
|
|
|
2013-07-04 20:20:27 +02:00
|
|
|
gboolean
|
|
|
|
|
nm_match_spec_string (const GSList *specs, const char *match)
|
|
|
|
|
{
|
|
|
|
|
const GSList *iter;
|
|
|
|
|
|
|
|
|
|
for (iter = specs; iter; iter = g_slist_next (iter)) {
|
|
|
|
|
if (!g_ascii_strcasecmp ((const char *) iter->data, match))
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-11 00:39:12 -04:00
|
|
|
gboolean
|
|
|
|
|
nm_match_spec_hwaddr (const GSList *specs, const char *hwaddr)
|
|
|
|
|
{
|
2014-06-16 11:30:47 -04:00
|
|
|
const GSList *iter;
|
2009-06-11 00:39:12 -04:00
|
|
|
|
|
|
|
|
g_return_val_if_fail (hwaddr != NULL, FALSE);
|
|
|
|
|
|
2014-06-16 11:30:47 -04:00
|
|
|
for (iter = specs; iter; iter = g_slist_next (iter)) {
|
|
|
|
|
const char *spec_str = iter->data;
|
2014-07-17 16:09:42 -04:00
|
|
|
|
2014-06-16 11:30:47 -04:00
|
|
|
if ( !g_ascii_strncasecmp (spec_str, "mac:", 4)
|
|
|
|
|
&& nm_utils_hwaddr_matches (spec_str + 4, -1, hwaddr, -1))
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
if (nm_utils_hwaddr_matches (spec_str, -1, hwaddr, -1))
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return FALSE;
|
2013-02-12 18:00:48 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
|
nm_match_spec_interface_name (const GSList *specs, const char *interface_name)
|
|
|
|
|
{
|
|
|
|
|
char *iface_match;
|
|
|
|
|
gboolean matched;
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (interface_name != NULL, FALSE);
|
|
|
|
|
|
2014-07-17 16:09:42 -04:00
|
|
|
if (nm_match_spec_string (specs, interface_name))
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
2013-02-12 18:00:48 -05:00
|
|
|
iface_match = g_strdup_printf ("interface-name:%s", interface_name);
|
|
|
|
|
matched = nm_match_spec_string (specs, iface_match);
|
|
|
|
|
g_free (iface_match);
|
|
|
|
|
return matched;
|
2009-06-11 00:39:12 -04:00
|
|
|
}
|
|
|
|
|
|
2010-05-26 16:28:51 -07:00
|
|
|
#define BUFSIZE 10
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
parse_subchannels (const char *subchannels, guint32 *a, guint32 *b, guint32 *c)
|
|
|
|
|
{
|
|
|
|
|
long unsigned int tmp;
|
|
|
|
|
char buf[BUFSIZE + 1];
|
|
|
|
|
const char *p = subchannels;
|
|
|
|
|
int i = 0;
|
|
|
|
|
char *pa = NULL, *pb = NULL, *pc = NULL;
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (subchannels != NULL, FALSE);
|
|
|
|
|
g_return_val_if_fail (a != NULL, FALSE);
|
|
|
|
|
g_return_val_if_fail (*a == 0, FALSE);
|
|
|
|
|
g_return_val_if_fail (b != NULL, FALSE);
|
|
|
|
|
g_return_val_if_fail (*b == 0, FALSE);
|
|
|
|
|
g_return_val_if_fail (c != NULL, FALSE);
|
|
|
|
|
g_return_val_if_fail (*c == 0, FALSE);
|
|
|
|
|
|
|
|
|
|
/* sanity check */
|
2012-09-25 10:44:23 -04:00
|
|
|
if (!g_ascii_isxdigit (subchannels[0]))
|
2010-05-26 16:28:51 -07:00
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
/* Get the first channel */
|
|
|
|
|
while (*p && (*p != ',')) {
|
2012-09-25 10:44:23 -04:00
|
|
|
if (!g_ascii_isxdigit (*p) && (*p != '.'))
|
2010-05-26 16:28:51 -07:00
|
|
|
return FALSE; /* Invalid chars */
|
|
|
|
|
if (i >= BUFSIZE)
|
|
|
|
|
return FALSE; /* Too long to be a subchannel */
|
|
|
|
|
buf[i++] = *p++;
|
|
|
|
|
}
|
|
|
|
|
buf[i] = '\0';
|
|
|
|
|
|
|
|
|
|
/* and grab each of its elements, there should be 3 */
|
|
|
|
|
pa = &buf[0];
|
|
|
|
|
pb = strchr (buf, '.');
|
|
|
|
|
if (pb)
|
|
|
|
|
pc = strchr (pb + 1, '.');
|
|
|
|
|
if (!pa || !pb || !pc)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
/* Split the string */
|
|
|
|
|
*pb++ = '\0';
|
|
|
|
|
*pc++ = '\0';
|
|
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
|
tmp = strtoul (pa, NULL, 16);
|
|
|
|
|
if (errno)
|
|
|
|
|
return FALSE;
|
|
|
|
|
*a = (guint32) tmp;
|
|
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
|
tmp = strtoul (pb, NULL, 16);
|
|
|
|
|
if (errno)
|
|
|
|
|
return FALSE;
|
|
|
|
|
*b = (guint32) tmp;
|
|
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
|
tmp = strtoul (pc, NULL, 16);
|
|
|
|
|
if (errno)
|
|
|
|
|
return FALSE;
|
|
|
|
|
*c = (guint32) tmp;
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2010-06-17 21:24:14 -07:00
|
|
|
#define SUBCHAN_TAG "s390-subchannels:"
|
2010-05-26 16:28:51 -07:00
|
|
|
|
|
|
|
|
gboolean
|
2010-06-17 21:24:14 -07:00
|
|
|
nm_match_spec_s390_subchannels (const GSList *specs, const char *subchannels)
|
2010-05-26 16:28:51 -07:00
|
|
|
{
|
|
|
|
|
const GSList *iter;
|
|
|
|
|
guint32 a = 0, b = 0, c = 0;
|
|
|
|
|
guint32 spec_a = 0, spec_b = 0, spec_c = 0;
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (subchannels != NULL, FALSE);
|
|
|
|
|
|
|
|
|
|
if (!parse_subchannels (subchannels, &a, &b, &c))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
for (iter = specs; iter; iter = g_slist_next (iter)) {
|
|
|
|
|
const char *spec = iter->data;
|
|
|
|
|
|
|
|
|
|
if (!strncmp (spec, SUBCHAN_TAG, strlen (SUBCHAN_TAG))) {
|
|
|
|
|
spec += strlen (SUBCHAN_TAG);
|
|
|
|
|
if (parse_subchannels (spec, &spec_a, &spec_b, &spec_c)) {
|
|
|
|
|
if (a == spec_a && b == spec_b && c == spec_c)
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-01 14:56:07 -05:00
|
|
|
const char *
|
|
|
|
|
nm_utils_get_shared_wifi_permission (NMConnection *connection)
|
|
|
|
|
{
|
|
|
|
|
NMSettingWireless *s_wifi;
|
|
|
|
|
NMSettingWirelessSecurity *s_wsec;
|
|
|
|
|
const char *method = NULL;
|
|
|
|
|
|
core: don't have IP4 and IP6 configs on slaves
Although it's convenient in some places to have IP configs on all
connections, it makes more sense in other places to not have IP
configs on slaves. (eg, it's confusing for nmcli, etc, to report a
full NMSettingIP4Config on a slave device). So revert parts of the
earlier patch. However, it's still safe to assume that s_ip4 != NULL
if method != DISABLED, so some of the earlier simplifications can
stay.
Also, add nm_utils_get_ip_config_method(), which returns the correct
IP config method for a connection, whether the connection has IP4 and
IP6 settings objects or not, and use that to keep some more of the
simplifications from the earlier patch.
2013-10-14 10:38:56 -04:00
|
|
|
method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG);
|
2013-09-26 17:34:23 -04:00
|
|
|
if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED) != 0)
|
2011-07-01 14:56:07 -05:00
|
|
|
return NULL; /* Not shared */
|
|
|
|
|
|
|
|
|
|
s_wifi = nm_connection_get_setting_wireless (connection);
|
|
|
|
|
if (s_wifi) {
|
|
|
|
|
s_wsec = nm_connection_get_setting_wireless_security (connection);
|
2013-08-27 13:13:23 +01:00
|
|
|
if (s_wsec)
|
2011-07-01 14:56:07 -05:00
|
|
|
return NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED;
|
|
|
|
|
else
|
|
|
|
|
return NM_AUTH_PERMISSION_WIFI_SHARE_OPEN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-22 12:35:04 +02:00
|
|
|
/*********************************/
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
nm_gvalue_destroy (gpointer data)
|
|
|
|
|
{
|
|
|
|
|
GValue *value = (GValue *) data;
|
|
|
|
|
|
|
|
|
|
g_value_unset (value);
|
|
|
|
|
g_slice_free (GValue, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GHashTable *
|
|
|
|
|
value_hash_create (void)
|
|
|
|
|
{
|
|
|
|
|
return g_hash_table_new_full (g_str_hash, g_str_equal, g_free, nm_gvalue_destroy);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
value_hash_add (GHashTable *hash,
|
|
|
|
|
const char *key,
|
|
|
|
|
GValue *value)
|
|
|
|
|
{
|
|
|
|
|
g_hash_table_insert (hash, g_strdup (key), value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
value_hash_add_str (GHashTable *hash,
|
|
|
|
|
const char *key,
|
|
|
|
|
const char *str)
|
|
|
|
|
{
|
|
|
|
|
GValue *value;
|
|
|
|
|
|
|
|
|
|
value = g_slice_new0 (GValue);
|
|
|
|
|
g_value_init (value, G_TYPE_STRING);
|
|
|
|
|
g_value_set_string (value, str);
|
|
|
|
|
|
|
|
|
|
value_hash_add (hash, key, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
value_hash_add_object_path (GHashTable *hash,
|
|
|
|
|
const char *key,
|
|
|
|
|
const char *op)
|
|
|
|
|
{
|
|
|
|
|
GValue *value;
|
|
|
|
|
|
|
|
|
|
value = g_slice_new0 (GValue);
|
|
|
|
|
g_value_init (value, DBUS_TYPE_G_OBJECT_PATH);
|
|
|
|
|
g_value_set_boxed (value, op);
|
|
|
|
|
|
|
|
|
|
value_hash_add (hash, key, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
value_hash_add_uint (GHashTable *hash,
|
|
|
|
|
const char *key,
|
|
|
|
|
guint32 val)
|
|
|
|
|
{
|
|
|
|
|
GValue *value;
|
|
|
|
|
|
|
|
|
|
value = g_slice_new0 (GValue);
|
|
|
|
|
g_value_init (value, G_TYPE_UINT);
|
|
|
|
|
g_value_set_uint (value, val);
|
|
|
|
|
|
|
|
|
|
value_hash_add (hash, key, value);
|
|
|
|
|
}
|
2009-06-11 00:39:12 -04:00
|
|
|
|
2010-03-16 23:51:55 -07:00
|
|
|
void
|
|
|
|
|
value_hash_add_bool (GHashTable *hash,
|
|
|
|
|
const char *key,
|
|
|
|
|
gboolean val)
|
|
|
|
|
{
|
|
|
|
|
GValue *value;
|
|
|
|
|
|
|
|
|
|
value = g_slice_new0 (GValue);
|
|
|
|
|
g_value_init (value, G_TYPE_BOOLEAN);
|
|
|
|
|
g_value_set_boolean (value, val);
|
|
|
|
|
|
|
|
|
|
value_hash_add (hash, key, value);
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-02 22:38:51 -05:00
|
|
|
void
|
|
|
|
|
value_hash_add_object_property (GHashTable *hash,
|
|
|
|
|
const char *key,
|
|
|
|
|
GObject *object,
|
|
|
|
|
const char *prop,
|
|
|
|
|
GType val_type)
|
|
|
|
|
{
|
|
|
|
|
GValue *value;
|
|
|
|
|
|
|
|
|
|
value = g_slice_new0 (GValue);
|
|
|
|
|
g_value_init (value, val_type);
|
|
|
|
|
g_object_get_property (object, prop, value);
|
|
|
|
|
value_hash_add (hash, key, value);
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-12 09:08:04 +02:00
|
|
|
|
2011-01-13 13:28:52 -06:00
|
|
|
static char *
|
|
|
|
|
get_new_connection_name (const GSList *existing,
|
2014-08-25 16:21:59 +02:00
|
|
|
const char *preferred,
|
|
|
|
|
const char *fallback_prefix)
|
2011-01-13 13:28:52 -06:00
|
|
|
{
|
|
|
|
|
GSList *names = NULL;
|
|
|
|
|
const GSList *iter;
|
|
|
|
|
char *cname = NULL;
|
|
|
|
|
int i = 0;
|
|
|
|
|
gboolean preferred_found = FALSE;
|
|
|
|
|
|
2014-08-25 16:21:59 +02:00
|
|
|
g_assert (fallback_prefix);
|
|
|
|
|
|
2011-01-13 13:28:52 -06:00
|
|
|
for (iter = existing; iter; iter = g_slist_next (iter)) {
|
|
|
|
|
NMConnection *candidate = NM_CONNECTION (iter->data);
|
|
|
|
|
const char *id;
|
|
|
|
|
|
2011-03-08 13:41:28 +01:00
|
|
|
id = nm_connection_get_id (candidate);
|
2011-01-13 13:28:52 -06:00
|
|
|
g_assert (id);
|
|
|
|
|
names = g_slist_append (names, (gpointer) id);
|
|
|
|
|
|
|
|
|
|
if (preferred && !preferred_found && (strcmp (preferred, id) == 0))
|
|
|
|
|
preferred_found = TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return the preferred name if it was unique */
|
|
|
|
|
if (preferred && !preferred_found) {
|
|
|
|
|
g_slist_free (names);
|
|
|
|
|
return g_strdup (preferred);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Otherwise find the next available unique connection name using the given
|
|
|
|
|
* connection name template.
|
|
|
|
|
*/
|
|
|
|
|
while (!cname && (i++ < 10000)) {
|
|
|
|
|
char *temp;
|
|
|
|
|
gboolean found = FALSE;
|
|
|
|
|
|
2014-08-25 16:21:59 +02:00
|
|
|
/* Translators: the first %s is a prefix for the connection id, such
|
|
|
|
|
* as "Wired Connection" or "VPN Connection". The %d is a number
|
|
|
|
|
* that is combined with the first argument to create a unique
|
|
|
|
|
* connection id. */
|
|
|
|
|
temp = g_strdup_printf (C_("connection id fallback", "%s %d"),
|
|
|
|
|
fallback_prefix, i);
|
2011-01-13 13:28:52 -06:00
|
|
|
for (iter = names; iter; iter = g_slist_next (iter)) {
|
|
|
|
|
if (!strcmp (iter->data, temp)) {
|
|
|
|
|
found = TRUE;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!found)
|
|
|
|
|
cname = temp;
|
|
|
|
|
else
|
|
|
|
|
g_free (temp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_slist_free (names);
|
|
|
|
|
return cname;
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-05 17:11:57 -04:00
|
|
|
static char *
|
|
|
|
|
get_new_connection_ifname (const GSList *existing,
|
|
|
|
|
const char *prefix)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
char *name;
|
|
|
|
|
const GSList *iter;
|
|
|
|
|
gboolean found;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < 500; i++) {
|
|
|
|
|
name = g_strdup_printf ("%s%d", prefix, i);
|
|
|
|
|
|
|
|
|
|
if (nm_platform_link_exists (name))
|
|
|
|
|
goto next;
|
|
|
|
|
|
|
|
|
|
for (iter = existing, found = FALSE; iter; iter = g_slist_next (iter)) {
|
|
|
|
|
NMConnection *candidate = iter->data;
|
|
|
|
|
|
|
|
|
|
if (g_strcmp0 (nm_connection_get_interface_name (candidate), name) == 0) {
|
|
|
|
|
found = TRUE;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!found)
|
|
|
|
|
return name;
|
|
|
|
|
|
|
|
|
|
next:
|
|
|
|
|
g_free (name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
core: don't have IP4 and IP6 configs on slaves
Although it's convenient in some places to have IP configs on all
connections, it makes more sense in other places to not have IP
configs on slaves. (eg, it's confusing for nmcli, etc, to report a
full NMSettingIP4Config on a slave device). So revert parts of the
earlier patch. However, it's still safe to assume that s_ip4 != NULL
if method != DISABLED, so some of the earlier simplifications can
stay.
Also, add nm_utils_get_ip_config_method(), which returns the correct
IP config method for a connection, whether the connection has IP4 and
IP6 settings objects or not, and use that to keep some more of the
simplifications from the earlier patch.
2013-10-14 10:38:56 -04:00
|
|
|
const char *
|
|
|
|
|
nm_utils_get_ip_config_method (NMConnection *connection,
|
|
|
|
|
GType ip_setting_type)
|
|
|
|
|
{
|
|
|
|
|
NMSettingConnection *s_con;
|
|
|
|
|
NMSettingIP4Config *s_ip4;
|
|
|
|
|
NMSettingIP6Config *s_ip6;
|
|
|
|
|
const char *method;
|
2013-09-26 17:34:23 -04:00
|
|
|
|
core: don't have IP4 and IP6 configs on slaves
Although it's convenient in some places to have IP configs on all
connections, it makes more sense in other places to not have IP
configs on slaves. (eg, it's confusing for nmcli, etc, to report a
full NMSettingIP4Config on a slave device). So revert parts of the
earlier patch. However, it's still safe to assume that s_ip4 != NULL
if method != DISABLED, so some of the earlier simplifications can
stay.
Also, add nm_utils_get_ip_config_method(), which returns the correct
IP config method for a connection, whether the connection has IP4 and
IP6 settings objects or not, and use that to keep some more of the
simplifications from the earlier patch.
2013-10-14 10:38:56 -04:00
|
|
|
s_con = nm_connection_get_setting_connection (connection);
|
|
|
|
|
|
|
|
|
|
if (ip_setting_type == NM_TYPE_SETTING_IP4_CONFIG) {
|
|
|
|
|
g_return_val_if_fail (s_con != NULL, NM_SETTING_IP4_CONFIG_METHOD_AUTO);
|
|
|
|
|
|
|
|
|
|
if (nm_setting_connection_get_master (s_con))
|
|
|
|
|
return NM_SETTING_IP4_CONFIG_METHOD_DISABLED;
|
|
|
|
|
else {
|
|
|
|
|
s_ip4 = nm_connection_get_setting_ip4_config (connection);
|
|
|
|
|
g_return_val_if_fail (s_ip4 != NULL, NM_SETTING_IP4_CONFIG_METHOD_AUTO);
|
|
|
|
|
method = nm_setting_ip4_config_get_method (s_ip4);
|
|
|
|
|
g_return_val_if_fail (method != NULL, NM_SETTING_IP4_CONFIG_METHOD_AUTO);
|
|
|
|
|
|
|
|
|
|
return method;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else if (ip_setting_type == NM_TYPE_SETTING_IP6_CONFIG) {
|
|
|
|
|
g_return_val_if_fail (s_con != NULL, NM_SETTING_IP6_CONFIG_METHOD_AUTO);
|
|
|
|
|
|
|
|
|
|
if (nm_setting_connection_get_master (s_con))
|
|
|
|
|
return NM_SETTING_IP6_CONFIG_METHOD_IGNORE;
|
|
|
|
|
else {
|
|
|
|
|
s_ip6 = nm_connection_get_setting_ip6_config (connection);
|
|
|
|
|
g_return_val_if_fail (s_ip6 != NULL, NM_SETTING_IP6_CONFIG_METHOD_AUTO);
|
|
|
|
|
method = nm_setting_ip6_config_get_method (s_ip6);
|
|
|
|
|
g_return_val_if_fail (method != NULL, NM_SETTING_IP6_CONFIG_METHOD_AUTO);
|
|
|
|
|
|
|
|
|
|
return method;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else
|
|
|
|
|
g_assert_not_reached ();
|
2013-09-26 17:34:23 -04:00
|
|
|
}
|
|
|
|
|
|
2011-01-13 13:28:52 -06:00
|
|
|
void
|
|
|
|
|
nm_utils_complete_generic (NMConnection *connection,
|
|
|
|
|
const char *ctype,
|
|
|
|
|
const GSList *existing,
|
2014-08-05 17:11:57 -04:00
|
|
|
const char *preferred_id,
|
|
|
|
|
const char *fallback_id_prefix,
|
|
|
|
|
const char *ifname_prefix,
|
2011-02-25 11:58:16 -06:00
|
|
|
gboolean default_enable_ipv6)
|
2011-01-13 13:28:52 -06:00
|
|
|
{
|
|
|
|
|
NMSettingConnection *s_con;
|
2014-08-05 17:11:57 -04:00
|
|
|
char *id, *uuid, *ifname;
|
|
|
|
|
GHashTable *parameters;
|
2014-08-25 16:21:59 +02:00
|
|
|
|
2014-08-05 17:11:57 -04:00
|
|
|
g_assert (fallback_id_prefix);
|
2011-01-13 13:28:52 -06:00
|
|
|
|
2011-12-05 12:27:47 +01:00
|
|
|
s_con = nm_connection_get_setting_connection (connection);
|
2011-01-13 13:28:52 -06:00
|
|
|
if (!s_con) {
|
|
|
|
|
s_con = (NMSettingConnection *) nm_setting_connection_new ();
|
|
|
|
|
nm_connection_add_setting (connection, NM_SETTING (s_con));
|
|
|
|
|
}
|
|
|
|
|
g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_TYPE, ctype, NULL);
|
|
|
|
|
|
|
|
|
|
if (!nm_setting_connection_get_uuid (s_con)) {
|
|
|
|
|
uuid = nm_utils_uuid_generate ();
|
|
|
|
|
g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_UUID, uuid, NULL);
|
|
|
|
|
g_free (uuid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Add a connection ID if absent */
|
|
|
|
|
if (!nm_setting_connection_get_id (s_con)) {
|
2014-08-05 17:11:57 -04:00
|
|
|
id = get_new_connection_name (existing, preferred_id, fallback_id_prefix);
|
2011-01-13 13:28:52 -06:00
|
|
|
g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_ID, id, NULL);
|
|
|
|
|
g_free (id);
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-05 17:11:57 -04:00
|
|
|
/* Add an interface name, if requested */
|
|
|
|
|
if (ifname_prefix && !nm_setting_connection_get_interface_name (s_con)) {
|
|
|
|
|
ifname = get_new_connection_ifname (existing, ifname_prefix);
|
|
|
|
|
g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_INTERFACE_NAME, ifname, NULL);
|
|
|
|
|
g_free (ifname);
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-26 17:34:23 -04:00
|
|
|
/* Normalize */
|
2014-08-05 17:11:57 -04:00
|
|
|
parameters = g_hash_table_new (g_str_hash, g_str_equal);
|
|
|
|
|
g_hash_table_insert (parameters, NM_CONNECTION_NORMALIZE_PARAM_IP6_CONFIG_METHOD,
|
|
|
|
|
default_enable_ipv6 ? NM_SETTING_IP6_CONFIG_METHOD_AUTO : NM_SETTING_IP6_CONFIG_METHOD_IGNORE);
|
2013-12-01 23:43:14 +01:00
|
|
|
nm_connection_normalize (connection, parameters, NULL, NULL);
|
|
|
|
|
g_hash_table_destroy (parameters);
|
2011-01-13 13:28:52 -06:00
|
|
|
}
|
|
|
|
|
|
2012-02-12 14:48:44 -06:00
|
|
|
char *
|
2012-02-22 23:40:18 -06:00
|
|
|
nm_utils_new_vlan_name (const char *parent_iface, guint32 vlan_id)
|
2012-02-12 14:48:44 -06:00
|
|
|
{
|
2012-08-06 13:05:05 -05:00
|
|
|
/* Basically VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD */
|
2012-02-22 23:40:18 -06:00
|
|
|
return g_strdup_printf ("%s.%d", parent_iface, vlan_id);
|
2012-02-12 14:48:44 -06:00
|
|
|
}
|
|
|
|
|
|
2013-11-19 22:28:36 -06:00
|
|
|
/**
|
|
|
|
|
* nm_utils_read_resolv_conf_nameservers():
|
|
|
|
|
* @rc_contents: contents of a resolv.conf; or %NULL to read /etc/resolv.conf
|
|
|
|
|
*
|
|
|
|
|
* Reads all nameservers out of @rc_contents or /etc/resolv.conf and returns
|
|
|
|
|
* them.
|
|
|
|
|
*
|
|
|
|
|
* Returns: a #GPtrArray of 'char *' elements of each nameserver line from
|
|
|
|
|
* @contents or resolv.conf
|
|
|
|
|
*/
|
|
|
|
|
GPtrArray *
|
|
|
|
|
nm_utils_read_resolv_conf_nameservers (const char *rc_contents)
|
|
|
|
|
{
|
|
|
|
|
GPtrArray *nameservers = NULL;
|
|
|
|
|
char *contents = NULL;
|
|
|
|
|
char **lines, **iter;
|
|
|
|
|
char *p;
|
|
|
|
|
|
|
|
|
|
if (rc_contents)
|
|
|
|
|
contents = g_strdup (rc_contents);
|
|
|
|
|
else {
|
|
|
|
|
if (!g_file_get_contents (_PATH_RESCONF, &contents, NULL, NULL))
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nameservers = g_ptr_array_new_full (3, g_free);
|
|
|
|
|
|
|
|
|
|
lines = g_strsplit_set (contents, "\r\n", -1);
|
|
|
|
|
for (iter = lines; *iter; iter++) {
|
|
|
|
|
if (!g_str_has_prefix (*iter, "nameserver"))
|
|
|
|
|
continue;
|
|
|
|
|
p = *iter + strlen ("nameserver");
|
|
|
|
|
if (!g_ascii_isspace (*p++))
|
|
|
|
|
continue;
|
|
|
|
|
/* Skip intermediate whitespace */
|
|
|
|
|
while (g_ascii_isspace (*p))
|
|
|
|
|
p++;
|
|
|
|
|
g_strchomp (p);
|
|
|
|
|
|
|
|
|
|
g_ptr_array_add (nameservers, g_strdup (p));
|
|
|
|
|
}
|
|
|
|
|
g_strfreev (lines);
|
|
|
|
|
g_free (contents);
|
|
|
|
|
|
|
|
|
|
return nameservers;
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-09 13:48:27 +02:00
|
|
|
static GHashTable *
|
|
|
|
|
check_property_in_hash (GHashTable *hash,
|
|
|
|
|
const char *s_name,
|
|
|
|
|
const char *p_name)
|
|
|
|
|
{
|
|
|
|
|
GHashTable *props;
|
|
|
|
|
|
|
|
|
|
props = g_hash_table_lookup (hash, s_name);
|
|
|
|
|
if ( !props
|
|
|
|
|
|| !g_hash_table_lookup (props, p_name)) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
return props;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
remove_from_hash (GHashTable *s_hash,
|
|
|
|
|
GHashTable *p_hash,
|
|
|
|
|
const char *s_name,
|
|
|
|
|
const char *p_name)
|
|
|
|
|
{
|
|
|
|
|
g_hash_table_remove (p_hash, p_name);
|
|
|
|
|
if (g_hash_table_size (p_hash) == 0)
|
|
|
|
|
g_hash_table_remove (s_hash, s_name);
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-25 17:44:07 -06:00
|
|
|
static gboolean
|
2014-04-09 13:48:27 +02:00
|
|
|
check_ip6_method (NMConnection *orig,
|
|
|
|
|
NMConnection *candidate,
|
|
|
|
|
GHashTable *settings)
|
2013-11-15 13:09:12 -06:00
|
|
|
{
|
|
|
|
|
GHashTable *props;
|
|
|
|
|
const char *orig_ip6_method, *candidate_ip6_method;
|
|
|
|
|
NMSettingIP6Config *candidate_ip6;
|
2014-04-09 13:48:27 +02:00
|
|
|
gboolean allow = FALSE;
|
2013-11-15 13:09:12 -06:00
|
|
|
|
2014-04-09 13:48:27 +02:00
|
|
|
props = check_property_in_hash (settings,
|
|
|
|
|
NM_SETTING_IP6_CONFIG_SETTING_NAME,
|
|
|
|
|
NM_SETTING_IP6_CONFIG_METHOD);
|
|
|
|
|
if (!props)
|
|
|
|
|
return TRUE;
|
2013-11-15 13:09:12 -06:00
|
|
|
|
2014-05-30 09:06:24 +02:00
|
|
|
/* If the generated connection is 'link-local' and the candidate is both 'auto'
|
2013-11-15 13:09:12 -06:00
|
|
|
* and may-fail=TRUE, then the candidate is OK to use. may-fail is included
|
|
|
|
|
* in the decision because if the candidate is 'auto' but may-fail=FALSE, then
|
|
|
|
|
* the connection could not possibly have been previously activated on the
|
|
|
|
|
* device if the device has no non-link-local IPv6 address.
|
|
|
|
|
*/
|
|
|
|
|
orig_ip6_method = nm_utils_get_ip_config_method (orig, NM_TYPE_SETTING_IP6_CONFIG);
|
|
|
|
|
candidate_ip6_method = nm_utils_get_ip_config_method (candidate, NM_TYPE_SETTING_IP6_CONFIG);
|
|
|
|
|
candidate_ip6 = nm_connection_get_setting_ip6_config (candidate);
|
|
|
|
|
|
|
|
|
|
if ( strcmp (orig_ip6_method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0
|
|
|
|
|
&& strcmp (candidate_ip6_method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0
|
|
|
|
|
&& (!candidate_ip6 || nm_setting_ip6_config_get_may_fail (candidate_ip6))) {
|
2014-04-09 13:48:27 +02:00
|
|
|
allow = TRUE;
|
2014-03-07 10:33:12 +01:00
|
|
|
}
|
|
|
|
|
|
2014-05-29 16:36:24 +02:00
|
|
|
/* If the generated connection method is 'link-local' or 'auto' and the candidate
|
|
|
|
|
* method is 'ignore' we can take the connection, because NM didn't simply take care
|
2014-03-07 10:33:12 +01:00
|
|
|
* of IPv6.
|
|
|
|
|
*/
|
2014-05-29 16:36:24 +02:00
|
|
|
if ( ( strcmp (orig_ip6_method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0
|
|
|
|
|
|| strcmp (orig_ip6_method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0)
|
2014-03-07 10:33:12 +01:00
|
|
|
&& strcmp (candidate_ip6_method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0) {
|
2014-04-09 13:48:27 +02:00
|
|
|
allow = TRUE;
|
2014-03-07 10:33:12 +01:00
|
|
|
}
|
|
|
|
|
|
2014-04-09 13:48:27 +02:00
|
|
|
if (allow) {
|
|
|
|
|
remove_from_hash (settings, props,
|
|
|
|
|
NM_SETTING_IP6_CONFIG_SETTING_NAME,
|
|
|
|
|
NM_SETTING_IP6_CONFIG_METHOD);
|
|
|
|
|
}
|
|
|
|
|
return allow;
|
2014-03-07 10:33:12 +01:00
|
|
|
}
|
|
|
|
|
|
2014-02-25 17:56:06 -06:00
|
|
|
static gboolean
|
2014-04-09 13:48:27 +02:00
|
|
|
check_ip4_method (NMConnection *orig,
|
|
|
|
|
NMConnection *candidate,
|
|
|
|
|
GHashTable *settings,
|
|
|
|
|
gboolean device_has_carrier)
|
2014-02-25 17:56:06 -06:00
|
|
|
{
|
|
|
|
|
GHashTable *props;
|
|
|
|
|
const char *orig_ip4_method, *candidate_ip4_method;
|
|
|
|
|
NMSettingIP4Config *candidate_ip4;
|
|
|
|
|
|
2014-04-09 13:48:27 +02:00
|
|
|
props = check_property_in_hash (settings,
|
|
|
|
|
NM_SETTING_IP4_CONFIG_SETTING_NAME,
|
|
|
|
|
NM_SETTING_IP4_CONFIG_METHOD);
|
|
|
|
|
if (!props)
|
|
|
|
|
return TRUE;
|
2014-02-25 17:56:06 -06:00
|
|
|
|
2014-05-30 09:06:24 +02:00
|
|
|
/* If the generated connection is 'disabled' (device had no IP addresses)
|
2014-02-25 17:56:06 -06:00
|
|
|
* but it has no carrier, that most likely means that IP addressing could
|
|
|
|
|
* not complete and thus no IP addresses were assigned. In that case, allow
|
|
|
|
|
* matching to the "auto" method.
|
|
|
|
|
*/
|
|
|
|
|
orig_ip4_method = nm_utils_get_ip_config_method (orig, NM_TYPE_SETTING_IP4_CONFIG);
|
|
|
|
|
candidate_ip4_method = nm_utils_get_ip_config_method (candidate, NM_TYPE_SETTING_IP4_CONFIG);
|
|
|
|
|
candidate_ip4 = nm_connection_get_setting_ip4_config (candidate);
|
|
|
|
|
|
|
|
|
|
if ( strcmp (orig_ip4_method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0
|
|
|
|
|
&& strcmp (candidate_ip4_method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0
|
|
|
|
|
&& (!candidate_ip4 || nm_setting_ip4_config_get_may_fail (candidate_ip4))
|
|
|
|
|
&& (device_has_carrier == FALSE)) {
|
2014-04-09 13:48:27 +02:00
|
|
|
remove_from_hash (settings, props,
|
|
|
|
|
NM_SETTING_IP4_CONFIG_SETTING_NAME,
|
|
|
|
|
NM_SETTING_IP4_CONFIG_METHOD);
|
2014-02-25 17:56:06 -06:00
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-18 14:21:59 +01:00
|
|
|
static gboolean
|
|
|
|
|
check_connection_interface_name (NMConnection *orig,
|
|
|
|
|
NMConnection *candidate,
|
|
|
|
|
GHashTable *settings)
|
|
|
|
|
{
|
|
|
|
|
GHashTable *props;
|
|
|
|
|
const char *orig_ifname, *cand_ifname;
|
|
|
|
|
NMSettingConnection *s_con_orig, *s_con_cand;
|
|
|
|
|
|
2014-04-09 13:48:27 +02:00
|
|
|
props = check_property_in_hash (settings,
|
|
|
|
|
NM_SETTING_CONNECTION_SETTING_NAME,
|
|
|
|
|
NM_SETTING_CONNECTION_INTERFACE_NAME);
|
|
|
|
|
if (!props)
|
|
|
|
|
return TRUE;
|
2014-03-18 14:21:59 +01:00
|
|
|
|
2014-05-30 09:06:24 +02:00
|
|
|
/* If one of the interface names is NULL, we accept that connection */
|
2014-03-18 14:21:59 +01:00
|
|
|
s_con_orig = nm_connection_get_setting_connection (orig);
|
|
|
|
|
s_con_cand = nm_connection_get_setting_connection (candidate);
|
|
|
|
|
orig_ifname = nm_setting_connection_get_interface_name (s_con_orig);
|
|
|
|
|
cand_ifname = nm_setting_connection_get_interface_name (s_con_cand);
|
|
|
|
|
|
2014-04-09 13:48:27 +02:00
|
|
|
if (!orig_ifname || !cand_ifname) {
|
|
|
|
|
remove_from_hash (settings, props,
|
|
|
|
|
NM_SETTING_CONNECTION_SETTING_NAME,
|
|
|
|
|
NM_SETTING_CONNECTION_INTERFACE_NAME);
|
2014-03-18 14:21:59 +01:00
|
|
|
return TRUE;
|
2014-04-09 13:48:27 +02:00
|
|
|
}
|
2014-03-18 14:21:59 +01:00
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-08 10:49:16 +02:00
|
|
|
static gboolean
|
|
|
|
|
check_connection_mac_address (NMConnection *orig,
|
|
|
|
|
NMConnection *candidate,
|
|
|
|
|
GHashTable *settings)
|
|
|
|
|
{
|
|
|
|
|
GHashTable *props;
|
2014-07-30 10:57:45 -04:00
|
|
|
const char *orig_mac = NULL, *cand_mac = NULL;
|
2014-04-08 10:49:16 +02:00
|
|
|
NMSettingWired *s_wired_orig, *s_wired_cand;
|
|
|
|
|
|
|
|
|
|
props = check_property_in_hash (settings,
|
|
|
|
|
NM_SETTING_WIRED_SETTING_NAME,
|
|
|
|
|
NM_SETTING_WIRED_MAC_ADDRESS);
|
|
|
|
|
if (!props)
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
/* If one of the MAC addresses is NULL, we accept that connection */
|
|
|
|
|
s_wired_orig = nm_connection_get_setting_wired (orig);
|
2014-06-06 15:49:52 -05:00
|
|
|
if (s_wired_orig)
|
|
|
|
|
orig_mac = nm_setting_wired_get_mac_address (s_wired_orig);
|
|
|
|
|
|
2014-04-08 10:49:16 +02:00
|
|
|
s_wired_cand = nm_connection_get_setting_wired (candidate);
|
2014-06-06 15:49:52 -05:00
|
|
|
if (s_wired_cand)
|
|
|
|
|
cand_mac = nm_setting_wired_get_mac_address (s_wired_cand);
|
2014-04-08 10:49:16 +02:00
|
|
|
|
|
|
|
|
if (!orig_mac || !cand_mac) {
|
|
|
|
|
remove_from_hash (settings, props,
|
|
|
|
|
NM_SETTING_WIRED_SETTING_NAME,
|
|
|
|
|
NM_SETTING_WIRED_MAC_ADDRESS);
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-25 17:44:07 -06:00
|
|
|
static NMConnection *
|
|
|
|
|
check_possible_match (NMConnection *orig,
|
|
|
|
|
NMConnection *candidate,
|
2014-02-25 17:56:06 -06:00
|
|
|
GHashTable *settings,
|
|
|
|
|
gboolean device_has_carrier)
|
2014-02-25 17:44:07 -06:00
|
|
|
{
|
|
|
|
|
g_return_val_if_fail (settings != NULL, NULL);
|
|
|
|
|
|
2014-04-09 13:48:27 +02:00
|
|
|
if (!check_ip6_method (orig, candidate, settings))
|
|
|
|
|
return NULL;
|
2014-02-25 17:44:07 -06:00
|
|
|
|
2014-04-09 13:48:27 +02:00
|
|
|
if (!check_ip4_method (orig, candidate, settings, device_has_carrier))
|
|
|
|
|
return NULL;
|
2014-03-07 10:33:12 +01:00
|
|
|
|
2014-04-09 13:48:27 +02:00
|
|
|
if (!check_connection_interface_name (orig, candidate, settings))
|
|
|
|
|
return NULL;
|
2014-02-25 17:56:06 -06:00
|
|
|
|
2014-04-08 10:49:16 +02:00
|
|
|
if (!check_connection_mac_address (orig, candidate, settings))
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2014-04-09 13:48:27 +02:00
|
|
|
if (g_hash_table_size (settings) == 0)
|
2014-03-18 14:21:59 +01:00
|
|
|
return candidate;
|
2014-04-09 13:48:27 +02:00
|
|
|
else
|
|
|
|
|
return NULL;
|
2013-11-15 13:09:12 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* nm_utils_match_connection:
|
|
|
|
|
* @connections: a (optionally pre-sorted) list of connections from which to
|
|
|
|
|
* find a matching connection to @original based on "inferrable" properties
|
|
|
|
|
* @original: the #NMConnection to find a match for from @connections
|
2014-02-25 17:56:06 -06:00
|
|
|
* @device_has_carrier: pass %TRUE if the device that generated @original has
|
|
|
|
|
* a carrier, %FALSE if not
|
2013-11-15 13:09:12 -06:00
|
|
|
* @match_filter_func: a function to check whether each connection from @connections
|
|
|
|
|
* should be considered for matching. This function should return %TRUE if the
|
|
|
|
|
* connection should be considered, %FALSE if the connection should be ignored
|
|
|
|
|
* @match_compat_data: data pointer passed to @match_filter_func
|
|
|
|
|
*
|
|
|
|
|
* Checks each connection from @connections until a matching connection is found
|
|
|
|
|
* considering only setting properties marked with %NM_SETTING_PARAM_INFERRABLE
|
|
|
|
|
* and checking a few other characteristics like IPv6 method. If the caller
|
|
|
|
|
* desires some priority order of the connections, @connections should be
|
|
|
|
|
* sorted before calling this function.
|
|
|
|
|
*
|
|
|
|
|
* Returns: the best #NMConnection matching @original, or %NULL if no connection
|
|
|
|
|
* matches well enough.
|
|
|
|
|
*/
|
|
|
|
|
NMConnection *
|
|
|
|
|
nm_utils_match_connection (GSList *connections,
|
|
|
|
|
NMConnection *original,
|
2014-02-25 17:56:06 -06:00
|
|
|
gboolean device_has_carrier,
|
2013-11-15 13:09:12 -06:00
|
|
|
NMUtilsMatchFilterFunc match_filter_func,
|
|
|
|
|
gpointer match_filter_data)
|
|
|
|
|
{
|
|
|
|
|
NMConnection *best_match = NULL;
|
|
|
|
|
GSList *iter;
|
|
|
|
|
|
|
|
|
|
for (iter = connections; iter; iter = iter->next) {
|
|
|
|
|
NMConnection *candidate = NM_CONNECTION (iter->data);
|
|
|
|
|
GHashTable *diffs = NULL;
|
|
|
|
|
|
|
|
|
|
if (match_filter_func) {
|
|
|
|
|
if (!match_filter_func (candidate, match_filter_data))
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!nm_connection_diff (original, candidate, NM_SETTING_COMPARE_FLAG_INFERRABLE, &diffs)) {
|
|
|
|
|
if (!best_match)
|
2014-02-25 17:56:06 -06:00
|
|
|
best_match = check_possible_match (original, candidate, diffs, device_has_carrier);
|
2014-05-20 13:19:41 -04:00
|
|
|
|
|
|
|
|
if (!best_match && nm_logging_enabled (LOGL_DEBUG, LOGD_CORE)) {
|
|
|
|
|
GString *diff_string;
|
|
|
|
|
GHashTableIter s_iter, p_iter;
|
|
|
|
|
gpointer setting_name, setting;
|
|
|
|
|
gpointer property_name, value;
|
|
|
|
|
|
|
|
|
|
diff_string = g_string_new (NULL);
|
|
|
|
|
g_hash_table_iter_init (&s_iter, diffs);
|
|
|
|
|
while (g_hash_table_iter_next (&s_iter, &setting_name, &setting)) {
|
|
|
|
|
g_hash_table_iter_init (&p_iter, setting);
|
|
|
|
|
while (g_hash_table_iter_next (&p_iter, &property_name, &value)) {
|
|
|
|
|
if (diff_string->len)
|
|
|
|
|
g_string_append (diff_string, ", ");
|
|
|
|
|
g_string_append_printf (diff_string, "%s.%s",
|
|
|
|
|
(char *) setting_name,
|
|
|
|
|
(char *) property_name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nm_log_dbg (LOGD_CORE, "Connection '%s' differs from candidate '%s' in %s",
|
|
|
|
|
nm_connection_get_id (original),
|
|
|
|
|
nm_connection_get_id (candidate),
|
|
|
|
|
diff_string->str);
|
|
|
|
|
g_string_free (diff_string, TRUE);
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-15 13:09:12 -06:00
|
|
|
g_hash_table_unref (diffs);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Exact match */
|
|
|
|
|
return candidate;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Best match (if any) */
|
|
|
|
|
return best_match;
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-21 13:07:06 +01:00
|
|
|
/* nm_utils_ascii_str_to_int64:
|
|
|
|
|
*
|
|
|
|
|
* A wrapper for g_ascii_strtoll, that checks whether the whole string
|
|
|
|
|
* can be successfully converted to a number and is within a given
|
2014-03-23 14:57:39 +01:00
|
|
|
* range. On any error, @fallback will be returned and %errno will be set
|
|
|
|
|
* to a non-zero value. On success, %errno will be set to zero, check %errno
|
|
|
|
|
* for errors. Any trailing or leading (ascii) white space is ignored and the
|
|
|
|
|
* functions is locale independent.
|
2014-01-21 13:07:06 +01:00
|
|
|
*
|
|
|
|
|
* The function is guaranteed to return a value between @min and @max
|
2014-03-23 14:57:39 +01:00
|
|
|
* (inclusive) or @fallback. Also, the parsing is rather strict, it does
|
2014-01-21 13:07:06 +01:00
|
|
|
* not allow for any unrecognized characters, except leading and trailing
|
|
|
|
|
* white space.
|
|
|
|
|
**/
|
|
|
|
|
gint64
|
|
|
|
|
nm_utils_ascii_str_to_int64 (const char *str, guint base, gint64 min, gint64 max, gint64 fallback)
|
|
|
|
|
{
|
|
|
|
|
gint64 v;
|
2014-03-18 11:42:31 +01:00
|
|
|
size_t len;
|
|
|
|
|
char buf[64], *s, *str_free = NULL;
|
2014-01-21 13:07:06 +01:00
|
|
|
|
|
|
|
|
if (str) {
|
2014-03-18 11:42:31 +01:00
|
|
|
while (g_ascii_isspace (str[0]))
|
2014-01-21 13:07:06 +01:00
|
|
|
str++;
|
|
|
|
|
}
|
|
|
|
|
if (!str || !str[0]) {
|
|
|
|
|
errno = EINVAL;
|
|
|
|
|
return fallback;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-18 11:42:31 +01:00
|
|
|
len = strlen (str);
|
|
|
|
|
if (g_ascii_isspace (str[--len])) {
|
|
|
|
|
/* backward search the first non-ws character.
|
|
|
|
|
* We already know that str[0] is non-ws. */
|
|
|
|
|
while (g_ascii_isspace (str[--len]))
|
|
|
|
|
;
|
2014-01-21 13:07:06 +01:00
|
|
|
|
2014-03-18 11:42:31 +01:00
|
|
|
/* str[len] is now the last non-ws character... */
|
|
|
|
|
len++;
|
2014-01-21 13:07:06 +01:00
|
|
|
|
2014-03-18 11:42:31 +01:00
|
|
|
if (len >= sizeof (buf))
|
|
|
|
|
s = str_free = g_malloc (len + 1);
|
|
|
|
|
else
|
|
|
|
|
s = buf;
|
2014-01-21 13:07:06 +01:00
|
|
|
|
2014-03-18 11:42:31 +01:00
|
|
|
memcpy (s, str, len);
|
|
|
|
|
s[len] = 0;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
g_assert (len > 0 && len < strlen (str) && len == strlen (s));
|
|
|
|
|
g_assert (!g_ascii_isspace (str[len-1]) && g_ascii_isspace (str[len]));
|
|
|
|
|
g_assert (strncmp (str, s, len) == 0);
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
str = s;
|
2014-01-21 13:07:06 +01:00
|
|
|
}
|
|
|
|
|
|
2014-03-18 11:42:31 +01:00
|
|
|
errno = 0;
|
|
|
|
|
v = g_ascii_strtoll (str, &s, base);
|
|
|
|
|
|
|
|
|
|
if (errno != 0)
|
|
|
|
|
v = fallback;
|
|
|
|
|
else if (s[0] != 0) {
|
|
|
|
|
errno = EINVAL;
|
|
|
|
|
v = fallback;
|
|
|
|
|
} else if (v > max || v < min) {
|
2014-01-21 13:07:06 +01:00
|
|
|
errno = ERANGE;
|
2014-03-18 11:42:31 +01:00
|
|
|
v = fallback;
|
2014-01-21 13:07:06 +01:00
|
|
|
}
|
|
|
|
|
|
2014-03-18 11:42:31 +01:00
|
|
|
if (G_UNLIKELY (str_free))
|
|
|
|
|
g_free (str_free);
|
2014-01-21 13:07:06 +01:00
|
|
|
return v;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-12-10 13:16:08 +01:00
|
|
|
static gint64 monotonic_timestamp_offset_sec;
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
monotonic_timestamp_get (struct timespec *tp)
|
|
|
|
|
{
|
|
|
|
|
static gboolean initialized = FALSE;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
err = clock_gettime (CLOCK_BOOTTIME, tp);
|
|
|
|
|
|
|
|
|
|
g_assert (err == 0); (void)err;
|
|
|
|
|
g_assert (tp->tv_nsec >= 0 && tp->tv_nsec < NM_UTILS_NS_PER_SECOND);
|
|
|
|
|
|
|
|
|
|
if (G_LIKELY (initialized))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* Calculate an offset for the time stamp.
|
|
|
|
|
*
|
|
|
|
|
* We always want positive values, because then we can initialize
|
|
|
|
|
* a timestamp with 0 and be sure, that it will be less then any
|
|
|
|
|
* value nm_utils_get_monotonic_timestamp_*() might return.
|
|
|
|
|
* For this to be true also for nm_utils_get_monotonic_timestamp_s() at
|
|
|
|
|
* early boot, we have to shift the timestamp to start counting at
|
|
|
|
|
* least from 1 second onward.
|
|
|
|
|
*
|
|
|
|
|
* Another advantage of shifting is, that this way we make use of the whole 31 bit
|
|
|
|
|
* range of signed int, before the time stamp for nm_utils_get_monotonic_timestamp_s()
|
|
|
|
|
* wraps (~68 years).
|
|
|
|
|
**/
|
|
|
|
|
monotonic_timestamp_offset_sec = (- ((gint64) tp->tv_sec)) + 1;
|
|
|
|
|
initialized = TRUE;
|
|
|
|
|
|
|
|
|
|
if (nm_logging_enabled (LOGL_DEBUG, LOGD_CORE)) {
|
|
|
|
|
time_t now = time (NULL);
|
|
|
|
|
struct tm tm;
|
|
|
|
|
char s[255];
|
|
|
|
|
|
|
|
|
|
strftime (s, sizeof (s), "%Y-%m-%d %H:%M:%S", localtime_r (&now, &tm));
|
2014-01-31 13:25:04 +01:00
|
|
|
nm_log_dbg (LOGD_CORE, "monotonic timestamp started counting 1.%09ld seconds ago with "
|
|
|
|
|
"an offset of %lld.0 seconds to CLOCK_BOOTTIME (local time is %s)",
|
|
|
|
|
tp->tv_nsec, (long long) -monotonic_timestamp_offset_sec, s);
|
2013-12-10 13:16:08 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-02 13:31:20 +02:00
|
|
|
/**
|
|
|
|
|
* nm_utils_get_monotonic_timestamp_ns:
|
|
|
|
|
*
|
|
|
|
|
* Returns: a monotonically increasing time stamp in nanoseconds,
|
|
|
|
|
* starting at an unspecified offset. See clock_gettime(), %CLOCK_BOOTTIME.
|
|
|
|
|
*
|
|
|
|
|
* The returned value will start counting at an undefined point
|
|
|
|
|
* in the past and will always be positive.
|
|
|
|
|
*
|
|
|
|
|
* All the nm_utils_get_monotonic_timestamp_*s functions return the same
|
|
|
|
|
* timestamp but in different scales (nsec, usec, msec, sec).
|
|
|
|
|
**/
|
|
|
|
|
gint64
|
|
|
|
|
nm_utils_get_monotonic_timestamp_ns (void)
|
|
|
|
|
{
|
|
|
|
|
struct timespec tp;
|
|
|
|
|
|
|
|
|
|
monotonic_timestamp_get (&tp);
|
|
|
|
|
|
|
|
|
|
/* Although the result will always be positive, we return a signed
|
|
|
|
|
* integer, which makes it easier to calculate time differences (when
|
|
|
|
|
* you want to subtract signed values).
|
|
|
|
|
**/
|
|
|
|
|
return (((gint64) tp.tv_sec) + monotonic_timestamp_offset_sec) * NM_UTILS_NS_PER_SECOND +
|
|
|
|
|
tp.tv_nsec;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-11 11:17:40 +01:00
|
|
|
/**
|
|
|
|
|
* nm_utils_get_monotonic_timestamp_us:
|
|
|
|
|
*
|
|
|
|
|
* Returns: a monotonically increasing time stamp in microseconds,
|
|
|
|
|
* starting at an unspecified offset. See clock_gettime(), %CLOCK_BOOTTIME.
|
|
|
|
|
*
|
|
|
|
|
* The returned value will start counting at an undefined point
|
|
|
|
|
* in the past and will always be positive.
|
2014-04-17 22:10:56 +02:00
|
|
|
*
|
|
|
|
|
* All the nm_utils_get_monotonic_timestamp_*s functions return the same
|
|
|
|
|
* timestamp but in different scales (nsec, usec, msec, sec).
|
2014-02-11 11:17:40 +01:00
|
|
|
**/
|
|
|
|
|
gint64
|
|
|
|
|
nm_utils_get_monotonic_timestamp_us (void)
|
|
|
|
|
{
|
|
|
|
|
struct timespec tp;
|
|
|
|
|
|
|
|
|
|
monotonic_timestamp_get (&tp);
|
|
|
|
|
|
|
|
|
|
/* Although the result will always be positive, we return a signed
|
|
|
|
|
* integer, which makes it easier to calculate time differences (when
|
|
|
|
|
* you want to subtract signed values).
|
|
|
|
|
**/
|
|
|
|
|
return (((gint64) tp.tv_sec) + monotonic_timestamp_offset_sec) * ((gint64) G_USEC_PER_SEC) +
|
|
|
|
|
(tp.tv_nsec / (NM_UTILS_NS_PER_SECOND/G_USEC_PER_SEC));
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-10 13:16:08 +01:00
|
|
|
/**
|
|
|
|
|
* nm_utils_get_monotonic_timestamp_ms:
|
|
|
|
|
*
|
|
|
|
|
* Returns: a monotonically increasing time stamp in milliseconds,
|
|
|
|
|
* starting at an unspecified offset. See clock_gettime(), %CLOCK_BOOTTIME.
|
|
|
|
|
*
|
|
|
|
|
* The returned value will start counting at an undefined point
|
|
|
|
|
* in the past and will always be positive.
|
2014-04-17 22:10:56 +02:00
|
|
|
*
|
|
|
|
|
* All the nm_utils_get_monotonic_timestamp_*s functions return the same
|
|
|
|
|
* timestamp but in different scales (nsec, usec, msec, sec).
|
2013-12-10 13:16:08 +01:00
|
|
|
**/
|
|
|
|
|
gint64
|
|
|
|
|
nm_utils_get_monotonic_timestamp_ms (void)
|
|
|
|
|
{
|
|
|
|
|
struct timespec tp;
|
|
|
|
|
|
|
|
|
|
monotonic_timestamp_get (&tp);
|
|
|
|
|
|
|
|
|
|
/* Although the result will always be positive, we return a signed
|
|
|
|
|
* integer, which makes it easier to calculate time differences (when
|
|
|
|
|
* you want to subtract signed values).
|
|
|
|
|
**/
|
|
|
|
|
return (((gint64) tp.tv_sec) + monotonic_timestamp_offset_sec) * ((gint64) 1000) +
|
|
|
|
|
(tp.tv_nsec / (NM_UTILS_NS_PER_SECOND/1000));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* nm_utils_get_monotonic_timestamp_s:
|
|
|
|
|
*
|
|
|
|
|
* Returns: nm_utils_get_monotonic_timestamp_ms() in seconds (throwing
|
|
|
|
|
* away sub second parts). The returned value will always be positive.
|
|
|
|
|
*
|
|
|
|
|
* This value wraps after roughly 68 years which should be fine for any
|
|
|
|
|
* practical purpose.
|
2014-04-17 22:10:56 +02:00
|
|
|
*
|
|
|
|
|
* All the nm_utils_get_monotonic_timestamp_*s functions return the same
|
|
|
|
|
* timestamp but in different scales (nsec, usec, msec, sec).
|
2013-12-10 13:16:08 +01:00
|
|
|
**/
|
|
|
|
|
gint32
|
|
|
|
|
nm_utils_get_monotonic_timestamp_s (void)
|
|
|
|
|
{
|
|
|
|
|
struct timespec tp;
|
|
|
|
|
|
|
|
|
|
monotonic_timestamp_get (&tp);
|
|
|
|
|
return (((gint64) tp.tv_sec) + monotonic_timestamp_offset_sec);
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-21 13:07:06 +01:00
|
|
|
|
2014-03-04 17:01:10 -05:00
|
|
|
/**
|
|
|
|
|
* nm_utils_ip6_property_path:
|
|
|
|
|
* @ifname: an interface name
|
|
|
|
|
* @property: a property name
|
|
|
|
|
*
|
|
|
|
|
* Returns the path to IPv6 property @property on @ifname. Note that
|
|
|
|
|
* this uses a static buffer.
|
|
|
|
|
*/
|
|
|
|
|
const char *
|
|
|
|
|
nm_utils_ip6_property_path (const char *ifname, const char *property)
|
|
|
|
|
{
|
|
|
|
|
#define IPV6_PROPERTY_DIR "/proc/sys/net/ipv6/conf/"
|
|
|
|
|
static char path[sizeof (IPV6_PROPERTY_DIR) + IFNAMSIZ + 32];
|
|
|
|
|
int len;
|
|
|
|
|
|
2014-03-12 12:49:34 +01:00
|
|
|
ifname = ASSERT_VALID_PATH_COMPONENT (ifname);
|
|
|
|
|
property = ASSERT_VALID_PATH_COMPONENT (property);
|
|
|
|
|
|
2014-03-04 17:01:10 -05:00
|
|
|
len = g_snprintf (path, sizeof (path), IPV6_PROPERTY_DIR "%s/%s",
|
|
|
|
|
ifname, property);
|
|
|
|
|
g_assert (len < sizeof (path) - 1);
|
|
|
|
|
|
|
|
|
|
return path;
|
|
|
|
|
}
|
2014-03-12 12:06:20 +01:00
|
|
|
|
|
|
|
|
const char *
|
|
|
|
|
ASSERT_VALID_PATH_COMPONENT (const char *name)
|
|
|
|
|
{
|
|
|
|
|
const char *n;
|
|
|
|
|
|
|
|
|
|
if (name == NULL || name[0] == '\0')
|
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
|
|
if (name[0] == '.') {
|
|
|
|
|
if (name[1] == '\0')
|
|
|
|
|
goto fail;
|
|
|
|
|
if (name[1] == '.' && name[2] == '\0')
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
n = name;
|
|
|
|
|
do {
|
|
|
|
|
if (*n == '/')
|
|
|
|
|
goto fail;
|
|
|
|
|
} while (*(++n) != '\0');
|
|
|
|
|
|
|
|
|
|
return name;
|
|
|
|
|
fail:
|
|
|
|
|
if (name)
|
|
|
|
|
nm_log_err (LOGD_CORE, "Failed asserting path component: NULL");
|
|
|
|
|
else
|
|
|
|
|
nm_log_err (LOGD_CORE, "Failed asserting path component: \"%s\"", name);
|
2014-04-15 16:54:38 +02:00
|
|
|
g_error ("FATAL: Failed asserting path component: %s", name ? name : "(null)");
|
2014-07-26 22:48:36 +02:00
|
|
|
g_assert_not_reached ();
|
2014-03-12 12:06:20 +01:00
|
|
|
}
|
|
|
|
|
|
2014-06-30 13:55:04 +02:00
|
|
|
gboolean
|
|
|
|
|
nm_utils_is_specific_hostname (const char *name)
|
|
|
|
|
{
|
|
|
|
|
if (!name)
|
|
|
|
|
return FALSE;
|
|
|
|
|
if ( strcmp (name, "(none)")
|
|
|
|
|
&& strcmp (name, "localhost")
|
|
|
|
|
&& strcmp (name, "localhost6")
|
|
|
|
|
&& strcmp (name, "localhost.localdomain")
|
|
|
|
|
&& strcmp (name, "localhost6.localdomain6"))
|
|
|
|
|
return TRUE;
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-10 15:29:45 -05:00
|
|
|
/******************************************************************/
|
|
|
|
|
|
|
|
|
|
/* Returns the "u" (universal/local) bit value for a Modified EUI-64 */
|
|
|
|
|
static gboolean
|
|
|
|
|
get_gre_eui64_u_bit (guint32 addr)
|
|
|
|
|
{
|
|
|
|
|
static const struct {
|
|
|
|
|
guint32 mask;
|
|
|
|
|
guint32 result;
|
|
|
|
|
} items[] = {
|
|
|
|
|
{ 0xff000000 }, { 0x7f000000 }, /* IPv4 loopback */
|
|
|
|
|
{ 0xf0000000 }, { 0xe0000000 }, /* IPv4 multicast */
|
|
|
|
|
{ 0xffffff00 }, { 0xe0000000 }, /* IPv4 local multicast */
|
|
|
|
|
{ 0xffffffff }, { INADDR_BROADCAST }, /* limited broadcast */
|
|
|
|
|
{ 0xff000000 }, { 0x00000000 }, /* zero net */
|
|
|
|
|
{ 0xff000000 }, { 0x0a000000 }, /* private 10 (RFC3330) */
|
|
|
|
|
{ 0xfff00000 }, { 0xac100000 }, /* private 172 */
|
|
|
|
|
{ 0xffff0000 }, { 0xc0a80000 }, /* private 192 */
|
|
|
|
|
{ 0xffff0000 }, { 0xa9fe0000 }, /* IPv4 link-local */
|
|
|
|
|
{ 0xffffff00 }, { 0xc0586300 }, /* anycast 6-to-4 */
|
|
|
|
|
{ 0xffffff00 }, { 0xc0000200 }, /* test 192 */
|
|
|
|
|
{ 0xfffe0000 }, { 0xc6120000 }, /* test 198 */
|
|
|
|
|
};
|
|
|
|
|
guint i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (items); i++) {
|
|
|
|
|
if ((addr & htonl (items[i].mask)) == htonl (items[i].result))
|
|
|
|
|
return 0x00; /* "local" scope */
|
|
|
|
|
}
|
|
|
|
|
return 0x02; /* "universal" scope */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* nm_utils_get_ipv6_interface_identifier:
|
|
|
|
|
* @link_type: the hardware link type
|
|
|
|
|
* @hwaddr: the hardware address of the interface
|
|
|
|
|
* @hwaddr_len: the length (in bytes) of @hwaddr
|
|
|
|
|
* @out_iid: on success, filled with the interface identifier; on failure
|
|
|
|
|
* zeroed out
|
|
|
|
|
*
|
|
|
|
|
* Constructs an interface identifier in "Modified EUI-64" format which is
|
|
|
|
|
* suitable for constructing IPv6 addresses. Note that the identifier is
|
|
|
|
|
* not obscured in any way (eg, RFC3041).
|
|
|
|
|
*
|
|
|
|
|
* Returns: %TRUE if the interface identifier could be constructed, %FALSE if
|
|
|
|
|
* if could not be constructed.
|
|
|
|
|
*/
|
|
|
|
|
gboolean
|
|
|
|
|
nm_utils_get_ipv6_interface_identifier (NMLinkType link_type,
|
|
|
|
|
const guint8 *hwaddr,
|
|
|
|
|
guint hwaddr_len,
|
|
|
|
|
NMUtilsIPv6IfaceId *out_iid)
|
|
|
|
|
{
|
|
|
|
|
guint32 addr;
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (hwaddr != NULL, FALSE);
|
|
|
|
|
g_return_val_if_fail (hwaddr_len > 0, FALSE);
|
|
|
|
|
g_return_val_if_fail (out_iid != NULL, FALSE);
|
|
|
|
|
|
|
|
|
|
out_iid->id = 0;
|
|
|
|
|
|
|
|
|
|
switch (link_type) {
|
|
|
|
|
case NM_LINK_TYPE_INFINIBAND:
|
|
|
|
|
/* Use the port GUID per http://tools.ietf.org/html/rfc4391#section-8,
|
|
|
|
|
* making sure to set the 'u' bit to 1. The GUID is the lower 64 bits
|
|
|
|
|
* of the IPoIB interface's hardware address.
|
|
|
|
|
*/
|
|
|
|
|
g_return_val_if_fail (hwaddr_len == INFINIBAND_ALEN, FALSE);
|
|
|
|
|
memcpy (out_iid->id_u8, hwaddr + INFINIBAND_ALEN - 8, 8);
|
|
|
|
|
out_iid->id_u8[0] |= 0x02;
|
|
|
|
|
return TRUE;
|
|
|
|
|
case NM_LINK_TYPE_GRE:
|
|
|
|
|
case NM_LINK_TYPE_GRETAP:
|
|
|
|
|
/* Hardware address is the network-endian IPv4 address */
|
|
|
|
|
g_return_val_if_fail (hwaddr_len == 4, FALSE);
|
|
|
|
|
addr = * (guint32 *) hwaddr;
|
|
|
|
|
out_iid->id_u8[0] = get_gre_eui64_u_bit (addr);
|
|
|
|
|
out_iid->id_u8[1] = 0x00;
|
|
|
|
|
out_iid->id_u8[2] = 0x5E;
|
|
|
|
|
out_iid->id_u8[3] = 0xFE;
|
|
|
|
|
memcpy (out_iid->id_u8 + 4, &addr, 4);
|
|
|
|
|
return TRUE;
|
|
|
|
|
default:
|
|
|
|
|
if (hwaddr_len == ETH_ALEN) {
|
|
|
|
|
/* Translate 48-bit MAC address to a 64-bit Modified EUI-64. See
|
|
|
|
|
* http://tools.ietf.org/html/rfc4291#appendix-A
|
|
|
|
|
*/
|
|
|
|
|
out_iid->id_u8[0] = hwaddr[0] ^ 0x02;
|
|
|
|
|
out_iid->id_u8[1] = hwaddr[1];
|
|
|
|
|
out_iid->id_u8[2] = hwaddr[2];
|
|
|
|
|
out_iid->id_u8[3] = 0xff;
|
|
|
|
|
out_iid->id_u8[4] = 0xfe;
|
|
|
|
|
out_iid->id_u8[5] = hwaddr[3];
|
|
|
|
|
out_iid->id_u8[6] = hwaddr[4];
|
|
|
|
|
out_iid->id_u8[7] = hwaddr[5];
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
nm_utils_ipv6_addr_set_interface_identfier (struct in6_addr *addr,
|
|
|
|
|
const NMUtilsIPv6IfaceId iid)
|
|
|
|
|
{
|
|
|
|
|
memcpy (addr->s6_addr + 8, &iid.id_u8, 8);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
nm_utils_ipv6_interface_identfier_get_from_addr (NMUtilsIPv6IfaceId *iid,
|
|
|
|
|
const struct in6_addr *addr)
|
|
|
|
|
{
|
|
|
|
|
memcpy (iid, addr->s6_addr + 8, 8);
|
|
|
|
|
}
|
|
|
|
|
|