summaryrefslogtreecommitdiff
path: root/src/libnm-client-impl/nm-libnm-utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libnm-client-impl/nm-libnm-utils.c')
-rw-r--r--src/libnm-client-impl/nm-libnm-utils.c915
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();
+}