NetworkManager/src/NetworkManagerUtils.c
Dan Williams f898dbf1a9 dispatcher: enhance dispatcher script environment (bgo #648382)
For VPN connections, the interface name would be that of the VPN's
IP interface, but the script environment would be the that of the
VPN's parent device.  Enhance the environment by adding any VPN
specific details as additional environment variables prefixed by
"VPN_".  Leave the existing environment setup intact for backwards
compatiblity.

Additionally, the dispatcher never got updated for IPv6 support,
so push IPv6 configuration and DHCPv6 configuration into the
environment too.

Even better, push everything the dispatcher needs to it instead
of making the dispatcher make D-Bus requests back to NM, which
sometimes fails if NM has already torn down the device or the
connection which the device was using.

And add some testcases to ensure that we don't break backwards compat,
the testcases here were grabbed from a 0.8.4 machine with a hacked up
dispatcher to dump everything it was given from NM.
2011-05-02 22:54:28 -05:00

944 lines
26 KiB
C

/* -*- 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) 2004 - 2011 Red Hat, Inc.
* Copyright (C) 2005 - 2008 Novell, Inc.
*/
#include <glib.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include "NetworkManagerUtils.h"
#include "nm-utils.h"
#include "nm-logging.h"
#include "nm-device.h"
#include "nm-device-wifi.h"
#include "nm-device-ethernet.h"
#include "nm-dbus-manager.h"
#include "nm-dispatcher-action.h"
#include "nm-dbus-glib-types.h"
#include "nm-setting-connection.h"
#include "nm-setting-ip4-config.h"
#include "nm-setting-ip6-config.h"
#include <netlink/addr.h>
#include <netinet/in.h>
/*
* nm_ethernet_address_is_valid
*
* Compares an Ethernet address against known invalid addresses.
*
*/
gboolean
nm_ethernet_address_is_valid (const struct ether_addr *test_addr)
{
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 */
g_return_val_if_fail (test_addr != NULL, FALSE);
/* Compare the AP address the card has with invalid ethernet MAC addresses. */
if (!memcmp (test_addr->ether_addr_octet, &invalid_addr1, ETH_ALEN))
return FALSE;
if (!memcmp (test_addr->ether_addr_octet, &invalid_addr2, ETH_ALEN))
return FALSE;
if (!memcmp (test_addr->ether_addr_octet, &invalid_addr3, ETH_ALEN))
return FALSE;
if (!memcmp (test_addr->ether_addr_octet, &invalid_addr4, ETH_ALEN))
return FALSE;
if (test_addr->ether_addr_octet[0] & 1) /* Multicast addresses */
return FALSE;
return TRUE;
}
int
nm_spawn_process (const char *args)
{
gint num_args;
char **argv = NULL;
int status = -1;
GError *error = NULL;
g_return_val_if_fail (args != NULL, -1);
if (!g_shell_parse_argv (args, &num_args, &argv, &error)) {
nm_log_warn (LOGD_CORE, "could not parse arguments for '%s': %s", args, error->message);
g_error_free (error);
return -1;
}
if (!g_spawn_sync ("/", argv, NULL, 0, NULL, NULL, NULL, NULL, &status, &error)) {
nm_log_warn (LOGD_CORE, "could not spawn process '%s': %s", args, error->message);
g_error_free (error);
}
g_strfreev (argv);
return status;
}
/*
* nm_utils_ip4_netmask_to_prefix
*
* Figure out the network prefix from a netmask. Netmask
* MUST be in network byte order.
*
*/
guint32
nm_utils_ip4_netmask_to_prefix (guint32 netmask)
{
guchar *p, *end;
guint32 prefix = 0;
p = (guchar *) &netmask;
end = p + sizeof (guint32);
while ((*p == 0xFF) && p < end) {
prefix += 8;
p++;
}
if (p < end) {
guchar v = *p;
while (v) {
prefix++;
v <<= 1;
}
}
return prefix;
}
/*
* nm_utils_ip4_prefix_to_netmask
*
* Figure out the netmask from a prefix.
*
*/
guint32
nm_utils_ip4_prefix_to_netmask (guint32 prefix)
{
guint32 msk = 0x80000000;
guint32 netmask = 0;
while (prefix > 0) {
netmask |= msk;
msk >>= 1;
prefix--;
}
return (guint32) htonl (netmask);
}
char *
nm_ether_ntop (const struct ether_addr *mac)
{
/* we like leading zeros and all-caps, instead
* of what glibc's ether_ntop() gives us
*/
return g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X",
mac->ether_addr_octet[0], mac->ether_addr_octet[1],
mac->ether_addr_octet[2], mac->ether_addr_octet[3],
mac->ether_addr_octet[4], mac->ether_addr_octet[5]);
}
void
nm_utils_merge_ip4_config (NMIP4Config *ip4_config, NMSettingIP4Config *setting)
{
int i, j;
if (!setting)
return; /* Defaults are just fine */
if (nm_setting_ip4_config_get_ignore_auto_dns (setting)) {
nm_ip4_config_reset_nameservers (ip4_config);
nm_ip4_config_reset_domains (ip4_config);
nm_ip4_config_reset_searches (ip4_config);
}
if (nm_setting_ip4_config_get_ignore_auto_routes (setting))
nm_ip4_config_reset_routes (ip4_config);
for (i = 0; i < nm_setting_ip4_config_get_num_dns (setting); i++) {
guint32 ns;
gboolean found = FALSE;
/* Avoid dupes */
ns = nm_setting_ip4_config_get_dns (setting, i);
for (j = 0; j < nm_ip4_config_get_num_nameservers (ip4_config); j++) {
if (nm_ip4_config_get_nameserver (ip4_config, j) == ns) {
found = TRUE;
break;
}
}
if (!found)
nm_ip4_config_add_nameserver (ip4_config, ns);
}
/* DNS search domains */
for (i = 0; i < nm_setting_ip4_config_get_num_dns_searches (setting); i++) {
const char *search = nm_setting_ip4_config_get_dns_search (setting, i);
gboolean found = FALSE;
/* Avoid dupes */
for (j = 0; j < nm_ip4_config_get_num_searches (ip4_config); j++) {
if (!strcmp (search, nm_ip4_config_get_search (ip4_config, j))) {
found = TRUE;
break;
}
}
if (!found)
nm_ip4_config_add_search (ip4_config, search);
}
/* IPv4 addresses */
for (i = 0; i < nm_setting_ip4_config_get_num_addresses (setting); i++) {
NMIP4Address *setting_addr = nm_setting_ip4_config_get_address (setting, i);
guint32 num;
num = nm_ip4_config_get_num_addresses (ip4_config);
for (j = 0; j < num; j++) {
NMIP4Address *cfg_addr = nm_ip4_config_get_address (ip4_config, j);
/* Dupe, override with user-specified address */
if (nm_ip4_address_get_address (cfg_addr) == nm_ip4_address_get_address (setting_addr)) {
nm_ip4_config_replace_address (ip4_config, j, setting_addr);
break;
}
}
if (j == num)
nm_ip4_config_add_address (ip4_config, setting_addr);
}
/* IPv4 routes */
for (i = 0; i < nm_setting_ip4_config_get_num_routes (setting); i++) {
NMIP4Route *setting_route = nm_setting_ip4_config_get_route (setting, i);
guint32 num;
num = nm_ip4_config_get_num_routes (ip4_config);
for (j = 0; j < num; j++) {
NMIP4Route *cfg_route = nm_ip4_config_get_route (ip4_config, j);
/* Dupe, override with user-specified route */
if ( (nm_ip4_route_get_dest (cfg_route) == nm_ip4_route_get_dest (setting_route))
&& (nm_ip4_route_get_prefix (cfg_route) == nm_ip4_route_get_prefix (setting_route))
&& (nm_ip4_route_get_next_hop (cfg_route) == nm_ip4_route_get_next_hop (setting_route))) {
nm_ip4_config_replace_route (ip4_config, j, setting_route);
break;
}
}
if (j == num)
nm_ip4_config_add_route (ip4_config, setting_route);
}
if (nm_setting_ip4_config_get_never_default (setting))
nm_ip4_config_set_never_default (ip4_config, TRUE);
}
static inline gboolean
ip6_addresses_equal (const struct in6_addr *a, const struct in6_addr *b)
{
return memcmp (a, b, sizeof (struct in6_addr)) == 0;
}
/* This is exactly identical to nm_utils_merge_ip4_config, with s/4/6/,
* except that we can't compare addresses with ==.
*/
void
nm_utils_merge_ip6_config (NMIP6Config *ip6_config, NMSettingIP6Config *setting)
{
int i, j;
if (!setting)
return; /* Defaults are just fine */
if (nm_setting_ip6_config_get_ignore_auto_dns (setting)) {
nm_ip6_config_reset_nameservers (ip6_config);
nm_ip6_config_reset_domains (ip6_config);
nm_ip6_config_reset_searches (ip6_config);
}
if (nm_setting_ip6_config_get_ignore_auto_routes (setting))
nm_ip6_config_reset_routes (ip6_config);
for (i = 0; i < nm_setting_ip6_config_get_num_dns (setting); i++) {
const struct in6_addr *ns;
gboolean found = FALSE;
/* Avoid dupes */
ns = nm_setting_ip6_config_get_dns (setting, i);
for (j = 0; j < nm_ip6_config_get_num_nameservers (ip6_config); j++) {
if (ip6_addresses_equal (nm_ip6_config_get_nameserver (ip6_config, j), ns)) {
found = TRUE;
break;
}
}
if (!found)
nm_ip6_config_add_nameserver (ip6_config, ns);
}
/* DNS search domains */
for (i = 0; i < nm_setting_ip6_config_get_num_dns_searches (setting); i++) {
const char *search = nm_setting_ip6_config_get_dns_search (setting, i);
gboolean found = FALSE;
/* Avoid dupes */
for (j = 0; j < nm_ip6_config_get_num_searches (ip6_config); j++) {
if (!strcmp (search, nm_ip6_config_get_search (ip6_config, j))) {
found = TRUE;
break;
}
}
if (!found)
nm_ip6_config_add_search (ip6_config, search);
}
/* IPv6 addresses */
for (i = 0; i < nm_setting_ip6_config_get_num_addresses (setting); i++) {
NMIP6Address *setting_addr = nm_setting_ip6_config_get_address (setting, i);
guint32 num;
num = nm_ip6_config_get_num_addresses (ip6_config);
for (j = 0; j < num; j++) {
NMIP6Address *cfg_addr = nm_ip6_config_get_address (ip6_config, j);
/* Dupe, override with user-specified address */
if (ip6_addresses_equal (nm_ip6_address_get_address (cfg_addr), nm_ip6_address_get_address (setting_addr))) {
nm_ip6_config_replace_address (ip6_config, j, setting_addr);
break;
}
}
if (j == num)
nm_ip6_config_add_address (ip6_config, setting_addr);
}
/* IPv6 routes */
for (i = 0; i < nm_setting_ip6_config_get_num_routes (setting); i++) {
NMIP6Route *setting_route = nm_setting_ip6_config_get_route (setting, i);
guint32 num;
num = nm_ip6_config_get_num_routes (ip6_config);
for (j = 0; j < num; j++) {
NMIP6Route *cfg_route = nm_ip6_config_get_route (ip6_config, j);
/* Dupe, override with user-specified route */
if ( ip6_addresses_equal (nm_ip6_route_get_dest (cfg_route), nm_ip6_route_get_dest (setting_route))
&& (nm_ip6_route_get_prefix (cfg_route) == nm_ip6_route_get_prefix (setting_route))
&& ip6_addresses_equal (nm_ip6_route_get_next_hop (cfg_route), nm_ip6_route_get_next_hop (setting_route))) {
nm_ip6_config_replace_route (ip6_config, j, setting_route);
break;
}
}
if (j == num)
nm_ip6_config_add_route (ip6_config, setting_route);
}
if (nm_setting_ip6_config_get_never_default (setting))
nm_ip6_config_set_never_default (ip6_config, TRUE);
}
static void
dump_object_to_props (GObject *object, GHashTable *hash)
{
GParamSpec **pspecs;
guint len = 0, i;
pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (object), &len);
for (i = 0; i < len; i++) {
value_hash_add_object_property (hash,
pspecs[i]->name,
object,
pspecs[i]->name,
pspecs[i]->value_type);
}
g_free (pspecs);
}
static void
fill_device_props (NMDevice *device,
GHashTable *dev_hash,
GHashTable *ip4_hash,
GHashTable *ip6_hash,
GHashTable *dhcp4_hash,
GHashTable *dhcp6_hash)
{
NMIP4Config *ip4_config;
NMIP6Config *ip6_config;
NMDHCP4Config *dhcp4_config;
NMDHCP6Config *dhcp6_config;
/* If the action is for a VPN, send the VPN's IP interface instead of the device's */
value_hash_add_str (dev_hash, NMD_DEVICE_PROPS_IP_INTERFACE, nm_device_get_ip_iface (device));
value_hash_add_str (dev_hash, NMD_DEVICE_PROPS_INTERFACE, nm_device_get_iface (device));
value_hash_add_uint (dev_hash, NMD_DEVICE_PROPS_TYPE, nm_device_get_device_type (device));
value_hash_add_uint (dev_hash, NMD_DEVICE_PROPS_STATE, nm_device_get_state (device));
value_hash_add_object_path (dev_hash, NMD_DEVICE_PROPS_PATH, nm_device_get_path (device));
ip4_config = nm_device_get_ip4_config (device);
if (ip4_config)
dump_object_to_props (G_OBJECT (ip4_config), ip4_hash);
ip6_config = nm_device_get_ip6_config (device);
if (ip6_config)
dump_object_to_props (G_OBJECT (ip6_config), ip6_hash);
dhcp4_config = nm_device_get_dhcp4_config (device);
if (dhcp4_config)
dump_object_to_props (G_OBJECT (dhcp4_config), dhcp4_hash);
dhcp6_config = nm_device_get_dhcp6_config (device);
if (dhcp6_config)
dump_object_to_props (G_OBJECT (dhcp6_config), dhcp6_hash);
}
static void
fill_vpn_props (NMIP4Config *ip4_config,
NMIP6Config *ip6_config,
GHashTable *ip4_hash,
GHashTable *ip6_hash)
{
if (ip4_config)
dump_object_to_props (G_OBJECT (ip4_config), ip4_hash);
if (ip6_config)
dump_object_to_props (G_OBJECT (ip6_config), ip6_hash);
}
static void
dispatcher_done_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
{
dbus_g_proxy_end_call (proxy, call, NULL, G_TYPE_INVALID);
g_object_unref (proxy);
}
void
nm_utils_call_dispatcher (const char *action,
NMConnection *connection,
NMDevice *device,
const char *vpn_iface,
NMIP4Config *vpn_ip4_config,
NMIP6Config *vpn_ip6_config)
{
NMDBusManager *dbus_mgr;
DBusGProxy *proxy;
DBusGConnection *g_connection;
GHashTable *connection_hash;
GHashTable *connection_props;
GHashTable *device_props;
GHashTable *device_ip4_props;
GHashTable *device_ip6_props;
GHashTable *device_dhcp4_props;
GHashTable *device_dhcp6_props;
GHashTable *vpn_ip4_props;
GHashTable *vpn_ip6_props;
g_return_if_fail (action != NULL);
/* All actions except 'hostname' require a device */
if (strcmp (action, "hostname") != 0)
g_return_if_fail (NM_IS_DEVICE (device));
/* VPN actions require at least an IPv4 config (for now) */
if (strcmp (action, "vpn-up") == 0)
g_return_if_fail (vpn_ip4_config != NULL);
dbus_mgr = nm_dbus_manager_get ();
g_connection = nm_dbus_manager_get_connection (dbus_mgr);
proxy = dbus_g_proxy_new_for_name (g_connection,
NM_DISPATCHER_DBUS_SERVICE,
NM_DISPATCHER_DBUS_PATH,
NM_DISPATCHER_DBUS_IFACE);
if (!proxy) {
nm_log_err (LOGD_CORE, "could not get dispatcher proxy!");
g_object_unref (dbus_mgr);
return;
}
if (connection) {
connection_hash = nm_connection_to_hash (connection, NM_SETTING_HASH_FLAG_NO_SECRETS);
connection_props = value_hash_create ();
/* path */
value_hash_add_object_path (connection_props,
NMD_CONNECTION_PROPS_PATH,
nm_connection_get_path (connection));
} else {
connection_hash = value_hash_create ();
connection_props = value_hash_create ();
}
device_props = value_hash_create ();
device_ip4_props = value_hash_create ();
device_ip6_props = value_hash_create ();
device_dhcp4_props = value_hash_create ();
device_dhcp6_props = value_hash_create ();
vpn_ip4_props = value_hash_create ();
vpn_ip6_props = value_hash_create ();
/* hostname actions only send the hostname */
if (strcmp (action, "hostname") != 0) {
fill_device_props (device,
device_props,
device_ip4_props,
device_ip6_props,
device_dhcp4_props,
device_dhcp6_props);
if (vpn_iface)
fill_vpn_props (vpn_ip4_config, NULL, vpn_ip4_props, vpn_ip6_props);
}
/* Do a non-blocking call, but wait for the reply, because dbus-glib
* sometimes needs time to complete internal housekeeping. If we use
* dbus_g_proxy_call_no_reply(), that housekeeping (specifically the
* GetNameOwner response) doesn't complete and we run into an assert
* on unreffing the proxy.
*/
dbus_g_proxy_begin_call_with_timeout (proxy, "Action",
dispatcher_done_cb,
dbus_mgr, /* automatically unref the dbus mgr when call is done */
g_object_unref,
5000,
G_TYPE_STRING, action,
DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, connection_hash,
DBUS_TYPE_G_MAP_OF_VARIANT, connection_props,
DBUS_TYPE_G_MAP_OF_VARIANT, device_props,
DBUS_TYPE_G_MAP_OF_VARIANT, device_ip4_props,
DBUS_TYPE_G_MAP_OF_VARIANT, device_ip6_props,
DBUS_TYPE_G_MAP_OF_VARIANT, device_dhcp4_props,
DBUS_TYPE_G_MAP_OF_VARIANT, device_dhcp6_props,
G_TYPE_STRING, vpn_iface ? vpn_iface : "",
DBUS_TYPE_G_MAP_OF_VARIANT, vpn_ip4_props,
DBUS_TYPE_G_MAP_OF_VARIANT, vpn_ip6_props,
G_TYPE_INVALID);
g_hash_table_destroy (connection_hash);
g_hash_table_destroy (connection_props);
g_hash_table_destroy (device_props);
g_hash_table_destroy (device_ip4_props);
g_hash_table_destroy (device_ip6_props);
g_hash_table_destroy (device_dhcp4_props);
g_hash_table_destroy (device_dhcp6_props);
g_hash_table_destroy (vpn_ip4_props);
g_hash_table_destroy (vpn_ip6_props);
}
gboolean
nm_match_spec_hwaddr (const GSList *specs, const char *hwaddr)
{
const GSList *iter;
char *hwaddr_match, *p;
g_return_val_if_fail (hwaddr != NULL, FALSE);
p = hwaddr_match = g_strdup_printf ("mac:%s", hwaddr);
while (*p) {
*p = g_ascii_tolower (*p);
p++;
}
for (iter = specs; iter; iter = g_slist_next (iter)) {
if (!strcmp ((const char *) iter->data, hwaddr_match)) {
g_free (hwaddr_match);
return TRUE;
}
}
g_free (hwaddr_match);
return FALSE;
}
#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 */
if (!isxdigit (subchannels[0]))
return FALSE;
/* Get the first channel */
while (*p && (*p != ',')) {
if (!isxdigit (*p) && (*p != '.'))
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;
}
#define SUBCHAN_TAG "s390-subchannels:"
gboolean
nm_match_spec_s390_subchannels (const GSList *specs, const char *subchannels)
{
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;
}
/*********************************/
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);
}
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);
}
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);
}
gboolean
nm_utils_do_sysctl (const char *path, const char *value)
{
int fd, len, nwrote, total;
fd = open (path, O_WRONLY | O_TRUNC);
if (fd == -1)
return FALSE;
len = strlen (value);
total = 0;
do {
nwrote = write (fd, value + total, len - total);
if (nwrote == -1) {
if (errno == EINTR)
continue;
close (fd);
return FALSE;
}
total += nwrote;
} while (total < len);
close (fd);
return TRUE;
}
gboolean
nm_utils_get_proc_sys_net_value (const char *path,
const char *iface,
guint32 *out_value)
{
GError *error = NULL;
char *contents = NULL;
gboolean success = FALSE;
long int tmp;
if (!g_file_get_contents (path, &contents, NULL, &error)) {
nm_log_dbg (LOGD_DEVICE, "(%s): error reading %s: (%d) %s",
iface, path,
error ? error->code : -1,
error && error->message ? error->message : "(unknown)");
g_clear_error (&error);
} else {
errno = 0;
tmp = strtol (contents, NULL, 10);
if ((errno == 0) && (tmp == 0 || tmp == 1)) {
*out_value = (guint32) tmp;
success = TRUE;
}
g_free (contents);
}
return success;
}
static char *
get_new_connection_name (const GSList *existing,
const char *format,
const char *preferred)
{
GSList *names = NULL;
const GSList *iter;
char *cname = NULL;
int i = 0;
gboolean preferred_found = FALSE;
for (iter = existing; iter; iter = g_slist_next (iter)) {
NMConnection *candidate = NM_CONNECTION (iter->data);
const char *id;
id = nm_connection_get_id (candidate);
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;
temp = g_strdup_printf (format, i);
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;
}
void
nm_utils_complete_generic (NMConnection *connection,
const char *ctype,
const GSList *existing,
const char *format,
const char *preferred,
gboolean default_enable_ipv6)
{
NMSettingConnection *s_con;
NMSettingIP4Config *s_ip4;
NMSettingIP6Config *s_ip6;
const char *method;
char *id, *uuid;
s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
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)) {
id = get_new_connection_name (existing, format, preferred);
g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_ID, id, NULL);
g_free (id);
}
/* Add an 'auto' IPv4 connection if present */
s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
if (!s_ip4) {
s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new ();
nm_connection_add_setting (connection, NM_SETTING (s_ip4));
}
method = nm_setting_ip4_config_get_method (s_ip4);
if (!method) {
g_object_set (G_OBJECT (s_ip4),
NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO,
NULL);
}
/* Add an 'auto' IPv6 setting if allowed and not preset */
s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG);
if (!s_ip6 && default_enable_ipv6) {
s_ip6 = (NMSettingIP6Config *) nm_setting_ip6_config_new ();
nm_connection_add_setting (connection, NM_SETTING (s_ip6));
}
if (s_ip6 && !nm_setting_ip6_config_get_method (s_ip6)) {
g_object_set (G_OBJECT (s_ip6),
NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO,
NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE,
NULL);
}
}