/* nmcli - command-line tool to control NetworkManager * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * (C) Copyright 2010 - 2014 Red Hat, Inc. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if WITH_WIMAX #include #endif #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" #include "common.h" #include "settings.h" #include "connections.h" /* Activation timeout waiting for bond/team/bridge slaves (in seconds) */ #define SLAVES_UP_TIMEOUT 10 /* define some prompts for connection editor */ #define EDITOR_PROMPT_SETTING _("Setting name? ") #define EDITOR_PROMPT_PROPERTY _("Property name? ") #define EDITOR_PROMPT_CON_TYPE _("Enter connection type: ") /* Available fields for 'connection show' */ static NmcOutputField nmc_fields_con_show[] = { {"NAME", N_("NAME"), 25}, /* 0 */ {"UUID", N_("UUID"), 38}, /* 1 */ {"TYPE", N_("TYPE"), 17}, /* 2 */ {"TIMESTAMP", N_("TIMESTAMP"), 12}, /* 3 */ {"TIMESTAMP-REAL", N_("TIMESTAMP-REAL"), 34}, /* 4 */ {"AUTOCONNECT", N_("AUTOCONNECT"), 13}, /* 5 */ {"READONLY", N_("READONLY"), 10}, /* 6 */ {"DBUS-PATH", N_("DBUS-PATH"), 42}, /* 7 */ {"ACTIVE", N_("ACTIVE"), 10}, /* 8 */ {"DEVICE", N_("DEVICE"), 10}, /* 9 */ {"STATE", N_("STATE"), 12}, /* 10 */ {"ACTIVE-PATH", N_("ACTIVE-PATH"), 51}, /* 11 */ {NULL, NULL, 0} }; #define NMC_FIELDS_CON_SHOW_ALL "NAME,UUID,TYPE,TIMESTAMP,TIMESTAMP-REAL,AUTOCONNECT,READONLY,DBUS-PATH,"\ "ACTIVE,DEVICE,STATE,ACTIVE-PATH" #define NMC_FIELDS_CON_SHOW_COMMON "NAME,UUID,TYPE,DEVICE" /* Helper macro to define fields */ #define SETTING_FIELD(setting, props) { setting, N_(setting), 0, props, NULL, FALSE, FALSE, 0 } /* defined in settings.c */ extern NmcOutputField nmc_fields_setting_connection[]; extern NmcOutputField nmc_fields_setting_wired[]; extern NmcOutputField nmc_fields_setting_8021X[]; extern NmcOutputField nmc_fields_setting_wireless[]; extern NmcOutputField nmc_fields_setting_wireless_security[]; extern NmcOutputField nmc_fields_setting_ip4_config[]; extern NmcOutputField nmc_fields_setting_ip6_config[]; extern NmcOutputField nmc_fields_setting_serial[]; extern NmcOutputField nmc_fields_setting_ppp[]; extern NmcOutputField nmc_fields_setting_pppoe[]; extern NmcOutputField nmc_fields_setting_adsl[]; extern NmcOutputField nmc_fields_setting_gsm[]; extern NmcOutputField nmc_fields_setting_cdma[]; extern NmcOutputField nmc_fields_setting_bluetooth[]; extern NmcOutputField nmc_fields_setting_olpc_mesh[]; extern NmcOutputField nmc_fields_setting_vpn[]; extern NmcOutputField nmc_fields_setting_wimax[]; extern NmcOutputField nmc_fields_setting_infiniband[]; extern NmcOutputField nmc_fields_setting_bond[]; extern NmcOutputField nmc_fields_setting_vlan[]; extern NmcOutputField nmc_fields_setting_bridge[]; extern NmcOutputField nmc_fields_setting_bridge_port[]; extern NmcOutputField nmc_fields_setting_team[]; extern NmcOutputField nmc_fields_setting_team_port[]; extern NmcOutputField nmc_fields_setting_dcb[]; /* Available settings for 'connection show ' - profile part */ static NmcOutputField nmc_fields_settings_names[] = { SETTING_FIELD (NM_SETTING_CONNECTION_SETTING_NAME, nmc_fields_setting_connection + 1), /* 0 */ SETTING_FIELD (NM_SETTING_WIRED_SETTING_NAME, nmc_fields_setting_wired + 1), /* 1 */ SETTING_FIELD (NM_SETTING_802_1X_SETTING_NAME, nmc_fields_setting_8021X + 1), /* 2 */ SETTING_FIELD (NM_SETTING_WIRELESS_SETTING_NAME, nmc_fields_setting_wireless + 1), /* 3 */ SETTING_FIELD (NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, nmc_fields_setting_wireless_security + 1), /* 4 */ SETTING_FIELD (NM_SETTING_IP4_CONFIG_SETTING_NAME, nmc_fields_setting_ip4_config + 1), /* 5 */ SETTING_FIELD (NM_SETTING_IP6_CONFIG_SETTING_NAME, nmc_fields_setting_ip6_config + 1), /* 6 */ SETTING_FIELD (NM_SETTING_SERIAL_SETTING_NAME, nmc_fields_setting_serial + 1), /* 7 */ SETTING_FIELD (NM_SETTING_PPP_SETTING_NAME, nmc_fields_setting_ppp + 1), /* 8 */ SETTING_FIELD (NM_SETTING_PPPOE_SETTING_NAME, nmc_fields_setting_pppoe + 1), /* 9 */ SETTING_FIELD (NM_SETTING_GSM_SETTING_NAME, nmc_fields_setting_gsm + 1), /* 10 */ SETTING_FIELD (NM_SETTING_CDMA_SETTING_NAME, nmc_fields_setting_cdma + 1), /* 11 */ SETTING_FIELD (NM_SETTING_BLUETOOTH_SETTING_NAME, nmc_fields_setting_bluetooth + 1), /* 12 */ SETTING_FIELD (NM_SETTING_OLPC_MESH_SETTING_NAME, nmc_fields_setting_olpc_mesh + 1), /* 13 */ SETTING_FIELD (NM_SETTING_VPN_SETTING_NAME, nmc_fields_setting_vpn + 1), /* 14 */ SETTING_FIELD (NM_SETTING_WIMAX_SETTING_NAME, nmc_fields_setting_wimax + 1), /* 15 */ SETTING_FIELD (NM_SETTING_INFINIBAND_SETTING_NAME, nmc_fields_setting_infiniband + 1), /* 16 */ SETTING_FIELD (NM_SETTING_BOND_SETTING_NAME, nmc_fields_setting_bond + 1), /* 17 */ SETTING_FIELD (NM_SETTING_VLAN_SETTING_NAME, nmc_fields_setting_vlan + 1), /* 18 */ SETTING_FIELD (NM_SETTING_ADSL_SETTING_NAME, nmc_fields_setting_adsl + 1), /* 19 */ SETTING_FIELD (NM_SETTING_BRIDGE_SETTING_NAME, nmc_fields_setting_bridge + 1), /* 20 */ SETTING_FIELD (NM_SETTING_BRIDGE_PORT_SETTING_NAME, nmc_fields_setting_bridge_port + 1), /* 21 */ SETTING_FIELD (NM_SETTING_TEAM_SETTING_NAME, nmc_fields_setting_team + 1), /* 22 */ SETTING_FIELD (NM_SETTING_TEAM_PORT_SETTING_NAME, nmc_fields_setting_team_port + 1), /* 23 */ SETTING_FIELD (NM_SETTING_DCB_SETTING_NAME, nmc_fields_setting_dcb + 1), /* 24 */ {NULL, NULL, 0, NULL, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTINGS_NAMES_ALL_X NM_SETTING_CONNECTION_SETTING_NAME","\ NM_SETTING_WIRED_SETTING_NAME","\ NM_SETTING_802_1X_SETTING_NAME","\ NM_SETTING_WIRELESS_SETTING_NAME","\ NM_SETTING_WIRELESS_SECURITY_SETTING_NAME","\ NM_SETTING_IP4_CONFIG_SETTING_NAME","\ NM_SETTING_IP6_CONFIG_SETTING_NAME","\ NM_SETTING_SERIAL_SETTING_NAME","\ NM_SETTING_PPP_SETTING_NAME","\ NM_SETTING_PPPOE_SETTING_NAME","\ NM_SETTING_ADSL_SETTING_NAME","\ NM_SETTING_GSM_SETTING_NAME","\ NM_SETTING_CDMA_SETTING_NAME","\ NM_SETTING_BLUETOOTH_SETTING_NAME","\ NM_SETTING_OLPC_MESH_SETTING_NAME","\ NM_SETTING_VPN_SETTING_NAME","\ NM_SETTING_INFINIBAND_SETTING_NAME","\ NM_SETTING_BOND_SETTING_NAME","\ NM_SETTING_VLAN_SETTING_NAME","\ NM_SETTING_BRIDGE_SETTING_NAME","\ NM_SETTING_BRIDGE_PORT_SETTING_NAME","\ NM_SETTING_TEAM_SETTING_NAME","\ NM_SETTING_TEAM_PORT_SETTING_NAME"," \ NM_SETTING_DCB_SETTING_NAME #if WITH_WIMAX #define NMC_FIELDS_SETTINGS_NAMES_ALL NMC_FIELDS_SETTINGS_NAMES_ALL_X","\ NM_SETTING_WIMAX_SETTING_NAME #else #define NMC_FIELDS_SETTINGS_NAMES_ALL NMC_FIELDS_SETTINGS_NAMES_ALL_X #endif /* Active connection data */ /* Available fields for GENERAL group */ static NmcOutputField nmc_fields_con_active_details_general[] = { {"GROUP", N_("GROUP"), 9}, /* 0 */ {"NAME", N_("NAME"), 25}, /* 1 */ {"UUID", N_("UUID"), 38}, /* 2 */ {"DEVICES", N_("DEVICES"), 10}, /* 3 */ {"STATE", N_("STATE"), 12}, /* 4 */ {"DEFAULT", N_("DEFAULT"), 8}, /* 5 */ {"DEFAULT6", N_("DEFAULT6"), 9}, /* 6 */ {"SPEC-OBJECT", N_("SPEC-OBJECT"), 10}, /* 7 */ {"VPN", N_("VPN"), 5}, /* 8 */ {"DBUS-PATH", N_("DBUS-PATH"), 51}, /* 9 */ {"CON-PATH", N_("CON-PATH"), 44}, /* 10 */ {"ZONE", N_("ZONE"), 15}, /* 11 */ {"MASTER-PATH", N_("MASTER-PATH"), 44}, /* 12 */ {NULL, NULL, 0} }; #define NMC_FIELDS_CON_ACTIVE_DETAILS_GENERAL_ALL "GROUP,NAME,UUID,DEVICES,STATE,DEFAULT,DEFAULT6,"\ "VPN,ZONE,DBUS-PATH,CON-PATH,SPEC-OBJECT,MASTER-PATH" /* IP group is handled by common.c */ /* Available fields for VPN group */ static NmcOutputField nmc_fields_con_active_details_vpn[] = { {"GROUP", N_("GROUP"), 9}, /* 0 */ {"TYPE", N_("TYPE"), 15}, /* 1 */ {"USERNAME", N_("USERNAME"), 15}, /* 2 */ {"GATEWAY", N_("GATEWAY"), 25}, /* 3 */ {"BANNER", N_("BANNER"), 120}, /* 4 */ {"VPN-STATE", N_("VPN-STATE"), 40}, /* 5 */ {"CFG", N_("CFG"), 120}, /* 6 */ {NULL, NULL, 0} }; #define NMC_FIELDS_CON_ACTIVE_DETAILS_VPN_ALL "GROUP,TYPE,USERNAME,GATEWAY,BANNER,VPN-STATE,CFG" /* defined in common.c */ extern NmcOutputField nmc_fields_ip4_config[]; extern NmcOutputField nmc_fields_ip6_config[]; extern NmcOutputField nmc_fields_dhcp4_config[]; extern NmcOutputField nmc_fields_dhcp6_config[]; /* Available fields for 'connection show ' - active part */ static NmcOutputField nmc_fields_con_active_details_groups[] = { {"GENERAL", N_("GENERAL"), 0, nmc_fields_con_active_details_general + 1}, /* 0 */ {"IP4", N_("IP4"), 0, nmc_fields_ip4_config + 1 }, /* 1 */ {"DHCP4", N_("DHCP4"), 0, nmc_fields_dhcp4_config + 1 }, /* 2 */ {"IP6", N_("IP6"), 0, nmc_fields_ip6_config + 1 }, /* 3 */ {"DHCP6", N_("DHCP6"), 0, nmc_fields_dhcp6_config + 1 }, /* 4 */ {"VPN", N_("VPN"), 0, nmc_fields_con_active_details_vpn + 1 }, /* 5 */ {NULL, NULL, 0, NULL} }; #define NMC_FIELDS_CON_ACTIVE_DETAILS_ALL "GENERAL,IP4,DHCP4,IP6,DHCP6,VPN" /* Pseudo group names for 'connection show ' */ /* e.g.: nmcli -f profile con show my-eth0 */ /* e.g.: nmcli -f active con show my-eth0 */ #define CON_SHOW_DETAIL_GROUP_PROFILE "profile" #define CON_SHOW_DETAIL_GROUP_ACTIVE "active" typedef struct { NmCli *nmc; int argc; char **argv; } ArgsInfo; /* glib main loop variable - defined in nmcli.c */ extern GMainLoop *loop; static ArgsInfo args_info; static guint progress_id = 0; /* ID of event source for displaying progress */ /* for readline TAB completion */ typedef struct { NmCli *nmc; char *con_type; NMConnection *connection; NMSetting *setting; } TabCompletionInfo; static TabCompletionInfo nmc_tab_completion = {NULL, NULL, NULL, NULL}; static void usage (void) { fprintf (stderr, _("Usage: nmcli connection { COMMAND | help }\n\n" "COMMAND := { show | up | down | add | modify | edit | delete | reload | load }\n\n" " show [--active] [[id | uuid | path | apath] ] ...\n\n" #if WITH_WIMAX " up [[id | uuid | path] ] [ifname ] [ap ] [nsp ]\n\n" #else " up [[id | uuid | path] ] [ifname ] [ap ]\n\n" #endif " down [id | uuid | path | apath] \n\n" " add COMMON_OPTIONS TYPE_SPECIFIC_OPTIONS IP_OPTIONS\n\n" " modify [id | uuid | path] . \n\n" " edit [id | uuid | path] \n" " edit [type ] [con-name ]\n\n" " delete [id | uuid | path] \n\n" " reload\n\n" " load [ ... ]\n\n")); } static void usage_connection_show (void) { fprintf (stderr, _("Usage: nmcli connection show { ARGUMENTS | help }\n" "\n" "ARGUMENTS := [--active]\n" "\n" "List in-memory and on-disk connection profiles, some of which may also be\n" "active if a device is using that connection profile. Without a parameter, all\n" "profiles are listed. When --active option is specified, only the active\n" "profiles are shown.\n" "\n" "ARGUMENTS := [--active] [id | uuid | path | apath] ...\n" "\n" "Show details for specified connections. By default, both static configuration\n" "and active connection data are displayed. It is possible to filter the output\n" "using global '--fields' option. Refer to the manual page for more information.\n" "When --active option is specified, only the active profiles are taken into\n" "account.\n")); } static void usage_connection_up (void) { fprintf (stderr, _("Usage: nmcli connection up { ARGUMENTS | help }\n" "\n" "ARGUMENTS := [id | uuid | path] [ifname ] [ap ] [nsp ]\n" "\n" "Activate a connection on a device. The profile to activate is identified by its\n" "name, UUID or D-Bus path.\n" "\n" "ARGUMENTS := ifname [ap ] [nsp ]\n" "\n" "Activate a device with a connection. The connection profile is selected\n" "automatically by NetworkManager.\n" "\n" "ifname - specifies the device to active the connection on\n" "ap - specifies AP to connect to (only valid for Wi-Fi)\n" "nsp - specifies NSP to connect to (only valid for WiMAX)\n\n")); } static void usage_connection_down (void) { fprintf (stderr, _("Usage: nmcli connection down { ARGUMENTS | help }\n" "\n" "ARGUMENTS := [id | uuid | path | apath] \n" "\n" "Deactivate a connection from a device (without preventing the device from\n" "further auto-activation). The profile to deactivate is identified by its name,\n" "UUID or D-Bus path.\n\n")); } static void usage_connection_add (void) { fprintf (stderr, _("Usage: nmcli connection add { ARGUMENTS | help }\n" "\n" "ARGUMENTS := COMMON_OPTIONS TYPE_SPECIFIC_OPTIONS IP_OPTIONS\n\n" " COMMON_OPTIONS:\n" " type \n" " ifname | \"*\"\n" " [con-name ]\n" " [autoconnect yes|no]\n\n" " TYPE_SPECIFIC_OPTIONS:\n" " ethernet: [mac ]\n" " [cloned-mac ]\n" " [mtu ]\n\n" " wifi: ssid \n" " [mac ]\n" " [cloned-mac ]\n" " [mtu ]\n\n" " wimax: [mac ]\n" " [nsp ]\n\n" " pppoe: username \n" " [password ]\n" " [service ]\n" " [mtu ]\n" " [mac ]\n\n" " gsm: apn \n" " [user ]\n" " [password ]\n\n" " cdma: [user ]\n" " [password ]\n\n" " infiniband: [mac ]\n" " [mtu ]\n" " [transport-mode datagram | connected]\n" " [parent ]\n" " [p-key ]\n\n" " bluetooth: [addr ]\n" " [bt-type panu|dun-gsm|dun-cdma]\n\n" " vlan: dev \n" " id \n" " [flags ]\n" " [ingress ]\n" " [egress ]\n" " [mtu ]\n\n" " bond: [mode balance-rr (0) | active-backup (1) | balance-xor (2) | broadcast (3) |\n" " 802.3ad (4) | balance-tlb (5) | balance-alb (6)]\n" " [primary ]\n" " [miimon ]\n" " [downdelay ]\n" " [updelay ]\n" " [arp-interval ]\n" " [arp-ip-target ]\n\n" " bond-slave: master \n\n" " team: [config |]\n\n" " team-slave: master \n" " [config |]\n\n" " bridge: [stp yes|no]\n" " [priority ]\n" " [forward-delay <2-30>]\n" " [hello-time <1-10>]\n" " [max-age <6-40>]\n" " [ageing-time <0-1000000>]\n\n" " bridge-slave: master \n" " [priority <0-63>]\n" " [path-cost <1-65535>]\n" " [hairpin yes|no]\n\n" " vpn: vpn-type vpnc|openvpn|pptp|openconnect|openswan\n" " [user ]\n\n" " olpc-mesh: ssid \n" " [channel <1-13>]\n" " [dhcp-anycast ]\n\n" " IP_OPTIONS:\n" " [ip4 ] [gw4 ]\n" " [ip6 ] [gw6 ]\n\n")); } static void usage_connection_modify (void) { fprintf (stderr, _("Usage: nmcli connection modify { ARGUMENTS | help }\n" "\n" "ARGUMENTS := [id | uuid | path] . []\n" "\n" "Modify a single property in the connection profile.\n" "The profile is identified by its name, UUID or D-Bus path.\n\n")); } static void usage_connection_edit (void) { fprintf (stderr, _("Usage: nmcli connection edit { ARGUMENTS | help }\n" "\n" "ARGUMENTS := [id | uuid | path] \n" "\n" "Edit an existing connection profile in an interactive editor.\n" "The profile is identified by its name, UUID or D-Bus path\n" "\n" "ARGUMENTS := [type ] [con-name ]\n" "\n" "Add a new connection profile in an interactive editor.\n\n")); } static void usage_connection_delete (void) { fprintf (stderr, _("Usage: nmcli connection delete { ARGUMENTS | help }\n" "\n" "ARGUMENTS := [id | uuid | path] \n" "\n" "Delete a connection profile.\n" "The profile is identified by its name, UUID or D-Bus path.\n\n")); } static void usage_connection_reload (void) { fprintf (stderr, _("Usage: nmcli connection reload { help }\n" "\n" "Reload all connection files from disk.\n\n")); } static void usage_connection_load (void) { fprintf (stderr, _("Usage: nmcli connection load { ARGUMENTS | help }\n" "\n" "ARGUMENTS := [...]\n" "\n" "Load/reload one or more connection files from disk. Use this after manually\n" "editing a connection file to ensure that NetworkManager is aware of its latest\n" "state.\n\n")); } /* The real commands that do something - i.e. not 'help', etc. */ static const char *real_con_commands[] = { "show", "up", "down", "add", "modify", "edit", "delete", "reload", "load", NULL }; /* quit main loop */ static void quit (void) { if (progress_id) { g_source_remove (progress_id); progress_id = 0; nmc_terminal_erase_line (); } g_main_loop_quit (loop); /* quit main loop */ } static const char * construct_header_name (const char *base, const char *spec) { static char header_name[128]; if (spec == NULL) return base; g_strlcpy (header_name, base, sizeof (header_name)); g_strlcat (header_name, " (", sizeof (header_name)); g_strlcat (header_name, spec, sizeof (header_name)); g_strlcat (header_name, ")", sizeof (header_name)); return header_name; } static const char * active_connection_state_to_string (NMActiveConnectionState state) { switch (state) { case NM_ACTIVE_CONNECTION_STATE_ACTIVATING: return _("activating"); case NM_ACTIVE_CONNECTION_STATE_ACTIVATED: return _("activated"); case NM_ACTIVE_CONNECTION_STATE_DEACTIVATING: return _("deactivating"); case NM_ACTIVE_CONNECTION_STATE_DEACTIVATED: return _("deactivated"); case NM_ACTIVE_CONNECTION_STATE_UNKNOWN: default: return _("unknown"); } } static const char * vpn_connection_state_to_string (NMVPNConnectionState state) { switch (state) { case NM_VPN_CONNECTION_STATE_PREPARE: return _("VPN connecting (prepare)"); case NM_VPN_CONNECTION_STATE_NEED_AUTH: return _("VPN connecting (need authentication)"); case NM_VPN_CONNECTION_STATE_CONNECT: return _("VPN connecting"); case NM_VPN_CONNECTION_STATE_IP_CONFIG_GET: return _("VPN connecting (getting IP configuration)"); case NM_VPN_CONNECTION_STATE_ACTIVATED: return _("VPN connected"); case NM_VPN_CONNECTION_STATE_FAILED: return _("VPN connection failed"); case NM_VPN_CONNECTION_STATE_DISCONNECTED: return _("VPN disconnected"); default: return _("unknown"); } } /* Caller has to free the returned string */ static char * get_ac_device_string (NMActiveConnection *active) { GString *dev_str; const GPtrArray *devices; int i; if (!active) return NULL; /* Get devices of the active connection */ dev_str = g_string_new (NULL); devices = nm_active_connection_get_devices (active); for (i = 0; devices && (i < devices->len); i++) { NMDevice *device = g_ptr_array_index (devices, i); const char *dev_iface = nm_device_get_iface (device); if (dev_iface) { g_string_append (dev_str, dev_iface); g_string_append_c (dev_str, ','); } } if (dev_str->len > 0) g_string_truncate (dev_str, dev_str->len - 1); /* Cut off last ',' */ return g_string_free (dev_str, FALSE); } static NMActiveConnection * get_ac_for_connection (const GPtrArray *active_cons, NMConnection *connection) { const char *con_path; int i; NMActiveConnection *ac = NULL; /* Is the connection active? */ con_path = nm_connection_get_path (connection); for (i = 0; active_cons && i < active_cons->len; i++) { NMActiveConnection *candidate = g_ptr_array_index (active_cons, i); if (!g_strcmp0 (nm_active_connection_get_connection (candidate), con_path)) { ac = candidate; break; } } return ac; } static NMConnection * get_connection_for_active (const GSList *con_list, NMActiveConnection *active) { const GSList *iter; const char *path; path = nm_active_connection_get_connection (active); g_return_val_if_fail (path != NULL, NULL); for (iter = con_list; iter; iter = g_slist_next (iter)) { NMConnection *candidate = NM_CONNECTION (iter->data); if (strcmp (nm_connection_get_path (candidate), path) == 0) return candidate; } return NULL; } static gboolean nmc_connection_profile_details (NMConnection *connection, NmCli *nmc) { GError *error = NULL; GArray *print_settings_array; GPtrArray *prop_array = NULL; int i; char *fields_str; char *fields_all = NMC_FIELDS_SETTINGS_NAMES_ALL; char *fields_common = NMC_FIELDS_SETTINGS_NAMES_ALL; const char *base_hdr = _("Connection profile details"); gboolean was_output = FALSE; if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0) fields_str = fields_common; else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0) fields_str = fields_all; else fields_str = nmc->required_fields; print_settings_array = parse_output_fields (fields_str, nmc_fields_settings_names, TRUE, &prop_array, &error); if (error) { g_string_printf (nmc->return_text, _("Error: 'connection show': %s"), error->message); g_error_free (error); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; return FALSE; } g_assert (print_settings_array); /* Main header */ nmc->print_fields.header_name = (char *) construct_header_name (base_hdr, nm_connection_get_id (connection)); nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_SETTINGS_NAMES_ALL, nmc_fields_settings_names, FALSE, NULL, NULL); nmc_fields_settings_names[0].flags = NMC_OF_FLAG_MAIN_HEADER_ONLY; print_required_fields (nmc, nmc_fields_settings_names); /* Loop through the required settings and print them. */ for (i = 0; i < print_settings_array->len; i++) { NMSetting *setting; int section_idx = g_array_index (print_settings_array, int, i); const char *prop_name = (const char *) g_ptr_array_index (prop_array, i); if (nmc->print_output != NMC_PRINT_TERSE && !nmc->multiline_output && was_output) printf ("\n"); /* Empty line */ was_output = FALSE; /* Remove any previous data */ nmc_empty_output_fields (nmc); setting = nm_connection_get_setting_by_name (connection, nmc_fields_settings_names[section_idx].name); if (setting) { setting_details (setting, nmc, prop_name); was_output = TRUE; continue; } } g_array_free (print_settings_array, TRUE); if (prop_array) g_ptr_array_free (prop_array, TRUE); return TRUE; } static NMConnection * find_connection (GSList *list, const char *filter_type, const char *filter_val) { NMConnection *connection; GSList *iterator; const char *id; const char *uuid; const char *path, *path_num; iterator = list; while (iterator) { connection = NM_CONNECTION (iterator->data); id = nm_connection_get_id (connection); uuid = nm_connection_get_uuid (connection); path = nm_connection_get_path (connection); path_num = path ? strrchr (path, '/') + 1 : NULL; /* When filter_type is NULL, compare connection ID (filter_val) * against all types. Otherwise, only compare against the specific * type. If 'path' filter type is specified, comparison against * numeric index (in addition to the whole path) is allowed. */ if ( ( (!filter_type || strcmp (filter_type, "id") == 0) && strcmp (filter_val, id) == 0) || ( (!filter_type || strcmp (filter_type, "uuid") == 0) && strcmp (filter_val, uuid) == 0) || ( (!filter_type || strcmp (filter_type, "path") == 0) && (g_strcmp0 (filter_val, path) == 0 || (filter_type && g_strcmp0 (filter_val, path_num) == 0)))) return connection; iterator = g_slist_next (iterator); } return NULL; } static NMActiveConnection * find_active_connection (const GPtrArray *active_cons, const GSList *cons, const char *filter_type, const char *filter_val) { int i; const char *path, *a_path, *path_num, *a_path_num; const char *id; const char *uuid; NMConnection *con; for (i = 0; active_cons && (i < active_cons->len); i++) { NMActiveConnection *candidate = g_ptr_array_index (active_cons, i); path = nm_active_connection_get_connection (candidate); a_path = nm_object_get_path (NM_OBJECT (candidate)); uuid = nm_active_connection_get_uuid (candidate); path_num = path ? strrchr (path, '/') + 1 : NULL; a_path_num = a_path ? strrchr (a_path, '/') + 1 : NULL; con = get_connection_for_active (cons, candidate); id = nm_connection_get_id (con); /* When filter_type is NULL, compare connection ID (filter_val) * against all types. Otherwise, only compare against the specific * type. If 'path' or 'apath' filter types are specified, comparison * against numeric index (in addition to the whole path) is allowed. */ if ( ( (!filter_type || strcmp (filter_type, "id") == 0) && strcmp (filter_val, id) == 0) || ( (!filter_type || strcmp (filter_type, "uuid") == 0) && strcmp (filter_val, uuid) == 0) || ( (!filter_type || strcmp (filter_type, "path") == 0) && (g_strcmp0 (filter_val, path) == 0 || (filter_type && g_strcmp0 (filter_val, path_num) == 0))) || ( (!filter_type || strcmp (filter_type, "apath") == 0) && (g_strcmp0 (filter_val, a_path) == 0 || (filter_type && g_strcmp0 (filter_val, a_path_num) == 0)))) return candidate; } return NULL; } static void fill_output_connection (gpointer data, gpointer user_data, gboolean active_only) { NMConnection *connection = (NMConnection *) data; NmCli *nmc = (NmCli *) user_data; NMSettingConnection *s_con; guint64 timestamp; time_t timestamp_real; char *timestamp_str; char *timestamp_real_str = ""; NmcOutputField *arr; NMActiveConnection *ac = NULL; const char *ac_path = NULL; const char *ac_state = NULL; char *ac_dev = NULL; s_con = nm_connection_get_setting_connection (connection); g_assert (s_con); ac = get_ac_for_connection (nm_client_get_active_connections (nmc->client), connection); if (active_only && !ac) return; if (ac) { ac_path = nm_object_get_path (NM_OBJECT (ac)); ac_state = active_connection_state_to_string (nm_active_connection_get_state (ac)); ac_dev = get_ac_device_string (ac); } /* Obtain field values */ timestamp = nm_setting_connection_get_timestamp (s_con); timestamp_str = g_strdup_printf ("%" G_GUINT64_FORMAT, timestamp); if (timestamp) { timestamp_real = timestamp; timestamp_real_str = g_malloc0 (64); strftime (timestamp_real_str, 64, "%c", localtime (×tamp_real)); } arr = nmc_dup_fields_array (nmc_fields_con_show, sizeof (nmc_fields_con_show), 0); set_val_strc (arr, 0, nm_setting_connection_get_id (s_con)); set_val_strc (arr, 1, nm_setting_connection_get_uuid (s_con)); set_val_strc (arr, 2, nm_setting_connection_get_connection_type (s_con)); set_val_str (arr, 3, timestamp_str); set_val_str (arr, 4, timestamp ? timestamp_real_str : g_strdup (_("never"))); set_val_strc (arr, 5, nm_setting_connection_get_autoconnect (s_con) ? _("yes") : _("no")); set_val_strc (arr, 6, nm_setting_connection_get_read_only (s_con) ? _("yes") : _("no")); set_val_strc (arr, 7, nm_connection_get_path (connection)); set_val_strc (arr, 8, ac ? _("yes") : _("no")); set_val_str (arr, 9, ac_dev); set_val_strc (arr, 10, ac_state); set_val_strc (arr, 11, ac_path); g_ptr_array_add (nmc->output_data, arr); } static void fill_output_active_connection (NMActiveConnection *active, NmCli *nmc, gboolean with_group, guint32 o_flags) { GSList *iter; const char *active_path; NMSettingConnection *s_con; const GPtrArray *devices; GString *dev_str; NMActiveConnectionState state; int i; GSList *con_list = nmc->system_connections; NmcOutputField *tmpl, *arr; size_t tmpl_len; int idx_start = with_group ? 0 : 1; active_path = nm_active_connection_get_connection (active); state = nm_active_connection_get_state (active); /* Get devices of the active connection */ dev_str = g_string_new (NULL); devices = nm_active_connection_get_devices (active); for (i = 0; devices && (i < devices->len); i++) { NMDevice *device = g_ptr_array_index (devices, i); const char *dev_iface = nm_device_get_iface (device); if (dev_iface) { g_string_append (dev_str, dev_iface); g_string_append_c (dev_str, ','); } } if (dev_str->len > 0) g_string_truncate (dev_str, dev_str->len - 1); /* Cut off last ',' */ tmpl = nmc_fields_con_active_details_general; tmpl_len = sizeof (nmc_fields_con_active_details_general); if (!with_group) { tmpl++; tmpl_len -= sizeof (NmcOutputField); } /* Fill field values */ arr = nmc_dup_fields_array (tmpl, tmpl_len, o_flags); if (with_group) set_val_strc (arr, 0, nmc_fields_con_active_details_groups[0].name); set_val_strc (arr, 1-idx_start, _("N/A")); set_val_strc (arr, 2-idx_start, nm_active_connection_get_uuid (active)); set_val_str (arr, 3-idx_start, dev_str->str); set_val_strc (arr, 4-idx_start, active_connection_state_to_string (state)); set_val_strc (arr, 5-idx_start, nm_active_connection_get_default (active) ? _("yes") : _("no")); set_val_strc (arr, 6-idx_start, nm_active_connection_get_default6 (active) ? _("yes") : _("no")); set_val_strc (arr, 7-idx_start, nm_active_connection_get_specific_object (active)); set_val_strc (arr, 8-idx_start, NM_IS_VPN_CONNECTION (active) ? _("yes") : _("no")); set_val_strc (arr, 9-idx_start, nm_object_get_path (NM_OBJECT (active))); set_val_strc (arr, 10-idx_start, nm_active_connection_get_connection (active)); set_val_strc (arr, 11-idx_start, _("N/A")); set_val_strc (arr, 12-idx_start, nm_active_connection_get_master (active)); for (iter = con_list; iter; iter = g_slist_next (iter)) { NMConnection *connection = (NMConnection *) iter->data; const char *con_path = nm_connection_get_path (connection); if (!strcmp (active_path, con_path)) { /* This connection is active */ s_con = nm_connection_get_setting_connection (connection); g_assert (s_con != NULL); /* Fill field values that depend on NMConnection */ set_val_strc (arr, 1-idx_start, nm_setting_connection_get_id (s_con)); set_val_strc (arr, 11-idx_start, nm_setting_connection_get_zone (s_con)); break; } } g_ptr_array_add (nmc->output_data, arr); g_string_free (dev_str, FALSE); } typedef struct { char **array; guint32 idx; } FillVPNDataInfo; static void fill_vpn_data_item (const char *key, const char *value, gpointer user_data) { FillVPNDataInfo *info = (FillVPNDataInfo *) user_data; info->array[info->idx++] = g_strdup_printf ("%s = %s", key, value); } // FIXME: The same or similar code for VPN info appears also in nm-applet (applet-dialogs.c), // and in gnome-control-center as well. It could probably be shared somehow. static char * get_vpn_connection_type (NMConnection *connection) { const char *type, *p; /* The service type is in form of "org.freedesktop.NetworkManager.vpnc". * Extract end part after last dot, e.g. "vpnc" */ type = nm_setting_vpn_get_service_type (nm_connection_get_setting_vpn (connection)); p = strrchr (type, '.'); return g_strdup (p ? p + 1 : type); } /* VPN parameters can be found at: * http://git.gnome.org/browse/network-manager-openvpn/tree/src/nm-openvpn-service.h * http://git.gnome.org/browse/network-manager-vpnc/tree/src/nm-vpnc-service.h * http://git.gnome.org/browse/network-manager-pptp/tree/src/nm-pptp-service.h * http://git.gnome.org/browse/network-manager-openconnect/tree/src/nm-openconnect-service.h * http://git.gnome.org/browse/network-manager-openswan/tree/src/nm-openswan-service.h * See also 'properties' directory in these plugins. */ static const gchar * find_vpn_gateway_key (const char *vpn_type) { if (g_strcmp0 (vpn_type, "openvpn") == 0) return "remote"; if (g_strcmp0 (vpn_type, "vpnc") == 0) return "IPSec gateway"; if (g_strcmp0 (vpn_type, "pptp") == 0) return "gateway"; if (g_strcmp0 (vpn_type, "openconnect") == 0) return "gateway"; if (g_strcmp0 (vpn_type, "openswan") == 0) return "right"; return ""; } static const gchar * find_vpn_username_key (const char *vpn_type) { if (g_strcmp0 (vpn_type, "openvpn") == 0) return "username"; if (g_strcmp0 (vpn_type, "vpnc") == 0) return "Xauth username"; if (g_strcmp0 (vpn_type, "pptp") == 0) return "user"; if (g_strcmp0 (vpn_type, "openconnect") == 0) return "username"; if (g_strcmp0 (vpn_type, "openswan") == 0) return "leftxauthusername"; return ""; } enum VpnDataItem { VPN_DATA_ITEM_GATEWAY, VPN_DATA_ITEM_USERNAME }; static const gchar * get_vpn_data_item (NMConnection *connection, enum VpnDataItem vpn_data_item) { const char *key; char *type = get_vpn_connection_type (connection); switch (vpn_data_item) { case VPN_DATA_ITEM_GATEWAY: key = find_vpn_gateway_key (type); break; case VPN_DATA_ITEM_USERNAME: key = find_vpn_username_key (type); break; default: key = ""; break; } g_free (type); return nm_setting_vpn_get_data_item (nm_connection_get_setting_vpn (connection), key); } /* FIXME end */ static gboolean nmc_active_connection_details (NMActiveConnection *acon, NmCli *nmc) { GError *error = NULL; GArray *print_groups; GPtrArray *group_fields = NULL; int i; char *fields_str; char *fields_all = NMC_FIELDS_CON_ACTIVE_DETAILS_ALL; char *fields_common = NMC_FIELDS_CON_ACTIVE_DETAILS_ALL; NmcOutputField *tmpl, *arr; size_t tmpl_len; const char *base_hdr = _("Activate connection details"); gboolean was_output = FALSE; if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0) fields_str = fields_common; else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0) fields_str = fields_all; else fields_str = nmc->required_fields; print_groups = parse_output_fields (fields_str, nmc_fields_con_active_details_groups, TRUE, &group_fields, &error); if (error) { g_string_printf (nmc->return_text, _("Error: 'connection show': %s"), error->message); g_error_free (error); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; return FALSE; } g_assert (print_groups); /* Main header */ nmc->print_fields.header_name = (char *) construct_header_name (base_hdr, nm_active_connection_get_uuid (acon)); nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_CON_ACTIVE_DETAILS_ALL, nmc_fields_con_active_details_groups, FALSE, NULL, NULL); nmc_fields_con_active_details_groups[0].flags = NMC_OF_FLAG_MAIN_HEADER_ONLY; print_required_fields (nmc, nmc_fields_con_active_details_groups); /* Loop through the groups and print them. */ for (i = 0; i < print_groups->len; i++) { int group_idx = g_array_index (print_groups, int, i); char *group_fld = (char *) g_ptr_array_index (group_fields, i); if (nmc->print_output != NMC_PRINT_TERSE && !nmc->multiline_output && was_output) printf ("\n"); /* Empty line */ was_output = FALSE; /* Remove any previous data */ nmc_empty_output_fields (nmc); /* GENERAL */ if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name, nmc_fields_con_active_details_groups[0].name) == 0) { /* Add field names */ tmpl = nmc_fields_con_active_details_general; tmpl_len = sizeof (nmc_fields_con_active_details_general); nmc->print_fields.indices = parse_output_fields (group_fld ? group_fld : NMC_FIELDS_CON_ACTIVE_DETAILS_GENERAL_ALL, tmpl, FALSE, NULL, NULL); arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); g_ptr_array_add (nmc->output_data, arr); /* Fill in values */ fill_output_active_connection (acon, nmc, TRUE, NMC_OF_FLAG_SECTION_PREFIX); print_data (nmc); /* Print all data */ was_output = TRUE; } /* IP4 */ if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name, nmc_fields_con_active_details_groups[1].name) == 0) { gboolean b1 = FALSE; NMIP4Config *cfg4 = nm_active_connection_get_ip4_config (acon); b1 = print_ip4_config (cfg4, nmc, "IP4", group_fld); was_output = was_output || b1; } /* DHCP4 */ if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name, nmc_fields_con_active_details_groups[2].name) == 0) { gboolean b1 = FALSE; NMDHCP4Config *dhcp4 = nm_active_connection_get_dhcp4_config (acon); b1 = print_dhcp4_config (dhcp4, nmc, "DHCP4", group_fld); was_output = was_output || b1; } /* IP6 */ if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name, nmc_fields_con_active_details_groups[3].name) == 0) { gboolean b1 = FALSE; NMIP6Config *cfg6 = nm_active_connection_get_ip6_config (acon); b1 = print_ip6_config (cfg6, nmc, "IP6", group_fld); was_output = was_output || b1; } /* DHCP6 */ if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name, nmc_fields_con_active_details_groups[4].name) == 0) { gboolean b1 = FALSE; NMDHCP6Config *dhcp6 = nm_active_connection_get_dhcp6_config (acon); b1 = print_dhcp6_config (dhcp6, nmc, "DHCP6", group_fld); was_output = was_output || b1; } /* VPN */ if (NM_IS_VPN_CONNECTION (acon) && strcasecmp (nmc_fields_con_active_details_groups[group_idx].name, nmc_fields_con_active_details_groups[5].name) == 0) { NMConnection *con; NMSettingConnection *s_con; NMSettingVPN *s_vpn; NMVPNConnectionState vpn_state; char *type_str, *banner_str, *vpn_state_str; const char *username = NULL; char **vpn_data_array = NULL; guint32 items_num; con = get_connection_for_active (nmc->system_connections, acon); s_con = nm_connection_get_setting_connection (con); g_assert (s_con != NULL); tmpl = nmc_fields_con_active_details_vpn; tmpl_len = sizeof (nmc_fields_con_active_details_vpn); nmc->print_fields.indices = parse_output_fields (group_fld ? group_fld : NMC_FIELDS_CON_ACTIVE_DETAILS_VPN_ALL, tmpl, FALSE, NULL, NULL); arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES); g_ptr_array_add (nmc->output_data, arr); s_vpn = nm_connection_get_setting_vpn (con); if (s_vpn) { items_num = nm_setting_vpn_get_num_data_items (s_vpn); if (items_num > 0) { FillVPNDataInfo info; vpn_data_array = g_new (char *, items_num + 1); info.array = vpn_data_array; info.idx = 0; nm_setting_vpn_foreach_data_item (s_vpn, &fill_vpn_data_item, &info); vpn_data_array[items_num] = NULL; } username = nm_setting_vpn_get_user_name (s_vpn); } type_str = get_vpn_connection_type (con); banner_str = g_strescape (nm_vpn_connection_get_banner (NM_VPN_CONNECTION (acon)), ""); vpn_state = nm_vpn_connection_get_vpn_state (NM_VPN_CONNECTION (acon)); vpn_state_str = g_strdup_printf ("%d - %s", vpn_state, vpn_connection_state_to_string (vpn_state)); /* Add values */ arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); set_val_strc (arr, 0, nmc_fields_con_active_details_groups[5].name); set_val_str (arr, 1, type_str); set_val_strc (arr, 2, username ? username : get_vpn_data_item (con, VPN_DATA_ITEM_USERNAME)); set_val_strc (arr, 3, get_vpn_data_item (con, VPN_DATA_ITEM_GATEWAY)); set_val_str (arr, 4, banner_str); set_val_str (arr, 5, vpn_state_str); set_val_arr (arr, 6, vpn_data_array); g_ptr_array_add (nmc->output_data, arr); print_data (nmc); /* Print all data */ was_output = TRUE; } } g_array_free (print_groups, TRUE); if (group_fields) g_ptr_array_free (group_fields, TRUE); return TRUE; } static gboolean split_required_fields_for_con_show (const char *input, char **profile_flds, char **active_flds, GError **error) { char **fields, **iter; char *dot; GString *str1, *str2; gboolean found; gboolean group_profile = FALSE; gboolean group_active = FALSE; gboolean success = TRUE; gboolean is_all, is_common; int i; if (!input) { *profile_flds = NULL; *active_flds = NULL; return TRUE; } str1 = g_string_new (NULL); str2 = g_string_new (NULL); /* Split supplied fields string */ fields = g_strsplit_set (input, ",", -1); for (iter = fields; iter && *iter; iter++) { g_strstrip (*iter); dot = strchr (*iter, '.'); if (dot) *dot = '\0'; is_all = !dot && strcasecmp (*iter, "all") == 0; is_common = !dot && strcasecmp (*iter, "common") == 0; found = FALSE; for (i = 0; nmc_fields_settings_names[i].name; i++) { if ( is_all || is_common || !strcasecmp (*iter, nmc_fields_settings_names[i].name)) { if (dot) *dot = '.'; g_string_append (str1, *iter); g_string_append_c (str1, ','); found = TRUE; break; } } if (found) continue; for (i = 0; nmc_fields_con_active_details_groups[i].name; i++) { if ( is_all || is_common || !strcasecmp (*iter, nmc_fields_con_active_details_groups[i].name)) { if (dot) *dot = '.'; g_string_append (str2, *iter); g_string_append_c (str2, ','); found = TRUE; break; } } if (!found) { if (dot) *dot = '.'; if (!strcasecmp (*iter, CON_SHOW_DETAIL_GROUP_PROFILE)) group_profile = TRUE; else if (!strcasecmp (*iter, CON_SHOW_DETAIL_GROUP_ACTIVE)) group_active = TRUE; else { char *allowed1 = nmc_get_allowed_fields (nmc_fields_settings_names, -1); char *allowed2 = nmc_get_allowed_fields (nmc_fields_con_active_details_groups, -1); g_set_error (error, NMCLI_ERROR, 0, _("invalid field '%s'; allowed fields: %s and %s, or %s,%s"), *iter, allowed1, allowed2, CON_SHOW_DETAIL_GROUP_PROFILE, CON_SHOW_DETAIL_GROUP_ACTIVE); g_free (allowed1); g_free (allowed2); success = FALSE; break; } } } if (fields) g_strfreev (fields); /* Handle pseudo groups: profile, active */ if (success && group_profile) { if (str1->len > 0) { g_set_error (error, NMCLI_ERROR, 0, _("'%s' has to be alone'"), CON_SHOW_DETAIL_GROUP_PROFILE); success = FALSE; } else g_string_assign (str1, "all,"); } if (success && group_active) { if (str2->len > 0) { g_set_error (error, NMCLI_ERROR, 0, _("'%s' has to be alone'"), CON_SHOW_DETAIL_GROUP_ACTIVE); success = FALSE; } else g_string_assign (str2, "all,"); } if (success) { if (str1->len > 0) g_string_truncate (str1, str1->len - 1); if (str2->len > 0) g_string_truncate (str2, str2->len - 1); *profile_flds = g_string_free (str1, str1->len == 0); *active_flds = g_string_free (str2, str2->len == 0); } else { g_string_free (str1, TRUE); g_string_free (str2, TRUE); } return success; } static NMCResultCode do_connections_show (NmCli *nmc, gboolean active_only, int argc, char **argv) { GError *err = NULL; char *profile_flds = NULL, *active_flds = NULL; nmc->should_wait = FALSE; nmc->get_client (nmc); if (!nm_client_get_manager_running (nmc->client)) { g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; goto finish; } if (argc == 0) { char *fields_str; char *fields_all = NMC_FIELDS_CON_SHOW_ALL; char *fields_common = NMC_FIELDS_CON_SHOW_COMMON; NmcOutputField *tmpl, *arr; size_t tmpl_len; GSList *iter; if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0) fields_str = fields_common; else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0) fields_str = fields_all; else fields_str = nmc->required_fields; tmpl = nmc_fields_con_show; tmpl_len = sizeof (nmc_fields_con_show); nmc->print_fields.indices = parse_output_fields (fields_str, tmpl, FALSE, NULL, &err); if (err) { goto finish; } if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &err)) goto finish; /* Add headers */ nmc->print_fields.header_name = active_only ? _("NetworkManager active profiles") : _("NetworkManager connection profiles"); arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES); g_ptr_array_add (nmc->output_data, arr); /* Add values */ for (iter = nmc->system_connections; iter; iter = g_slist_next (iter)) { NMConnection *con = NM_CONNECTION (iter->data); fill_output_connection (con, nmc, active_only); } print_data (nmc); /* Print all data */ } else { gboolean new_line = FALSE; gboolean without_fields = (nmc->required_fields == NULL); const GPtrArray *active_cons = nm_client_get_active_connections (nmc->client); /* multiline mode is default for 'connection show ' */ if (!nmc->mode_specified) nmc->multiline_output = TRUE; /* Split required fields into the settings and active ones. */ if (!split_required_fields_for_con_show (nmc->required_fields, &profile_flds, &active_flds, &err)) goto finish; g_free (nmc->required_fields); nmc->required_fields = NULL; while (argc > 0) { NMConnection *con; NMActiveConnection *acon = NULL; const char *selector = NULL; if ( strcmp (*argv, "id") == 0 || strcmp (*argv, "uuid") == 0 || strcmp (*argv, "path") == 0 || strcmp (*argv, "apath") == 0) { selector = *argv; if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto finish; } } /* Find connection by id, uuid, path or apath */ con = find_connection (nmc->system_connections, selector, *argv); if (!con) { acon = find_active_connection (active_cons, nmc->system_connections, selector, *argv); if (acon) con = get_connection_for_active (nmc->system_connections, acon); } /* Print connection details */ if (con) { gboolean res; /* Filter only active connections */ if (!acon) acon = get_ac_for_connection (active_cons, con); if (active_only && !acon) { next_arg (&argc, &argv); continue; } /* Show an empty line between connections */ if (new_line) printf ("\n"); /* Show profile configuration */ if (without_fields || profile_flds) { nmc->required_fields = profile_flds; res = nmc_connection_profile_details (con, nmc); nmc->required_fields = NULL; if (!res) goto finish; } /* If the profile is active, print also active details */ if (without_fields || active_flds) { if (acon) { nmc->required_fields = active_flds; res = nmc_active_connection_details (acon, nmc); nmc->required_fields = NULL; if (!res) goto finish; } } new_line = TRUE; } else { g_string_printf (nmc->return_text, _("Error: %s - no such connection profile."), *argv); nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; goto finish; } next_arg (&argc, &argv); } } finish: if (err) { g_string_printf (nmc->return_text, _("Error: %s."), err->message); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; g_error_free (err); } g_free (profile_flds); g_free (active_flds); return nmc->return_value; } static NMActiveConnection * get_default_active_connection (NmCli *nmc, NMDevice **device) { NMActiveConnection *default_ac = NULL; NMDevice *non_default_device = NULL; NMActiveConnection *non_default_ac = NULL; const GPtrArray *connections; int i; g_return_val_if_fail (nmc != NULL, NULL); g_return_val_if_fail (device != NULL, NULL); g_return_val_if_fail (*device == NULL, NULL); connections = nm_client_get_active_connections (nmc->client); for (i = 0; connections && (i < connections->len); i++) { NMActiveConnection *candidate = g_ptr_array_index (connections, i); const GPtrArray *devices; devices = nm_active_connection_get_devices (candidate); if (!devices || !devices->len) continue; if (nm_active_connection_get_default (candidate)) { if (!default_ac) { *device = g_ptr_array_index (devices, 0); default_ac = candidate; } } else { if (!non_default_ac) { non_default_device = g_ptr_array_index (devices, 0); non_default_ac = candidate; } } } /* Prefer the default connection if one exists, otherwise return the first * non-default connection. */ if (!default_ac && non_default_ac) { default_ac = non_default_ac; *device = non_default_device; } return default_ac; } /* Find a device to activate the connection on. * IN: connection: connection to activate * iface: device interface name to use (optional) * ap: access point to use (optional; valid just for 802-11-wireless) * nsp: Network Service Provider to use (option; valid only for wimax) * OUT: device: found device * spec_object: specific_object path of NMAccessPoint * RETURNS: TRUE when a device is found, FALSE otherwise. */ static gboolean find_device_for_connection (NmCli *nmc, NMConnection *connection, const char *iface, const char *ap, const char *nsp, NMDevice **device, const char **spec_object, GError **error) { NMSettingConnection *s_con; const char *con_type; int i, j; g_return_val_if_fail (nmc != NULL, FALSE); g_return_val_if_fail (device != NULL && *device == NULL, FALSE); g_return_val_if_fail (spec_object != NULL && *spec_object == NULL, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); s_con = nm_connection_get_setting_connection (connection); g_assert (s_con); con_type = nm_setting_connection_get_connection_type (s_con); if (strcmp (con_type, NM_SETTING_VPN_SETTING_NAME) == 0) { /* VPN connections */ NMActiveConnection *active = NULL; if (iface) { *device = nm_client_get_device_by_iface (nmc->client, iface); if (*device) active = nm_device_get_active_connection (*device); if (!active) { g_set_error (error, NMCLI_ERROR, 0, _("no active connection on device '%s'"), iface); return FALSE; } *spec_object = nm_object_get_path (NM_OBJECT (active)); return TRUE; } else { active = get_default_active_connection (nmc, device); if (!active) { g_set_error_literal (error, NMCLI_ERROR, 0, _("no active connection or device")); return FALSE; } *spec_object = nm_object_get_path (NM_OBJECT (active)); return TRUE; } } else { /* Other connections */ NMDevice *found_device = NULL; const GPtrArray *devices = nm_client_get_devices (nmc->client); for (i = 0; devices && (i < devices->len) && !found_device; i++) { NMDevice *dev = g_ptr_array_index (devices, i); if (iface) { const char *dev_iface = nm_device_get_iface (dev); if ( !g_strcmp0 (dev_iface, iface) && nm_device_connection_compatible (dev, connection, NULL)) { found_device = dev; } } else { if (nm_device_connection_compatible (dev, connection, NULL)) { found_device = dev; } } if (found_device && ap && !strcmp (con_type, NM_SETTING_WIRELESS_SETTING_NAME) && NM_IS_DEVICE_WIFI (dev)) { char *bssid_up = g_ascii_strup (ap, -1); const GPtrArray *aps = nm_device_wifi_get_access_points (NM_DEVICE_WIFI (dev)); found_device = NULL; /* Mark as not found; set to the device again later, only if AP matches */ for (j = 0; aps && (j < aps->len); j++) { NMAccessPoint *candidate_ap = g_ptr_array_index (aps, j); const char *candidate_bssid = nm_access_point_get_bssid (candidate_ap); if (!strcmp (bssid_up, candidate_bssid)) { found_device = dev; *spec_object = nm_object_get_path (NM_OBJECT (candidate_ap)); break; } } g_free (bssid_up); } #if WITH_WIMAX if ( found_device && nsp && !strcmp (con_type, NM_SETTING_WIMAX_SETTING_NAME) && NM_IS_DEVICE_WIMAX (dev)) { const GPtrArray *nsps = nm_device_wimax_get_nsps (NM_DEVICE_WIMAX (dev)); found_device = NULL; /* Mark as not found; set to the device again later, only if NSP matches */ for (j = 0; nsps && (j < nsps->len); j++) { NMWimaxNsp *candidate_nsp = g_ptr_array_index (nsps, j); const char *candidate_name = nm_wimax_nsp_get_name (candidate_nsp); if (!strcmp (nsp, candidate_name)) { found_device = dev; *spec_object = nm_object_get_path (NM_OBJECT (candidate_nsp)); break; } } } #endif } if (found_device) { *device = found_device; return TRUE; } else { if (iface) g_set_error (error, NMCLI_ERROR, 0, _("device '%s' not compatible with connection '%s'"), iface, nm_setting_connection_get_id (s_con)); else g_set_error (error, NMCLI_ERROR, 0, _("no device found for connection '%s'"), nm_setting_connection_get_id (s_con)); return FALSE; } } } static const char * vpn_connection_state_reason_to_string (NMVPNConnectionStateReason reason) { switch (reason) { case NM_VPN_CONNECTION_STATE_REASON_UNKNOWN: return _("unknown reason"); case NM_VPN_CONNECTION_STATE_REASON_NONE: return _("none"); case NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED: return _("the user was disconnected"); case NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED: return _("the base network connection was interrupted"); case NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED: return _("the VPN service stopped unexpectedly"); case NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID: return _("the VPN service returned invalid configuration"); case NM_VPN_CONNECTION_STATE_REASON_CONNECT_TIMEOUT: return _("the connection attempt timed out"); case NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT: return _("the VPN service did not start in time"); case NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED: return _("the VPN service failed to start"); case NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS: return _("no valid VPN secrets"); case NM_VPN_CONNECTION_STATE_REASON_LOGIN_FAILED: return _("invalid VPN secrets"); case NM_VPN_CONNECTION_STATE_REASON_CONNECTION_REMOVED: return _("the connection was removed"); default: return _("unknown"); } } static void active_connection_state_cb (NMActiveConnection *active, GParamSpec *pspec, gpointer user_data) { NmCli *nmc = (NmCli *) user_data; NMActiveConnectionState state; state = nm_active_connection_get_state (active); if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { if (nmc->print_output == NMC_PRINT_PRETTY) nmc_terminal_erase_line (); printf (_("Connection successfully activated (D-Bus active path: %s)\n"), nm_object_get_path (NM_OBJECT (active))); quit (); } else if ( state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED || state == NM_ACTIVE_CONNECTION_STATE_UNKNOWN) { g_string_printf (nmc->return_text, _("Error: Connection activation failed.")); nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; quit (); } } static void vpn_connection_state_cb (NMVPNConnection *vpn, NMVPNConnectionState state, NMVPNConnectionStateReason reason, gpointer user_data) { NmCli *nmc = (NmCli *) user_data; switch (state) { case NM_VPN_CONNECTION_STATE_PREPARE: case NM_VPN_CONNECTION_STATE_NEED_AUTH: case NM_VPN_CONNECTION_STATE_CONNECT: case NM_VPN_CONNECTION_STATE_IP_CONFIG_GET: /* no operation */ break; case NM_VPN_CONNECTION_STATE_ACTIVATED: if (nmc->print_output == NMC_PRINT_PRETTY) nmc_terminal_erase_line (); printf (_("VPN connection successfully activated (D-Bus active path: %s)\n"), nm_object_get_path (NM_OBJECT (vpn))); quit (); break; case NM_VPN_CONNECTION_STATE_FAILED: case NM_VPN_CONNECTION_STATE_DISCONNECTED: g_string_printf (nmc->return_text, _("Error: Connection activation failed: %s."), vpn_connection_state_reason_to_string (reason)); nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; quit (); break; default: break; } } static gboolean timeout_cb (gpointer user_data) { /* Time expired -> exit nmcli */ NmCli *nmc = (NmCli *) user_data; g_string_printf (nmc->return_text, _("Error: Timeout %d sec expired."), nmc->timeout); nmc->return_value = NMC_RESULT_ERROR_TIMEOUT_EXPIRED; quit (); return FALSE; } static gboolean progress_cb (gpointer user_data) { const char *str = (const char *) user_data; nmc_terminal_show_progress (str); return TRUE; } static gboolean progress_device_cb (gpointer user_data) { NMDevice *device = (NMDevice *) user_data; nmc_terminal_show_progress (device ? nmc_device_state_to_string (nm_device_get_state (device)) : ""); return TRUE; } static gboolean progress_vpn_cb (gpointer user_data) { NMVPNConnection *vpn = (NMVPNConnection *) user_data; const char *str; str = NM_IS_VPN_CONNECTION (vpn) ? vpn_connection_state_to_string (nm_vpn_connection_get_vpn_state (vpn)) : ""; nmc_terminal_show_progress (str); return TRUE; } typedef struct { NmCli *nmc; NMDevice *device; } ActivateConnectionInfo; static gboolean master_iface_slaves_check (gpointer user_data) { ActivateConnectionInfo *info = (ActivateConnectionInfo *) user_data; NmCli *nmc = info->nmc; NMDevice *device = info->device; const GPtrArray *slaves = NULL; if (NM_IS_DEVICE_BOND (device)) slaves = nm_device_bond_get_slaves (NM_DEVICE_BOND (device)); else if (NM_IS_DEVICE_TEAM (device)) slaves = nm_device_team_get_slaves (NM_DEVICE_TEAM (device)); else if (NM_IS_DEVICE_BRIDGE (device)) slaves = nm_device_bridge_get_slaves (NM_DEVICE_BRIDGE (device)); else g_warning ("%s: should not be reached.", __func__); if (!slaves) { g_string_printf (nmc->return_text, _("Error: Device '%s' is waiting for slaves before proceeding with activation."), nm_device_get_iface (device)); nmc->return_value = NMC_RESULT_ERROR_TIMEOUT_EXPIRED; quit (); } g_free (info); return FALSE; } static void activate_connection_cb (NMClient *client, NMActiveConnection *active, GError *error, gpointer user_data) { ActivateConnectionInfo *info = (ActivateConnectionInfo *) user_data; NmCli *nmc = info->nmc; NMDevice *device = info->device; NMActiveConnectionState state; const GPtrArray *ac_devs; if (error) { g_string_printf (nmc->return_text, _("Error: Connection activation failed: %s"), error->message); nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; quit (); } else { state = nm_active_connection_get_state (active); if (!device) { /* device could be NULL for virtual devices. Fill it here. */ ac_devs = nm_active_connection_get_devices (active); info->device = device = ac_devs && ac_devs->len > 0 ? g_ptr_array_index (ac_devs, 0) : NULL; } if (nmc->nowait_flag || state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { /* User doesn't want to wait or already activated */ if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { if (nmc->print_output == NMC_PRINT_PRETTY) nmc_terminal_erase_line (); printf (_("Connection successfully activated (D-Bus active path: %s)\n"), nm_object_get_path (NM_OBJECT (active))); } quit (); } else { if (NM_IS_VPN_CONNECTION (active)) { /* Monitor VPN state */ g_signal_connect (G_OBJECT (active), "vpn-state-changed", G_CALLBACK (vpn_connection_state_cb), nmc); /* Start progress indication showing VPN states */ if (nmc->print_output == NMC_PRINT_PRETTY) { if (progress_id) g_source_remove (progress_id); progress_id = g_timeout_add (120, progress_vpn_cb, NM_VPN_CONNECTION (active)); } } else { g_signal_connect (active, "notify::state", G_CALLBACK (active_connection_state_cb), nmc); /* Start progress indication showing device states */ if (nmc->print_output == NMC_PRINT_PRETTY) { if (progress_id) g_source_remove (progress_id); progress_id = g_timeout_add (120, progress_device_cb, device); } } /* Start timer not to loop forever when signals are not emitted */ g_timeout_add_seconds (nmc->timeout, timeout_cb, nmc); /* Check for bond or team or bridge slaves */ if ( NM_IS_DEVICE_BOND (device) || NM_IS_DEVICE_TEAM (device) || NM_IS_DEVICE_BRIDGE (device)) { g_timeout_add_seconds (SLAVES_UP_TIMEOUT, master_iface_slaves_check, info); return; /* info will be freed in master_iface_slaves_check () */ } } } g_free (info); } /* We were using nm_connection_get_virtual_iface_name() to determine whether the * connection is virtual or not. But it did't work for VLANs without * vlan.interface-name. nm_connection_get_virtual_iface_name() returns NULL for those. * So we need to use our own implementation for now. */ static gboolean is_connection_virtual (NMConnection *connection) { if ( nm_connection_is_type (connection, NM_SETTING_BOND_SETTING_NAME) || nm_connection_is_type (connection, NM_SETTING_TEAM_SETTING_NAME) || nm_connection_is_type (connection, NM_SETTING_VLAN_SETTING_NAME) || nm_connection_is_type (connection, NM_SETTING_BRIDGE_SETTING_NAME)) return TRUE; if (nm_connection_is_type (connection, NM_SETTING_INFINIBAND_SETTING_NAME)) { NMSettingInfiniband *s_infi = nm_connection_get_setting_infiniband (connection); int p_key = nm_setting_infiniband_get_p_key (s_infi); const char *parent = nm_setting_infiniband_get_parent (s_infi); if (p_key != -1 && parent) return TRUE; } return FALSE; } static gboolean nmc_activate_connection (NmCli *nmc, NMConnection *connection, const char *ifname, const char *ap, const char *nsp, NMClientActivateFn callback, GError **error) { ActivateConnectionInfo *info; NMDevice *device = NULL; const char *spec_object = NULL; gboolean device_found; GError *local = NULL; g_return_val_if_fail (nmc != NULL, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); if (connection) { device_found = find_device_for_connection (nmc, connection, ifname, ap, nsp, &device, &spec_object, &local); /* Virtual connection may not have their interfaces created yet */ if (!device_found && !is_connection_virtual (connection)) { g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_CON_ACTIVATION, "%s", local && local->message ? local->message : _("unknown error")); g_clear_error (&local); return FALSE; } } else if (ifname) { device = nm_client_get_device_by_iface (nmc->client, ifname); if (!device) { g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_NOT_FOUND, _("unknown device '%s'."), ifname); return FALSE; } } else { g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_NOT_FOUND, _("neither a valid connection nor device given")); return FALSE; } info = g_malloc0 (sizeof (ActivateConnectionInfo)); info->nmc = nmc; info->device = device; nm_client_activate_connection (nmc->client, connection, device, spec_object, callback, info); return TRUE; } static NMCResultCode do_connection_up (NmCli *nmc, int argc, char **argv) { NMConnection *connection = NULL; const char *ifname = NULL; const char *ap = NULL; const char *nsp = NULL; GError *error = NULL; const char *selector = NULL; const char *name = NULL; char *line = NULL; /* * Set default timeout for connection activation. * Activation can take quite a long time, use 90 seconds. */ if (nmc->timeout == -1) nmc->timeout = 90; if (argc == 0) { if (nmc->ask) { line = nmc_get_user_input (_("Connection (name, UUID, or path): ")); name = line ? line : ""; // TODO: enhancement: when just Enter is pressed (line is NULL), list // available connections so that the user can select one } } else if (strcmp (*argv, "ifname") != 0) { if ( strcmp (*argv, "id") == 0 || strcmp (*argv, "uuid") == 0 || strcmp (*argv, "path") == 0) { selector = *argv; if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto error; } name = *argv; } name = *argv; next_arg (&argc, &argv); } if (name) connection = find_connection (nmc->system_connections, selector, name); while (argc > 0) { if (strcmp (*argv, "ifname") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto error; } ifname = *argv; } else if (strcmp (*argv, "ap") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto error; } ap = *argv; } #if WITH_WIMAX else if (strcmp (*argv, "nsp") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto error; } nsp = *argv; } #endif else { fprintf (stderr, _("Unknown parameter: %s\n"), *argv); } argc--; argv++; } /* create NMClient */ nmc->get_client (nmc); if (!nm_client_get_manager_running (nmc->client)) { g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; goto error; } /* Use nowait_flag instead of should_wait because exiting has to be postponed till * active_connection_state_cb() is called. That gives NM time to check our permissions * and we can follow activation progress. */ nmc->nowait_flag = (nmc->timeout == 0); nmc->should_wait = TRUE; if (!nmc_activate_connection (nmc, connection, ifname, ap, nsp, activate_connection_cb, &error)) { g_string_printf (nmc->return_text, _("Error: %s."), error ? error->message : _("unknown error")); nmc->return_value = error ? error->code : NMC_RESULT_ERROR_CON_ACTIVATION; g_clear_error (&error); goto error; } /* Start progress indication */ if (nmc->print_output == NMC_PRINT_PRETTY) progress_id = g_timeout_add (120, progress_cb, _("preparing")); g_free (line); return nmc->return_value; error: nmc->should_wait = FALSE; g_free (line); return nmc->return_value; } static NMCResultCode do_connection_down (NmCli *nmc, int argc, char **argv) { NMActiveConnection *active; const GPtrArray *active_cons; char *line = NULL; char **arg_arr = NULL; char **arg_ptr = argv; int arg_num = argc; if (argc == 0) { if (nmc->ask) { line = nmc_get_user_input (_("Connection (name, UUID, or path): ")); nmc_string_to_arg_array (line, "", &arg_arr, &arg_num); arg_ptr = arg_arr; } if (arg_num == 0) { g_string_printf (nmc->return_text, _("Error: No connection specified.")); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto error; } } /* create NMClient */ nmc->get_client (nmc); if (!nm_client_get_manager_running (nmc->client)) { g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; goto error; } /* Get active connections */ active_cons = nm_client_get_active_connections (nmc->client); while (arg_num > 0) { const char *selector = NULL; if ( strcmp (*arg_ptr, "id") == 0 || strcmp (*arg_ptr, "uuid") == 0 || strcmp (*arg_ptr, "path") == 0 || strcmp (*arg_ptr, "apath") == 0) { selector = *arg_ptr; if (next_arg (&arg_num, &arg_ptr) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto error; } } active = find_active_connection (active_cons, nmc->system_connections, selector, *arg_ptr); if (active) { nm_client_deactivate_connection (nmc->client, active); } else { g_string_printf (nmc->return_text, _("Error: '%s' is not an active connection."), *arg_ptr); nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; goto error; } next_arg (&arg_num, &arg_ptr); } // FIXME: do something better then sleep() /* Don't quit immediatelly and give NM time to check our permissions */ sleep (1); error: nmc->should_wait = FALSE; g_strfreev (arg_arr); return nmc->return_value; } /*----------------------------------------------------------------------------*/ typedef struct NameItem { const char *name; const char *alias; const struct NameItem *settings; gboolean mandatory; } NameItem; static const NameItem nmc_generic_settings [] = { { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, { NULL, NULL, NULL, FALSE } }; static const NameItem nmc_ethernet_settings [] = { { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, TRUE }, { NM_SETTING_802_1X_SETTING_NAME, NULL, NULL, FALSE }, { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, { NM_SETTING_DCB_SETTING_NAME, NULL, NULL, FALSE }, { NULL, NULL, NULL, FALSE } }; static const NameItem nmc_infiniband_settings [] = { { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, { NM_SETTING_INFINIBAND_SETTING_NAME, NULL, NULL, TRUE }, { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, { NULL, NULL, NULL, FALSE } }; static const NameItem nmc_wifi_settings [] = { { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, { NM_SETTING_WIRELESS_SETTING_NAME, "wifi", NULL, TRUE }, { NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, "wifi-sec", NULL, FALSE }, { NM_SETTING_802_1X_SETTING_NAME, NULL, NULL, FALSE }, { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, { NULL, NULL, NULL, FALSE } }; static const NameItem nmc_wimax_settings [] = { { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, { NM_SETTING_WIMAX_SETTING_NAME, NULL, NULL, TRUE }, { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, { NULL, NULL, NULL, FALSE } }; static const NameItem nmc_gsm_settings [] = { { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, { NM_SETTING_GSM_SETTING_NAME, NULL, NULL, TRUE }, { NM_SETTING_SERIAL_SETTING_NAME, NULL, NULL, FALSE }, { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, { NULL, NULL, NULL, FALSE } }; static const NameItem nmc_cdma_settings [] = { { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, { NM_SETTING_CDMA_SETTING_NAME, NULL, NULL, TRUE }, { NM_SETTING_SERIAL_SETTING_NAME, NULL, NULL, FALSE }, { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, { NULL, NULL, NULL, FALSE } }; static const NameItem nmc_mobile_settings [] = { { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, { NM_SETTING_SERIAL_SETTING_NAME, NULL, NULL, FALSE }, { NM_SETTING_PPP_SETTING_NAME, NULL, NULL, FALSE }, { NM_SETTING_GSM_SETTING_NAME, NULL, NULL, TRUE }, { NM_SETTING_CDMA_SETTING_NAME, NULL, NULL, TRUE }, { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, { NULL, NULL, NULL, FALSE } }; static const NameItem nmc_bluetooth_settings [] = { { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, { NM_SETTING_BLUETOOTH_SETTING_NAME, NULL, NULL, TRUE }, { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, { NULL, NULL, NULL, FALSE } }; static const NameItem nmc_adsl_settings [] = { { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, { NM_SETTING_ADSL_SETTING_NAME, NULL, NULL, TRUE }, { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, { NULL, NULL, NULL, FALSE } }; /* PPPoE is a base connection type from historical reasons. * See libnm-util/nm-setting.c:_nm_setting_is_base_type() */ static const NameItem nmc_pppoe_settings [] = { { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, TRUE }, { NM_SETTING_PPPOE_SETTING_NAME, NULL, NULL, TRUE }, { NM_SETTING_PPP_SETTING_NAME, NULL, NULL, FALSE }, { NM_SETTING_802_1X_SETTING_NAME, NULL, NULL, FALSE }, { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, { NULL, NULL, NULL, FALSE } }; static const NameItem nmc_olpc_mesh_settings [] = { { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, { NM_SETTING_OLPC_MESH_SETTING_NAME, "olpc-mesh", NULL, TRUE }, { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, { NULL, NULL, NULL, FALSE } }; static const NameItem nmc_vpn_settings [] = { { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, { NM_SETTING_VPN_SETTING_NAME, NULL, NULL, TRUE }, { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, { NULL, NULL, NULL, FALSE } }; static const NameItem nmc_vlan_settings [] = { { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, FALSE }, { NM_SETTING_VLAN_SETTING_NAME, NULL, NULL, TRUE }, { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, { NULL, NULL, NULL, FALSE } }; static const NameItem nmc_bond_settings [] = { { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, { NM_SETTING_BOND_SETTING_NAME, NULL, NULL, TRUE }, { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, FALSE }, { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, { NULL, NULL, NULL, FALSE } }; static const NameItem nmc_team_settings [] = { { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, { NM_SETTING_TEAM_SETTING_NAME, NULL, NULL, TRUE }, { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, FALSE }, { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, { NULL, NULL, NULL, FALSE } }; static const NameItem nmc_bridge_settings [] = { { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, { NM_SETTING_BRIDGE_SETTING_NAME, NULL, NULL, TRUE }, { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, FALSE }, { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE }, { NULL, NULL, NULL, FALSE } }; static const NameItem nmc_bond_slave_settings [] = { { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, TRUE }, { NM_SETTING_802_1X_SETTING_NAME, NULL, NULL, FALSE }, { NULL, NULL, NULL, FALSE } }; static const NameItem nmc_team_slave_settings [] = { { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, TRUE }, { NM_SETTING_TEAM_PORT_SETTING_NAME, NULL, NULL, TRUE }, { NM_SETTING_802_1X_SETTING_NAME, NULL, NULL, FALSE }, { NULL, NULL, NULL, FALSE } }; static const NameItem nmc_bridge_slave_settings [] = { { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE }, { NM_SETTING_BRIDGE_PORT_SETTING_NAME, NULL, NULL, TRUE }, { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, TRUE }, { NM_SETTING_802_1X_SETTING_NAME, NULL, NULL, FALSE }, { NULL, NULL, NULL, FALSE } }; /* Available connection types */ static const NameItem nmc_valid_connection_types[] = { { NM_SETTING_GENERIC_SETTING_NAME, NULL, nmc_generic_settings }, { NM_SETTING_WIRED_SETTING_NAME, "ethernet", nmc_ethernet_settings }, { NM_SETTING_PPPOE_SETTING_NAME, NULL, nmc_pppoe_settings }, { NM_SETTING_WIRELESS_SETTING_NAME, "wifi", nmc_wifi_settings }, { NM_SETTING_WIMAX_SETTING_NAME, NULL, nmc_wimax_settings }, { NM_SETTING_GSM_SETTING_NAME, NULL, nmc_gsm_settings }, { NM_SETTING_CDMA_SETTING_NAME, NULL, nmc_cdma_settings }, { NM_SETTING_INFINIBAND_SETTING_NAME, NULL, nmc_infiniband_settings }, { NM_SETTING_ADSL_SETTING_NAME, NULL, nmc_adsl_settings }, { NM_SETTING_BLUETOOTH_SETTING_NAME, NULL, nmc_bluetooth_settings }, { NM_SETTING_VPN_SETTING_NAME, NULL, nmc_vpn_settings }, { NM_SETTING_OLPC_MESH_SETTING_NAME, "olpc-mesh", nmc_olpc_mesh_settings }, { NM_SETTING_VLAN_SETTING_NAME, NULL, nmc_vlan_settings }, { NM_SETTING_BOND_SETTING_NAME, NULL, nmc_bond_settings }, { NM_SETTING_TEAM_SETTING_NAME, NULL, nmc_team_settings }, { NM_SETTING_BRIDGE_SETTING_NAME, NULL, nmc_bridge_settings }, { "bond-slave", NULL, nmc_bond_slave_settings }, { "team-slave", NULL, nmc_team_slave_settings }, { "bridge-slave", NULL, nmc_bridge_slave_settings }, { NULL, NULL, NULL } }; /* * Return an alias for the 'name' if exists, else return the 'name'. * The returned string must not be freed. */ static const char * get_name_alias (const char *name, const NameItem array[]) { const NameItem *iter = &array[0]; if (!name) return NULL; while (iter && iter->name) { if (!strcmp (name, iter->name)) { if (iter->alias) return iter->alias; else return iter->name; } iter++; } return name; } /* * Construct a string with names and aliases from the array formatted as: * "name (alias), name, name (alias), name, name" * * Returns: string; the caller is responsible for freeing it. */ static char * get_valid_options_string (const NameItem array[]) { const NameItem *iter = &array[0]; GString *str; str = g_string_sized_new (150); while (iter && iter->name) { if (str->len) g_string_append (str, ", "); if (iter->alias) g_string_append_printf (str, "%s (%s)", iter->name, iter->alias); else g_string_append (str, iter->name); iter++; } return g_string_free (str, FALSE); } /* * Check if 'val' is valid string in either array->name or array->alias. * It accepts shorter string provided they are not ambiguous. * 'val' == NULL doesn't hurt. * * Returns: pointer to array->name string or NULL on failure. * The returned string must not be freed. */ static const char * check_valid_name (const char *val, const NameItem array[], GError **error) { const NameItem *iter; GPtrArray *tmp_arr; const char *str; GError *tmp_err = NULL; /* Create a temporary array that can be used in nmc_string_is_valid() */ tmp_arr = g_ptr_array_sized_new (30); iter = &array[0]; while (iter && iter->name) { g_ptr_array_add (tmp_arr, (gpointer) iter->name); if (iter->alias) g_ptr_array_add (tmp_arr, (gpointer) iter->alias); iter++; } g_ptr_array_add (tmp_arr, (gpointer) NULL); /* Check string validity */ str = nmc_string_is_valid (val, (const char **) tmp_arr->pdata, &tmp_err); if (!str) { if (tmp_err->code == 1) g_propagate_error (error, tmp_err); else { /* We want to handle aliases, so construct own error message */ char *err_str = get_valid_options_string (array); g_set_error (error, 1, 0, _("'%s' not among [%s]"), val ? val : "", err_str); g_free (err_str); g_clear_error (&tmp_err); } g_ptr_array_free (tmp_arr, TRUE); return NULL; } /* Return a pointer to the found string in passed 'array' */ iter = &array[0]; while (iter && iter->name) { if ( (iter->name && g_strcmp0 (iter->name, str) == 0) || (iter->alias && g_strcmp0 (iter->alias, str) == 0)) { g_ptr_array_free (tmp_arr, TRUE); return iter->name; } iter++; } /* We should not really come here */ g_ptr_array_free (tmp_arr, TRUE); g_set_error (error, 1, 0, _("Unknown error")); return NULL; } static const NameItem * get_valid_settings_array (const char *con_type) { guint i, num; if (!con_type) return NULL; num = G_N_ELEMENTS (nmc_valid_connection_types); for (i = 0; i < num; i++) { if (!g_strcmp0 (con_type, nmc_valid_connection_types[i].name)) return nmc_valid_connection_types[i].settings; } return NULL; } static gboolean is_setting_mandatory (NMConnection *connection, NMSetting *setting) { NMSettingConnection *s_con; const char *c_type; const NameItem *item; const char *name; s_con = nm_connection_get_setting_connection (connection); g_assert (s_con); c_type = nm_setting_connection_get_connection_type (s_con); name = nm_setting_get_name (setting); item = get_valid_settings_array (c_type); while (item && item->name) { if (!strcmp (name, item->name)) return item->mandatory; item++; } return FALSE; } /*----------------------------------------------------------------------------*/ static gboolean check_and_convert_mac (const char *mac, GByteArray **mac_array, int type, const char *keyword, GError **error) { GByteArray *local_mac_array = NULL; g_return_val_if_fail (mac_array == NULL || *mac_array == NULL, FALSE); if (!mac) return TRUE; local_mac_array = nm_utils_hwaddr_atoba (mac, type); if (!local_mac_array) { g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: '%s': '%s' is not a valid %s MAC address."), keyword, mac, type == ARPHRD_INFINIBAND ? _("InfiniBand") : _("Ethernet")); return FALSE; } if (mac_array) *mac_array = local_mac_array; else if (local_mac_array) g_byte_array_free (local_mac_array, TRUE); return TRUE; } static gboolean check_and_convert_mtu (const char *mtu, guint32 *mtu_int, GError **error) { unsigned long local_mtu_int; if (!mtu) return TRUE; if (!nmc_string_to_uint (mtu, TRUE, 0, G_MAXUINT32, &local_mtu_int)) { g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: 'mtu': '%s' is not a valid MTU."), mtu); return FALSE; } if (mtu_int) *mtu_int = (guint32) local_mtu_int; return TRUE; } static gboolean check_infiniband_parent (const char *parent, GError **error) { if (!parent) return TRUE; if (!nm_utils_iface_valid_name (parent)) { g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: 'parent': '%s' is not a valid interface name."), parent); return FALSE; } return TRUE; } static gboolean check_infiniband_p_key (const char *p_key, guint32 *p_key_int, GError **error) { unsigned long local_p_key_int; gboolean p_key_valid = FALSE; if (!p_key) return TRUE; if (!strncmp (p_key, "0x", 2)) p_key_valid = nmc_string_to_uint_base (p_key + 2, 16, TRUE, 0, G_MAXUINT16, &local_p_key_int); else p_key_valid = nmc_string_to_uint (p_key, TRUE, 0, G_MAXUINT16, &local_p_key_int); if (!p_key_valid) { g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: 'p-key': '%s' is not a valid InfiniBand P_KEY."), p_key); return FALSE; } if (p_key_int) *p_key_int = (guint32) local_p_key_int; return TRUE; } static gboolean check_infiniband_mode (const char *mode, GError **error) { if (!mode) return TRUE; if (strcmp (mode, "datagram") && strcmp (mode, "connected")) { g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: 'mode': '%s' is not a valid InfiniBand transport mode [datagram, connected]."), mode); return FALSE; } return TRUE; } static gboolean check_and_convert_vlan_flags (const char *flags, guint32 *flags_int, GError **error) { unsigned long local_flags_int; if (!flags) return TRUE; if (!nmc_string_to_uint (flags, TRUE, 0, 7, &local_flags_int)) { g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: 'flags': '%s' is not valid; use <0-7>."), flags); return FALSE; } if (flags_int) *flags_int = (guint32) local_flags_int; return TRUE; } static gboolean check_and_convert_vlan_prio_maps (const char *prio_map, NMVlanPriorityMap type, char ***prio_map_arr, GError **error) { char **local_prio_map_arr; GError *local_err = NULL; if (!prio_map) return TRUE; if (!(local_prio_map_arr = nmc_vlan_parse_priority_maps (prio_map, type, &local_err))) { g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: '%s': '%s' is not valid; %s "), type == NM_VLAN_INGRESS_MAP ? "ingress" : "egress", prio_map, local_err->message); return FALSE; } if (prio_map_arr) *prio_map_arr = local_prio_map_arr; return TRUE; } static gboolean add_ip4_address_to_connection (NMIP4Address *ip4addr, NMConnection *connection) { NMSettingIP4Config *s_ip4; gboolean ret; if (!ip4addr) return TRUE; s_ip4 = nm_connection_get_setting_ip4_config (connection); if (!s_ip4) { s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new (); nm_connection_add_setting (connection, NM_SETTING (s_ip4)); g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, NULL); } ret = nm_setting_ip4_config_add_address (s_ip4, ip4addr); nm_ip4_address_unref (ip4addr); return ret; } static gboolean add_ip6_address_to_connection (NMIP6Address *ip6addr, NMConnection *connection) { NMSettingIP6Config *s_ip6; gboolean ret; if (!ip6addr) return TRUE; s_ip6 = nm_connection_get_setting_ip6_config (connection); if (!s_ip6) { s_ip6 = (NMSettingIP6Config *) nm_setting_ip6_config_new (); nm_connection_add_setting (connection, NM_SETTING (s_ip6)); g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_MANUAL, NULL); } ret = nm_setting_ip6_config_add_address (s_ip6, ip6addr); nm_ip6_address_unref (ip6addr); return ret; } static char * unique_master_iface_ifname (GSList *list, const char *type, const char *ifname_property, const char *try_name) { NMConnection *connection; NMSetting *setting; char *new_name; unsigned int num = 1; GSList *iterator = list; char *ifname_val = NULL; new_name = g_strdup (try_name); while (iterator) { connection = NM_CONNECTION (iterator->data); setting = nm_connection_get_setting_by_name (connection, type); if (!setting) { iterator = g_slist_next (iterator); continue; } g_object_get (setting, ifname_property, &ifname_val, NULL); if (g_strcmp0 (new_name, ifname_val) == 0) { g_free (new_name); new_name = g_strdup_printf ("%s%d", try_name, num++); iterator = list; } else iterator = g_slist_next (iterator); g_free (ifname_val); } return new_name; } static gboolean bridge_prop_string_to_uint (const char *str, const char *nmc_arg, GType bridge_type, const char *propname, unsigned long *out_val, GError **error) { GParamSpecUInt *pspec; pspec = (GParamSpecUInt *) g_object_class_find_property (g_type_class_peek (bridge_type), propname); g_assert (G_IS_PARAM_SPEC_UINT (pspec)); if (!nmc_string_to_uint (str, TRUE, pspec->minimum, pspec->maximum, out_val)) { g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: '%s': '%s' is not valid; use <%u-%u>."), nmc_arg, str, pspec->minimum, pspec->maximum); return FALSE; } return TRUE; } static void do_questionnaire_ethernet (gboolean ethernet, char **mtu, char **mac, char **cloned_mac) { char *answer; gboolean answer_bool; gboolean once_more; GError *error = NULL; const char *type = ethernet ? _("ethernet") : _("Wi-Fi"); /* Ask for optional arguments */ printf (_("There are 3 optional arguments for '%s' connection type.\n"), type);; answer = nmc_get_user_input (_("Do you want to provide them? (yes/no) [yes] ")); if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) { g_free (answer); return; } if (!*mtu) { do { *mtu = nmc_get_user_input (_("MTU [auto]: ")); once_more = !check_and_convert_mtu (*mtu, NULL, &error); if (once_more) { printf ("%s\n", error->message); g_clear_error (&error); g_free (*mtu); } } while (once_more); } if (!*mac) { do { *mac = nmc_get_user_input (_("MAC [none]: ")); once_more = !check_and_convert_mac (*mac, NULL, ARPHRD_ETHER, "mac", &error); if (once_more) { printf ("%s\n", error->message); g_clear_error (&error); g_free (*mac); } } while (once_more); } if (!*cloned_mac) { do { *cloned_mac = nmc_get_user_input (_("Cloned MAC [none]: ")); once_more = !check_and_convert_mac (*cloned_mac, NULL, ARPHRD_ETHER, "cloned-mac", &error); if (once_more) { printf ("%s\n", error->message); g_clear_error (&error); g_free (*cloned_mac); } } while (once_more); } g_free (answer); return; } static void do_questionnaire_infiniband (char **mtu, char **mac, char **mode, char **parent, char **p_key) { char *answer; gboolean answer_bool; gboolean once_more; GError *error = NULL; /* Ask for optional arguments */ printf (_("There are 5 optional arguments for 'InfiniBand' connection type.\n")); answer = nmc_get_user_input (_("Do you want to provide them? (yes/no) [yes] ")); if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) { g_free (answer); return; } if (!*mtu) { do { *mtu = nmc_get_user_input (_("MTU [auto]: ")); once_more = !check_and_convert_mtu (*mtu, NULL, &error); if (once_more) { printf ("%s\n", error->message); g_clear_error (&error); g_free (*mtu); } } while (once_more); } if (!*mac) { do { *mac = nmc_get_user_input (_("MAC [none]: ")); once_more = !check_and_convert_mac (*mac, NULL, ARPHRD_INFINIBAND, "mac", &error); if (once_more) { printf ("%s\n", error->message); g_clear_error (&error); g_free (*mac); } } while (once_more); } if (!*mode) { do { *mode = nmc_get_user_input (_("Transport mode (datagram or connected) [datagram]: ")); if (!*mode) *mode = g_strdup ("datagram"); once_more = !check_infiniband_mode (*mode, &error); if (once_more) { printf ("%s\n", error->message); g_clear_error (&error); g_free (*mode); } } while (once_more); } if (!*parent) { do { *parent = nmc_get_user_input (_("Parent interface [none]: ")); once_more = !check_infiniband_parent (*parent, &error); if (once_more) { printf ("%s\n", error->message); g_clear_error (&error); g_free (*parent); } } while (once_more); } if (!*p_key) { do { *p_key = nmc_get_user_input (_("P_KEY [none]: ")); once_more = !check_infiniband_p_key (*p_key, NULL, &error); if (once_more) { printf ("%s\n", error->message); g_clear_error (&error); g_free (*p_key); } /* If parent is specified, so has to be P_KEY */ if (!once_more && *parent && !*p_key) { once_more = TRUE; printf (_("Error: 'p-key' is mandatory when 'parent' is specified.\n")); } } while (once_more); } g_free (answer); return; } static void do_questionnaire_wifi (char **mtu, char **mac, char **cloned_mac) { /* At present, the optional Wi-Fi arguments are the same as for ethernet. */ return do_questionnaire_ethernet (FALSE, mtu, mac, cloned_mac); } static void do_questionnaire_wimax (char **mac) { char *answer; gboolean answer_bool; gboolean once_more; GError *error = NULL; /* Ask for optional 'wimax' arguments. */ printf (_("There is 1 optional argument for 'WiMax' connection type.\n")); answer = nmc_get_user_input (_("Do you want to provide it? (yes/no) [yes] ")); if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) { g_free (answer); return; } if (!*mac) { do { *mac = nmc_get_user_input (_("MAC [none]: ")); once_more = !check_and_convert_mac (*mac, NULL, ARPHRD_ETHER, "mac", &error); if (once_more) { printf ("%s\n", error->message); g_clear_error (&error); g_free (*mac); } } while (once_more); } g_free (answer); return; } static void do_questionnaire_pppoe (char **password, char **service, char **mtu, char **mac) { char *answer; gboolean answer_bool; gboolean once_more; GError *error = NULL; /* Ask for optional 'pppoe' arguments. */ printf (_("There are 4 optional arguments for 'PPPoE' connection type.\n")); answer = nmc_get_user_input (_("Do you want to provide them? (yes/no) [yes] ")); if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) { g_free (answer); return; } if (!*password) *password = nmc_get_user_input (_("Password [none]: ")); if (!*service) *service = nmc_get_user_input (_("Service [none]: ")); if (!*mtu) { do { *mtu = nmc_get_user_input (_("MTU [auto]: ")); once_more = !check_and_convert_mtu (*mtu, NULL, &error); if (once_more) { printf ("%s\n", error->message); g_clear_error (&error); g_free (*mtu); } } while (once_more); } if (!*mac) { do { *mac = nmc_get_user_input (_("MAC [none]: ")); once_more = !check_and_convert_mac (*mac, NULL, ARPHRD_ETHER, "mac", &error); if (once_more) { printf ("%s\n", error->message); g_clear_error (&error); g_free (*mac); } } while (once_more); } g_free (answer); return; } static void do_questionnaire_mobile (char **user, char **password) { char *answer; gboolean answer_bool; /* Ask for optional 'gsm' or 'cdma' arguments. */ printf (_("There are 2 optional arguments for 'mobile broadband' connection type.\n")); answer = nmc_get_user_input (_("Do you want to provide them? (yes/no) [yes] ")); if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) { g_free (answer); return; } if (!*user) *user = nmc_get_user_input (_("Username [none]: ")); if (!*password) *password = nmc_get_user_input (_("Password [none]: ")); g_free (answer); return; } static void do_questionnaire_bluetooth (char **bt_type) { char *answer; gboolean answer_bool; gboolean once_more; /* Ask for optional 'bluetooth' arguments. */ printf (_("There is 1 optional argument for 'bluetooth' connection type.\n")); answer = nmc_get_user_input (_("Do you want to provide it? (yes/no) [yes] ")); if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) { g_free (answer); return; } if (!*bt_type) { do { *bt_type = nmc_get_user_input (_("Bluetooth type (panu, dun-gsm or dun-cdma) [panu]: ")); if (!*bt_type) *bt_type = g_strdup ("panu"); once_more = strcmp (*bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN) && strcmp (*bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN"-gsm") && strcmp (*bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN"-cdma") && strcmp (*bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU); if (once_more) { printf (_("Error: 'bt-type': '%s' is not a valid bluetooth type.\n"), *bt_type); g_free (*bt_type); } } while (once_more); } g_free (answer); return; } static void do_questionnaire_vlan (char **mtu, char **flags, char **ingress, char **egress) { char *answer; gboolean answer_bool; gboolean once_more; GError *error = NULL; /* Ask for optional 'vlan' arguments. */ printf (_("There are 4 optional arguments for 'VLAN' connection type.\n")); answer = nmc_get_user_input (_("Do you want to provide them? (yes/no) [yes] ")); if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) { g_free (answer); return; } if (!*mtu) { do { *mtu = nmc_get_user_input (_("MTU [auto]: ")); once_more = !check_and_convert_mtu (*mtu, NULL, &error); if (once_more) { printf ("%s\n", error->message); g_clear_error (&error); g_free (*mtu); } } while (once_more); } if (!*flags) { do { *flags = nmc_get_user_input (_("VLAN flags (<0-7>) [none]: ")); once_more = !check_and_convert_vlan_flags (*flags, NULL, &error); if (once_more) { printf ("%s\n", error->message); g_clear_error (&error); g_free (*flags); } } while (once_more); } if (!*ingress) { do { *ingress = nmc_get_user_input (_("Ingress priority maps [none]: ")); once_more = !check_and_convert_vlan_prio_maps (*ingress, NM_VLAN_INGRESS_MAP, NULL, &error); if (once_more) { printf ("%s\n", error->message); g_clear_error (&error); g_free (*ingress); } } while (once_more); } if (!*egress) { do { *egress = nmc_get_user_input (_("Egress priority maps [none]: ")); once_more = !check_and_convert_vlan_prio_maps (*egress, NM_VLAN_EGRESS_MAP, NULL, &error); if (once_more) { printf ("%s\n", error->message); g_clear_error (&error); g_free (*egress); } } while (once_more); } g_free (answer); return; } static void do_questionnaire_bond (char **mode, char **primary, char **miimon, char **downdelay, char **updelay, char **arpinterval, char **arpiptarget) { char *answer, *monitor_mode; gboolean answer_bool; unsigned long tmp; gboolean once_more; GError *error = NULL; /* Ask for optional 'bond' arguments. */ printf (_("There are optional arguments for 'bond' connection type.\n")); answer = nmc_get_user_input (_("Do you want to provide them? (yes/no) [yes] ")); if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) { g_free (answer); return; } if (!*mode) { const char *mode_tmp; do { *mode = nmc_get_user_input (_("Bonding mode [balance-rr]: ")); if (!*mode) *mode = g_strdup ("balance-rr"); mode_tmp = nmc_bond_validate_mode (*mode, &error); g_free (*mode); if (mode_tmp) { *mode = g_strdup (mode_tmp); } else { printf ("%s\n", error->message); g_clear_error (&error); } } while (!mode_tmp); } if (g_strcmp0 (*mode, "active-backup") == 0 && !*primary) { do { *primary = nmc_get_user_input (_("Bonding primary interface [none]: ")); once_more = *primary && !nm_utils_iface_valid_name (*primary); if (once_more) { printf (_("Error: 'primary': '%s' is not a valid interface name.\n"), *primary); g_free (*primary); } } while (once_more); } do { monitor_mode = nmc_get_user_input (_("Bonding monitoring mode (miimon or arp) [miimon]: ")); if (!monitor_mode) monitor_mode = g_strdup ("miimon"); once_more = strcmp (monitor_mode, "miimon") && strcmp (monitor_mode, "arp"); if (once_more) { printf (_("Error: '%s' is not a valid monitoring mode; use '%s' or '%s'.\n"), monitor_mode, "miimon", "arp"); g_free (monitor_mode); } } while (once_more); if (strcmp (monitor_mode, "miimon") == 0) { if (!*miimon) { do { *miimon = nmc_get_user_input (_("Bonding miimon [100]: ")); once_more = *miimon && !nmc_string_to_uint (*miimon, TRUE, 0, G_MAXUINT32, &tmp); if (once_more) { printf (_("Error: 'miimon': '%s' is not a valid number <0-%u>.\n"), *miimon, G_MAXUINT32); g_free (*miimon); } } while (once_more); } if (!*downdelay) { do { *downdelay = nmc_get_user_input (_("Bonding downdelay [0]: ")); once_more = *downdelay && !nmc_string_to_uint (*downdelay, TRUE, 0, G_MAXUINT32, &tmp); if (once_more) { printf (_("Error: 'downdelay': '%s' is not a valid number <0-%u>.\n"), *downdelay, G_MAXUINT32); g_free (*downdelay); } } while (once_more); } if (!*updelay) { do { *updelay = nmc_get_user_input (_("Bonding updelay [0]: ")); once_more = *updelay && !nmc_string_to_uint (*updelay, TRUE, 0, G_MAXUINT32, &tmp); if (once_more) { printf (_("Error: 'updelay': '%s' is not a valid number <0-%u>.\n"), *updelay, G_MAXUINT32); g_free (*updelay); } } while (once_more); } } else { if (!*arpinterval) { do { *arpinterval = nmc_get_user_input (_("Bonding arp-interval [0]: ")); once_more = *arpinterval && !nmc_string_to_uint (*arpinterval, TRUE, 0, G_MAXUINT32, &tmp); if (once_more) { printf (_("Error: 'arp-interval': '%s' is not a valid number <0-%u>.\n"), *arpinterval, G_MAXUINT32); g_free (*arpinterval); } } while (once_more); } if (!*arpiptarget) { //FIXME: verify the string *arpiptarget = nmc_get_user_input (_("Bonding arp-ip-target [none]: ")); } } g_free (answer); g_free (monitor_mode); return; } static void do_questionnaire_team_common (const char *type_name, char **config) { char *answer; gboolean answer_bool; gboolean once_more; char *json = NULL; GError *error = NULL; /* Ask for optional 'team' arguments. */ printf (_("There is 1 optional argument for '%s' connection type.\n"), type_name); answer = nmc_get_user_input (_("Do you want to provide it? (yes/no) [yes] ")); if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) { g_free (answer); return; } if (!*config) { do { *config = nmc_get_user_input (_("Team JSON configuration [none]: ")); once_more = !nmc_team_check_config (*config, &json, &error); if (once_more) { printf ("Error: %s\n", error->message); g_clear_error (&error); g_free (*config); } } while (once_more); } *config = json; g_free (answer); return; } /* Both team and team-slave curently have just ithe same one optional argument */ static void do_questionnaire_team (char **config) { do_questionnaire_team_common (_("team"), config); } static void do_questionnaire_team_slave (char **config) { do_questionnaire_team_common (_("team-slave"), config); } static void do_questionnaire_bridge (char **stp, char **priority, char **fwd_delay, char **hello_time, char **max_age, char **ageing_time) { char *answer; gboolean answer_bool; unsigned long tmp; gboolean once_more; GError *error = NULL; /* Ask for optional 'bridge' arguments. */ printf (_("There are 6 optional arguments for 'bridge' connection type.\n")); answer = nmc_get_user_input (_("Do you want to provide them? (yes/no) [yes] ")); if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) { g_free (answer); return; } if (!*stp) { gboolean stp_bool; do { *stp = nmc_get_user_input (_("Enable STP (yes/no) [yes]: ")); *stp = *stp ? *stp : g_strdup ("yes"); once_more = !nmc_string_to_bool (*stp, &stp_bool, &error); if (once_more) { printf (_("Error: 'stp': '%s'.\n"), error->message); g_clear_error (&error); g_free (*stp); } } while (once_more); } if (!*priority) { do { *priority = nmc_get_user_input (_("STP priority [128]: ")); *priority = *priority ? *priority : g_strdup ("128"); once_more = !nmc_string_to_uint (*priority, TRUE, 0, G_MAXUINT16, &tmp); if (once_more) { printf (_("Error: 'priority': '%s' is not a valid number <0-%d>.\n"), *priority, G_MAXUINT16); g_free (*priority); } } while (once_more); } if (!*fwd_delay) { do { *fwd_delay = nmc_get_user_input (_("Forward delay [15]: ")); *fwd_delay = *fwd_delay ? *fwd_delay : g_strdup ("15"); once_more = !nmc_string_to_uint (*fwd_delay, TRUE, 2, 30, &tmp); if (once_more) { printf (_("Error: 'forward-delay': '%s' is not a valid number <2-30>.\n"), *fwd_delay); g_free (*fwd_delay); } } while (once_more); } if (!*hello_time) { do { *hello_time = nmc_get_user_input (_("Hello time [2]: ")); *hello_time = *hello_time ? *hello_time : g_strdup ("2"); once_more = !nmc_string_to_uint (*hello_time, TRUE, 1, 10, &tmp); if (once_more) { printf (_("Error: 'hello-time': '%s' is not a valid number <1-10>.\n"), *hello_time); g_free (*hello_time); } } while (once_more); } if (!*max_age) { do { *max_age = nmc_get_user_input (_("Max age [20]: ")); *max_age = *max_age ? *max_age : g_strdup ("20"); once_more = !nmc_string_to_uint (*max_age, TRUE, 6, 40, &tmp); if (once_more) { printf (_("Error: 'max-age': '%s' is not a valid number <6-40>.\n"), *max_age); g_free (*max_age); } } while (once_more); } if (!*ageing_time) { do { *ageing_time = nmc_get_user_input (_("MAC address ageing time [300]: ")); *ageing_time = *ageing_time ? *ageing_time : g_strdup ("300"); once_more = !nmc_string_to_uint (*ageing_time, TRUE, 0, 1000000, &tmp); if (once_more) { printf (_("Error: 'ageing-time': '%s' is not a valid number <0-1000000>.\n"), *ageing_time); g_free (*ageing_time); } } while (once_more); } g_free (answer); return; } static void do_questionnaire_bridge_slave (char **priority, char **path_cost, char **hairpin) { char *answer; gboolean answer_bool; unsigned long tmp; gboolean once_more; GError *error = NULL; /* Ask for optional 'bridge-slave' arguments. */ printf (_("There are 3 optional arguments for 'bridge-slave' connection type.\n")); answer = nmc_get_user_input (_("Do you want to provide them? (yes/no) [yes] ")); if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) { g_free (answer); return; } if (!*priority) { do { *priority = nmc_get_user_input (_("Bridge port priority [32]: ")); *priority = *priority ? *priority : g_strdup ("32"); once_more = !bridge_prop_string_to_uint (*priority, "priority", NM_TYPE_SETTING_BRIDGE_PORT, NM_SETTING_BRIDGE_PORT_PRIORITY, &tmp, &error); if (once_more) { printf ("%s\n", error->message); g_clear_error (&error); g_free (*priority); } } while (once_more); } if (!*path_cost) { do { *path_cost = nmc_get_user_input (_("Bridge port STP path cost [100]: ")); *path_cost = *path_cost ? *path_cost : g_strdup ("100"); once_more = !bridge_prop_string_to_uint (*path_cost, "path-cost", NM_TYPE_SETTING_BRIDGE_PORT, NM_SETTING_BRIDGE_PORT_PATH_COST, &tmp, &error); if (once_more) { printf ("%s\n", error->message); g_clear_error (&error); g_free (*path_cost); } } while (once_more); } if (!*hairpin) { gboolean hairpin_bool; do { *hairpin = nmc_get_user_input (_("Hairpin (yes/no) [yes]: ")); *hairpin = *hairpin ? *hairpin : g_strdup ("yes"); once_more = !nmc_string_to_bool (*hairpin, &hairpin_bool, &error); if (once_more) { printf (_("Error: 'hairpin': '%s'.\n"), error->message); g_clear_error (&error); g_free (*hairpin); } } while (once_more); } g_free (answer); return; } static void do_questionnaire_vpn (char **user) { char *answer; gboolean answer_bool; /* Ask for optional 'vpn' arguments. */ printf (_("There is 1 optional argument for 'VPN' connection type.\n")); answer = nmc_get_user_input (_("Do you want to provide it? (yes/no) [yes] ")); if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) { g_free (answer); return; } if (!*user) *user = nmc_get_user_input (_("Username [none]: ")); g_free (answer); return; } static void do_questionnaire_olpc (char **channel, char **dhcp_anycast) { char *answer; gboolean answer_bool; unsigned long tmp; gboolean once_more; GError *error = NULL; /* Ask for optional 'olpc' arguments. */ printf (_("There are 2 optional arguments for 'OLPC Mesh' connection type.\n")); answer = nmc_get_user_input (_("Do you want to provide them? (yes/no) [yes] ")); if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) { g_free (answer); return; } if (!*channel) { do { *channel = nmc_get_user_input (_("OLPC Mesh channel [1]: ")); once_more = *channel && !nmc_string_to_uint (*channel, TRUE, 1, 13, &tmp); if (once_more) { printf (_("Error: 'channel': '%s' is not a valid number <1-13>.\n"), *channel); g_free (*channel); } } while (once_more); } if (!*dhcp_anycast) { do { *dhcp_anycast = nmc_get_user_input (_("DHCP anycast MAC address [none]: ")); once_more = !check_and_convert_mac (*dhcp_anycast, NULL, ARPHRD_ETHER, "dhcp-anycast", &error); if (once_more) { printf ("%s\n", error->message); g_clear_error (&error); g_free (*dhcp_anycast); } } while (once_more); } g_free (answer); return; } static gboolean split_address (char* str, char **ip, char **gw, char **rest) { size_t n1, n2, n3, n4, n5; *ip = *gw = *rest = NULL; if (!str) return FALSE; n1 = strspn (str, " \t"); n2 = strcspn (str+n1, " \t\0") + n1; n3 = strspn (str+n2, " \t") + n2; n4 = strcspn (str+n3, " \t\0") + n3; n5 = strspn (str+n4, " \t") + n4; str[n2] = str[n4] = '\0'; *ip = str[n1] ? str + n1 : NULL; *gw = str[n3] ? str + n3 : NULL; *rest = str[n5] ? str + n5 : NULL; return TRUE; } static void ask_for_ip_addresses (NMConnection *connection, int family) { gboolean ip_loop; GError *error = NULL; char *str, *ip, *gw, *rest; const char *prompt; gboolean added; gpointer ipaddr; if (family == 4) prompt =_("IPv4 address (IP[/plen] [gateway]) [none]: "); else prompt =_("IPv6 address (IP[/plen] [gateway]) [none]: "); ip_loop = TRUE; do { str = nmc_get_user_input (prompt); split_address (str, &ip, &gw, &rest); if (ip) { if (family == 4) ipaddr = nmc_parse_and_build_ip4_address (ip, gw, &error); else ipaddr = nmc_parse_and_build_ip6_address (ip, gw, &error); if (ipaddr) { if (family == 4) added = add_ip4_address_to_connection ((NMIP4Address *) ipaddr, connection); else added = add_ip6_address_to_connection ((NMIP6Address *) ipaddr, connection); gw = gw ? gw : (family == 4) ? "0.0.0.0" : "::"; if (added) printf (_(" Address successfully added: %s %s\n"), ip, gw); else printf (_(" Warning: address already present: %s %s\n"), ip, gw); if (rest) printf (_(" Warning: ignoring garbage at the end: '%s'\n"), rest); } else { g_prefix_error (&error, _("Error: ")); printf ("%s\n", error->message); g_clear_error (&error); } } else ip_loop = FALSE; g_free (str); } while (ip_loop); } static void do_questionnaire_ip (NMConnection *connection) { char *answer; gboolean answer_bool; /* Ask for IP addresses */ answer = nmc_get_user_input (_("Do you want to add IP addresses? (yes/no) [yes] ")); if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) { g_free (answer); return; } printf (_("Press to finish adding addresses.\n")); ask_for_ip_addresses (connection, 4); ask_for_ip_addresses (connection, 6); g_free (answer); return; } static gboolean complete_connection_by_type (NMConnection *connection, const char *con_type, GSList *all_connections, gboolean ask, int argc, char **argv, GError **error) { NMSettingConnection *s_con; NMSettingWired *s_wired; NMSettingInfiniband *s_infiniband; NMSettingWireless *s_wifi; NMSettingWimax *s_wimax; NMSettingPPPOE *s_pppoe; NMSettingGsm *s_gsm; NMSettingCdma *s_cdma; NMSettingBluetooth *s_bt; NMSettingVlan *s_vlan; NMSettingBond *s_bond; NMSettingTeam *s_team; NMSettingTeamPort *s_team_port; NMSettingBridge *s_bridge; NMSettingBridgePort *s_bridge_port; NMSettingVPN *s_vpn; NMSettingOlpcMesh *s_olpc_mesh; g_return_val_if_fail (error == NULL || *error == NULL, FALSE); s_con = nm_connection_get_setting_connection (connection); g_assert (s_con); if (!strcmp (con_type, NM_SETTING_WIRED_SETTING_NAME)) { /* Build up the settings required for 'ethernet' */ gboolean success = FALSE; const char *mtu_c = NULL; char *mtu = NULL; guint32 mtu_int = 0; const char *mac_c = NULL; char *mac = NULL; const char *cloned_mac_c = NULL; char *cloned_mac = NULL; GByteArray *array = NULL; GByteArray *cloned_array = NULL; nmc_arg_t exp_args[] = { {"mtu", TRUE, &mtu_c, FALSE}, {"mac", TRUE, &mac_c, FALSE}, {"cloned-mac", TRUE, &cloned_mac_c, FALSE}, {NULL} }; if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) return FALSE; /* Also ask for all optional arguments if '--ask' is specified. */ mtu = mtu_c ? g_strdup (mtu_c) : NULL; mac = mac_c ? g_strdup (mac_c) : NULL; cloned_mac = cloned_mac_c ? g_strdup (cloned_mac_c) : NULL; if (ask) do_questionnaire_ethernet (TRUE, &mtu, &mac, &cloned_mac); if (!check_and_convert_mtu (mtu, &mtu_int, error)) goto cleanup_wired; if (!check_and_convert_mac (mac, &array, ARPHRD_ETHER, "mac", error)) goto cleanup_wired; if (!check_and_convert_mac (cloned_mac, &cloned_array, ARPHRD_ETHER, "cloned-mac", error)) goto cleanup_wired; /* Add ethernet setting */ s_wired = (NMSettingWired *) nm_setting_wired_new (); nm_connection_add_setting (connection, NM_SETTING (s_wired)); if (mtu) g_object_set (s_wired, NM_SETTING_WIRED_MTU, mtu_int, NULL); if (array) g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, array, NULL); if (cloned_array) g_object_set (s_wired, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, cloned_array, NULL); success = TRUE; cleanup_wired: g_free (mtu); g_free (mac); g_free (cloned_mac); if (array) g_byte_array_free (array, TRUE); if (cloned_array) g_byte_array_free (cloned_array, TRUE); if (!success) return FALSE; } else if (!strcmp (con_type, NM_SETTING_INFINIBAND_SETTING_NAME)) { /* Build up the settings required for 'infiniband' */ gboolean success = FALSE; const char *mtu_c = NULL; char *mtu = NULL; guint32 mtu_int = 0; const char *mac_c = NULL; char *mac = NULL; GByteArray *array = NULL; const char *mode_c = NULL; char *mode = NULL; const char *parent_c = NULL; char *parent = NULL; const char *p_key_c = NULL; char *p_key = NULL; guint32 p_key_int = 0; nmc_arg_t exp_args[] = { {"mtu", TRUE, &mtu_c, FALSE}, {"mac", TRUE, &mac_c, FALSE}, {"transport-mode", TRUE, &mode_c, FALSE}, {"parent", TRUE, &parent_c, FALSE}, {"p-key", TRUE, &p_key_c, FALSE}, {NULL} }; if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) return FALSE; /* Also ask for all optional arguments if '--ask' is specified. */ mtu = mtu_c ? g_strdup (mtu_c) : NULL; mac = mac_c ? g_strdup (mac_c) : NULL; mode = mode_c ? g_strdup (mode_c) : NULL; parent = parent_c ? g_strdup (parent_c) : NULL; p_key = p_key_c ? g_strdup (p_key_c) : NULL; if (ask) do_questionnaire_infiniband (&mtu, &mac, &mode, &parent, &p_key); if (!check_and_convert_mtu (mtu, &mtu_int, error)) goto cleanup_ib; if (!check_and_convert_mac (mac, &array, ARPHRD_INFINIBAND, "mac", error)) goto cleanup_ib; if (!check_infiniband_mode (mode, error)) goto cleanup_ib; if (p_key) { if (!check_infiniband_p_key (p_key, &p_key_int, error)) goto cleanup_ib; if (!check_infiniband_parent (parent, error)) goto cleanup_ib; } else if (parent) { g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: 'parent': not valid without 'p-key'.")); goto cleanup_ib; } /* Add 'infiniband' setting */ s_infiniband = (NMSettingInfiniband *) nm_setting_infiniband_new (); nm_connection_add_setting (connection, NM_SETTING (s_infiniband)); g_object_set (s_infiniband, NM_SETTING_INFINIBAND_TRANSPORT_MODE, mode ? mode : "datagram", NULL); if (mtu) g_object_set (s_infiniband, NM_SETTING_INFINIBAND_MTU, mtu_int, NULL); if (array) { g_object_set (s_infiniband, NM_SETTING_INFINIBAND_MAC_ADDRESS, array, NULL); g_byte_array_free (array, TRUE); } if (p_key) g_object_set (s_infiniband, NM_SETTING_INFINIBAND_P_KEY, p_key_int, NULL); if (parent) g_object_set (s_infiniband, NM_SETTING_INFINIBAND_PARENT, parent, NULL); success = TRUE; cleanup_ib: g_free (mtu); g_free (mac); g_free (mode); g_free (parent); g_free (p_key); if (!success) return FALSE; } else if (!strcmp (con_type, NM_SETTING_WIRELESS_SETTING_NAME)) { /* Build up the settings required for 'wifi' */ gboolean success = FALSE; char *ssid_ask = NULL; const char *ssid = NULL; GByteArray *ssid_arr = NULL; const char *mtu_c = NULL; char *mtu = NULL; guint32 mtu_int = 0; const char *mac_c = NULL; char *mac = NULL; GByteArray *mac_array = NULL; const char *cloned_mac_c = NULL; char *cloned_mac = NULL; GByteArray *cloned_mac_array = NULL; nmc_arg_t exp_args[] = { {"ssid", TRUE, &ssid, !ask}, {"mtu", TRUE, &mtu_c, FALSE}, {"mac", TRUE, &mac_c, FALSE}, {"cloned-mac", TRUE, &cloned_mac_c, FALSE}, {NULL} }; if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) return FALSE; if (!ssid && ask) ssid = ssid_ask = nmc_get_user_input (_("SSID: ")); if (!ssid) { g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: 'ssid' is required.")); return FALSE; } /* Also ask for all optional arguments if '--ask' is specified. */ mtu = mtu_c ? g_strdup (mtu_c) : NULL; mac = mac_c ? g_strdup (mac_c) : NULL; cloned_mac = cloned_mac_c ? g_strdup (cloned_mac_c) : NULL; if (ask) do_questionnaire_wifi (&mtu, &mac, &cloned_mac); if (!check_and_convert_mtu (mtu, &mtu_int, error)) goto cleanup_wifi; if (!check_and_convert_mac (mac, &mac_array, ARPHRD_ETHER, "mac", error)) goto cleanup_wifi; if (!check_and_convert_mac (cloned_mac, &cloned_mac_array, ARPHRD_ETHER, "cloned-mac", error)) goto cleanup_wifi; /* Add wifi setting */ s_wifi = (NMSettingWireless *) nm_setting_wireless_new (); nm_connection_add_setting (connection, NM_SETTING (s_wifi)); ssid_arr = g_byte_array_sized_new (strlen (ssid)); g_byte_array_append (ssid_arr, (const guint8 *) ssid, strlen (ssid)); g_object_set (s_wifi, NM_SETTING_WIRELESS_SSID, ssid_arr, NULL); if (mtu) g_object_set (s_wifi, NM_SETTING_WIRELESS_MTU, mtu_int, NULL); if (mac_array) g_object_set (s_wifi, NM_SETTING_WIRELESS_MAC_ADDRESS, mac_array, NULL); if (cloned_mac_array) g_object_set (s_wifi, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, cloned_mac_array, NULL); success = TRUE; cleanup_wifi: g_free (ssid_ask); g_free (mtu); g_free (mac); g_free (cloned_mac); if (ssid_arr) g_byte_array_free (ssid_arr, TRUE); if (mac_array) g_byte_array_free (mac_array, TRUE); if (cloned_mac_array) g_byte_array_free (cloned_mac_array, TRUE); if (!success) return FALSE; } else if (!strcmp (con_type, NM_SETTING_WIMAX_SETTING_NAME)) { /* Build up the settings required for 'wimax' */ gboolean success = FALSE; const char *nsp_name = NULL; char *nsp_name_ask = NULL; const char *mac_c = NULL; char *mac = NULL; GByteArray *mac_array = NULL; nmc_arg_t exp_args[] = { {"nsp", TRUE, &nsp_name, !ask}, {"mac", TRUE, &mac_c, FALSE}, {NULL} }; if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) return FALSE; if (!nsp_name && ask) nsp_name = nsp_name_ask = nmc_get_user_input (_("WiMAX NSP name: ")); if (!nsp_name) { g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: 'nsp' is required.")); goto cleanup_wimax; } /* Also ask for all optional arguments if '--ask' is specified. */ mac = mac_c ? g_strdup (mac_c) : NULL; if (ask) do_questionnaire_wimax (&mac); if (!check_and_convert_mac (mac, &mac_array, ARPHRD_ETHER, "mac", error)) goto cleanup_wimax; /* Add 'wimax' setting */ s_wimax = (NMSettingWimax *) nm_setting_wimax_new (); nm_connection_add_setting (connection, NM_SETTING (s_wimax)); g_object_set (s_wimax, NM_SETTING_WIMAX_NETWORK_NAME, nsp_name, NULL); if (mac_array) { g_object_set (s_wimax, NM_SETTING_WIMAX_MAC_ADDRESS, mac_array, NULL); g_byte_array_free (mac_array, TRUE); } success = TRUE; cleanup_wimax: g_free (nsp_name_ask); g_free (mac); if (!success) return FALSE; } else if (!strcmp (con_type, NM_SETTING_PPPOE_SETTING_NAME)) { /* Build up the settings required for 'pppoe' */ gboolean success = FALSE; const char *username = NULL; char *username_ask = NULL; const char *password_c = NULL; char *password = NULL; const char *service_c = NULL; char *service = NULL; const char *mtu_c = NULL; char *mtu = NULL; guint32 mtu_int = 0; const char *mac_c = NULL; char *mac = NULL; GByteArray *mac_array = NULL; nmc_arg_t exp_args[] = { {"username", TRUE, &username, !ask}, {"password", TRUE, &password_c, FALSE}, {"service", TRUE, &service_c, FALSE}, {"mtu", TRUE, &mtu_c, FALSE}, {"mac", TRUE, &mac_c, FALSE}, {NULL} }; if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) return FALSE; if (!username && ask) username = username_ask = nmc_get_user_input (_("PPPoE username: ")); if (!username) { g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: 'username' is required.")); goto cleanup_pppoe; } /* Also ask for all optional arguments if '--ask' is specified. */ password = g_strdup (password_c); service = g_strdup (service_c); mtu = g_strdup (mtu_c); mac = g_strdup (mac_c); if (ask) do_questionnaire_pppoe (&password, &service, &mtu, &mac); if (!check_and_convert_mtu (mtu, &mtu_int, error)) goto cleanup_pppoe; if (!check_and_convert_mac (mac, &mac_array, ARPHRD_ETHER, "mac", error)) goto cleanup_pppoe; /* Add 'pppoe' setting */ s_pppoe = (NMSettingPPPOE *) nm_setting_pppoe_new (); nm_connection_add_setting (connection, NM_SETTING (s_pppoe)); g_object_set (s_pppoe, NM_SETTING_PPPOE_USERNAME, username, NULL); g_object_set (s_pppoe, NM_SETTING_PPPOE_PASSWORD, password, NULL); g_object_set (s_pppoe, NM_SETTING_PPPOE_SERVICE, service, NULL); /* Add ethernet setting */ s_wired = (NMSettingWired *) nm_setting_wired_new (); nm_connection_add_setting (connection, NM_SETTING (s_wired)); if (mtu) g_object_set (s_wired, NM_SETTING_WIRED_MTU, mtu_int, NULL); if (mac_array) g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, mac_array, NULL); success = TRUE; cleanup_pppoe: g_free (username_ask); g_free (password); g_free (service); g_free (mtu); g_free (mac); if (mac_array) g_byte_array_free (mac_array, TRUE); if (!success) return FALSE; } else if ( !strcmp (con_type, NM_SETTING_GSM_SETTING_NAME) || !strcmp (con_type, NM_SETTING_CDMA_SETTING_NAME)) { /* Build up the settings required for 'gsm' or 'cdma' mobile broadband */ gboolean success = FALSE; const char *apn = NULL; char *apn_ask = NULL; const char *user_c = NULL; char *user = NULL; const char *password_c = NULL; char *password = NULL; gboolean is_gsm; int i = 0; nmc_arg_t gsm_args[] = { {NULL}, {NULL}, {NULL}, /* placeholders */ {NULL} }; is_gsm = !strcmp (con_type, NM_SETTING_GSM_SETTING_NAME); if (is_gsm) gsm_args[i++] = (nmc_arg_t) {"apn", TRUE, &apn, !ask}; gsm_args[i++] = (nmc_arg_t) {"user", TRUE, &user_c, FALSE}; gsm_args[i++] = (nmc_arg_t) {"password", TRUE, &password_c, FALSE}; gsm_args[i++] = (nmc_arg_t) {NULL}; if (!nmc_parse_args (gsm_args, FALSE, &argc, &argv, error)) return FALSE; if (!apn && ask && is_gsm) apn = apn_ask = nmc_get_user_input (_("APN: ")); if (!apn && is_gsm) { g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: 'apn' is required.")); goto cleanup_mobile; } /* Also ask for all optional arguments if '--ask' is specified. */ user = user_c ? g_strdup (user_c) : NULL; password = password_c ? g_strdup (password_c) : NULL; if (ask) do_questionnaire_mobile (&user, &password); if (is_gsm) { g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_GSM_SETTING_NAME, NULL); /* Add 'gsm' setting */ s_gsm = (NMSettingGsm *) nm_setting_gsm_new (); nm_connection_add_setting (connection, NM_SETTING (s_gsm)); g_object_set (s_gsm, NM_SETTING_GSM_NUMBER, "*99#", NM_SETTING_GSM_APN, apn, NM_SETTING_GSM_USERNAME, user, NM_SETTING_GSM_PASSWORD, password, NULL); g_free (apn_ask); } else { g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_CDMA_SETTING_NAME, NULL); /* Add 'cdma' setting */ s_cdma = (NMSettingCdma *) nm_setting_cdma_new (); nm_connection_add_setting (connection, NM_SETTING (s_cdma)); g_object_set (s_cdma, NM_SETTING_CDMA_NUMBER, "#777", NM_SETTING_CDMA_USERNAME, user, NM_SETTING_CDMA_PASSWORD, password, NULL); } success = TRUE; cleanup_mobile: g_free (user); g_free (password); if (!success) return FALSE; } else if (!strcmp (con_type, NM_SETTING_BLUETOOTH_SETTING_NAME)) { /* Build up the settings required for 'bluetooth' */ gboolean success = FALSE; const char *addr = NULL; char *addr_ask = NULL; const char *bt_type_c = NULL; char *bt_type = NULL; GByteArray *array = NULL; nmc_arg_t exp_args[] = { {"addr", TRUE, &addr, !ask}, {"bt-type", TRUE, &bt_type_c, FALSE}, {NULL} }; if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) return FALSE; if (!addr && ask) addr = addr_ask = nmc_get_user_input (_("Bluetooth device address: ")); if (!addr) { g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: 'addr' is required.")); return FALSE; } if (!check_and_convert_mac (addr, &array, ARPHRD_ETHER, "addr", error)) goto cleanup_bt; /* Also ask for all optional arguments if '--ask' is specified. */ bt_type = bt_type_c ? g_strdup (bt_type_c) : NULL; if (ask) do_questionnaire_bluetooth (&bt_type); /* Default to 'panu' if bt-type is not provided. */ if (!bt_type) bt_type = g_strdup (NM_SETTING_BLUETOOTH_TYPE_PANU); /* Add 'bluetooth' setting */ s_bt = (NMSettingBluetooth *) nm_setting_bluetooth_new (); nm_connection_add_setting (connection, NM_SETTING (s_bt)); if (array) { g_object_set (s_bt, NM_SETTING_BLUETOOTH_BDADDR, array, NULL); g_byte_array_free (array, TRUE); } /* 'dun' type requires adding 'gsm' or 'cdma' setting */ if ( !strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN) || !strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN"-gsm")) { bt_type = g_strdup (NM_SETTING_BLUETOOTH_TYPE_DUN); s_gsm = (NMSettingGsm *) nm_setting_gsm_new (); nm_connection_add_setting (connection, NM_SETTING (s_gsm)); g_object_set (s_gsm, NM_SETTING_GSM_NUMBER, "*99#", NULL); // g_object_set (s_gsm, NM_SETTING_GSM_APN, "FIXME", NULL; } else if (!strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN"-cdma")) { bt_type = g_strdup (NM_SETTING_BLUETOOTH_TYPE_DUN); s_cdma = (NMSettingCdma *) nm_setting_cdma_new (); nm_connection_add_setting (connection, NM_SETTING (s_cdma)); g_object_set (s_cdma, NM_SETTING_CDMA_NUMBER, "#777", NULL); } else if (!strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU)) { /* no op */ } else { g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: 'bt-type': '%s' not valid; use [%s, %s (%s), %s]."), bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU, NM_SETTING_BLUETOOTH_TYPE_DUN, NM_SETTING_BLUETOOTH_TYPE_DUN"-gsm", NM_SETTING_BLUETOOTH_TYPE_DUN"-cdma"); goto cleanup_bt; } g_object_set (s_bt, NM_SETTING_BLUETOOTH_TYPE, bt_type, NULL); success = TRUE; cleanup_bt: g_free (addr_ask); g_free (bt_type); if (!success) return FALSE; } else if (!strcmp (con_type, NM_SETTING_VLAN_SETTING_NAME)) { /* Build up the settings required for 'vlan' */ gboolean success = FALSE; const char *ifname = NULL; const char *parent = NULL; char *parent_ask = NULL; const char *vlan_id = NULL; char *vlan_id_ask = NULL; unsigned long id = 0; const char *flags_c = NULL; char *flags = NULL; guint32 flags_int = 0; const char *ingress_c = NULL, *egress_c = NULL; char *ingress = NULL, *egress = NULL; char **ingress_arr = NULL, **egress_arr = NULL, **p; const char *mtu_c = NULL; char *mtu = NULL; guint32 mtu_int; GByteArray *addr_array = NULL; nmc_arg_t exp_args[] = { {"dev", TRUE, &parent, !ask}, {"id", TRUE, &vlan_id, !ask}, {"flags", TRUE, &flags_c, FALSE}, {"ingress", TRUE, &ingress_c, FALSE}, {"egress", TRUE, &egress_c, FALSE}, {"mtu", TRUE, &mtu_c, FALSE}, {NULL} }; if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) return FALSE; if (!parent && ask) parent = parent_ask = nmc_get_user_input (_("VLAN parent device or connection UUID: ")); if (!parent) { g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: 'dev' is required.")); return FALSE; } if (!vlan_id && ask) vlan_id = vlan_id_ask = nmc_get_user_input (_("VLAN ID <0-4095>: ")); if (!vlan_id) { g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: 'id' is required.")); goto cleanup_vlan; } if (vlan_id) { if (!nmc_string_to_uint (vlan_id, TRUE, 0, 4095, &id)) { g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: 'id': '%s' is not valid; use <0-4095>."), vlan_id); goto cleanup_vlan; } } if ( !(addr_array = nm_utils_hwaddr_atoba (parent, ARPHRD_ETHER)) && !nm_utils_is_uuid (parent) && !nm_utils_iface_valid_name (parent)) { g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: 'dev': '%s' is neither UUID, interface name, nor MAC."), parent); goto cleanup_vlan; } /* Also ask for all optional arguments if '--ask' is specified. */ mtu = mtu_c ? g_strdup (mtu_c) : NULL; flags = flags_c ? g_strdup (flags_c) : NULL; ingress = ingress_c ? g_strdup (ingress_c) : NULL; egress = egress_c ? g_strdup (egress_c) : NULL; if (ask) do_questionnaire_vlan (&mtu, &flags, &ingress, &egress); /* ifname is taken from connection's ifname */ ifname = nm_setting_connection_get_interface_name (s_con); if (!check_and_convert_mtu (mtu, &mtu_int, error)) goto cleanup_vlan; if (!check_and_convert_vlan_flags (flags, &flags_int, error)) goto cleanup_vlan; if (!check_and_convert_vlan_prio_maps (ingress, NM_VLAN_INGRESS_MAP, &ingress_arr, error)) goto cleanup_vlan; if (!check_and_convert_vlan_prio_maps (egress, NM_VLAN_EGRESS_MAP, &egress_arr, error)) goto cleanup_vlan; /* Add 'vlan' setting */ s_vlan = (NMSettingVlan *) nm_setting_vlan_new (); nm_connection_add_setting (connection, NM_SETTING (s_vlan)); /* Add 'wired' setting if necessary */ if (mtu || addr_array) { s_wired = (NMSettingWired *) nm_setting_wired_new (); nm_connection_add_setting (connection, NM_SETTING (s_wired)); if (mtu) g_object_set (s_wired, NM_SETTING_WIRED_MTU, mtu_int, NULL); if (addr_array) g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, addr_array, NULL); } /* Set 'vlan' properties */ if (!addr_array) g_object_set (s_vlan, NM_SETTING_VLAN_PARENT, parent, NULL); if (ifname) g_object_set (s_vlan, NM_SETTING_VLAN_INTERFACE_NAME, ifname, NULL); g_object_set (s_vlan, NM_SETTING_VLAN_ID, id, NULL); if (flags) g_object_set (s_vlan, NM_SETTING_VLAN_FLAGS, flags_int, NULL); for (p = ingress_arr; p && *p; p++) nm_setting_vlan_add_priority_str (s_vlan, NM_VLAN_INGRESS_MAP, *p); for (p = egress_arr; p && *p; p++) nm_setting_vlan_add_priority_str (s_vlan, NM_VLAN_EGRESS_MAP, *p); success = TRUE; cleanup_vlan: g_free (mtu); g_free (flags); g_free (ingress); g_free (egress); if (addr_array) g_byte_array_free (addr_array, TRUE); g_free (parent_ask); g_free (vlan_id_ask); g_strfreev (ingress_arr); g_strfreev (egress_arr); if (!success) return FALSE; } else if (!strcmp (con_type, NM_SETTING_BOND_SETTING_NAME)) { /* Build up the settings required for 'bond' */ gboolean success = FALSE; char *bond_ifname = NULL; const char *ifname = NULL; const char *bond_mode_c = NULL; char *bond_mode = NULL; const char *bond_primary_c = NULL; char *bond_primary = NULL; const char *bond_miimon_c = NULL; char *bond_miimon = NULL; const char *bond_downdelay_c = NULL; char *bond_downdelay = NULL; const char *bond_updelay_c = NULL; char *bond_updelay = NULL; const char *bond_arpinterval_c = NULL; char *bond_arpinterval = NULL; const char *bond_arpiptarget_c = NULL; char *bond_arpiptarget = NULL; nmc_arg_t exp_args[] = { {"mode", TRUE, &bond_mode_c, FALSE}, {"primary", TRUE, &bond_primary_c, FALSE}, {"miimon", TRUE, &bond_miimon_c, FALSE}, {"downdelay", TRUE, &bond_downdelay_c, FALSE}, {"updelay", TRUE, &bond_updelay_c, FALSE}, {"arp-interval", TRUE, &bond_arpinterval_c, FALSE}, {"arp-ip-target", TRUE, &bond_arpiptarget_c, FALSE}, {NULL} }; if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) return FALSE; /* Also ask for all optional arguments if '--ask' is specified. */ bond_mode = bond_mode_c ? g_strdup (bond_mode_c) : NULL; bond_primary = bond_primary_c ? g_strdup (bond_primary_c) : NULL; bond_miimon = bond_miimon_c ? g_strdup (bond_miimon_c) : NULL; bond_downdelay = bond_downdelay_c ? g_strdup (bond_downdelay_c) : NULL; bond_updelay = bond_updelay_c ? g_strdup (bond_updelay_c) : NULL; bond_arpinterval = bond_arpinterval_c ? g_strdup (bond_arpinterval_c) : NULL; bond_arpiptarget = bond_arpiptarget_c ? g_strdup (bond_arpiptarget_c) : NULL; if (ask) do_questionnaire_bond (&bond_mode, &bond_primary, &bond_miimon, &bond_downdelay, &bond_updelay, &bond_arpinterval, &bond_arpiptarget); /* Use connection's ifname as 'bond' ifname if exists, else generate one */ ifname = nm_setting_connection_get_interface_name (s_con); if (!ifname) bond_ifname = unique_master_iface_ifname (all_connections, NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_INTERFACE_NAME, "nm-bond"); else bond_ifname = g_strdup (ifname); /* Add 'bond' setting */ s_bond = (NMSettingBond *) nm_setting_bond_new (); nm_connection_add_setting (connection, NM_SETTING (s_bond)); /* Set bond options */ g_object_set (s_bond, NM_SETTING_BOND_INTERFACE_NAME, bond_ifname, NULL); if (bond_mode) { GError *err = NULL; const char *bm; if (!(bm = nmc_bond_validate_mode (bond_mode, &err))) { g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: 'mode': %s."), err->message); g_clear_error (&err); goto cleanup_bond; } nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_MODE, bm); } if (bond_primary) { if (!nm_utils_iface_valid_name (bond_primary)) { g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: 'primary': '%s' is not a valid interface name."), bond_primary); goto cleanup_bond; } nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_PRIMARY, bond_primary); } if (bond_miimon) nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_MIIMON, bond_miimon); if (bond_downdelay && strcmp (bond_downdelay, "0") != 0) nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY, bond_downdelay); if (bond_updelay && strcmp (bond_updelay, "0") != 0) nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_UPDELAY, bond_updelay); if (bond_arpinterval && strcmp (bond_arpinterval, "0") != 0) nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_ARP_INTERVAL, bond_arpinterval); if (bond_arpiptarget) nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET, bond_arpiptarget); success = TRUE; cleanup_bond: g_free (bond_ifname); g_free (bond_mode); g_free (bond_primary); g_free (bond_miimon); g_free (bond_downdelay); g_free (bond_updelay); g_free (bond_arpinterval); g_free (bond_arpiptarget); if (!success) return FALSE; } else if (!strcmp (con_type, "bond-slave")) { /* Build up the settings required for 'bond-slave' */ const char *master = NULL; char *master_ask = NULL; const char *type = NULL; nmc_arg_t exp_args[] = { {"master", TRUE, &master, !ask}, {"type", TRUE, &type, FALSE}, {NULL} }; if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, error)) return FALSE; if (!master && ask) master = master_ask = nmc_get_user_input (_("Bond master: ")); if (!master) { g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: 'master' is required.")); return FALSE; } if (type) printf (_("Warning: 'type' is currently ignored. " "We only support ethernet slaves for now.\n")); /* Change properties in 'connection' setting */ g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_CONNECTION_MASTER, master, NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BOND_SETTING_NAME, NULL); /* Add ethernet setting */ s_wired = (NMSettingWired *) nm_setting_wired_new (); nm_connection_add_setting (connection, NM_SETTING (s_wired)); g_free (master_ask); } else if (!strcmp (con_type, NM_SETTING_TEAM_SETTING_NAME)) { /* Build up the settings required for 'team' */ gboolean success = FALSE; char *team_ifname = NULL; const char *ifname = NULL; const char *config_c = NULL; char *config = NULL; char *json = NULL; nmc_arg_t exp_args[] = { {"config", TRUE, &config_c, FALSE}, {NULL} }; if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) return FALSE; /* Also ask for all optional arguments if '--ask' is specified. */ config = g_strdup (config_c); if (ask) do_questionnaire_team (&config); /* Use connection's ifname as 'team' ifname if exists, else generate one */ ifname = nm_setting_connection_get_interface_name (s_con); if (!ifname) team_ifname = unique_master_iface_ifname (all_connections, NM_SETTING_TEAM_SETTING_NAME, NM_SETTING_TEAM_INTERFACE_NAME, "nm-team"); else team_ifname = g_strdup (ifname); /* Add 'team' setting */ s_team = (NMSettingTeam *) nm_setting_team_new (); nm_connection_add_setting (connection, NM_SETTING (s_team)); if (!nmc_team_check_config (config, &json, error)) { g_prefix_error (error, _("Error: ")); goto cleanup_team; } /* Set team options */ g_object_set (s_team, NM_SETTING_TEAM_INTERFACE_NAME, team_ifname, NULL); g_object_set (s_team, NM_SETTING_TEAM_CONFIG, json, NULL); success = TRUE; cleanup_team: g_free (team_ifname); g_free (config); g_free (json); if (!success) return FALSE; } else if (!strcmp (con_type, "team-slave")) { /* Build up the settings required for 'team-slave' */ gboolean success = FALSE; const char *master = NULL; char *master_ask = NULL; const char *type = NULL; const char *config_c = NULL; char *config = NULL; char *json = NULL; nmc_arg_t exp_args[] = { {"master", TRUE, &master, !ask}, {"type", TRUE, &type, FALSE}, {"config", TRUE, &config_c, FALSE}, {NULL} }; if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, error)) return FALSE; if (!master && ask) master = master_ask = nmc_get_user_input (_("Team master: ")); if (!master) { g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: 'master' is required.")); return FALSE; } /* Also ask for all optional arguments if '--ask' is specified. */ config = g_strdup (config_c); if (ask) do_questionnaire_team_slave (&config); if (type) printf (_("Warning: 'type' is currently ignored. " "We only support ethernet slaves for now.\n")); /* Add 'team-port' setting */ s_team_port = (NMSettingTeamPort *) nm_setting_team_port_new (); nm_connection_add_setting (connection, NM_SETTING (s_team_port)); if (!nmc_team_check_config (config, &json, error)) { g_prefix_error (error, _("Error: ")); goto cleanup_team_slave; } /* Set team-port options */ g_object_set (s_team_port, NM_SETTING_TEAM_PORT_CONFIG, json, NULL); /* Change properties in 'connection' setting */ g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_CONNECTION_MASTER, master, NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_TEAM_SETTING_NAME, NULL); /* Add ethernet setting */ s_wired = (NMSettingWired *) nm_setting_wired_new (); nm_connection_add_setting (connection, NM_SETTING (s_wired)); success = TRUE; cleanup_team_slave: g_free (master_ask); g_free (config); g_free (json); if (!success) return FALSE; } else if (!strcmp (con_type, NM_SETTING_BRIDGE_SETTING_NAME)) { /* Build up the settings required for 'bridge' */ gboolean success = FALSE; char *bridge_ifname = NULL; const char *ifname = NULL; const char *stp_c = NULL; char *stp = NULL; const char *priority_c = NULL; char *priority = NULL; const char *fwd_delay_c = NULL; char *fwd_delay = NULL; const char *hello_time_c = NULL; char *hello_time = NULL; const char *max_age_c = NULL; char *max_age = NULL; const char *ageing_time_c = NULL; char *ageing_time = NULL; gboolean stp_bool; unsigned long stp_prio_int, fwd_delay_int, hello_time_int, max_age_int, ageing_time_int; nmc_arg_t exp_args[] = { {"stp", TRUE, &stp_c, FALSE}, {"priority", TRUE, &priority_c, FALSE}, {"forward-delay", TRUE, &fwd_delay_c, FALSE}, {"hello-time", TRUE, &hello_time_c, FALSE}, {"max-age", TRUE, &max_age_c, FALSE}, {"ageing-time", TRUE, &ageing_time_c, FALSE}, {NULL} }; if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) return FALSE; /* Also ask for all optional arguments if '--ask' is specified. */ stp = stp_c ? g_strdup (stp_c) : NULL; priority = priority_c ? g_strdup (priority_c) : NULL; fwd_delay = fwd_delay_c ? g_strdup (fwd_delay_c) : NULL; hello_time = hello_time_c ? g_strdup (hello_time_c) : NULL; max_age = max_age_c ? g_strdup (max_age_c) : NULL; ageing_time = ageing_time_c ? g_strdup (ageing_time_c) : NULL; if (ask) do_questionnaire_bridge (&stp, &priority, &fwd_delay, &hello_time, &max_age, &ageing_time); /* Use connection's ifname as 'bridge' ifname if exists, else generate one */ ifname = nm_setting_connection_get_interface_name (s_con); if (!ifname) bridge_ifname = unique_master_iface_ifname (all_connections, NM_SETTING_BRIDGE_SETTING_NAME, NM_SETTING_BRIDGE_INTERFACE_NAME, "nm-bridge"); else bridge_ifname = g_strdup (ifname); if (stp) { GError *tmp_err = NULL; if (!nmc_string_to_bool (stp, &stp_bool, &tmp_err)) { g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: 'stp': %s."), tmp_err->message); g_clear_error (&tmp_err); goto cleanup_bridge; } } /* Add 'bond' setting */ /* Must be done *before* bridge_prop_string_to_uint() so that the type is known */ s_bridge = (NMSettingBridge *) nm_setting_bridge_new (); nm_connection_add_setting (connection, NM_SETTING (s_bridge)); if (priority) if (!bridge_prop_string_to_uint (priority, "priority", NM_TYPE_SETTING_BRIDGE, NM_SETTING_BRIDGE_PRIORITY, &stp_prio_int, error)) goto cleanup_bridge; if (fwd_delay) if (!bridge_prop_string_to_uint (fwd_delay, "forward-delay", NM_TYPE_SETTING_BRIDGE, NM_SETTING_BRIDGE_FORWARD_DELAY, &fwd_delay_int, error)) goto cleanup_bridge; if (hello_time) if (!bridge_prop_string_to_uint (hello_time, "hello-time", NM_TYPE_SETTING_BRIDGE, NM_SETTING_BRIDGE_HELLO_TIME, &hello_time_int, error)) goto cleanup_bridge; if (max_age) if (!bridge_prop_string_to_uint (max_age, "max-age", NM_TYPE_SETTING_BRIDGE, NM_SETTING_BRIDGE_MAX_AGE, &max_age_int, error)) goto cleanup_bridge; if (ageing_time) if (!bridge_prop_string_to_uint (ageing_time, "ageing-time", NM_TYPE_SETTING_BRIDGE, NM_SETTING_BRIDGE_AGEING_TIME, &ageing_time_int, error)) goto cleanup_bridge; /* Set bridge options */ g_object_set (s_bridge, NM_SETTING_BRIDGE_INTERFACE_NAME, bridge_ifname, NULL); if (stp) g_object_set (s_bridge, NM_SETTING_BRIDGE_STP, stp_bool, NULL); if (priority) g_object_set (s_bridge, NM_SETTING_BRIDGE_PRIORITY, stp_prio_int, NULL); if (fwd_delay) g_object_set (s_bridge, NM_SETTING_BRIDGE_FORWARD_DELAY, fwd_delay_int, NULL); if (hello_time) g_object_set (s_bridge, NM_SETTING_BRIDGE_HELLO_TIME, hello_time_int, NULL); if (max_age) g_object_set (s_bridge, NM_SETTING_BRIDGE_MAX_AGE, max_age_int, NULL); if (ageing_time) g_object_set (s_bridge, NM_SETTING_BRIDGE_AGEING_TIME, ageing_time_int, NULL); success = TRUE; cleanup_bridge: g_free (bridge_ifname); g_free (stp); g_free (priority); g_free (fwd_delay); g_free (hello_time); g_free (max_age); g_free (ageing_time); if (!success) return FALSE; } else if (!strcmp (con_type, "bridge-slave")) { /* Build up the settings required for 'bridge-slave' */ gboolean success = FALSE; const char *master = NULL; char *master_ask = NULL; const char *type = NULL; const char *priority_c = NULL; char *priority = NULL; const char *path_cost_c = NULL; char *path_cost = NULL; const char *hairpin_c = NULL; char *hairpin = NULL; unsigned long prio_int, path_cost_int; gboolean hairpin_bool; nmc_arg_t exp_args[] = { {"master", TRUE, &master, !ask}, {"type", TRUE, &type, FALSE}, {"priority", TRUE, &priority_c, FALSE}, {"path-cost", TRUE, &path_cost_c, FALSE}, {"hairpin", TRUE, &hairpin_c, FALSE}, {NULL} }; if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, error)) return FALSE; if (!master && ask) master = master_ask = nmc_get_user_input (_("Bridge master: ")); if (!master) { g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: 'master' is required.")); return FALSE; } if (!nm_utils_is_uuid (master) && !nm_utils_iface_valid_name (master)) { g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: 'master': '%s' is not valid UUID nor interface."), master); goto cleanup_bridge_slave; } if (type) printf (_("Warning: 'type' is currently ignored. " "We only support ethernet slaves for now.\n")); /* Add 'bridge-port' setting */ /* Must be done *before* bridge_prop_string_to_uint() so that the type is known */ s_bridge_port = (NMSettingBridgePort *) nm_setting_bridge_port_new (); nm_connection_add_setting (connection, NM_SETTING (s_bridge_port)); /* Also ask for all optional arguments if '--ask' is specified. */ priority = priority_c ? g_strdup (priority_c) : NULL; path_cost = path_cost_c ? g_strdup (path_cost_c) : NULL; hairpin = hairpin_c ? g_strdup (hairpin_c) : NULL; if (ask) do_questionnaire_bridge_slave (&priority, &path_cost, &hairpin); if (priority) if (!bridge_prop_string_to_uint (priority, "priority", NM_TYPE_SETTING_BRIDGE_PORT, NM_SETTING_BRIDGE_PORT_PRIORITY, &prio_int, error)) goto cleanup_bridge_slave; if (path_cost) if (!bridge_prop_string_to_uint (path_cost, "path-cost", NM_TYPE_SETTING_BRIDGE_PORT, NM_SETTING_BRIDGE_PORT_PATH_COST, &path_cost_int, error)) goto cleanup_bridge_slave; if (hairpin) { GError *tmp_err = NULL; if (!nmc_string_to_bool (hairpin, &hairpin_bool, &tmp_err)) { g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: 'hairpin': %s."), tmp_err->message); g_clear_error (&tmp_err); goto cleanup_bridge_slave; } } /* Change properties in 'connection' setting */ g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_CONNECTION_MASTER, master, NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BRIDGE_SETTING_NAME, NULL); /* Add ethernet setting */ s_wired = (NMSettingWired *) nm_setting_wired_new (); nm_connection_add_setting (connection, NM_SETTING (s_wired)); if (priority) g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_PRIORITY, prio_int, NULL); if (path_cost) g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_PATH_COST, path_cost_int, NULL); if (hairpin) g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE, hairpin_bool, NULL); success = TRUE; cleanup_bridge_slave: g_free (master_ask); g_free (priority); g_free (path_cost); g_free (hairpin); if (!success) return FALSE; } else if (!strcmp (con_type, NM_SETTING_VPN_SETTING_NAME)) { /* Build up the settings required for 'vpn' */ gboolean success = FALSE; const char *valid_vpns[] = { "openvpn", "vpnc", "pptp", "openconnect", "openswan", NULL }; const char *vpn_type = NULL; char *vpn_type_ask = NULL; const char *user_c = NULL; char *user = NULL; const char *st; char *service_type = NULL; GError *tmp_err = NULL; nmc_arg_t exp_args[] = { {"vpn-type", TRUE, &vpn_type, !ask}, {"user", TRUE, &user_c, FALSE}, {NULL} }; if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) return FALSE; if (!vpn_type && ask) vpn_type = vpn_type_ask = nmc_get_user_input (_("VPN type: ")); if (!vpn_type) { g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: 'vpn-type' is required.")); goto cleanup_vpn; } /* Also ask for all optional arguments if '--ask' is specified. */ user = user_c ? g_strdup (user_c) : NULL; if (ask) do_questionnaire_vpn (&user); if (!(st = nmc_string_is_valid (vpn_type, valid_vpns, &tmp_err))) { g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: 'vpn-type': %s."), tmp_err->message); g_clear_error (&tmp_err); goto cleanup_vpn; } service_type = g_strdup_printf ("%s.%s", NM_DBUS_INTERFACE, st); /* Add 'vpn' setting */ s_vpn = (NMSettingVPN *) nm_setting_vpn_new (); nm_connection_add_setting (connection, NM_SETTING (s_vpn)); g_object_set (s_vpn, NM_SETTING_VPN_SERVICE_TYPE, service_type, NULL); g_object_set (s_vpn, NM_SETTING_VPN_USER_NAME, user, NULL); success = TRUE; cleanup_vpn: g_free (vpn_type_ask); g_free (service_type); g_free (user); if (!success) return FALSE; } else if (!strcmp (con_type, NM_SETTING_OLPC_MESH_SETTING_NAME)) { /* Build up the settings required for 'olpc' */ gboolean success = FALSE; char *ssid_ask = NULL; const char *ssid = NULL; GByteArray *ssid_arr; const char *channel_c = NULL; char *channel = NULL; unsigned long chan; const char *dhcp_anycast_c = NULL; char *dhcp_anycast = NULL; GByteArray *array = NULL; nmc_arg_t exp_args[] = { {"ssid", TRUE, &ssid, !ask}, {"channel", TRUE, &channel_c, FALSE}, {"dhcp-anycast", TRUE, &dhcp_anycast_c, FALSE}, {NULL} }; if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) return FALSE; if (!ssid && ask) ssid = ssid_ask = nmc_get_user_input (_("SSID: ")); if (!ssid) { g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: 'ssid' is required.")); goto cleanup_olpc; } /* Also ask for all optional arguments if '--ask' is specified. */ channel = channel_c ? g_strdup (channel_c) : NULL; dhcp_anycast = dhcp_anycast_c ? g_strdup (dhcp_anycast_c) : NULL; if (ask) do_questionnaire_olpc (&channel, &dhcp_anycast); if (channel) { if (!nmc_string_to_uint (channel, TRUE, 1, 13, &chan)) { g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: 'channel': '%s' is not valid; use <1-13>."), channel); goto cleanup_olpc; } } if (!check_and_convert_mac (dhcp_anycast, &array, ARPHRD_ETHER, "dhcp-anycast", error)) goto cleanup_olpc; /* Add OLPC mesh setting */ s_olpc_mesh = (NMSettingOlpcMesh *) nm_setting_olpc_mesh_new (); nm_connection_add_setting (connection, NM_SETTING (s_olpc_mesh)); ssid_arr = g_byte_array_sized_new (strlen (ssid)); g_byte_array_append (ssid_arr, (const guint8 *) ssid, strlen (ssid)); g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_SSID, ssid_arr, NULL); if (channel) g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_CHANNEL, chan, NULL); else g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_CHANNEL, 1, NULL); if (array) { g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS, array, NULL); g_byte_array_free (array, TRUE); } g_byte_array_free (ssid_arr, TRUE); success = TRUE; cleanup_olpc: g_free (ssid_ask); g_free (channel); g_free (dhcp_anycast); if (!success) return FALSE; } else { g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: '%s' is not a valid connection type."), con_type); return FALSE; } /* Read and add IP configuration */ if ( strcmp (con_type, "bond-slave") != 0 && strcmp (con_type, "team-slave") != 0 && strcmp (con_type, "bridge-slave") != 0) { NMIP4Address *ip4addr = NULL; NMIP6Address *ip6addr = NULL; const char *ip4 = NULL, *gw4 = NULL, *ip6 = NULL, *gw6 = NULL; nmc_arg_t exp_args[] = { {"ip4", TRUE, &ip4, FALSE}, {"gw4", TRUE, &gw4, FALSE}, {"ip6", TRUE, &ip6, FALSE}, {"gw6", TRUE, &gw6, FALSE}, {NULL} }; while (argc) { nmc_arg_t *p; /* reset 'found' flag */ for (p = exp_args; p->name; p++) p->found = FALSE; ip4 = gw4 = ip6 = gw6 = NULL; if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, error)) return FALSE; /* coverity[dead_error_begin] */ if (ip4) { ip4addr = nmc_parse_and_build_ip4_address (ip4, gw4, error); if (!ip4addr) { g_prefix_error (error, _("Error: ")); return FALSE; } add_ip4_address_to_connection (ip4addr, connection); } /* coverity[dead_error_begin] */ if (ip6) { ip6addr = nmc_parse_and_build_ip6_address (ip6, gw6, error); if (!ip6addr) { g_prefix_error (error, _("Error: ")); return FALSE; } add_ip6_address_to_connection (ip6addr, connection); } } /* Ask for addresses if '--ask' is specified. */ if (ask) do_questionnaire_ip (connection); } return TRUE; } static char * unique_connection_name (GSList *list, const char *try_name) { NMConnection *connection; const char *name; char *new_name; unsigned int num = 1; GSList *iterator = list; new_name = g_strdup (try_name); while (iterator) { connection = NM_CONNECTION (iterator->data); name = nm_connection_get_id (connection); if (g_strcmp0 (new_name, name) == 0) { g_free (new_name); new_name = g_strdup_printf ("%s-%d", try_name, num++); iterator = list; } iterator = g_slist_next (iterator); } return new_name; } typedef struct { NmCli *nmc; char *con_name; } AddConnectionInfo; static void add_connection_cb (NMRemoteSettings *settings, NMRemoteConnection *connection, GError *error, gpointer user_data) { AddConnectionInfo *info = (AddConnectionInfo *) user_data; NmCli *nmc = info->nmc; if (error) { g_string_printf (nmc->return_text, _("Error: Failed to add '%s' connection: (%d) %s"), info->con_name, error->code, error->message); nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; } else { printf (_("Connection '%s' (%s) successfully added.\n"), nm_connection_get_id (NM_CONNECTION (connection)), nm_connection_get_uuid (NM_CONNECTION (connection))); } g_free (info->con_name); g_free (info); quit (); } static NMCResultCode do_connection_add (NmCli *nmc, int argc, char **argv) { NMConnection *connection = NULL; NMSettingConnection *s_con; char *uuid; char *default_name = NULL; const char *type = NULL; char *type_ask = NULL; const char *con_name = NULL; const char *autoconnect = NULL; gboolean auto_bool = TRUE; const char *ifname = NULL; char *ifname_ask = NULL; gboolean ifname_mandatory = TRUE; AddConnectionInfo *info = NULL; const char *setting_name; GError *error = NULL; nmc_arg_t exp_args[] = { {"type", TRUE, &type, !nmc->ask}, {"con-name", TRUE, &con_name, FALSE}, {"autoconnect", TRUE, &autoconnect, FALSE}, {"ifname", TRUE, &ifname, FALSE}, {NULL} }; nmc->return_value = NMC_RESULT_SUCCESS; if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, &error)) { g_string_assign (nmc->return_text, error->message); nmc->return_value = error->code; g_clear_error (&error); goto error; } if (!type && nmc->ask) { char *types_tmp = get_valid_options_string (nmc_valid_connection_types); printf ("Valid types: [%s]\n", types_tmp); type = type_ask = nmc_get_user_input (_("Connection type: ")); g_free (types_tmp); } if (!type) { g_string_printf (nmc->return_text, _("Error: 'type' argument is required.")); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto error; } if (!(setting_name = check_valid_name (type, nmc_valid_connection_types, &error))) { g_string_printf (nmc->return_text, _("Error: invalid connection type; %s."), error->message); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; g_clear_error (&error); goto error; } if (autoconnect) { GError *tmp_err = NULL; if (!nmc_string_to_bool (autoconnect, &auto_bool, &tmp_err)) { g_string_printf (nmc->return_text, _("Error: 'autoconnect': %s."), tmp_err->message); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; g_clear_error (&tmp_err); goto error; } } /* ifname is mandatory for all connection types except virtual ones (bond, team, bridge, vlan) */ if ( strcmp (type, NM_SETTING_BOND_SETTING_NAME) == 0 || strcmp (type, NM_SETTING_TEAM_SETTING_NAME) == 0 || strcmp (type, NM_SETTING_BRIDGE_SETTING_NAME) == 0 || strcmp (type, NM_SETTING_VLAN_SETTING_NAME) == 0) ifname_mandatory = FALSE; if (!ifname && ifname_mandatory && nmc->ask) { ifname = ifname_ask = nmc_get_user_input (_("Interface name [*]: ")); if (!ifname) ifname = ifname_ask = g_strdup ("*"); } if (!ifname && ifname_mandatory) { g_string_printf (nmc->return_text, _("Error: 'ifname' argument is required.")); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto error; } if (ifname) { if (!nm_utils_iface_valid_name (ifname) && strcmp (ifname, "*") != 0) { g_string_printf (nmc->return_text, _("Error: 'ifname': '%s' is not a valid interface nor '*'."), ifname); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto error; } /* Special value of '*' means no specific interface name */ if (strcmp (ifname, "*") == 0) ifname = NULL; } /* Create a new connection object */ connection = nm_connection_new (); /* Build up the 'connection' setting */ s_con = (NMSettingConnection *) nm_setting_connection_new (); uuid = nm_utils_uuid_generate (); if (con_name) default_name = g_strdup (con_name); else { char *try_name = ifname ? g_strdup_printf ("%s-%s", get_name_alias (setting_name, nmc_valid_connection_types), ifname) : g_strdup (get_name_alias (setting_name, nmc_valid_connection_types)); default_name = unique_connection_name (nmc->system_connections, try_name); g_free (try_name); } g_object_set (s_con, NM_SETTING_CONNECTION_ID, default_name, NM_SETTING_CONNECTION_UUID, uuid, NM_SETTING_CONNECTION_TYPE, setting_name, NM_SETTING_CONNECTION_AUTOCONNECT, auto_bool, NM_SETTING_CONNECTION_INTERFACE_NAME, ifname, NULL); g_free (uuid); g_free (default_name); nm_connection_add_setting (connection, NM_SETTING (s_con)); if (!complete_connection_by_type (connection, setting_name, nmc->system_connections, nmc->ask, argc, argv, &error)) { g_string_assign (nmc->return_text, error->message); nmc->return_value = error->code; g_clear_error (&error); goto error; } nmc->should_wait = TRUE; info = g_malloc0 (sizeof (AddConnectionInfo)); info->nmc = nmc; info->con_name = g_strdup (nm_connection_get_id (connection)); /* Tell the settings service to add the new connection */ nm_remote_settings_add_connection (nmc->system_settings, connection, add_connection_cb, info); if (connection) g_object_unref (connection); return nmc->return_value; error: if (connection) g_object_unref (connection); g_free (type_ask); g_free (ifname_ask); nmc->should_wait = FALSE; return nmc->return_value; } /*----------------------------------------------------------------------------*/ typedef char *CPFunction (); typedef char **CPPFunction (); /* History entry struct copied from libreadline's history.h */ typedef struct _hist_entry { char *line; char *timestamp; char *data; } HIST_ENTRY; typedef char * (*ReadLineFunc) (const char *); typedef void (*AddHistoryFunc) (const char *); typedef HIST_ENTRY** (*HistoryListFunc) (void); typedef int (*RlInsertTextFunc) (char *); typedef char ** (*RlCompletionMatchesFunc) (char *, CPFunction *); typedef struct { ReadLineFunc readline_func; AddHistoryFunc add_history_func; HistoryListFunc history_list_func; RlInsertTextFunc rl_insert_text_func; void **rl_startup_hook_x; RlCompletionMatchesFunc completion_matches_func; void **rl_attempted_completion_function_x; void **rl_completion_entry_function_x; char **rl_line_buffer_x; char **rl_prompt_x; int *rl_attempted_completion_over_x; int *rl_complete_with_tilde_expansion_x; int *rl_completion_append_character_x; const char **rl_completer_word_break_characters_x; void (*rl_free_line_state_func) (void); void (*rl_cleanup_after_signal_func) (void); } EditLibSymbols; static EditLibSymbols edit_lib_symbols; static char *pre_input_deftext; static int set_deftext (void) { if ( pre_input_deftext && edit_lib_symbols.rl_insert_text_func && edit_lib_symbols.rl_startup_hook_x) { edit_lib_symbols.rl_insert_text_func (pre_input_deftext); g_free (pre_input_deftext); pre_input_deftext = NULL; *edit_lib_symbols.rl_startup_hook_x = NULL; } return 0; } static char * gen_func_basic (char *text, int state, const char **words) { static int list_idx, len; const char *name; if (!state) { list_idx = 0; len = strlen (text); } /* Return the next name which partially matches one from the 'words' list. */ while ((name = words[list_idx])) { list_idx++; if (strncmp (name, text, len) == 0) return g_strdup (name); } return NULL; } static char * gen_nmcli_cmds_menu (char *text, int state) { const char *words[] = { "goto", "set", "remove", "describe", "print", "verify", "save", "activate", "back", "help", "quit", "nmcli", NULL }; return gen_func_basic (text, state, words); } static char * gen_nmcli_cmds_submenu (char *text, int state) { const char *words[] = { "set", "add", "change", "remove", "describe", "print", "back", "help", "quit", NULL }; return gen_func_basic (text, state, words); } static char * gen_cmd_nmcli (char *text, int state) { const char *words[] = { "status-line", "save-confirmation", "prompt-color", NULL }; return gen_func_basic (text, state, words); } static char * gen_cmd_nmcli_prompt_color (char *text, int state) { const char *words[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", NULL }; return gen_func_basic (text, state, words); } static char * gen_func_bool_values (char *text, int state) { const char *words[] = { "yes", "no", NULL }; return gen_func_basic (text, state, words); } static char * gen_cmd_verify0 (char *text, int state) { const char *words[] = { "all", NULL }; return gen_func_basic (text, state, words); } static char * gen_cmd_print2 (char *text, int state) { const char *words[] = { "setting", "connection", "all", NULL }; return gen_func_basic (text, state, words); } static char * gen_connection_types (char *text, int state) { static int list_idx, len; const char *c_type, *a_type; if (!state) { list_idx = 0; len = strlen (text); } while (nmc_valid_connection_types[list_idx].name) { a_type = nmc_valid_connection_types[list_idx].alias; c_type = nmc_valid_connection_types[list_idx].name; list_idx++; if (a_type && !strncmp (text, a_type, len)) return g_strdup (a_type); if (c_type && !strncmp (text, c_type, len)) return g_strdup (c_type); } return NULL; } static char * gen_setting_names (char *text, int state) { static int list_idx, len; const char *s_name, *a_name; const NameItem *valid_settings_arr; if (!state) { list_idx = 0; len = strlen (text); } valid_settings_arr = get_valid_settings_array (nmc_tab_completion.con_type); if (!valid_settings_arr) return NULL; while (valid_settings_arr[list_idx].name) { a_name = valid_settings_arr[list_idx].alias; s_name = valid_settings_arr[list_idx].name; list_idx++; if (len == 0 && a_name) return g_strdup_printf ("%s (%s)", s_name, a_name); if (a_name && !strncmp (text, a_name, len)) return g_strdup (a_name); if (s_name && !strncmp (text, s_name, len)) return g_strdup (s_name); } return NULL; } static char * gen_property_names (char *text, int state) { NMSetting *setting = NULL; char **valid_props = NULL; char *ret = NULL; char *line = g_strdup (*edit_lib_symbols.rl_line_buffer_x); const char *setting_name; char **strv = NULL; const NameItem *valid_settings_arr; const char *p1; /* Try to get the setting from 'line' - setting_name.property */ p1 = strchr (line, '.'); if (p1) { while (p1 > line && !g_ascii_isspace (*p1)) p1--; strv = g_strsplit (p1+1, ".", 2); valid_settings_arr = get_valid_settings_array (nmc_tab_completion.con_type); setting_name = check_valid_name (strv[0], valid_settings_arr, NULL); setting = nmc_setting_new_for_name (setting_name); } else { /* Else take the current setting, if any */ setting = nmc_tab_completion.setting ? g_object_ref (nmc_tab_completion.setting) : NULL; } if (setting) { valid_props = nmc_setting_get_valid_properties (setting); ret = gen_func_basic (text, state, (const char **) valid_props); } g_free (line); g_strfreev (strv); g_strfreev (valid_props); if (setting) g_object_unref (setting); return ret; } static char * gen_compat_devices (char *text, int state) { int i, j = 0; const GPtrArray *devices; const char **compatible_devices; char *ret; devices = nm_client_get_devices (nmc_tab_completion.nmc->client); if (!devices || devices->len < 1) return NULL; compatible_devices = g_new (const char *, devices->len + 1); for (i = 0; i < devices->len; i++) { NMDevice *dev = g_ptr_array_index (devices, i); const char *ifname = nm_device_get_ip_iface (dev); NMDevice *device = NULL; const char *spec_object = NULL; if (find_device_for_connection (nmc_tab_completion.nmc, nmc_tab_completion.connection, ifname, NULL, NULL, &device, &spec_object, NULL)) { compatible_devices[j++] = ifname; } } compatible_devices[j] = NULL; ret = gen_func_basic (text, state, compatible_devices); g_free (compatible_devices); return ret; } typedef char * (*my_gen_func_ptr) (char *, int); static my_gen_func_ptr get_gen_func_cmd_nmcli (char *str) { if (!str) return NULL; if (matches (str, "status-line") == 0) return gen_func_bool_values; if (matches (str, "save-confirmation") == 0) return gen_func_bool_values; if (matches (str, "prompt-color") == 0) return gen_cmd_nmcli_prompt_color; return NULL; } /* * Helper function parsing line for completion. * IN: * line : the whole line to be parsed * end : the position of cursor in the line * cmd : command to match * OUT: * cw_num : is set to the word number being completed (1, 2, 3, 4). * prev_word : returns the previous word (so that we have some context). * * Returns TRUE when the first word of the 'line' matches 'cmd'. * * Examples: * line="rem" cmd="remove" -> TRUE cw_num=1 * line="set con" cmd="set" -> TRUE cw_num=2 * line="go ipv4.method" cmd="goto" -> TRUE cw_num=2 * line=" des eth.mtu " cmd="describe" -> TRUE cw_num=3 * line=" bla ipv4.method" cmd="goto" -> FALSE */ static gboolean should_complete_cmd (const char *line, int end, const char *cmd, int *cw_num, char **prev_word) { char *tmp; const char *word1, *word2, *word3; size_t n1, n2, n3, n4, n5, n6; gboolean word1_done, word2_done, word3_done; gboolean ret = FALSE; if (!line) return FALSE; tmp = g_strdup (line); n1 = strspn (tmp, " \t"); n2 = strcspn (tmp+n1, " \t\0") + n1; n3 = strspn (tmp+n2, " \t") + n2; n4 = strcspn (tmp+n3, " \t\0") + n3; n5 = strspn (tmp+n4, " \t") + n4; n6 = strcspn (tmp+n5, " \t\0") + n5; word1_done = end > n2; word2_done = end > n4; word3_done = end > n6; tmp[n2] = tmp[n4] = tmp[n6] = '\0'; word1 = tmp[n1] ? tmp + n1 : NULL; word2 = tmp[n3] ? tmp + n3 : NULL; word3 = tmp[n5] ? tmp + n5 : NULL; if (!word1_done) { if (cw_num) *cw_num = 1; if (prev_word) *prev_word = NULL; } else if (!word2_done) { if (cw_num) *cw_num = 2; if (prev_word) *prev_word = g_strdup (word1); } else if (!word3_done) { if (cw_num) *cw_num = 3; if (prev_word) *prev_word = g_strdup (word2); } else { if (cw_num) *cw_num = 4; if (prev_word) *prev_word = g_strdup (word3); } if (word1 && matches (word1, cmd) == 0) ret = TRUE; g_free (tmp); return ret; } static gboolean should_complete_files (const char *prompt, const char *line) { char *prop = NULL; gboolean found = FALSE; const char *file_properties[] = { /* '802-1x' properties */ "ca-cert", "ca-path", "client-cert", "pac-file", "phase2-ca-cert", "phase2-ca-path", "phase2-client-cert", "private-key", "phase2-private-key", /* 'team' and 'team-port' properties */ "config", NULL }; /* If prompt is set take the property name from it, else extract it from line */ if (!prompt) { const char *p1; size_t num; p1 = strchr (line, '.'); if (p1) { p1++; } else { size_t n1, n2, n3; n1 = strspn (line, " \t"); n2 = strcspn (line+n1, " \t\0") + n1; n3 = strspn (line+n2, " \t") + n2; p1 = line + n3; } num = strcspn (p1, " \t\0"); prop = g_strndup (p1, num); } else { const char *p1, *dot; size_t num; p1 = strchr (prompt, ' '); /* prompt looks like this: "nmcli 802-1x>" or "nmcli 802-1x.pac-file>" */ if (p1) { dot = strchr (p1 + 1, '.'); p1 = dot ? dot + 1 : p1; num = strcspn (p1, ">"); prop = g_strndup (p1, num); } } if (prop) { found = !!nmc_string_is_valid (prop, file_properties, NULL); g_free (prop); } return found; } /* * Attempt to complete on the contents of TEXT. START and END show the * region of TEXT that contains the word to complete. We can use the * entire line in case we want to do some simple parsing. Return the * array of matches, or NULL if there aren't any. */ static char ** nmcli_editor_tab_completion (char *text, int start, int end) { char **match_array = NULL; const char *line = *edit_lib_symbols.rl_line_buffer_x; const char *prompt = *edit_lib_symbols.rl_prompt_x; CPFunction *generator_func = NULL; gboolean copy_char; const char *p1; char *p2, *prompt_tmp; char *word = NULL; size_t n1; int num; /* Restore standard append character to space */ *edit_lib_symbols.rl_completion_append_character_x = ' '; /* Disable default filename completion */ *edit_lib_symbols.rl_attempted_completion_over_x = 1; /* Enable tilde expansion when filenames are completed */ *edit_lib_symbols.rl_complete_with_tilde_expansion_x = 1; /* Filter out possible ANSI color escape sequences */ p1 = prompt; p2 = prompt_tmp = g_strdup (prompt); copy_char = TRUE; while (*p1) { if (*p1 == '\33') copy_char = FALSE; if (copy_char) *p2++ = *p1; if (!copy_char && *p1 == 'm') copy_char = TRUE; p1++; } *p2 = '\0'; /* Find the first non-space character */ n1 = strspn (line, " \t"); /* Choose the right generator function */ if (strcmp (prompt_tmp, EDITOR_PROMPT_CON_TYPE) == 0) generator_func = gen_connection_types; else if (strcmp (prompt_tmp, EDITOR_PROMPT_SETTING) == 0) generator_func = gen_setting_names; else if (strcmp (prompt_tmp, EDITOR_PROMPT_PROPERTY) == 0) generator_func = gen_property_names; else if (g_str_has_prefix (prompt_tmp, "nmcli")) { if (!strchr (prompt_tmp, '.')) { int level = g_str_has_prefix (prompt_tmp, "nmcli>") ? 0 : 1; const char *dot = strchr (line, '.'); /* Main menu - level 0,1 */ if (start == n1) generator_func = gen_nmcli_cmds_menu; else { if (should_complete_cmd (line, end, "goto", &num, NULL) && num <= 2) { if (level == 0 && (!dot || dot >= line + end)) generator_func = gen_setting_names; else generator_func = gen_property_names; } else if (should_complete_cmd (line, end, "set", &num, NULL)) { if (num < 3) { if (level == 0 && (!dot || dot >= line + end)) { generator_func = gen_setting_names; *edit_lib_symbols.rl_completion_append_character_x = '.'; } else generator_func = gen_property_names; } else if (num == 3 && should_complete_files (NULL, line)) { *edit_lib_symbols.rl_attempted_completion_over_x = 0; } } else if ( ( should_complete_cmd (line, end, "remove", &num, NULL) || should_complete_cmd (line, end, "describe", &num, NULL)) && num <= 2) { if (level == 0 && (!dot || dot >= line + end)) { generator_func = gen_setting_names; *edit_lib_symbols.rl_completion_append_character_x = '.'; } else generator_func = gen_property_names; } else if (should_complete_cmd (line, end, "nmcli", &num, &word)) { if (num < 3) generator_func = gen_cmd_nmcli; else if (num == 3) generator_func = get_gen_func_cmd_nmcli (word); } else if ( should_complete_cmd (line, end, "print", &num, NULL) || should_complete_cmd (line, end, "verify", &num, NULL)) { if (num <= 2) generator_func = gen_cmd_verify0; } else if (should_complete_cmd (line, end, "activate", &num, NULL) && num <= 2) { generator_func = gen_compat_devices; } else if (should_complete_cmd (line, end, "help", &num, NULL) && num <= 2) generator_func = gen_nmcli_cmds_menu; } } else { /* Submenu - level 2 */ if (start == n1) generator_func = gen_nmcli_cmds_submenu; else { if ( should_complete_cmd (line, end, "add", &num, NULL) || should_complete_cmd (line, end, "set", &num, NULL)) { if (num <= 2 && should_complete_files (prompt_tmp, line)) *edit_lib_symbols.rl_attempted_completion_over_x = 0; } if (should_complete_cmd (line, end, "print", &num, NULL) && num <= 2) generator_func = gen_cmd_print2; else if (should_complete_cmd (line, end, "help", &num, NULL) && num <= 2) generator_func = gen_nmcli_cmds_submenu; } } } if (generator_func) match_array = edit_lib_symbols.completion_matches_func (text, generator_func); g_free (prompt_tmp); g_free (word); return match_array; } static GModule * load_cmd_line_edit_lib (void) { GModule *module; char *lib_path; int i; static const char * const edit_lib_table[] = { "libreadline.so.6", /* GNU Readline library version 6 - latest */ "libreadline.so.5", /* GNU Readline library version 5 - previous */ "libedit.so.0", /* NetBSD Editline library port (http://www.thrysoee.dk/editline/) */ }; /* Try to load a library for line editing */ for (i = 0; i < G_N_ELEMENTS (edit_lib_table); i++) { lib_path = g_module_build_path (NULL, edit_lib_table[i]); module = g_module_open (lib_path, G_MODULE_BIND_LOCAL); g_free (lib_path); if (module) break; } if (!module) return NULL; if (!g_module_symbol (module, "readline", (gpointer) (&edit_lib_symbols.readline_func))) goto error; if (!g_module_symbol (module, "add_history", (gpointer) (&edit_lib_symbols.add_history_func))) goto error; if (!g_module_symbol (module, "history_list", (gpointer) (&edit_lib_symbols.history_list_func))) goto error; if (!g_module_symbol (module, "rl_insert_text", (gpointer) (&edit_lib_symbols.rl_insert_text_func))) goto error; if (!g_module_symbol (module, "rl_startup_hook", (gpointer) (&edit_lib_symbols.rl_startup_hook_x))) goto error; if (!g_module_symbol (module, "rl_attempted_completion_function", (gpointer) (&edit_lib_symbols.rl_attempted_completion_function_x))) goto error; if (!g_module_symbol (module, "completion_matches", (gpointer) (&edit_lib_symbols.completion_matches_func))) goto error; if (!g_module_symbol (module, "rl_line_buffer", (gpointer) (&edit_lib_symbols.rl_line_buffer_x))) goto error; if (!g_module_symbol (module, "rl_prompt", (gpointer) (&edit_lib_symbols.rl_prompt_x))) goto error; if (!g_module_symbol (module, "rl_attempted_completion_over", (gpointer) (&edit_lib_symbols.rl_attempted_completion_over_x))) goto error; if (!g_module_symbol (module, "rl_complete_with_tilde_expansion", (gpointer) (&edit_lib_symbols.rl_complete_with_tilde_expansion_x))) goto error; if (!g_module_symbol (module, "rl_completion_append_character", (gpointer) (&edit_lib_symbols.rl_completion_append_character_x))) goto error; if (!g_module_symbol (module, "rl_completer_word_break_characters", (gpointer) (&edit_lib_symbols.rl_completer_word_break_characters_x))) goto error; if (!g_module_symbol (module, "rl_free_line_state", (gpointer) (&edit_lib_symbols.rl_free_line_state_func))) goto error; if (!g_module_symbol (module, "rl_cleanup_after_signal", (gpointer) (&edit_lib_symbols.rl_cleanup_after_signal_func))) goto error; /* Set a pointer to an alternative function to create matches */ *edit_lib_symbols.rl_attempted_completion_function_x = (CPPFunction *) nmcli_editor_tab_completion; /* Use ' ' and '.' as word break characters */ *edit_lib_symbols.rl_completer_word_break_characters_x = ". "; return module; error: g_module_close (module); return NULL; } void nmc_cleanup_readline (void) { if (edit_lib_symbols.rl_free_line_state_func) edit_lib_symbols.rl_free_line_state_func (); if (edit_lib_symbols.rl_cleanup_after_signal_func) edit_lib_symbols.rl_cleanup_after_signal_func (); } static char * readline_x (const char *prompt) { char *str; if (edit_lib_symbols.readline_func) { str = edit_lib_symbols.readline_func (prompt); /* Return NULL, not empty string */ if (str && *str == '\0') { g_free (str); str = NULL; } } else str = nmc_get_user_input (prompt); if (edit_lib_symbols.add_history_func && str && *str) edit_lib_symbols.add_history_func (str); return str; } #define NMCLI_EDITOR_HISTORY ".nmcli-history" static void load_history_cmds (const char *uuid) { GKeyFile *kf; char *filename; char **keys; char *line; size_t i; GError *err = NULL; /* Nothing to do if readline library is not used */ if (!edit_lib_symbols.add_history_func) return; filename = g_build_filename (g_get_home_dir (), NMCLI_EDITOR_HISTORY, NULL); kf = g_key_file_new (); if (!g_key_file_load_from_file (kf, filename, G_KEY_FILE_KEEP_COMMENTS, &err)) { if (err->code == G_KEY_FILE_ERROR_PARSE) printf ("Warning: %s parse error: %s\n", filename, err->message); g_key_file_free (kf); g_free (filename); return; } keys = g_key_file_get_keys (kf, uuid, NULL, NULL); for (i = 0; keys && keys[i]; i++) { line = g_key_file_get_string (kf, uuid, keys[i], NULL); if (line && *line) edit_lib_symbols.add_history_func (line); g_free (line); } g_strfreev (keys); g_key_file_free (kf); g_free (filename); } static void save_history_cmds (const char *uuid) { HIST_ENTRY **hist = NULL; GKeyFile *kf; char *filename; size_t i; char *key; char *data; gsize len = 0; GError *err = NULL; if (edit_lib_symbols.history_list_func) hist = edit_lib_symbols.history_list_func(); if (hist) { filename = g_build_filename (g_get_home_dir (), NMCLI_EDITOR_HISTORY, NULL); kf = g_key_file_new (); if (!g_key_file_load_from_file (kf, filename, G_KEY_FILE_KEEP_COMMENTS, &err)) { if ( err->code != G_FILE_ERROR_NOENT && err->code != G_KEY_FILE_ERROR_NOT_FOUND) { printf ("Warning: %s parse error: %s\n", filename, err->message); g_key_file_free (kf); g_free (filename); g_clear_error (&err); return; } g_clear_error (&err); } /* Remove previous history group and save new history entries */ g_key_file_remove_group (kf, uuid, NULL); for (i = 0; hist[i]; i++) { key = g_strdup_printf ("%zd", i); g_key_file_set_string (kf, uuid, key, hist[i]->line); g_free (key); } /* Write history to file */ data = g_key_file_to_data (kf, &len, NULL); if (data) { g_file_set_contents (filename, data, len, NULL); g_free (data); } g_key_file_free (kf); g_free (filename); } } /*----------------------------------------------------------------------------*/ static void editor_show_connection (NMConnection *connection, NmCli *nmc) { nmc->print_output = NMC_PRINT_PRETTY; nmc->multiline_output = TRUE; nmc->escape_values = 0; /* Remove any previous data */ nmc_empty_output_fields (nmc); nmc_connection_profile_details (connection, nmc); } static void editor_show_setting (NMSetting *setting, NmCli *nmc) { printf (_("['%s' setting values]\n"), nm_setting_get_name (setting)); nmc->multiline_output = TRUE; nmc->escape_values = 0; /* Remove any previous data */ nmc_empty_output_fields (nmc); setting_details (setting, nmc, NULL); } typedef enum { NMC_EDITOR_MAIN_CMD_UNKNOWN = 0, NMC_EDITOR_MAIN_CMD_GOTO, NMC_EDITOR_MAIN_CMD_REMOVE, NMC_EDITOR_MAIN_CMD_SET, NMC_EDITOR_MAIN_CMD_DESCRIBE, NMC_EDITOR_MAIN_CMD_PRINT, NMC_EDITOR_MAIN_CMD_VERIFY, NMC_EDITOR_MAIN_CMD_SAVE, NMC_EDITOR_MAIN_CMD_ACTIVATE, NMC_EDITOR_MAIN_CMD_BACK, NMC_EDITOR_MAIN_CMD_HELP, NMC_EDITOR_MAIN_CMD_NMCLI, NMC_EDITOR_MAIN_CMD_QUIT, } NmcEditorMainCmd; static NmcEditorMainCmd parse_editor_main_cmd (const char *cmd, char **cmd_arg) { NmcEditorMainCmd editor_cmd = NMC_EDITOR_MAIN_CMD_UNKNOWN; char **vec; vec = nmc_strsplit_set (cmd, " \t", 2); if (g_strv_length (vec) < 1) { if (cmd_arg) *cmd_arg = NULL; return NMC_EDITOR_MAIN_CMD_UNKNOWN; } if (matches (vec[0], "goto") == 0) editor_cmd = NMC_EDITOR_MAIN_CMD_GOTO; else if (matches (vec[0], "remove") == 0) editor_cmd = NMC_EDITOR_MAIN_CMD_REMOVE; else if (matches (vec[0], "set") == 0) editor_cmd = NMC_EDITOR_MAIN_CMD_SET; else if (matches (vec[0], "describe") == 0) editor_cmd = NMC_EDITOR_MAIN_CMD_DESCRIBE; else if (matches (vec[0], "print") == 0) editor_cmd = NMC_EDITOR_MAIN_CMD_PRINT; else if (matches (vec[0], "verify") == 0) editor_cmd = NMC_EDITOR_MAIN_CMD_VERIFY; else if (matches (vec[0], "save") == 0) editor_cmd = NMC_EDITOR_MAIN_CMD_SAVE; else if (matches (vec[0], "activate") == 0) editor_cmd = NMC_EDITOR_MAIN_CMD_ACTIVATE; else if (matches (vec[0], "back") == 0) editor_cmd = NMC_EDITOR_MAIN_CMD_BACK; else if (matches (vec[0], "help") == 0 || strcmp (vec[0], "?") == 0) editor_cmd = NMC_EDITOR_MAIN_CMD_HELP; else if (matches (vec[0], "quit") == 0) editor_cmd = NMC_EDITOR_MAIN_CMD_QUIT; else if (matches (vec[0], "nmcli") == 0) editor_cmd = NMC_EDITOR_MAIN_CMD_NMCLI; /* set pointer to command argument */ if (cmd_arg) *cmd_arg = vec[1] ? g_strstrip (g_strdup (vec[1])) : NULL; g_strfreev (vec); return editor_cmd; } static void editor_main_usage (void) { printf ("------------------------------------------------------------------------------\n"); /* TRANSLATORS: do not translate command names and keywords before :: * However, you should translate terms enclosed in <>. */ printf (_("---[ Main menu ]---\n" "goto [ | ] :: go to a setting or property\n" "remove [.] | :: remove setting or reset property value\n" "set [. ] :: set property value\n" "describe [.] :: describe property\n" "print [all] :: print the connection\n" "verify [all] :: verify the connection\n" "save :: save the connection\n" "activate [] [/|] :: activate the connection\n" "back :: go one level up (back)\n" "help/? [] :: print this help\n" "nmcli :: nmcli configuration\n" "quit :: exit nmcli\n")); printf ("------------------------------------------------------------------------------\n"); } static void editor_main_help (const char *command) { if (!command) editor_main_usage (); else { /* detailed command descriptions */ NmcEditorMainCmd cmd = parse_editor_main_cmd (command, NULL); switch (cmd) { case NMC_EDITOR_MAIN_CMD_GOTO: printf (_("goto [.] | :: enter setting/property for editing\n\n" "This command enters into a setting or property for editing it.\n\n" "Examples: nmcli> goto connection\n" " nmcli connection> goto secondaries\n" " nmcli> goto ipv4.addresses\n")); break; case NMC_EDITOR_MAIN_CMD_REMOVE: printf (_("remove [.] :: remove setting or reset property value\n\n" "This command removes an entire setting from the connection, or if a property\n" "is given, resets that property to the default value.\n\n" "Examples: nmcli> remove wifi-sec\n" " nmcli> remove eth.mtu\n")); break; case NMC_EDITOR_MAIN_CMD_SET: printf (_("set [. ] :: set property value\n\n" "This command sets property value.\n\n" "Example: nmcli> set con.id My connection\n")); break; case NMC_EDITOR_MAIN_CMD_DESCRIBE: printf (_("describe [.] :: describe property\n\n" "Shows property description. You can consult nm-settings(5) " "manual page to see all NM settings and properties.\n")); break; case NMC_EDITOR_MAIN_CMD_PRINT: printf (_("print [all] :: print setting or connection values\n\n" "Shows current property or the whole connection.\n\n" "Example: nmcli ipv4> print all\n")); break; case NMC_EDITOR_MAIN_CMD_VERIFY: printf (_("verify [all] :: verify setting or connection validity\n\n" "Verifies whether the setting or connection is valid and can " "be saved later. It indicates invalid values on error.\n\n" "Examples: nmcli> verify\n" " nmcli bond> verify\n")); break; case NMC_EDITOR_MAIN_CMD_SAVE: printf (_("save :: save the connection\n\n" "Sends the connection to NetworkManager that will save it.\n")); break; case NMC_EDITOR_MAIN_CMD_ACTIVATE: printf (_("activate [] [/|] :: activate the connection\n\n" "Activates the connection.\n\n" "Available options:\n" " - device the connection will be activated on\n" "/| - AP (Wi-Fi) or NSP (WiMAX) (prepend with / when is not specified)\n")); break; case NMC_EDITOR_MAIN_CMD_BACK: printf (_("back :: go to upper menu level\n\n")); break; case NMC_EDITOR_MAIN_CMD_HELP: printf (_("help/? [] :: help for the nmcli commands\n\n")); break; case NMC_EDITOR_MAIN_CMD_NMCLI: printf (_("nmcli [ ] :: nmcli configuration\n\n" "Configures nmcli. The following options are available:\n" "status-line yes | no [default: no]\n" "save-confirmation yes | no [default: yes]\n" "prompt-color <0-8> [default: 0]\n" " 0 = normal\n" " 1 = \33[30mblack\33[0m\n" " 2 = \33[31mred\33[0m\n" " 3 = \33[32mgreen\33[0m\n" " 4 = \33[33myellow\33[0m\n" " 5 = \33[34mblue\33[0m\n" " 6 = \33[35mmagenta\33[0m\n" " 7 = \33[36mcyan\33[0m\n" " 8 = \33[37mwhite\33[0m\n" "\n" "Examples: nmcli> nmcli status-line yes\n" " nmcli> nmcli save-confirmation no\n" " nmcli> nmcli prompt-color 3\n")); break; case NMC_EDITOR_MAIN_CMD_QUIT: printf (_("quit :: exit nmcli\n\n" "This command exits nmcli. When the connection being edited " "is not saved, the user is asked to confirm the action.\n")); break; default: printf (_("Unknown command: '%s'\n"), command); break; } } } typedef enum { NMC_EDITOR_SUB_CMD_UNKNOWN = 0, NMC_EDITOR_SUB_CMD_SET, NMC_EDITOR_SUB_CMD_ADD, NMC_EDITOR_SUB_CMD_CHANGE, NMC_EDITOR_SUB_CMD_REMOVE, NMC_EDITOR_SUB_CMD_DESCRIBE, NMC_EDITOR_SUB_CMD_PRINT, NMC_EDITOR_SUB_CMD_BACK, NMC_EDITOR_SUB_CMD_HELP, NMC_EDITOR_SUB_CMD_QUIT } NmcEditorSubCmd; static NmcEditorSubCmd parse_editor_sub_cmd (const char *cmd, char **cmd_arg) { NmcEditorSubCmd editor_cmd = NMC_EDITOR_SUB_CMD_UNKNOWN; char **vec; vec = nmc_strsplit_set (cmd, " \t", 2); if (g_strv_length (vec) < 1) { if (cmd_arg) *cmd_arg = NULL; return NMC_EDITOR_SUB_CMD_UNKNOWN; } if (matches (vec[0], "set") == 0) editor_cmd = NMC_EDITOR_SUB_CMD_SET; else if (matches (vec[0], "add") == 0) editor_cmd = NMC_EDITOR_SUB_CMD_ADD; else if (matches (vec[0], "change") == 0) editor_cmd = NMC_EDITOR_SUB_CMD_CHANGE; else if (matches (vec[0], "remove") == 0) editor_cmd = NMC_EDITOR_SUB_CMD_REMOVE; else if (matches (vec[0], "describe") == 0) editor_cmd = NMC_EDITOR_SUB_CMD_DESCRIBE; else if (matches (vec[0], "print") == 0) editor_cmd = NMC_EDITOR_SUB_CMD_PRINT; else if (matches (vec[0], "back") == 0) editor_cmd = NMC_EDITOR_SUB_CMD_BACK; else if (matches (vec[0], "help") == 0 || strcmp (vec[0], "?") == 0) editor_cmd = NMC_EDITOR_SUB_CMD_HELP; else if (matches (vec[0], "quit") == 0) editor_cmd = NMC_EDITOR_SUB_CMD_QUIT; /* set pointer to command argument */ if (cmd_arg) *cmd_arg = g_strdup (vec[1]); g_strfreev (vec); return editor_cmd; } static void editor_sub_help (void) { printf ("------------------------------------------------------------------------------\n"); /* TRANSLATORS: do not translate command names and keywords before :: * However, you should translate terms enclosed in <>. */ printf (_("---[ Property menu ]---\n" "set [] :: set new value\n" "add [] :: add new option to the property\n" "change :: change current value\n" "remove [ |