/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2010 - 2017 Red Hat, Inc. */ #include "nm-default.h" #include "nm-client-utils.h" #include "nm-glib-aux/nm-secret-utils.h" #include "nm-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 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) { const char *s_true[] = {"true", "yes", "on", "1", NULL}; const char *s_false[] = {"false", "no", "off", "0", NULL}; g_return_val_if_fail(error == NULL || *error == NULL, FALSE); if (g_strcmp0(str, "o") == 0) { g_set_error(error, 1, 0, /* 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 x off"); return FALSE; } if (nmc_string_is_valid(str, s_true, NULL)) *val_bool = TRUE; else if (nmc_string_is_valid(str, s_false, NULL)) *val_bool = FALSE; else { g_set_error(error, 1, 0, _("'%s' is not valid; use [%s] or [%s]"), str, "true, yes, on", "false, no, off"); return FALSE; } return TRUE; } gboolean nmc_string_to_ternary(const char *str, NMTernary *val, GError **error) { const char *s_true[] = {"true", "yes", "on", NULL}; const char *s_false[] = {"false", "no", "off", NULL}; const char *s_unknown[] = {"unknown", NULL}; g_return_val_if_fail(error == NULL || *error == NULL, FALSE); if (g_strcmp0(str, "o") == 0) { g_set_error(error, 1, 0, /* 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 x off"); return FALSE; } if (nmc_string_is_valid(str, s_true, NULL)) *val = NM_TERNARY_TRUE; else if (nmc_string_is_valid(str, s_false, NULL)) *val = NM_TERNARY_FALSE; else if (nmc_string_is_valid(str, s_unknown, NULL)) *val = NM_TERNARY_DEFAULT; else { g_set_error(error, 1, 0, _("'%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 **allowed, GError **error) { const char **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); } } g_set_error(error, 1, 1, _("'%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) g_set_error(error, 1, 0, _("missing name, try one of [%s]"), valid_vals); else g_set_error(error, 1, 0, _("'%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 \".:\" 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 \".:\" 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 \".:\" 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 \".:\" 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); }