diff options
Diffstat (limited to 'src/libnm-client-impl/nm-libnm-utils.c')
-rw-r--r-- | src/libnm-client-impl/nm-libnm-utils.c | 915 |
1 files changed, 915 insertions, 0 deletions
diff --git a/src/libnm-client-impl/nm-libnm-utils.c b/src/libnm-client-impl/nm-libnm-utils.c new file mode 100644 index 0000000000..fe1b911b68 --- /dev/null +++ b/src/libnm-client-impl/nm-libnm-utils.c @@ -0,0 +1,915 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2007 - 2018 Red Hat, Inc. + */ + +#include "libnm-client-impl/nm-default-libnm.h" + +#include "nm-libnm-utils.h" + +#include "libnm-glib-aux/nm-time-utils.h" +#include "libnm-core-aux-intern/nm-common-macros.h" +#include "nm-object.h" + +/*****************************************************************************/ + +volatile int _nml_dbus_log_level = 0; + +int +_nml_dbus_log_level_init(void) +{ + const GDebugKey keys[] = { + {"trace", _NML_DBUS_LOG_LEVEL_TRACE}, + {"debug", _NML_DBUS_LOG_LEVEL_DEBUG}, + {"warning", _NML_DBUS_LOG_LEVEL_WARN}, + {"error", _NML_DBUS_LOG_LEVEL_ERROR}, + {"stdout", NML_DBUS_LOG_STDOUT}, + }; + int l; + + l = _NML_DBUS_LOG_LEVEL_INITIALIZED + | nm_utils_parse_debug_string(g_getenv("LIBNM_CLIENT_DEBUG"), keys, G_N_ELEMENTS(keys)); + + if (!g_atomic_int_compare_and_exchange(&_nml_dbus_log_level, 0, l)) + l = g_atomic_int_get(&_nml_dbus_log_level); + + nm_assert(l & _NML_DBUS_LOG_LEVEL_INITIALIZED); + return l; +} + +void +_nml_dbus_log(NMLDBusLogLevel level, gboolean use_stdout, const char *fmt, ...) +{ + NMLDBusLogLevel configured_log_level; + gs_free char * msg = NULL; + va_list args; + const char * prefix = ""; + gint64 ts; + pid_t pid; + + /* we only call _nml_dbus_log() after nml_dbus_log_enabled(), which already does + * an atomic access to the variable. Since the value is only initialized once and + * never changes, we can just access it without additional locking. */ + configured_log_level = _nml_dbus_log_level; + + nm_assert(level & configured_log_level); + + va_start(args, fmt); + msg = g_strdup_vprintf(fmt, args); + va_end(args); + + switch (level) { + case NML_DBUS_LOG_LEVEL_TRACE: + prefix = "<trace> "; + break; + case NML_DBUS_LOG_LEVEL_DEBUG: + prefix = "<debug> "; + break; + case NML_DBUS_LOG_LEVEL_WARN: + prefix = "<warn > "; + if (NM_FLAGS_HAS(configured_log_level, _NML_DBUS_LOG_LEVEL_WARN)) { + g_warning("libnm-dbus: %s%s", prefix, msg); + return; + } + break; + case NML_DBUS_LOG_LEVEL_ERROR: + prefix = "<error> "; + if (NM_FLAGS_HAS(configured_log_level, _NML_DBUS_LOG_LEVEL_ERROR)) { + g_critical("libnm-dbus: %s%s", prefix, msg); + return; + } + if (NM_FLAGS_HAS(configured_log_level, _NML_DBUS_LOG_LEVEL_WARN)) { + g_warning("libnm-dbus: %s%s", prefix, msg); + return; + } + break; + default: + break; + } + + ts = nm_utils_clock_gettime_nsec(CLOCK_BOOTTIME); + + pid = getpid(); + + if (use_stdout) { + g_print("libnm-dbus[%lld]: %s[%" G_GINT64_FORMAT ".%05" G_GINT64_FORMAT "] %s\n", + (long long) pid, + prefix, + ts / NM_UTILS_NSEC_PER_SEC, + (ts / (NM_UTILS_NSEC_PER_SEC / 10000)) % 10000, + msg); + } else { + g_printerr("libnm-dbus[%lld]: %s[%" G_GINT64_FORMAT ".%05" G_GINT64_FORMAT "] %s\n", + (long long) pid, + prefix, + ts / NM_UTILS_NSEC_PER_SEC, + (ts / (NM_UTILS_NSEC_PER_SEC / 10000)) % 10000, + msg); + } +} + +/*****************************************************************************/ + +/* Stolen from dbus-glib */ +char * +nm_utils_wincaps_to_dash(const char *caps) +{ + const char *p; + GString * str; + + str = g_string_new(NULL); + p = caps; + while (*p) { + if (g_ascii_isupper(*p)) { + if (str->len > 0 && (str->len < 2 || str->str[str->len - 2] != '-')) + g_string_append_c(str, '-'); + g_string_append_c(str, g_ascii_tolower(*p)); + } else + g_string_append_c(str, *p); + ++p; + } + + return g_string_free(str, FALSE); +} + +/*****************************************************************************/ + +static char * +_fixup_string(const char * desc, + const char *const *ignored_phrases, + const char *const *ignored_words, + gboolean square_brackets_sensible) +{ + char * desc_full; + gboolean in_paren = FALSE; + char * p, *q; + int i; + + if (!desc || !desc[0]) + return NULL; + + /* restore original non-UTF-8-safe text. */ + desc_full = nm_utils_str_utf8safe_unescape_cp(desc, NM_UTILS_STR_UTF8_SAFE_FLAG_NONE); + + /* replace all invalid UTF-8 bytes with space. */ + p = desc_full; + while (!g_utf8_validate(p, -1, (const char **) &q)) { + /* the byte is invalid UTF-8. Replace it with space and proceed. */ + *q = ' '; + p = q + 1; + } + + /* replace '_', ',', ASCII control characters and parentheses, with space. */ + for (p = desc_full; p[0]; p++) { + if (*p == '(') + in_paren = TRUE; + if (NM_IN_SET(*p, '_', ',') || *p < ' ' || in_paren) + *p = ' '; + if (*p == ')') + in_paren = FALSE; + } + + /* Attempt to shorten ID by ignoring certain phrases */ + for (i = 0; ignored_phrases[i]; i++) { + p = strstr(desc_full, ignored_phrases[i]); + if (p) { + const char *eow = &p[strlen(ignored_phrases[i])]; + + /* require that the phrase is delimited by space, or + * at the beginning or end of the description. */ + if ((p == desc_full || p[-1] == ' ') && NM_IN_SET(eow[0], '\0', ' ')) + memmove(p, eow, strlen(eow) + 1); /* +1 for the \0 */ + } + } + + /* Attempt to shorten ID by ignoring certain individual words. + * - word-split the description at spaces + * - coalesce multiple spaces + * - skip over ignored_words */ + p = desc_full; + q = desc_full; + for (;;) { + char *eow; + gsize l; + + /* skip leading spaces. */ + while (p[0] == ' ') + p++; + + if (!p[0]) + break; + + /* split leading word on first space */ + eow = strchr(p, ' '); + if (eow) + *eow = '\0'; + + if (nm_utils_strv_find_first((char **) ignored_words, -1, p) >= 0) + goto next; + + l = strlen(p); + if (q != p) { + if (q != desc_full) + *q++ = ' '; + memmove(q, p, l); + } + q += l; + +next: + if (!eow) + break; + p = eow + 1; + } + + *q++ = '\0'; + + p = strchr(desc_full, '['); + if (p == desc_full) { + /* All we're left with is in square brackets. + * Always prefer that to a blank string.*/ + square_brackets_sensible = TRUE; + } + if (square_brackets_sensible) { + /* If there's a [<string>] that survived the substitution, then the string + * is a short form that is generally preferable. */ + q = strchr(desc_full, ']'); + if (p && q > p) { + p++; + memmove(desc_full, p, q - p); + desc_full[q - p] = '\0'; + } + } else { + /* [<string>] sometimes contains the preferred human-readable name, but + * mostly it's utterly useless. Sigh. Drop it. */ + if (p) { + if (p > desc_full && p[-1] == ' ') + p--; + *p = '\0'; + } + } + + if (!desc_full[0]) { + g_free(desc_full); + return NULL; + } + + return desc_full; +} + +char * +nm_utils_fixup_vendor_string(const char *desc) +{ + static const char *const IGNORED_PHRASES[] = { + "Access Systems", + "Business Mobile Networks BV", + "Communications & Multimedia", + "Company of Japan", + "Computer Co.", + "Computer Corp.", + "Computer Corporation", + "Computer Inc.", + "Computer, Inc.", + "Information and Communication Products", + "Macao Commercial Offshore", + "Mobile Phones", + "(M) Son", + "Multimedia Internet Technology", + "Technology Group Ltd.", + "Wireless Networks", + "Wireless Solutions", + NULL, + }; + static const char *const IGNORED_WORDS[] = { + "AB", + "AG", + "A/S", + "ASA", + "B.V.", + "Chips", + "Co.", + "Co", + "Communications", + "Components", + "Computers", + "Computertechnik", + "corp.", + "Corp.", + "Corp", + "Corporation", + "Design", + "Electronics", + "Enterprise", + "Enterprises", + "Europe", + "GmbH", + "Hardware", + "[hex]", + "Holdings", + "Inc.", + "Inc", + "INC.", + "Incorporated", + "Instruments", + "International", + "Intl.", + "Labs", + "Limited.", + "Limited", + "Ltd.", + "Ltd", + "Microelectronics", + "Microsystems", + "MSM", + "Multimedia", + "Networks", + "Norway", + "Optical", + "PCS", + "Semiconductor", + "Systems", + "Systemtechnik", + "Techcenter", + "Technik", + "Technologies", + "Technology", + "TECHNOLOGY", + "Telephonics", + "USA", + "WCDMA", + NULL, + }; + char *desc_full; + char *p; + + desc_full = _fixup_string(desc, IGNORED_PHRASES, IGNORED_WORDS, TRUE); + if (!desc_full) + return NULL; + + /* Chop off everything after a slash. */ + for (p = desc_full; *p; p++) { + if ((p[0] == ' ' && p[1] == '/') || p[0] == '/') { + p[0] = '\0'; + break; + } + } + + nm_assert(g_utf8_validate(desc_full, -1, NULL)); + + return desc_full; +} + +char * +nm_utils_fixup_product_string(const char *desc) +{ + static const char *const IGNORED_PHRASES[] = { + "100/10 MBit", + "10/100 Mbps", + "1.0 GbE", + "10 GbE", + "10 Gigabit", + "10 Mbps", + "1/10 Gigabit", + "150 Mbps", + "2.5 GbE", + "54 Mbps", + "Attached Port", + "+ BT", + "\"CDC Subset\"", + "CE Media Processor", + "Controller Area Network", + "Converged Network", + "DEC-Tulip compatible", + "Dish Adapter", + "Double 108 Mbps", + "Dual Band", + "Dual Port", + "Embedded UTP", + "Ethernet Connection", + "Ethernet Pro 100", + "Express Module", + "Fabric Adapter", + "Fast Ethernet", + "for 10GBASE-T", + "for 10GbE backplane", + "for 10GbE QSFP+", + "for 10GbE SFP+", + "for 1GbE", + "for 20GbE backplane", + "for 25GbE backplane", + "for 25GbE SFP28", + "for 40GbE backplane", + "for 40GbE QSFP+", + "G Adapter", + "Gigabit Desktop Network", + "Gigabit Ethernet", + "Gigabit or", + "Host Interface", + "Host Virtual Interface", + "IEEE 802.11a/b/g", + "IEEE 802.11g", + "IEEE 802.11G", + "IEEE 802.11n", + "MAC + PHY", + "Mini Card", + "Mini Wireless", + "multicore SoC", + "Multi Function", + "N Draft 11n Wireless", + "Network Connection", + "Network Everywhere", + "N Wireless", + "N+ Wireless", + "OCT To Fast Ethernet Converter", + "PC Card", + "PCI Express", + "Platform Controller Hub", + "Plus Bluetooth", + "Quad Gigabit", + "rev 1", + "rev 17", + "rev 2", + "rev A", + "rev B", + "rev F", + "TO Ethernet", + "Turbo Wireless Adapter", + "Unified Wire", + "USB 1.1", + "USB 2.0", + "Virtual media for", + "WiFi Link", + "+ WiMAX", + "WiMAX/WiFi Link", + "Wireless G", + "Wireless G+", + "Wireless Lan", + "Wireless Mini adapter", + "Wireless Mini Adapter", + "Wireless N", + "with 1000-BASE-T interface", + "with CX4 copper interface", + "with Range Amplifier", + "with SR-XFP optical interface", + "w/ Upgradable Antenna", + NULL, + }; + static const char *const IGNORED_WORDS[] = { + "1000BaseSX", + "1000BASE-T", + "1000Base-ZX", + "100/10M", + "100baseFx", + "100Base-MII", + "100Base-T", + "100BaseT4", + "100Base-TX", + "100BaseTX", + "100GbE", + "100Mbps", + "100MBps", + "10/100", + "10/100/1000", + "10/100/1000Base-T", + "10/100/1000BASE-T", + "10/100BaseT", + "10/100baseTX", + "10/100BaseTX", + "10/100/BNC", + "10/100M", + "10/20-Gigabit", + "10/25/40/50GbE", + "10/40G", + "10base-FL", + "10BaseT", + "10BASE-T", + "10G", + "10Gb", + "10Gb/25Gb", + "10Gb/25Gb/40Gb/50Gb", + "10Gbase-T", + "10GBase-T", + "10GBASE-T", + "10GbE", + "10Gbps", + "10-Giga", + "10-Gigabit", + "10mbps", + "10Mbps", + "1/10GbE", + "1/10-Gigabit", + "11b/g/n", + "11g", + "150Mbps", + "16Gbps/10Gbps", + "1GbE", + "1x2:2", + "20GbE", + "25Gb", + "25GbE", + "2-Port", + "2x3:3", + "3G", + "3G/4G", + "3x3:3", + "40GbE", + "4G", + "54g", + "54M", + "54Mbps", + "56k", + "5G", + "802.11", + "802.11a/b/g", + "802.11abg", + "802.11a/b/g/n", + "802.11abgn", + "802.11ac", + "802.11ad", + "802.11a/g", + "802.11b", + "802.11b/g", + "802.11bg", + "802.11b/g/n", + "802.11bgn", + "802.11b/g/n-draft", + "802.11g", + "802.11n", + "802.11N", + "802.11n/b/g", + "802.11ng", + "802AIN", + "802UIG-1", + "adapter", + "Adapter", + "adaptor", + "ADSL", + "Basic", + "CAN-Bus", + "card", + "Card", + "Cardbus", + "CardBus", + "CDMA", + "CNA", + "Composite", + "controller", + "Controller", + "Copper", + "DB", + "Desktop", + "device", + "Device", + "dongle", + "driver", + "Dual-band", + "Dual-Protocol", + "EISA", + "Enhanced", + "ethernet.", + "ethernet", + "Ethernet", + "Ethernet/RNDIS", + "ExpressModule", + "family", + "Family", + "Fast/Gigabit", + "Fiber", + "gigabit", + "Gigabit", + "G-NIC", + "Hi-Gain", + "Hi-Speed", + "HSDPA", + "HSUPA", + "integrated", + "Integrated", + "interface", + "LAN", + "LAN+Winmodem", + "Laptop", + "LTE", + "LTE/UMTS/GSM", + "MAC", + "Micro", + "Mini-Card", + "Mini-USB", + "misprogrammed", + "modem", + "Modem", + "Modem/Networkcard", + "Module", + "Multimode", + "Multithreaded", + "Name:", + "net", + "network", + "Network", + "n/g/b", + "NIC", + "Notebook", + "OEM", + "PCI", + "PCI64", + "PCIe", + "PCI-E", + "PCI-Express", + "PCI-X", + "PCMCIA", + "PDA", + "PnP", + "RDMA", + "RJ-45", + "Series", + "Server", + "SoC", + "Switch", + "Technologies", + "TOE", + "USB", + "USB2.0", + "USB/Ethernet", + "UTP", + "UTP/Coax", + "v1", + "v1.1", + "v2", + "V2.0", + "v3", + "v4", + "wifi", + "Wi-Fi", + "WiFi", + "wireless", + "Wireless", + "Wireless-150N", + "Wireless-300N", + "Wireless-G", + "Wireless-N", + "WLAN", + NULL, + }; + char *desc_full; + char *p; + + desc_full = _fixup_string(desc, IGNORED_PHRASES, IGNORED_WORDS, FALSE); + if (!desc_full) + return NULL; + + /* Chop off everything after a '-'. */ + for (p = desc_full; *p; p++) { + if (p[0] == ' ' && p[1] == '-' && p[2] == ' ') { + p[0] = '\0'; + break; + } + } + + nm_assert(g_utf8_validate(desc_full, -1, NULL)); + + return desc_full; +} + +/*****************************************************************************/ + +const NMLDBusMetaIface *const _nml_dbus_meta_ifaces[] = { + &_nml_dbus_meta_iface_nm, + &_nml_dbus_meta_iface_nm_accesspoint, + &_nml_dbus_meta_iface_nm_agentmanager, + &_nml_dbus_meta_iface_nm_checkpoint, + &_nml_dbus_meta_iface_nm_connection_active, + &_nml_dbus_meta_iface_nm_dhcp4config, + &_nml_dbus_meta_iface_nm_dhcp6config, + &_nml_dbus_meta_iface_nm_device, + &_nml_dbus_meta_iface_nm_device_adsl, + &_nml_dbus_meta_iface_nm_device_bluetooth, + &_nml_dbus_meta_iface_nm_device_bond, + &_nml_dbus_meta_iface_nm_device_bridge, + &_nml_dbus_meta_iface_nm_device_dummy, + &_nml_dbus_meta_iface_nm_device_generic, + &_nml_dbus_meta_iface_nm_device_iptunnel, + &_nml_dbus_meta_iface_nm_device_infiniband, + &_nml_dbus_meta_iface_nm_device_lowpan, + &_nml_dbus_meta_iface_nm_device_macsec, + &_nml_dbus_meta_iface_nm_device_macvlan, + &_nml_dbus_meta_iface_nm_device_modem, + &_nml_dbus_meta_iface_nm_device_olpcmesh, + &_nml_dbus_meta_iface_nm_device_ovsbridge, + &_nml_dbus_meta_iface_nm_device_ovsinterface, + &_nml_dbus_meta_iface_nm_device_ovsport, + &_nml_dbus_meta_iface_nm_device_ppp, + &_nml_dbus_meta_iface_nm_device_statistics, + &_nml_dbus_meta_iface_nm_device_team, + &_nml_dbus_meta_iface_nm_device_tun, + &_nml_dbus_meta_iface_nm_device_veth, + &_nml_dbus_meta_iface_nm_device_vlan, + &_nml_dbus_meta_iface_nm_device_vrf, + &_nml_dbus_meta_iface_nm_device_vxlan, + &_nml_dbus_meta_iface_nm_device_wifip2p, + &_nml_dbus_meta_iface_nm_device_wireguard, + &_nml_dbus_meta_iface_nm_device_wired, + &_nml_dbus_meta_iface_nm_device_wireless, + &_nml_dbus_meta_iface_nm_device_wpan, + &_nml_dbus_meta_iface_nm_dnsmanager, + &_nml_dbus_meta_iface_nm_ip4config, + &_nml_dbus_meta_iface_nm_ip6config, + &_nml_dbus_meta_iface_nm_settings, + &_nml_dbus_meta_iface_nm_settings_connection, + &_nml_dbus_meta_iface_nm_vpn_connection, + &_nml_dbus_meta_iface_nm_wifip2ppeer, +}; + +#define COMMON_PREFIX "org.freedesktop.NetworkManager" + +static int +_strcmp_common_prefix(gconstpointer a, gconstpointer b, gpointer user_data) +{ + const NMLDBusMetaIface *iface = a; + const char * dbus_iface_name = b; + + nm_assert(g_str_has_prefix(iface->dbus_iface_name, COMMON_PREFIX)); + + return strcmp(&iface->dbus_iface_name[NM_STRLEN(COMMON_PREFIX)], dbus_iface_name); +} + +const NMLDBusMetaIface * +nml_dbus_meta_iface_get(const char *dbus_iface_name) +{ + gssize idx; + + nm_assert(dbus_iface_name); + + G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMLDBusMetaIface, dbus_iface_name) == 0); + + /* we assume that NetworkManager only uses unique interface names. E.g. one + * interface name always has one particular meaning (and offers one set of + * properties, signals and methods). This is a convenient assumption, and + * we sure would never violate it when extending NM's D-Bus API. */ + + if (NM_STR_HAS_PREFIX(dbus_iface_name, COMMON_PREFIX)) { + /* optimize, that in fact all our interfaces have the same prefix. */ + idx = nm_utils_ptrarray_find_binary_search((gconstpointer *) _nml_dbus_meta_ifaces, + G_N_ELEMENTS(_nml_dbus_meta_ifaces), + &dbus_iface_name[NM_STRLEN(COMMON_PREFIX)], + _strcmp_common_prefix, + NULL, + NULL, + NULL); + } else + return NULL; + + if (idx < 0) + return NULL; + return _nml_dbus_meta_ifaces[idx]; +} + +const NMLDBusMetaProperty * +nml_dbus_meta_property_get(const NMLDBusMetaIface *meta_iface, + const char * dbus_property_name, + guint * out_idx) +{ + gssize idx; + + nm_assert(meta_iface); + nm_assert(dbus_property_name); + + idx = nm_utils_array_find_binary_search(meta_iface->dbus_properties, + sizeof(meta_iface->dbus_properties[0]), + meta_iface->n_dbus_properties, + &dbus_property_name, + nm_strcmp_p_with_data, + NULL); + if (idx < 0) { + NM_SET_OUT(out_idx, meta_iface->n_dbus_properties); + return NULL; + } + NM_SET_OUT(out_idx, idx); + return &meta_iface->dbus_properties[idx]; +} + +void +_nml_dbus_meta_class_init_with_properties_impl(GObjectClass * object_class, + const NMLDBusMetaIface *const *meta_ifaces) +{ + int i_iface; + + nm_assert(G_IS_OBJECT_CLASS(object_class)); + nm_assert(meta_ifaces); + nm_assert(meta_ifaces[0]); + + for (i_iface = 0; meta_ifaces[i_iface]; i_iface++) { + const NMLDBusMetaIface *meta_iface = meta_ifaces[i_iface]; + guint8 * reverse_idx; + guint8 i; + + nm_assert(g_type_is_a(meta_iface->get_type_fcn(), G_OBJECT_CLASS_TYPE(object_class))); + nm_assert(meta_iface->n_obj_properties > 0); + nm_assert(meta_iface->obj_properties); + nm_assert(meta_iface->obj_properties_reverse_idx[0] == 0); + nm_assert(meta_iface->obj_properties == meta_ifaces[0]->obj_properties); + + if (i_iface == 0) + g_object_class_install_properties(object_class, + meta_iface->n_obj_properties, + (GParamSpec **) meta_iface->obj_properties); + + reverse_idx = (guint8 *) meta_iface->obj_properties_reverse_idx; + + for (i = 0; i < meta_iface->n_obj_properties; i++) + reverse_idx[i] = 0xFFu; + for (i = 0; i < meta_iface->n_dbus_properties; i++) { + const NMLDBusMetaProperty *mpr = &meta_iface->dbus_properties[i]; + + if (mpr->obj_properties_idx != 0 && !mpr->obj_property_no_reverse_idx) { + nm_assert(mpr->obj_properties_idx < meta_iface->n_obj_properties); + nm_assert(reverse_idx[mpr->obj_properties_idx] == 0xFFu); + + reverse_idx[mpr->obj_properties_idx] = i; + } + } + } +} + +gboolean +nm_utils_g_param_spec_is_default(const GParamSpec *pspec) +{ + g_return_val_if_fail(pspec, FALSE); + + if (pspec->value_type == G_TYPE_BOOLEAN) + return ((((GParamSpecBoolean *) pspec)->default_value) == FALSE); + if (pspec->value_type == G_TYPE_UCHAR) + return ((((GParamSpecUChar *) pspec)->default_value) == 0u); + if (pspec->value_type == G_TYPE_INT) + return ((((GParamSpecInt *) pspec)->default_value) == 0); + if (pspec->value_type == G_TYPE_UINT) + return ((((GParamSpecUInt *) pspec)->default_value) == 0u); + if (pspec->value_type == G_TYPE_INT64) + return ((((GParamSpecInt64 *) pspec)->default_value) == 0); + if (pspec->value_type == G_TYPE_UINT64) + return ((((GParamSpecUInt64 *) pspec)->default_value) == 0u); + if (g_type_is_a(pspec->value_type, G_TYPE_ENUM)) + return ((((GParamSpecEnum *) pspec)->default_value) == 0); + if (g_type_is_a(pspec->value_type, G_TYPE_FLAGS)) + return ((((GParamSpecFlags *) pspec)->default_value) == 0u); + if (pspec->value_type == G_TYPE_STRING) + return ((((GParamSpecString *) pspec)->default_value) == NULL); + if (NM_IN_SET(pspec->value_type, + G_TYPE_BYTES, + G_TYPE_PTR_ARRAY, + G_TYPE_ARRAY, + G_TYPE_HASH_TABLE, + G_TYPE_STRV)) { + /* boxed types have NULL default. */ + g_return_val_if_fail(G_IS_PARAM_SPEC_BOXED(pspec), FALSE); + g_return_val_if_fail(G_TYPE_IS_BOXED(pspec->value_type), FALSE); + return TRUE; + } + if (g_type_is_a(pspec->value_type, NM_TYPE_OBJECT)) { + /* object types have NULL default. */ + g_return_val_if_fail(G_IS_PARAM_SPEC_OBJECT(pspec), FALSE); + g_return_val_if_fail(G_TYPE_IS_OBJECT(pspec->value_type), FALSE); + return TRUE; + } + + /* This function is only used for asserting/testing. It thus + * strictly asserts and only support argument types that we expect. */ + g_return_val_if_reached(FALSE); +} + +/*****************************************************************************/ + +/** + * nm_utils_print: + * @output_mode: if 1 it uses g_print(). If 2, it uses g_printerr(). + * If 0, it uses either g_print() or g_printerr(), depending + * on LIBNM_CLIENT_DEBUG (and the "stdout" flag). + * @msg: the message to print. The function does not append + * a trailing newline. + * + * The only purpose of this function is to give access to g_print() + * or g_printerr() from pygobject. libnm can do debug logging by + * setting LIBNM_CLIENT_DEBUG and uses thereby g_printerr() or + * g_print(). A plain "print()" function in python is not in sync + * with these functions (it implements additional buffering). By + * using nm_utils_print(), the same logging mechanisms can be used. + * + * Since: 1.30 + */ +void +nm_utils_print(int output_mode, const char *msg) +{ + gboolean use_stdout; + + g_return_if_fail(msg); + + if (output_mode == 0) { + nml_dbus_log_enabled_full(NML_DBUS_LOG_LEVEL_ANY, &use_stdout); + output_mode = use_stdout ? 1 : 2; + } + + if (output_mode == 1) + g_print("%s", msg); + else if (output_mode == 2) + g_printerr("%s", msg); + else + g_return_if_reached(); +} |