summary refs log tree commit diff
path: root/src/libnmc-base/nm-client-utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libnmc-base/nm-client-utils.c')
-rw-r--r--src/libnmc-base/nm-client-utils.c912
1 files changed, 912 insertions, 0 deletions
diff --git a/src/libnmc-base/nm-client-utils.c b/src/libnmc-base/nm-client-utils.c
new file mode 100644
index 00000000..49234d97
--- /dev/null
+++ b/src/libnmc-base/nm-client-utils.c
@@ -0,0 +1,912 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2010 - 2017 Red Hat, Inc.
+ */
+
+#include "libnm-client-aux-extern/nm-default-client.h"
+
+#include "nm-client-utils.h"
+
+#include "libnm-glib-aux/nm-secret-utils.h"
+#include "libnm-glib-aux/nm-io-utils.h"
+#include "nm-utils.h"
+#include "nm-device-bond.h"
+#include "nm-device-bridge.h"
+#include "nm-device-team.h"
+
+/*****************************************************************************/
+
+static int
+_nmc_objects_sort_by_path_cmp(gconstpointer pa, gconstpointer pb, gpointer user_data)
+{
+    NMObject *a = *((NMObject **) pa);
+    NMObject *b = *((NMObject **) pb);
+
+    NM_CMP_SELF(a, b);
+    NM_CMP_RETURN(nm_utils_dbus_path_cmp(nm_object_get_path(a), nm_object_get_path(b)));
+    return 0;
+}
+
+const NMObject **
+nmc_objects_sort_by_path(const NMObject *const *objs, gssize len)
+{
+    const NMObject **arr;
+    gsize            i, l;
+
+    if (len < 0)
+        l = NM_PTRARRAY_LEN(objs);
+    else
+        l = len;
+
+    arr = g_new(const NMObject *, l + 1);
+    for (i = 0; i < l; i++)
+        arr[i] = objs[i];
+    arr[l] = NULL;
+
+    if (l > 1) {
+        g_qsort_with_data(arr, l, sizeof(gpointer), _nmc_objects_sort_by_path_cmp, NULL);
+    }
+    return arr;
+}
+
+/*****************************************************************************/
+/*
+ * Convert string to unsigned integer.
+ * If required, the resulting number is checked to be in the <min,max> range.
+ */
+static gboolean
+nmc_string_to_uint_base(const char *       str,
+                        int                base,
+                        gboolean           range_check,
+                        unsigned long int  min,
+                        unsigned long int  max,
+                        unsigned long int *value)
+{
+    char *            end;
+    unsigned long int tmp;
+
+    if (!str || !str[0])
+        return FALSE;
+
+    /* FIXME: don't use this function, replace by _nm_utils_ascii_str_to_int64() */
+    errno = 0;
+    tmp   = strtoul(str, &end, base);
+    if (errno || *end != '\0' || (range_check && (tmp < min || tmp > max))) {
+        return FALSE;
+    }
+    *value = tmp;
+    return TRUE;
+}
+
+gboolean
+nmc_string_to_uint(const char *       str,
+                   gboolean           range_check,
+                   unsigned long int  min,
+                   unsigned long int  max,
+                   unsigned long int *value)
+{
+    return nmc_string_to_uint_base(str, 10, range_check, min, max, value);
+}
+
+gboolean
+nmc_string_to_bool(const char *str, gboolean *val_bool, GError **error)
+{
+    gs_free char *str_to_free = NULL;
+    int           i;
+
+    nm_assert(!error || !*error);
+    nm_assert(val_bool);
+
+    str = nm_strstrip_avoid_copy_a(300, str, &str_to_free);
+
+    if (nm_streq0(str, "o")) {
+        nm_utils_error_set(error,
+                           NM_UTILS_ERROR_UNKNOWN,
+                           /* TRANSLATORS: the first %s is the partial value entered by
+                            * the user, the second %s a list of compatible values.
+                            */
+                           _("'%s' is ambiguous (%s)"),
+                           str,
+                           "on, off");
+        return FALSE;
+    }
+
+    if (nmc_string_is_valid(str, NM_MAKE_STRV("true", "yes", "on"), NULL))
+        *val_bool = TRUE;
+    else if (nmc_string_is_valid(str, NM_MAKE_STRV("false", "no", "off"), NULL))
+        *val_bool = FALSE;
+    else if ((i = _nm_utils_ascii_str_to_int64(str, 0, 0, 1, -1)) >= 0) {
+        *val_bool = !!i;
+    } else {
+        nm_utils_error_set(error,
+                           NM_UTILS_ERROR_UNKNOWN,
+                           _("'%s' is not valid; use [%s] or [%s]"),
+                           str,
+                           "true, yes, on",
+                           "false, no, off");
+        return FALSE;
+    }
+    return TRUE;
+}
+
+gboolean
+nmc_string_to_ternary_full(const char *            str,
+                           NMCStringToTernaryFlags flags,
+                           NMTernary *             val,
+                           GError **               error)
+{
+    gs_free char *str_to_free = NULL;
+    int           i;
+
+    nm_assert(!error || !*error);
+    nm_assert(val);
+
+    str = nm_strstrip_avoid_copy_a(300, str, &str_to_free);
+
+    if (nm_streq0(str, "o")) {
+        nm_utils_error_set(error,
+                           NM_UTILS_ERROR_UNKNOWN,
+                           /* TRANSLATORS: the first %s is the partial value entered by
+                            * the user, the second %s a list of compatible values.
+                            */
+                           _("'%s' is ambiguous (%s)"),
+                           str,
+                           "on, off");
+        return FALSE;
+    }
+
+    if (nmc_string_is_valid(str, NM_MAKE_STRV("true", "yes", "on"), NULL))
+        *val = NM_TERNARY_TRUE;
+    else if (nmc_string_is_valid(str, NM_MAKE_STRV("false", "no", "off"), NULL))
+        *val = NM_TERNARY_FALSE;
+    else if (nmc_string_is_valid(
+                 str,
+                 NM_MAKE_STRV("unknown",
+                              "default",
+                              NM_FLAGS_HAS(flags, NMC_STRING_TO_TERNARY_FLAGS_IGNORE_FOR_DEFAULT)
+                                  ? "ignore"
+                                  : NULL),
+                 NULL))
+        *val = NM_TERNARY_DEFAULT;
+    else if ((i = _nm_utils_ascii_str_to_int64(str, 0, -1, 1, -2)) != -2)
+        *val = (NMTernary) i;
+    else {
+        nm_utils_error_set(error,
+                           NM_UTILS_ERROR_UNKNOWN,
+                           _("'%s' is not valid; use [%s], [%s] or [%s]"),
+                           str,
+                           "true, yes, on",
+                           "false, no, off",
+                           "unknown");
+        return FALSE;
+    }
+    return TRUE;
+}
+
+/*
+ * Check whether 'input' is contained in 'allowed' array. It performs case
+ * insensitive comparison and supports shortcut strings if they are unique.
+ * Returns: a pointer to found string in allowed array on success or NULL.
+ * On failure: error->code : 0 - string not found; 1 - string is ambiguous
+ */
+const char *
+_nmc_string_is_valid(const char *input, const char *const *allowed, GError **error)
+{
+    const char *const *p;
+    size_t             input_ln, p_len;
+    const char *       partial_match = NULL;
+    gboolean           ambiguous     = FALSE;
+
+    g_return_val_if_fail(!error || !*error, NULL);
+
+    if (!input || !*input)
+        goto finish;
+
+    input_ln = strlen(input);
+    for (p = allowed; p && *p; p++) {
+        p_len = strlen(*p);
+        if (g_ascii_strncasecmp(input, *p, input_ln) == 0) {
+            if (input_ln == p_len)
+                return *p;
+            if (!partial_match)
+                partial_match = *p;
+            else
+                ambiguous = TRUE;
+        }
+    }
+
+    if (ambiguous) {
+        GString *candidates = g_string_new("");
+
+        for (p = allowed; *p; p++) {
+            if (g_ascii_strncasecmp(input, *p, input_ln) == 0) {
+                if (candidates->len > 0)
+                    g_string_append(candidates, ", ");
+                g_string_append(candidates, *p);
+            }
+        }
+        nm_utils_error_set(error,
+                           NM_UTILS_ERROR_AMBIGUOUS,
+                           _("'%s' is ambiguous: %s"),
+                           input,
+                           candidates->str);
+        g_string_free(candidates, TRUE);
+        return NULL;
+    }
+finish:
+    if (!partial_match) {
+        char *valid_vals = g_strjoinv(", ", (char **) allowed);
+
+        if (!input || !*input)
+            nm_utils_error_set(error,
+                               NM_UTILS_ERROR_UNKNOWN,
+                               _("missing name, try one of [%s]"),
+                               valid_vals);
+        else
+            nm_utils_error_set(error,
+                               NM_UTILS_ERROR_UNKNOWN,
+                               _("'%s' not among [%s]"),
+                               input,
+                               valid_vals);
+
+        g_free(valid_vals);
+    }
+
+    return partial_match;
+}
+
+gboolean
+matches(const char *cmd, const char *pattern)
+{
+    size_t len = strlen(cmd);
+    if (!len || len > strlen(pattern))
+        return FALSE;
+    return memcmp(pattern, cmd, len) == 0;
+}
+
+const char *
+nmc_bond_validate_mode(const char *mode, GError **error)
+{
+    unsigned long      mode_int;
+    static const char *valid_modes[] = {"balance-rr",
+                                        "active-backup",
+                                        "balance-xor",
+                                        "broadcast",
+                                        "802.3ad",
+                                        "balance-tlb",
+                                        "balance-alb",
+                                        NULL};
+    if (nmc_string_to_uint(mode, TRUE, 0, 6, &mode_int)) {
+        /* Translate bonding mode numbers to mode names:
+         * https://www.kernel.org/doc/Documentation/networking/bonding.txt
+         */
+        return valid_modes[mode_int];
+    } else
+        return nmc_string_is_valid(mode, valid_modes, error);
+}
+
+NM_UTILS_LOOKUP_STR_DEFINE(
+    nmc_device_state_to_string,
+    NMDeviceState,
+    NM_UTILS_LOOKUP_DEFAULT(N_("unknown")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_UNMANAGED, N_("unmanaged")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_UNAVAILABLE, N_("unavailable")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_DISCONNECTED, N_("disconnected")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_PREPARE, N_("connecting (prepare)")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_CONFIG, N_("connecting (configuring)")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_NEED_AUTH, N_("connecting (need authentication)")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_IP_CONFIG, N_("connecting (getting IP configuration)")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_IP_CHECK, N_("connecting (checking IP connectivity)")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_SECONDARIES,
+                         N_("connecting (starting secondary connections)")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_ACTIVATED, N_("connected")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_DEACTIVATING, N_("deactivating")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_FAILED, N_("connection failed")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_UNKNOWN, N_("unknown")), );
+
+static NM_UTILS_LOOKUP_STR_DEFINE(
+    _device_state_to_string,
+    NMDeviceState,
+    NM_UTILS_LOOKUP_DEFAULT(NULL),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_PREPARE, N_("connecting (externally)")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_CONFIG, N_("connecting (externally)")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_NEED_AUTH, N_("connecting (externally)")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_IP_CONFIG, N_("connecting (externally)")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_IP_CHECK, N_("connecting (externally)")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_SECONDARIES, N_("connecting (externally)")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_ACTIVATED, N_("connected (externally)")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_DEACTIVATING, N_("deactivating (externally)")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_FAILED, N_("deactivating (externally)")),
+    NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER(), );
+
+const char *
+nmc_device_state_to_string_with_external(NMDevice *device)
+{
+    NMActiveConnection *ac;
+    NMDeviceState       state;
+    const char *        s;
+
+    state = nm_device_get_state(device);
+
+    if ((ac = nm_device_get_active_connection(device))
+        && NM_FLAGS_HAS(nm_active_connection_get_state_flags(ac), NM_ACTIVATION_STATE_FLAG_EXTERNAL)
+        && (s = _device_state_to_string(state)))
+        return s;
+
+    return nmc_device_state_to_string(state);
+}
+
+NM_UTILS_LOOKUP_STR_DEFINE(nmc_device_metered_to_string,
+                           NMMetered,
+                           NM_UTILS_LOOKUP_DEFAULT(N_("unknown")),
+                           NM_UTILS_LOOKUP_ITEM(NM_METERED_YES, N_("yes")),
+                           NM_UTILS_LOOKUP_ITEM(NM_METERED_NO, N_("no")),
+                           NM_UTILS_LOOKUP_ITEM(NM_METERED_GUESS_YES, N_("yes (guessed)")),
+                           NM_UTILS_LOOKUP_ITEM(NM_METERED_GUESS_NO, N_("no (guessed)")),
+                           NM_UTILS_LOOKUP_ITEM(NM_METERED_UNKNOWN, N_("unknown")), );
+
+NM_UTILS_LOOKUP_STR_DEFINE(
+    nmc_device_reason_to_string,
+    NMDeviceStateReason,
+    /* TRANSLATORS: Unknown reason for a device state change (NMDeviceStateReason) */
+    NM_UTILS_LOOKUP_DEFAULT(N_("Unknown")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_NONE, N_("No reason given")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_UNKNOWN, N_("Unknown error")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_NOW_MANAGED, N_("Device is now managed")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_NOW_UNMANAGED, N_("Device is now unmanaged")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_CONFIG_FAILED,
+                         N_("The device could not be readied for configuration")),
+    NM_UTILS_LOOKUP_ITEM(
+        NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE,
+        N_("IP configuration could not be reserved (no available address, timeout, etc.)")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED,
+                         N_("The IP configuration is no longer valid")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_NO_SECRETS,
+                         N_("Secrets were required, but not provided")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT,
+                         N_("802.1X supplicant disconnected")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED,
+                         N_("802.1X supplicant configuration failed")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED, N_("802.1X supplicant failed")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT,
+                         N_("802.1X supplicant took too long to authenticate")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_PPP_START_FAILED,
+                         N_("PPP service failed to start")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_PPP_DISCONNECT, N_("PPP service disconnected")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_PPP_FAILED, N_("PPP failed")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_DHCP_START_FAILED,
+                         N_("DHCP client failed to start")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_DHCP_ERROR, N_("DHCP client error")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_DHCP_FAILED, N_("DHCP client failed")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_SHARED_START_FAILED,
+                         N_("Shared connection service failed to start")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_SHARED_FAILED,
+                         N_("Shared connection service failed")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED,
+                         N_("AutoIP service failed to start")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_AUTOIP_ERROR, N_("AutoIP service error")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_AUTOIP_FAILED, N_("AutoIP service failed")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_MODEM_BUSY, N_("The line is busy")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_MODEM_NO_DIAL_TONE, N_("No dial tone")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER,
+                         N_("No carrier could be established")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT,
+                         N_("The dialing request timed out")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_MODEM_DIAL_FAILED,
+                         N_("The dialing attempt failed")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED,
+                         N_("Modem initialization failed")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_GSM_APN_FAILED,
+                         N_("Failed to select the specified APN")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING,
+                         N_("Not searching for networks")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED,
+                         N_("Network registration denied")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_GSM_REGISTRATION_TIMEOUT,
+                         N_("Network registration timed out")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_GSM_REGISTRATION_FAILED,
+                         N_("Failed to register with the requested network")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_GSM_PIN_CHECK_FAILED, N_("PIN check failed")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_FIRMWARE_MISSING,
+                         N_("Necessary firmware for the device may be missing")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_REMOVED, N_("The device was removed")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_SLEEPING, N_("NetworkManager went to sleep")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_CONNECTION_REMOVED,
+                         N_("The device's active connection disappeared")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_USER_REQUESTED,
+                         N_("Device disconnected by user or client")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_CARRIER, N_("Carrier/link changed")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED,
+                         N_("The device's existing connection was assumed")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE,
+                         N_("The supplicant is now available")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_MODEM_NOT_FOUND,
+                         N_("The modem could not be found")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_BT_FAILED,
+                         N_("The Bluetooth connection failed or timed out")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_GSM_SIM_NOT_INSERTED,
+                         N_("GSM Modem's SIM card not inserted")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED,
+                         N_("GSM Modem's SIM PIN required")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_GSM_SIM_PUK_REQUIRED,
+                         N_("GSM Modem's SIM PUK required")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_GSM_SIM_WRONG, N_("GSM Modem's SIM wrong")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_INFINIBAND_MODE,
+                         N_("InfiniBand device does not support connected mode")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED,
+                         N_("A dependency of the connection failed")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_BR2684_FAILED,
+                         N_("A problem with the RFC 2684 Ethernet over ADSL bridge")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_MODEM_MANAGER_UNAVAILABLE,
+                         N_("ModemManager is unavailable")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_SSID_NOT_FOUND,
+                         N_("The Wi-Fi network could not be found")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_SECONDARY_CONNECTION_FAILED,
+                         N_("A secondary connection of the base connection failed")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_DCB_FCOE_FAILED, N_("DCB or FCoE setup failed")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED, N_("teamd control failed")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_MODEM_FAILED,
+                         N_("Modem failed or no longer available")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_MODEM_AVAILABLE,
+                         N_("Modem now ready and available")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT, N_("SIM PIN was incorrect")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_NEW_ACTIVATION,
+                         N_("New connection activation was enqueued")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_PARENT_CHANGED, N_("The device's parent changed")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED,
+                         N_("The device parent's management changed")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_OVSDB_FAILED,
+                         N_("Open vSwitch database connection failed")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_IP_ADDRESS_DUPLICATE,
+                         N_("A duplicate IP address was detected")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_IP_METHOD_UNSUPPORTED,
+                         N_("The selected IP method is not supported")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED,
+                         N_("Failed to configure SR-IOV parameters")),
+    NM_UTILS_LOOKUP_ITEM(NM_DEVICE_STATE_REASON_PEER_NOT_FOUND,
+                         N_("The Wi-Fi P2P peer could not be found")), );
+
+NM_UTILS_LOOKUP_STR_DEFINE(
+    nm_active_connection_state_reason_to_string,
+    NMActiveConnectionStateReason,
+    /* TRANSLATORS: Unknown reason for a connection state change (NMActiveConnectionStateReason) */
+    NM_UTILS_LOOKUP_DEFAULT(N_("Unknown")),
+    NM_UTILS_LOOKUP_ITEM(NM_ACTIVE_CONNECTION_STATE_REASON_UNKNOWN, N_("Unknown reason")),
+    NM_UTILS_LOOKUP_ITEM(NM_ACTIVE_CONNECTION_STATE_REASON_NONE,
+                         N_("The connection was disconnected")),
+    NM_UTILS_LOOKUP_ITEM(NM_ACTIVE_CONNECTION_STATE_REASON_USER_DISCONNECTED,
+                         N_("Disconnected by user")),
+    NM_UTILS_LOOKUP_ITEM(NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED,
+                         N_("The base network connection was interrupted")),
+    NM_UTILS_LOOKUP_ITEM(NM_ACTIVE_CONNECTION_STATE_REASON_SERVICE_STOPPED,
+                         N_("The VPN service stopped unexpectedly")),
+    NM_UTILS_LOOKUP_ITEM(NM_ACTIVE_CONNECTION_STATE_REASON_IP_CONFIG_INVALID,
+                         N_("The VPN service returned invalid configuration")),
+    NM_UTILS_LOOKUP_ITEM(NM_ACTIVE_CONNECTION_STATE_REASON_CONNECT_TIMEOUT,
+                         N_("The connection attempt timed out")),
+    NM_UTILS_LOOKUP_ITEM(NM_ACTIVE_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT,
+                         N_("The VPN service did not start in time")),
+    NM_UTILS_LOOKUP_ITEM(NM_ACTIVE_CONNECTION_STATE_REASON_SERVICE_START_FAILED,
+                         N_("The VPN service failed to start")),
+    NM_UTILS_LOOKUP_ITEM(NM_ACTIVE_CONNECTION_STATE_REASON_NO_SECRETS, N_("No valid secrets")),
+    NM_UTILS_LOOKUP_ITEM(NM_ACTIVE_CONNECTION_STATE_REASON_LOGIN_FAILED, N_("Invalid secrets")),
+    NM_UTILS_LOOKUP_ITEM(NM_ACTIVE_CONNECTION_STATE_REASON_CONNECTION_REMOVED,
+                         N_("The connection was removed")),
+    NM_UTILS_LOOKUP_ITEM(NM_ACTIVE_CONNECTION_STATE_REASON_DEPENDENCY_FAILED,
+                         N_("Master connection failed")),
+    NM_UTILS_LOOKUP_ITEM(NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_REALIZE_FAILED,
+                         N_("Could not create a software link")),
+    NM_UTILS_LOOKUP_ITEM(NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_REMOVED,
+                         N_("The device disappeared")), );
+
+NMActiveConnectionState
+nmc_activation_get_effective_state(NMActiveConnection *active,
+                                   NMDevice *          device,
+                                   const char **       reason)
+{
+    NMActiveConnectionState       ac_state;
+    NMActiveConnectionStateReason ac_reason;
+    NMDeviceState                 dev_state  = NM_DEVICE_STATE_UNKNOWN;
+    NMDeviceStateReason           dev_reason = NM_DEVICE_STATE_REASON_UNKNOWN;
+
+    g_return_val_if_fail(active, NM_ACTIVE_CONNECTION_STATE_UNKNOWN);
+    g_return_val_if_fail(reason, NM_ACTIVE_CONNECTION_STATE_UNKNOWN);
+
+    *reason   = NULL;
+    ac_reason = nm_active_connection_get_state_reason(active);
+
+    if (device) {
+        dev_state  = nm_device_get_state(device);
+        dev_reason = nm_device_get_state_reason(device);
+    }
+
+    ac_state = nm_active_connection_get_state(active);
+    switch (ac_state) {
+    case NM_ACTIVE_CONNECTION_STATE_DEACTIVATED:
+        if (!device || ac_reason != NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED
+            || nm_device_get_active_connection(device) != active) {
+            /* (1)
+             * - we have no device,
+             * - or, @ac_reason is specific
+             * - or, @device no longer references the current @active
+             * >> we complete with @ac_reason. */
+            *reason = gettext(nm_active_connection_state_reason_to_string(ac_reason));
+        } else if (dev_state <= NM_DEVICE_STATE_DISCONNECTED
+                   || dev_state >= NM_DEVICE_STATE_FAILED) {
+            /* (2)
+             * - not (1)
+             * - and, the device is no longer in an activated state,
+             * >> we complete with @dev_reason. */
+            *reason = gettext(nmc_device_reason_to_string(dev_reason));
+        } else {
+            /* (3)
+             * we wait for the device go disconnect. We will get a better
+             * failure reason from the device (2). */
+            return NM_ACTIVE_CONNECTION_STATE_UNKNOWN;
+        }
+        break;
+    case NM_ACTIVE_CONNECTION_STATE_ACTIVATING:
+        /* activating master connection does not automatically activate any slaves, so their
+         * active connection state will not progress beyond ACTIVATING state.
+         * Monitor the device instead. */
+        if (device
+            && (NM_IS_DEVICE_BOND(device) || NM_IS_DEVICE_TEAM(device)
+                || NM_IS_DEVICE_BRIDGE(device))
+            && dev_state >= NM_DEVICE_STATE_IP_CONFIG && dev_state <= NM_DEVICE_STATE_ACTIVATED) {
+            *reason = "master waiting for slaves";
+            return NM_ACTIVE_CONNECTION_STATE_ACTIVATED;
+        }
+        break;
+    default:
+        break;
+    }
+
+    return ac_state;
+}
+
+static gboolean
+can_show_utf8(void)
+{
+    static gboolean can_show_utf8_set = FALSE;
+    static gboolean can_show_utf8     = TRUE;
+    char *          locale_str;
+
+    if (G_LIKELY(can_show_utf8_set))
+        return can_show_utf8;
+
+    if (!g_get_charset(NULL)) {
+        /* Non-UTF-8 locale */
+        locale_str = g_locale_from_utf8("\342\226\202\342\226\204\342\226\206\342\226\210",
+                                        -1,
+                                        NULL,
+                                        NULL,
+                                        NULL);
+        if (locale_str)
+            g_free(locale_str);
+        else
+            can_show_utf8 = FALSE;
+    }
+
+    return can_show_utf8;
+}
+
+static gboolean
+can_show_graphics(void)
+{
+    static gboolean can_show_graphics_set = FALSE;
+    static gboolean can_show_graphics     = TRUE;
+
+    if (G_LIKELY(can_show_graphics_set))
+        return can_show_graphics;
+
+    can_show_graphics = can_show_utf8();
+
+    /* The linux console font typically doesn't have characters we need */
+    if (g_strcmp0(g_getenv("TERM"), "linux") == 0)
+        can_show_graphics = FALSE;
+
+    return can_show_graphics;
+}
+
+/**
+ * nmc_wifi_strength_bars:
+ * @strength: the access point strength, from 0 to 100
+ *
+ * Converts @strength into a 4-character-wide graphical representation of
+ * strength suitable for printing to stdout. If the current locale and terminal
+ * support it, this will use unicode graphics characters to represent
+ * "bars". Otherwise, it will use 0 to 4 asterisks.
+ *
+ * Returns: the graphical representation of the access point strength
+ */
+const char *
+nmc_wifi_strength_bars(guint8 strength)
+{
+    if (!can_show_graphics())
+        return nm_utils_wifi_strength_bars(strength);
+
+    if (strength > 80)
+        return /* ▂▄▆█ */ "\342\226\202\342\226\204\342\226\206\342\226\210";
+    else if (strength > 55)
+        return /* ▂▄▆_ */ "\342\226\202\342\226\204\342\226\206_";
+    else if (strength > 30)
+        return /* ▂▄__ */ "\342\226\202\342\226\204__";
+    else if (strength > 5)
+        return /* ▂___ */ "\342\226\202___";
+    else
+        return /* ____ */ "____";
+}
+
+/**
+ * nmc_utils_password_subst_char:
+ *
+ * Returns: the string substituted when hiding actual password glyphs
+ */
+const char *
+nmc_password_subst_char(void)
+{
+    if (can_show_graphics())
+        return "\u2022"; /* Bullet */
+    else
+        return "*";
+}
+
+/*
+ * We actually use a small part of qrcodegen.c, but we'd prefer to keep it
+ * intact. Include it instead of linking to it to give the compiler a
+ * chance to optimize bits we don't need away.
+ */
+
+#pragma GCC visibility push(hidden)
+NM_PRAGMA_WARNING_DISABLE("-Wdeclaration-after-statement")
+#undef NDEBUG
+#define NDEBUG
+#include "qrcodegen.c"
+NM_PRAGMA_WARNING_REENABLE
+#pragma GCC visibility pop
+
+void
+nmc_print_qrcode(const char *str)
+{
+    uint8_t  tempBuffer[qrcodegen_BUFFER_LEN_FOR_VERSION(qrcodegen_VERSION_MAX)];
+    uint8_t  qrcode[qrcodegen_BUFFER_LEN_FOR_VERSION(qrcodegen_VERSION_MAX)];
+    gboolean term_linux;
+    int      size;
+    int      x;
+    int      y;
+
+    term_linux = g_strcmp0(g_getenv("TERM"), "linux") == 0;
+    if (!term_linux && !can_show_graphics())
+        return;
+
+    if (!qrcodegen_encodeText(str,
+                              tempBuffer,
+                              qrcode,
+                              qrcodegen_Ecc_LOW,
+                              qrcodegen_VERSION_MIN,
+                              qrcodegen_VERSION_MAX,
+                              qrcodegen_Mask_AUTO,
+                              FALSE)) {
+        return;
+    }
+
+    size = qrcodegen_getSize(qrcode);
+
+    g_print("\n");
+
+    if (term_linux) {
+        /* G1 alternate character set on Linux console. */
+        for (y = -1; y < size + 1; y += 1) {
+            g_print("  \033[37;40;1m\016");
+            for (x = -1; x < size + 1; x++) {
+                g_print(qrcodegen_getModule(qrcode, x, y) ? "  " : "\060\060");
+            }
+            g_print("\017\033[0m\n");
+        }
+    } else {
+        /* UTF-8 */
+        for (y = -2; y < size + 2; y += 2) {
+            g_print("  \033[37;40m");
+            for (x = -2; x < size + 2; x++) {
+                bool top    = qrcodegen_getModule(qrcode, x, y);
+                bool bottom = qrcodegen_getModule(qrcode, x, y + 1);
+                if (top) {
+                    g_print(bottom ? " " : "\u2584");
+                } else {
+                    g_print(bottom ? "\u2580" : "\u2588");
+                }
+            }
+            g_print("\033[0m\n");
+        }
+    }
+}
+
+/**
+ * nmc_utils_read_passwd_file:
+ * @passwd_file: file with passwords to parse
+ * @out_error_line: returns in case of a syntax error in the file, the line
+ *   on which it occurred.
+ * @error: location to store error, or %NULL
+ *
+ * Parse passwords given in @passwd_file and insert them into a hash table.
+ * Example of @passwd_file contents:
+ *   wifi.psk:tajne heslo
+ *   802-1x.password:krakonos
+ *   802-11-wireless-security:leap-password:my leap password
+ *
+ * Returns: (transfer full): hash table with parsed passwords, or %NULL on an error
+ */
+GHashTable *
+nmc_utils_read_passwd_file(const char *passwd_file, gssize *out_error_line, GError **error)
+{
+    nm_auto_clear_secret_ptr NMSecretPtr contents = {0};
+
+    NM_SET_OUT(out_error_line, -1);
+
+    if (!passwd_file)
+        return g_hash_table_new_full(nm_str_hash,
+                                     g_str_equal,
+                                     g_free,
+                                     (GDestroyNotify) nm_free_secret);
+
+    if (!nm_utils_file_get_contents(-1,
+                                    passwd_file,
+                                    1024 * 1024,
+                                    NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET,
+                                    &contents.str,
+                                    &contents.len,
+                                    NULL,
+                                    error))
+        return NULL;
+
+    return nmc_utils_parse_passwd_file(contents.str, out_error_line, error);
+}
+
+GHashTable *
+nmc_utils_parse_passwd_file(char *   contents /* will be modified */,
+                            gssize * out_error_line,
+                            GError **error)
+{
+    gs_unref_hashtable GHashTable *pwds_hash = NULL;
+    const char *                   contents_str;
+    gsize                          contents_line;
+
+    pwds_hash =
+        g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, (GDestroyNotify) nm_free_secret);
+
+    NM_SET_OUT(out_error_line, -1);
+
+    contents_str  = contents;
+    contents_line = 0;
+    while (contents_str[0]) {
+        nm_auto_free_secret char *l_hash_key = NULL;
+        nm_auto_free_secret char *l_hash_val = NULL;
+        const char *              l_content_line;
+        const char *              l_setting;
+        const char *              l_prop;
+        const char *              l_val;
+        const char *              s;
+        gsize                     l_hash_val_len;
+
+        /* consume first line. As line delimiters we accept "\r\n", "\n", and "\r". */
+        l_content_line = contents_str;
+        s              = l_content_line;
+        while (!NM_IN_SET(s[0], '\0', '\r', '\n'))
+            s++;
+        if (s[0] != '\0') {
+            if (s[0] == '\r' && s[1] == '\n') {
+                ((char *) s)[0] = '\0';
+                s += 2;
+            } else {
+                ((char *) s)[0] = '\0';
+                s += 1;
+            }
+        }
+        contents_str = s;
+        contents_line++;
+
+        l_content_line = nm_str_skip_leading_spaces(l_content_line);
+        if (NM_IN_SET(l_content_line[0], '\0', '#')) {
+            /* a comment or empty line. Ignore. */
+            continue;
+        }
+
+        l_setting = l_content_line;
+
+        s = l_setting;
+        while (!NM_IN_SET(s[0], '\0', ':', '='))
+            s++;
+        if (s[0] == '\0') {
+            NM_SET_OUT(out_error_line, contents_line);
+            nm_utils_error_set(error,
+                               NM_UTILS_ERROR_UNKNOWN,
+                               _("missing colon for \"<setting>.<property>:<secret>\" format"));
+            return NULL;
+        }
+        ((char *) s)[0] = '\0';
+        s++;
+
+        l_val = s;
+
+        g_strchomp((char *) l_setting);
+
+        nm_assert(nm_str_is_stripped(l_setting));
+
+        s = strchr(l_setting, '.');
+        if (!s) {
+            NM_SET_OUT(out_error_line, contents_line);
+            nm_utils_error_set(error,
+                               NM_UTILS_ERROR_UNKNOWN,
+                               _("missing dot for \"<setting>.<property>:<secret>\" format"));
+            return NULL;
+        } else if (s == l_setting) {
+            NM_SET_OUT(out_error_line, contents_line);
+            nm_utils_error_set(error,
+                               NM_UTILS_ERROR_UNKNOWN,
+                               _("missing setting for \"<setting>.<property>:<secret>\" format"));
+            return NULL;
+        }
+        ((char *) s)[0] = '\0';
+        s++;
+
+        l_prop = s;
+        if (l_prop[0] == '\0') {
+            NM_SET_OUT(out_error_line, contents_line);
+            nm_utils_error_set(error,
+                               NM_UTILS_ERROR_UNKNOWN,
+                               _("missing property for \"<setting>.<property>:<secret>\" format"));
+            return NULL;
+        }
+
+        /* Accept wifi-sec or wifi instead of cumbersome '802-11-wireless-security' */
+        if (NM_IN_STRSET(l_setting, "wifi-sec", "wifi"))
+            l_setting = NM_SETTING_WIRELESS_SECURITY_SETTING_NAME;
+
+        if (nm_setting_lookup_type(l_setting) == G_TYPE_INVALID) {
+            NM_SET_OUT(out_error_line, contents_line);
+            nm_utils_error_set(error, NM_UTILS_ERROR_UNKNOWN, _("invalid setting name"));
+            return NULL;
+        }
+
+        if (nm_streq(l_setting, "vpn") && NM_STR_HAS_PREFIX(l_prop, "secret.")) {
+            /* in 1.12.0, we wrongly required the VPN secrets to be named
+             * "vpn.secret". It should be "vpn.secrets". Work around it
+             * (rh#1628833). */
+            l_hash_key = g_strdup_printf("vpn.secrets.%s", &l_prop[NM_STRLEN("secret.")]);
+        } else
+            l_hash_key = g_strdup_printf("%s.%s", l_setting, l_prop);
+
+        if (!g_utf8_validate(l_hash_key, -1, NULL)) {
+            NM_SET_OUT(out_error_line, contents_line);
+            nm_utils_error_set(error, NM_UTILS_ERROR_UNKNOWN, _("property name is not UTF-8"));
+            return NULL;
+        }
+
+        /* Support backslash escaping in the secret value. We strip non-escaped leading/trailing whitespaces. */
+        s = nm_utils_buf_utf8safe_unescape(l_val,
+                                           NM_UTILS_STR_UTF8_SAFE_UNESCAPE_STRIP_SPACES,
+                                           &l_hash_val_len,
+                                           (gpointer *) &l_hash_val);
+        if (!l_hash_val)
+            l_hash_val = g_strdup(s);
+
+        if (!g_utf8_validate(l_hash_val, -1, NULL)) {
+            /* In some cases it might make sense to support binary secrets (like the WPA-PSK which has no
+             * defined encoding. However, all API that follows can only handle UTF-8, and no mechanism
+             * to escape the secrets. Reject non-UTF-8 early. */
+            NM_SET_OUT(out_error_line, contents_line);
+            nm_utils_error_set(error, NM_UTILS_ERROR_UNKNOWN, _("secret is not UTF-8"));
+            return NULL;
+        }
+
+        if (strlen(l_hash_val) != l_hash_val_len) {
+            NM_SET_OUT(out_error_line, contents_line);
+            nm_utils_error_set(error, NM_UTILS_ERROR_UNKNOWN, _("secret is not UTF-8"));
+            return NULL;
+        }
+
+        g_hash_table_insert(pwds_hash, g_steal_pointer(&l_hash_key), g_steal_pointer(&l_hash_val));
+    }
+
+    return g_steal_pointer(&pwds_hash);
+}