1 /* nmcli - command-line tool to control NetworkManager
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 * Copyright 2010 - 2015 Red Hat, Inc.
20 #include "nm-default.h"
28 #include <netinet/ether.h>
29 #include <readline/readline.h>
30 #include <readline/history.h>
35 #include "connections.h"
36 #include "nm-secret-agent-simple.h"
37 #include "polkit-agent.h"
38 #include "nm-vpn-helpers.h"
40 /* define some prompts for connection editor */
41 #define EDITOR_PROMPT_SETTING _("Setting name? ")
42 #define EDITOR_PROMPT_PROPERTY _("Property name? ")
43 #define EDITOR_PROMPT_CON_TYPE _("Enter connection type: ")
45 /* define some other prompts */
46 #define PROMPT_CON_TYPE _("Connection type: ")
47 #define PROMPT_VPN_TYPE _("VPN type: ")
48 #define PROMPT_MASTER _("Master: ")
49 #define PROMPT_CONNECTION _("Connection (name, UUID, or path): ")
50 #define PROMPT_VPN_CONNECTION _("VPN connection (name, UUID, or path): ")
51 #define PROMPT_CONNECTIONS _("Connection(s) (name, UUID, or path): ")
52 #define PROMPT_ACTIVE_CONNECTIONS _("Connection(s) (name, UUID, path or apath): ")
53 #define PROMPT_IP_TUNNEL_MODE _("Tunnel mode: ")
54 #define PROMPT_MACVLAN_MODE _("MACVLAN mode: ")
56 static const char *nmc_known_vpns[] = {
71 /* Available fields for 'connection show' */
72 static NmcOutputField nmc_fields_con_show[] = {
73 {"NAME", N_("NAME")}, /* 0 */
74 {"UUID", N_("UUID")}, /* 1 */
75 {"TYPE", N_("TYPE")}, /* 2 */
76 {"TIMESTAMP", N_("TIMESTAMP")}, /* 3 */
77 {"TIMESTAMP-REAL", N_("TIMESTAMP-REAL")}, /* 4 */
78 {"AUTOCONNECT", N_("AUTOCONNECT")}, /* 5 */
79 {"AUTOCONNECT-PRIORITY", N_("AUTOCONNECT-PRIORITY")}, /* 6 */
80 {"READONLY", N_("READONLY")}, /* 7 */
81 {"DBUS-PATH", N_("DBUS-PATH")}, /* 8 */
82 {"ACTIVE", N_("ACTIVE")}, /* 9 */
83 {"DEVICE", N_("DEVICE")}, /* 10 */
84 {"STATE", N_("STATE")}, /* 11 */
85 {"ACTIVE-PATH", N_("ACTIVE-PATH")}, /* 12 */
88 #define NMC_FIELDS_CON_SHOW_ALL "NAME,UUID,TYPE,TIMESTAMP,TIMESTAMP-REAL,AUTOCONNECT,AUTOCONNECT-PRIORITY,READONLY,DBUS-PATH,"\
89 "ACTIVE,DEVICE,STATE,ACTIVE-PATH"
90 #define NMC_FIELDS_CON_SHOW_COMMON "NAME,UUID,TYPE,DEVICE"
92 /* Helper macro to define fields */
93 #define SETTING_FIELD(setting, props) { setting, N_(setting), 0, props, NULL, FALSE, FALSE, 0 }
95 /* defined in settings.c */
96 extern NmcOutputField nmc_fields_setting_connection[];
97 extern NmcOutputField nmc_fields_setting_wired[];
98 extern NmcOutputField nmc_fields_setting_8021X[];
99 extern NmcOutputField nmc_fields_setting_wireless[];
100 extern NmcOutputField nmc_fields_setting_wireless_security[];
101 extern NmcOutputField nmc_fields_setting_ip4_config[];
102 extern NmcOutputField nmc_fields_setting_ip6_config[];
103 extern NmcOutputField nmc_fields_setting_serial[];
104 extern NmcOutputField nmc_fields_setting_ppp[];
105 extern NmcOutputField nmc_fields_setting_pppoe[];
106 extern NmcOutputField nmc_fields_setting_adsl[];
107 extern NmcOutputField nmc_fields_setting_gsm[];
108 extern NmcOutputField nmc_fields_setting_cdma[];
109 extern NmcOutputField nmc_fields_setting_bluetooth[];
110 extern NmcOutputField nmc_fields_setting_olpc_mesh[];
111 extern NmcOutputField nmc_fields_setting_vpn[];
112 extern NmcOutputField nmc_fields_setting_wimax[];
113 extern NmcOutputField nmc_fields_setting_infiniband[];
114 extern NmcOutputField nmc_fields_setting_bond[];
115 extern NmcOutputField nmc_fields_setting_vlan[];
116 extern NmcOutputField nmc_fields_setting_bridge[];
117 extern NmcOutputField nmc_fields_setting_bridge_port[];
118 extern NmcOutputField nmc_fields_setting_team[];
119 extern NmcOutputField nmc_fields_setting_team_port[];
120 extern NmcOutputField nmc_fields_setting_dcb[];
121 extern NmcOutputField nmc_fields_setting_tun[];
122 extern NmcOutputField nmc_fields_setting_ip_tunnel[];
123 extern NmcOutputField nmc_fields_setting_macvlan[];
124 extern NmcOutputField nmc_fields_setting_vxlan[];
126 /* Available settings for 'connection show <con>' - profile part */
127 static NmcOutputField nmc_fields_settings_names[] = {
128 SETTING_FIELD (NM_SETTING_CONNECTION_SETTING_NAME, nmc_fields_setting_connection + 1), /* 0 */
129 SETTING_FIELD (NM_SETTING_WIRED_SETTING_NAME, nmc_fields_setting_wired + 1), /* 1 */
130 SETTING_FIELD (NM_SETTING_802_1X_SETTING_NAME, nmc_fields_setting_8021X + 1), /* 2 */
131 SETTING_FIELD (NM_SETTING_WIRELESS_SETTING_NAME, nmc_fields_setting_wireless + 1), /* 3 */
132 SETTING_FIELD (NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, nmc_fields_setting_wireless_security + 1), /* 4 */
133 SETTING_FIELD (NM_SETTING_IP4_CONFIG_SETTING_NAME, nmc_fields_setting_ip4_config + 1), /* 5 */
134 SETTING_FIELD (NM_SETTING_IP6_CONFIG_SETTING_NAME, nmc_fields_setting_ip6_config + 1), /* 6 */
135 SETTING_FIELD (NM_SETTING_SERIAL_SETTING_NAME, nmc_fields_setting_serial + 1), /* 7 */
136 SETTING_FIELD (NM_SETTING_PPP_SETTING_NAME, nmc_fields_setting_ppp + 1), /* 8 */
137 SETTING_FIELD (NM_SETTING_PPPOE_SETTING_NAME, nmc_fields_setting_pppoe + 1), /* 9 */
138 SETTING_FIELD (NM_SETTING_GSM_SETTING_NAME, nmc_fields_setting_gsm + 1), /* 10 */
139 SETTING_FIELD (NM_SETTING_CDMA_SETTING_NAME, nmc_fields_setting_cdma + 1), /* 11 */
140 SETTING_FIELD (NM_SETTING_BLUETOOTH_SETTING_NAME, nmc_fields_setting_bluetooth + 1), /* 12 */
141 SETTING_FIELD (NM_SETTING_OLPC_MESH_SETTING_NAME, nmc_fields_setting_olpc_mesh + 1), /* 13 */
142 SETTING_FIELD (NM_SETTING_VPN_SETTING_NAME, nmc_fields_setting_vpn + 1), /* 14 */
143 SETTING_FIELD (NM_SETTING_WIMAX_SETTING_NAME, nmc_fields_setting_wimax + 1), /* 15 */
144 SETTING_FIELD (NM_SETTING_INFINIBAND_SETTING_NAME, nmc_fields_setting_infiniband + 1), /* 16 */
145 SETTING_FIELD (NM_SETTING_BOND_SETTING_NAME, nmc_fields_setting_bond + 1), /* 17 */
146 SETTING_FIELD (NM_SETTING_VLAN_SETTING_NAME, nmc_fields_setting_vlan + 1), /* 18 */
147 SETTING_FIELD (NM_SETTING_ADSL_SETTING_NAME, nmc_fields_setting_adsl + 1), /* 19 */
148 SETTING_FIELD (NM_SETTING_BRIDGE_SETTING_NAME, nmc_fields_setting_bridge + 1), /* 20 */
149 SETTING_FIELD (NM_SETTING_BRIDGE_PORT_SETTING_NAME, nmc_fields_setting_bridge_port + 1), /* 21 */
150 SETTING_FIELD (NM_SETTING_TEAM_SETTING_NAME, nmc_fields_setting_team + 1), /* 22 */
151 SETTING_FIELD (NM_SETTING_TEAM_PORT_SETTING_NAME, nmc_fields_setting_team_port + 1), /* 23 */
152 SETTING_FIELD (NM_SETTING_DCB_SETTING_NAME, nmc_fields_setting_dcb + 1), /* 24 */
153 SETTING_FIELD (NM_SETTING_TUN_SETTING_NAME, nmc_fields_setting_tun + 1), /* 25 */
154 SETTING_FIELD (NM_SETTING_IP_TUNNEL_SETTING_NAME, nmc_fields_setting_ip_tunnel + 1), /* 26 */
155 SETTING_FIELD (NM_SETTING_MACVLAN_SETTING_NAME, nmc_fields_setting_macvlan + 1), /* 27 */
156 SETTING_FIELD (NM_SETTING_VXLAN_SETTING_NAME, nmc_fields_setting_vxlan + 1), /* 28 */
157 {NULL, NULL, 0, NULL, NULL, FALSE, FALSE, 0}
159 #define NMC_FIELDS_SETTINGS_NAMES_ALL_X NM_SETTING_CONNECTION_SETTING_NAME","\
160 NM_SETTING_WIRED_SETTING_NAME","\
161 NM_SETTING_802_1X_SETTING_NAME","\
162 NM_SETTING_WIRELESS_SETTING_NAME","\
163 NM_SETTING_WIRELESS_SECURITY_SETTING_NAME","\
164 NM_SETTING_IP4_CONFIG_SETTING_NAME","\
165 NM_SETTING_IP6_CONFIG_SETTING_NAME","\
166 NM_SETTING_SERIAL_SETTING_NAME","\
167 NM_SETTING_PPP_SETTING_NAME","\
168 NM_SETTING_PPPOE_SETTING_NAME","\
169 NM_SETTING_ADSL_SETTING_NAME","\
170 NM_SETTING_GSM_SETTING_NAME","\
171 NM_SETTING_CDMA_SETTING_NAME","\
172 NM_SETTING_BLUETOOTH_SETTING_NAME","\
173 NM_SETTING_OLPC_MESH_SETTING_NAME","\
174 NM_SETTING_VPN_SETTING_NAME","\
175 NM_SETTING_INFINIBAND_SETTING_NAME","\
176 NM_SETTING_BOND_SETTING_NAME","\
177 NM_SETTING_VLAN_SETTING_NAME","\
178 NM_SETTING_BRIDGE_SETTING_NAME","\
179 NM_SETTING_BRIDGE_PORT_SETTING_NAME","\
180 NM_SETTING_TEAM_SETTING_NAME","\
181 NM_SETTING_TEAM_PORT_SETTING_NAME"," \
182 NM_SETTING_DCB_SETTING_NAME"," \
183 NM_SETTING_TUN_SETTING_NAME"," \
184 NM_SETTING_IP_TUNNEL_SETTING_NAME"," \
185 NM_SETTING_MACVLAN_SETTING_NAME"," \
186 NM_SETTING_VXLAN_SETTING_NAME
187 #define NMC_FIELDS_SETTINGS_NAMES_ALL NMC_FIELDS_SETTINGS_NAMES_ALL_X
189 /* Active connection data */
190 /* Available fields for GENERAL group */
191 static NmcOutputField nmc_fields_con_active_details_general[] = {
192 {"GROUP", N_("GROUP")}, /* 0 */
193 {"NAME", N_("NAME")}, /* 1 */
194 {"UUID", N_("UUID")}, /* 2 */
195 {"DEVICES", N_("DEVICES")}, /* 3 */
196 {"STATE", N_("STATE")}, /* 4 */
197 {"DEFAULT", N_("DEFAULT")}, /* 5 */
198 {"DEFAULT6", N_("DEFAULT6")}, /* 6 */
199 {"SPEC-OBJECT", N_("SPEC-OBJECT")}, /* 7 */
200 {"VPN", N_("VPN")}, /* 8 */
201 {"DBUS-PATH", N_("DBUS-PATH")}, /* 9 */
202 {"CON-PATH", N_("CON-PATH")}, /* 10 */
203 {"ZONE", N_("ZONE")}, /* 11 */
204 {"MASTER-PATH", N_("MASTER-PATH")}, /* 12 */
207 #define NMC_FIELDS_CON_ACTIVE_DETAILS_GENERAL_ALL "GROUP,NAME,UUID,DEVICES,STATE,DEFAULT,DEFAULT6,"\
208 "VPN,ZONE,DBUS-PATH,CON-PATH,SPEC-OBJECT,MASTER-PATH"
210 /* IP group is handled by common.c */
212 /* Available fields for VPN group */
213 static NmcOutputField nmc_fields_con_active_details_vpn[] = {
214 {"GROUP", N_("GROUP")}, /* 0 */
215 {"TYPE", N_("TYPE")}, /* 1 */
216 {"USERNAME", N_("USERNAME")}, /* 2 */
217 {"GATEWAY", N_("GATEWAY")}, /* 3 */
218 {"BANNER", N_("BANNER")}, /* 4 */
219 {"VPN-STATE", N_("VPN-STATE")}, /* 5 */
220 {"CFG", N_("CFG")}, /* 6 */
223 #define NMC_FIELDS_CON_ACTIVE_DETAILS_VPN_ALL "GROUP,TYPE,USERNAME,GATEWAY,BANNER,VPN-STATE,CFG"
225 /* defined in common.c */
226 extern NmcOutputField nmc_fields_ip4_config[];
227 extern NmcOutputField nmc_fields_ip6_config[];
228 extern NmcOutputField nmc_fields_dhcp4_config[];
229 extern NmcOutputField nmc_fields_dhcp6_config[];
231 /* Available fields for 'connection show <con>' - active part */
232 static NmcOutputField nmc_fields_con_active_details_groups[] = {
233 {"GENERAL", N_("GENERAL"), 0, nmc_fields_con_active_details_general + 1}, /* 0 */
234 {"IP4", N_("IP4"), 0, nmc_fields_ip4_config + 1 }, /* 1 */
235 {"DHCP4", N_("DHCP4"), 0, nmc_fields_dhcp4_config + 1 }, /* 2 */
236 {"IP6", N_("IP6"), 0, nmc_fields_ip6_config + 1 }, /* 3 */
237 {"DHCP6", N_("DHCP6"), 0, nmc_fields_dhcp6_config + 1 }, /* 4 */
238 {"VPN", N_("VPN"), 0, nmc_fields_con_active_details_vpn + 1 }, /* 5 */
239 {NULL, NULL, 0, NULL}
241 #define NMC_FIELDS_CON_ACTIVE_DETAILS_ALL "GENERAL,IP4,DHCP4,IP6,DHCP6,VPN"
243 /* Pseudo group names for 'connection show <con>' */
244 /* e.g.: nmcli -f profile con show my-eth0 */
245 /* e.g.: nmcli -f active con show my-eth0 */
246 #define CON_SHOW_DETAIL_GROUP_PROFILE "profile"
247 #define CON_SHOW_DETAIL_GROUP_ACTIVE "active"
249 /* glib main loop variable - defined in nmcli.c */
250 extern GMainLoop *loop;
252 static guint progress_id = 0; /* ID of event source for displaying progress */
254 /* for readline TAB completion in editor */
258 NMConnection *connection;
260 const char *property;
262 static TabCompletionInfo nmc_tab_completion = {NULL, NULL, NULL, NULL};
264 /* Global variable defined in nmcli.c - used for TAB completion */
267 static char *gen_connection_types (const char *text, int state);
272 g_printerr (_("Usage: nmcli connection { COMMAND | help }\n\n"
273 "COMMAND := { show | up | down | add | modify | edit | delete | monitor | reload | load }\n\n"
274 " show [--active] [--order <order spec>]\n"
275 " show [--active] [id | uuid | path | apath] <ID> ...\n\n"
276 " up [[id | uuid | path] <ID>] [ifname <ifname>] [ap <BSSID>] [passwd-file <file with passwords>]\n\n"
277 " down [id | uuid | path | apath] <ID> ...\n\n"
278 " add COMMON_OPTIONS TYPE_SPECIFIC_OPTIONS SLAVE_OPTIONS IP_OPTIONS [-- ([+|-]<setting>.<property> <value>)+]\n\n"
279 " modify [--temporary] [id | uuid | path] <ID> ([+|-]<setting>.<property> <value>)+\n\n"
280 " clone [--temporary] [id | uuid | path ] <ID> <new name>\n\n"
281 " edit [id | uuid | path] <ID>\n"
282 " edit [type <new_con_type>] [con-name <new_con_name>]\n\n"
283 " delete [id | uuid | path] <ID>\n\n"
284 " monitor [id | uuid | path] <ID> ...\n\n"
286 " load <filename> [ <filename>... ]\n\n"
287 " import [--temporary] type <type> file <file to import>\n\n"
288 " export [id | uuid | path] <ID> [<output file>]\n\n"));
292 usage_connection_show (void)
294 g_printerr (_("Usage: nmcli connection show { ARGUMENTS | help }\n"
296 "ARGUMENTS := [--active] [--order <order spec>]\n"
298 "List in-memory and on-disk connection profiles, some of which may also be\n"
299 "active if a device is using that connection profile. Without a parameter, all\n"
300 "profiles are listed. When --active option is specified, only the active\n"
301 "profiles are shown. --order allows custom connection ordering (see manual page).\n"
303 "ARGUMENTS := [--active] [id | uuid | path | apath] <ID> ...\n"
305 "Show details for specified connections. By default, both static configuration\n"
306 "and active connection data are displayed. It is possible to filter the output\n"
307 "using global '--fields' option. Refer to the manual page for more information.\n"
308 "When --active option is specified, only the active profiles are taken into\n"
309 "account. Use global --show-secrets option to reveal associated secrets as well.\n"));
313 usage_connection_up (void)
315 g_printerr (_("Usage: nmcli connection up { ARGUMENTS | help }\n"
317 "ARGUMENTS := [id | uuid | path] <ID> [ifname <ifname>] [ap <BSSID>] [nsp <name>] [passwd-file <file with passwords>]\n"
319 "Activate a connection on a device. The profile to activate is identified by its\n"
320 "name, UUID or D-Bus path.\n"
322 "ARGUMENTS := ifname <ifname> [ap <BSSID>] [nsp <name>] [passwd-file <file with passwords>]\n"
324 "Activate a device with a connection. The connection profile is selected\n"
325 "automatically by NetworkManager.\n"
327 "ifname - specifies the device to active the connection on\n"
328 "ap - specifies AP to connect to (only valid for Wi-Fi)\n"
329 "nsp - specifies NSP to connect to (only valid for WiMAX)\n"
330 "passwd-file - file with password(s) required to activate the connection\n\n"));
334 usage_connection_down (void)
336 g_printerr (_("Usage: nmcli connection down { ARGUMENTS | help }\n"
338 "ARGUMENTS := [id | uuid | path | apath] <ID> ...\n"
340 "Deactivate a connection from a device (without preventing the device from\n"
341 "further auto-activation). The profile to deactivate is identified by its name,\n"
342 "UUID or D-Bus path.\n\n"));
346 usage_connection_add (void)
348 g_printerr (_("Usage: nmcli connection add { ARGUMENTS | help }\n"
350 "ARGUMENTS := COMMON_OPTIONS TYPE_SPECIFIC_OPTIONS SLAVE_OPTIONS IP_OPTIONS [-- ([+|-]<setting>.<property> <value>)+]\n\n"
353 " ifname <interface name> | \"*\"\n"
354 " [con-name <connection name>]\n"
355 " [autoconnect yes|no]\n"
357 " [master <master (ifname, or connection UUID or name)>]\n"
358 " [slave-type <master connection type>]\n\n"
359 " TYPE_SPECIFIC_OPTIONS:\n"
360 " ethernet: [mac <MAC address>]\n"
361 " [cloned-mac <cloned MAC address>]\n"
363 " wifi: ssid <SSID>\n"
364 " [mac <MAC address>]\n"
365 " [cloned-mac <cloned MAC address>]\n"
367 " [mode infrastructure|ap|adhoc]\n\n"
368 " wimax: [mac <MAC address>]\n"
370 " pppoe: username <PPPoE username>\n"
371 " [password <PPPoE password>]\n"
372 " [service <PPPoE service name>]\n"
374 " [mac <MAC address>]\n\n"
376 " [user <username>]\n"
377 " [password <password>]\n\n"
378 " cdma: [user <username>]\n"
379 " [password <password>]\n\n"
380 " infiniband: [mac <MAC address>]\n"
382 " [transport-mode datagram | connected]\n"
383 " [parent <ifname>]\n"
384 " [p-key <IPoIB P_Key>]\n\n"
385 " bluetooth: [addr <bluetooth address>]\n"
386 " [bt-type panu|dun-gsm|dun-cdma]\n\n"
387 " vlan: dev <parent device (connection UUID, ifname, or MAC)>\n"
389 " [flags <VLAN flags>]\n"
390 " [ingress <ingress priority mapping>]\n"
391 " [egress <egress priority mapping>]\n"
393 " bond: [mode balance-rr (0) | active-backup (1) | balance-xor (2) | broadcast (3) |\n"
394 " 802.3ad (4) | balance-tlb (5) | balance-alb (6)]\n"
395 " [primary <ifname>]\n"
397 " [downdelay <num>]\n"
399 " [arp-interval <num>]\n"
400 " [arp-ip-target <num>]\n"
401 " [lacp-rate slow (0) | fast (1)]\n\n"
402 " bond-slave: master <master (ifname, or connection UUID or name)>\n\n"
403 " team: [config <file>|<raw JSON data>]\n\n"
404 " team-slave: master <master (ifname, or connection UUID or name)>\n"
405 " [config <file>|<raw JSON data>]\n\n"
406 " bridge: [stp yes|no]\n"
407 " [priority <num>]\n"
408 " [forward-delay <2-30>]\n"
409 " [hello-time <1-10>]\n"
410 " [max-age <6-40>]\n"
411 " [ageing-time <0-1000000>]\n"
412 " [multicast-snooping yes|no]\n"
413 " [mac <MAC address>]\n\n"
414 " bridge-slave: master <master (ifname, or connection UUID or name)>\n"
415 " [priority <0-63>]\n"
416 " [path-cost <1-65535>]\n"
417 " [hairpin yes|no]\n\n"
418 " vpn: vpn-type vpnc|openvpn|pptp|openconnect|openswan|libreswan|ssh|l2tp|iodine|...\n"
419 " [user <username>]\n\n"
420 " olpc-mesh: ssid <SSID>\n"
421 " [channel <1-13>]\n"
422 " [dhcp-anycast <MAC address>]\n\n"
423 " adsl: username <username>\n"
424 " protocol pppoa|pppoe|ipoatm\n"
425 " [password <password>]\n"
426 " [encapsulation vcmux|llc]\n\n"
427 " tun: mode tun|tap\n"
431 " [vnet-hdr yes|no]\n"
432 " [multi-queue yes|no]\n\n"
433 " ip-tunnel: mode ipip|gre|sit|isatap|vti|ip6ip6|ipip6|ip6gre|vti6\n"
434 " remote <remote endpoint IP>\n"
435 " [local <local endpoint IP>]\n"
436 " [dev <parent device (ifname or connection UUID)>]\n\n"
437 " macvlan: dev <parent device (connection UUID, ifname, or MAC)>\n"
438 " mode vepa|bridge|private|passthru|source\n"
440 " vxlan: id <VXLAN ID>\n"
441 " remote <IP of multicast group or remote address>\n"
442 " [local <source IP>]\n"
443 " [dev <parent device (ifname or connection UUID)>]\n"
444 " [source-port-min <0-65535>]\n"
445 " [source-port-max <0-65535>]\n"
446 " [destination-port <0-65535>]\n\n"
448 " bridge: [priority <0-63>]\n"
449 " [path-cost <1-65535>]\n"
450 " [hairpin yes|no]\n\n"
451 " team: [config <file>|<raw JSON data>]\n\n"
453 " [ip4 <IPv4 address>] [gw4 <IPv4 gateway>]\n"
454 " [ip6 <IPv6 address>] [gw6 <IPv6 gateway>]\n\n"));
458 usage_connection_modify (void)
460 g_printerr (_("Usage: nmcli connection modify { ARGUMENTS | help }\n"
462 "ARGUMENTS := [id | uuid | path] <ID> ([+|-]<setting>.<property> <value>)+\n"
464 "Modify one or more properties of the connection profile.\n"
465 "The profile is identified by its name, UUID or D-Bus path. For multi-valued\n"
466 "properties you can use optional '+' or '-' prefix to the property name.\n"
467 "The '+' sign allows appending items instead of overwriting the whole value.\n"
468 "The '-' sign allows removing selected items instead of the whole value.\n"
471 "nmcli con mod home-wifi wifi.ssid rakosnicek\n"
472 "nmcli con mod em1-1 ipv4.method manual ipv4.addr \"192.168.1.2/24, 10.10.1.5/8\"\n"
473 "nmcli con mod em1-1 +ipv4.dns 8.8.4.4\n"
474 "nmcli con mod em1-1 -ipv4.dns 1\n"
475 "nmcli con mod em1-1 -ipv6.addr \"abbe::cafe/56\"\n"
476 "nmcli con mod bond0 +bond.options mii=500\n"
477 "nmcli con mod bond0 -bond.options downdelay\n\n"));
481 usage_connection_clone (void)
483 g_printerr (_("Usage: nmcli connection clone { ARGUMENTS | help }\n"
485 "ARGUMENTS := [--temporary] [id | uuid | path] <ID> <new name>\n"
487 "Clone an existing connection profile. The newly created connection will be\n"
488 "the exact copy of the <ID>, except the uuid property (will be generated) and\n"
489 "id (provided as <new name> argument).\n\n"));
493 usage_connection_edit (void)
495 g_printerr (_("Usage: nmcli connection edit { ARGUMENTS | help }\n"
497 "ARGUMENTS := [id | uuid | path] <ID>\n"
499 "Edit an existing connection profile in an interactive editor.\n"
500 "The profile is identified by its name, UUID or D-Bus path\n"
502 "ARGUMENTS := [type <new connection type>] [con-name <new connection name>]\n"
504 "Add a new connection profile in an interactive editor.\n\n"));
508 usage_connection_delete (void)
510 g_printerr (_("Usage: nmcli connection delete { ARGUMENTS | help }\n"
512 "ARGUMENTS := [id | uuid | path] <ID>\n"
514 "Delete a connection profile.\n"
515 "The profile is identified by its name, UUID or D-Bus path.\n\n"));
519 usage_connection_monitor (void)
521 g_printerr (_("Usage: nmcli connection monitor { ARGUMENTS | help }\n"
523 "ARGUMENTS := [id | uuid | path] <ID> ...\n"
525 "Monitor connection profile activity.\n"
526 "This command prints a line whenever the specified connection changes.\n"
527 "Monitors all connection profiles in case none is specified.\n\n"));
531 usage_connection_reload (void)
533 g_printerr (_("Usage: nmcli connection reload { help }\n"
535 "Reload all connection files from disk.\n\n"));
539 usage_connection_load (void)
541 g_printerr (_("Usage: nmcli connection load { ARGUMENTS | help }\n"
543 "ARGUMENTS := <filename> [<filename>...]\n"
545 "Load/reload one or more connection files from disk. Use this after manually\n"
546 "editing a connection file to ensure that NetworkManager is aware of its latest\n"
551 usage_connection_import (void)
553 g_printerr (_("Usage: nmcli connection import { ARGUMENTS | help }\n"
555 "ARGUMENTS := [--temporary] type <type> file <file to import>\n"
557 "Import an external/foreign configuration as a NetworkManager connection profile.\n"
558 "The type of the input file is specified by type option.\n"
559 "Only VPN configurations are supported at the moment. The configuration\n"
560 "is imported by NetworkManager VPN plugins.\n\n"));
564 usage_connection_export (void)
566 g_printerr (_("Usage: nmcli connection export { ARGUMENTS | help }\n"
568 "ARGUMENTS := [id | uuid | path] <ID> [<output file>]\n"
570 "Export a connection. Only VPN connections are supported at the moment.\n"
571 "The data are directed to standard output or to a file if a name is given.\n\n"));
575 usage_connection_second_level (const char *cmd)
579 if (matches (cmd, "show") == 0)
580 usage_connection_show ();
581 else if (matches (cmd, "up") == 0)
582 usage_connection_up ();
583 else if (matches (cmd, "down") == 0)
584 usage_connection_down ();
585 else if (matches (cmd, "add") == 0)
586 usage_connection_add ();
587 else if (matches (cmd, "modify") == 0)
588 usage_connection_modify ();
589 else if (matches (cmd, "clone") == 0)
590 usage_connection_clone ();
591 else if (matches (cmd, "edit") == 0)
592 usage_connection_edit ();
593 else if (matches (cmd, "delete") == 0)
594 usage_connection_delete ();
595 else if (matches (cmd, "monitor") == 0)
596 usage_connection_monitor ();
597 else if (matches (cmd, "reload") == 0)
598 usage_connection_reload ();
599 else if (matches (cmd, "load") == 0)
600 usage_connection_load ();
601 else if (matches (cmd, "import") == 0)
602 usage_connection_import ();
603 else if (matches (cmd, "export") == 0)
604 usage_connection_export ();
615 g_source_remove (progress_id);
617 nmc_terminal_erase_line ();
620 g_main_loop_quit (loop); /* quit main loop */
624 construct_header_name (const char *base, const char *spec)
626 static char header_name[128];
631 g_strlcpy (header_name, base, sizeof (header_name));
632 g_strlcat (header_name, " (", sizeof (header_name));
633 g_strlcat (header_name, spec, sizeof (header_name));
634 g_strlcat (header_name, ")", sizeof (header_name));
640 active_connection_state_to_string (NMActiveConnectionState state)
643 case NM_ACTIVE_CONNECTION_STATE_ACTIVATING:
644 return _("activating");
645 case NM_ACTIVE_CONNECTION_STATE_ACTIVATED:
646 return _("activated");
647 case NM_ACTIVE_CONNECTION_STATE_DEACTIVATING:
648 return _("deactivating");
649 case NM_ACTIVE_CONNECTION_STATE_DEACTIVATED:
650 return _("deactivated");
651 case NM_ACTIVE_CONNECTION_STATE_UNKNOWN:
658 vpn_connection_state_to_string (NMVpnConnectionState state)
661 case NM_VPN_CONNECTION_STATE_PREPARE:
662 return _("VPN connecting (prepare)");
663 case NM_VPN_CONNECTION_STATE_NEED_AUTH:
664 return _("VPN connecting (need authentication)");
665 case NM_VPN_CONNECTION_STATE_CONNECT:
666 return _("VPN connecting");
667 case NM_VPN_CONNECTION_STATE_IP_CONFIG_GET:
668 return _("VPN connecting (getting IP configuration)");
669 case NM_VPN_CONNECTION_STATE_ACTIVATED:
670 return _("VPN connected");
671 case NM_VPN_CONNECTION_STATE_FAILED:
672 return _("VPN connection failed");
673 case NM_VPN_CONNECTION_STATE_DISCONNECTED:
674 return _("VPN disconnected");
680 /* Caller has to free the returned string */
682 get_ac_device_string (NMActiveConnection *active)
685 const GPtrArray *devices;
691 /* Get devices of the active connection */
692 dev_str = g_string_new (NULL);
693 devices = nm_active_connection_get_devices (active);
694 for (i = 0; i < devices->len; i++) {
695 NMDevice *device = g_ptr_array_index (devices, i);
696 const char *dev_iface = nm_device_get_iface (device);
699 g_string_append (dev_str, dev_iface);
700 g_string_append_c (dev_str, ',');
703 if (dev_str->len > 0)
704 g_string_truncate (dev_str, dev_str->len - 1); /* Cut off last ',' */
706 return g_string_free (dev_str, FALSE);
709 static NMActiveConnection *
710 get_ac_for_connection (const GPtrArray *active_cons, NMConnection *connection)
712 const char *con_path, *ac_con_path;
714 NMActiveConnection *ac = NULL;
716 /* Is the connection active? */
717 con_path = nm_connection_get_path (connection);
718 for (i = 0; i < active_cons->len; i++) {
719 NMActiveConnection *candidate = g_ptr_array_index (active_cons, i);
720 NMRemoteConnection *con;
722 con = nm_active_connection_get_connection (candidate);
723 ac_con_path = con ? nm_connection_get_path (NM_CONNECTION (con)) : NULL;
724 if (!g_strcmp0 (ac_con_path, con_path)) {
732 /* Put secrets into local connection. */
734 update_secrets_in_connection (NMRemoteConnection *remote, NMConnection *local)
738 GError *error = NULL;
740 for (i = 0; nmc_fields_settings_names[i].name; i++) {
741 secrets = nm_remote_connection_get_secrets (remote, nmc_fields_settings_names[i].name, NULL, NULL);
743 if (!nm_connection_update_secrets (local, NULL, secrets, &error) && error) {
744 g_printerr (_("Error updating secrets for %s: %s\n"),
745 nmc_fields_settings_names[i].name,
747 g_clear_error (&error);
749 g_variant_unref (secrets);
755 nmc_connection_profile_details (NMConnection *connection, NmCli *nmc, gboolean secrets)
757 GError *error = NULL;
758 GArray *print_settings_array;
759 GPtrArray *prop_array = NULL;
762 char *fields_all = NMC_FIELDS_SETTINGS_NAMES_ALL;
763 char *fields_common = NMC_FIELDS_SETTINGS_NAMES_ALL;
764 const char *base_hdr = _("Connection profile details");
765 gboolean was_output = FALSE;
767 if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0)
768 fields_str = fields_common;
769 else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0)
770 fields_str = fields_all;
772 fields_str = nmc->required_fields;
774 print_settings_array = parse_output_fields (fields_str, nmc_fields_settings_names, TRUE, &prop_array, &error);
776 g_string_printf (nmc->return_text, _("Error: 'connection show': %s"), error->message);
777 g_error_free (error);
778 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
781 g_assert (print_settings_array);
784 nmc->print_fields.header_name = (char *) construct_header_name (base_hdr, nm_connection_get_id (connection));
785 nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_SETTINGS_NAMES_ALL,
786 nmc_fields_settings_names, FALSE, NULL, NULL);
788 nmc_fields_settings_names[0].flags = NMC_OF_FLAG_MAIN_HEADER_ONLY;
789 print_required_fields (nmc, nmc_fields_settings_names);
791 /* Loop through the required settings and print them. */
792 for (i = 0; i < print_settings_array->len; i++) {
794 int section_idx = g_array_index (print_settings_array, int, i);
795 const char *prop_name = (const char *) g_ptr_array_index (prop_array, i);
797 if (nmc->print_output != NMC_PRINT_TERSE && !nmc->multiline_output && was_output)
798 g_print ("\n"); /* Empty line */
802 /* Remove any previous data */
803 nmc_empty_output_fields (nmc);
805 setting = nm_connection_get_setting_by_name (connection, nmc_fields_settings_names[section_idx].name);
807 setting_details (setting, nmc, prop_name, secrets);
813 g_array_free (print_settings_array, TRUE);
815 g_ptr_array_free (prop_array, TRUE);
820 static NMActiveConnection *
821 find_active_connection (const GPtrArray *active_cons,
822 const GPtrArray *cons,
823 const char *filter_type,
824 const char *filter_val,
828 int start = (idx && *idx > 0) ? *idx : 0;
829 const char *path, *a_path, *path_num, *a_path_num;
832 NMRemoteConnection *con;
833 NMActiveConnection *found = NULL;
835 for (i = start; i < active_cons->len; i++) {
836 NMActiveConnection *candidate = g_ptr_array_index (active_cons, i);
838 con = nm_active_connection_get_connection (candidate);
840 id = nm_active_connection_get_id (candidate);
841 uuid = nm_active_connection_get_uuid (candidate);
842 path = con ? nm_connection_get_path (NM_CONNECTION (con)) : NULL;
843 path_num = path ? strrchr (path, '/') + 1 : NULL;
844 a_path = nm_object_get_path (NM_OBJECT (candidate));
845 a_path_num = a_path ? strrchr (a_path, '/') + 1 : NULL;
847 /* When filter_type is NULL, compare connection ID (filter_val)
848 * against all types. Otherwise, only compare against the specific
849 * type. If 'path' or 'apath' filter types are specified, comparison
850 * against numeric index (in addition to the whole path) is allowed.
852 if ( ( (!filter_type || strcmp (filter_type, "id") == 0)
853 && strcmp (filter_val, id) == 0)
854 || ( (!filter_type || strcmp (filter_type, "uuid") == 0)
855 && strcmp (filter_val, uuid) == 0)
856 || ( (!filter_type || strcmp (filter_type, "path") == 0)
857 && (g_strcmp0 (filter_val, path) == 0 || (filter_type && g_strcmp0 (filter_val, path_num) == 0)))
858 || ( (!filter_type || strcmp (filter_type, "apath") == 0)
859 && (g_strcmp0 (filter_val, a_path) == 0 || (filter_type && g_strcmp0 (filter_val, a_path_num) == 0)))) {
876 fill_output_connection (NMConnection *connection, NmCli *nmc, gboolean active_only)
878 NMSettingConnection *s_con;
880 time_t timestamp_real;
882 char *timestamp_real_str = "";
885 NMActiveConnection *ac = NULL;
886 const char *ac_path = NULL;
887 const char *ac_state = NULL;
888 NMActiveConnectionState ac_state_int = NM_ACTIVE_CONNECTION_STATE_UNKNOWN;
891 s_con = nm_connection_get_setting_connection (connection);
894 ac = get_ac_for_connection (nm_client_get_active_connections (nmc->client), connection);
895 if (active_only && !ac)
899 ac_path = nm_object_get_path (NM_OBJECT (ac));
900 ac_state_int = nm_active_connection_get_state (ac);
901 ac_state = active_connection_state_to_string (ac_state_int);
902 ac_dev = get_ac_device_string (ac);
905 /* Obtain field values */
906 timestamp = nm_setting_connection_get_timestamp (s_con);
907 timestamp_str = g_strdup_printf ("%" G_GUINT64_FORMAT, timestamp);
909 timestamp_real = timestamp;
910 timestamp_real_str = g_malloc0 (64);
911 strftime (timestamp_real_str, 64, "%c", localtime (×tamp_real));
913 prio_str = g_strdup_printf ("%u", nm_setting_connection_get_autoconnect_priority (s_con));
915 arr = nmc_dup_fields_array (nmc_fields_con_show,
916 sizeof (nmc_fields_con_show),
918 /* Show active connections in color */
920 if (ac_state_int == NM_ACTIVE_CONNECTION_STATE_ACTIVATING)
921 set_val_color_all (arr, NMC_TERM_COLOR_YELLOW);
922 else if (ac_state_int == NM_ACTIVE_CONNECTION_STATE_ACTIVATED)
923 set_val_color_all (arr, NMC_TERM_COLOR_GREEN);
924 else if (ac_state_int > NM_ACTIVE_CONNECTION_STATE_ACTIVATED)
925 set_val_color_all (arr, NMC_TERM_COLOR_RED);
928 set_val_strc (arr, 0, nm_setting_connection_get_id (s_con));
929 set_val_strc (arr, 1, nm_setting_connection_get_uuid (s_con));
930 set_val_strc (arr, 2, nm_setting_connection_get_connection_type (s_con));
931 set_val_str (arr, 3, timestamp_str);
932 set_val_str (arr, 4, timestamp ? timestamp_real_str : g_strdup (_("never")));
933 set_val_strc (arr, 5, nm_setting_connection_get_autoconnect (s_con) ? _("yes") : _("no"));
934 set_val_str (arr, 6, prio_str);
935 set_val_strc (arr, 7, nm_setting_connection_get_read_only (s_con) ? _("yes") : _("no"));
936 set_val_strc (arr, 8, nm_connection_get_path (connection));
937 set_val_strc (arr, 9, ac ? _("yes") : _("no"));
938 set_val_str (arr, 10, ac_dev);
939 set_val_strc (arr, 11, ac_state);
940 set_val_strc (arr, 12, ac_path);
942 g_ptr_array_add (nmc->output_data, arr);
946 fill_output_connection_for_invisible (NMActiveConnection *ac, NmCli *nmc)
949 const char *ac_path = NULL;
950 const char *ac_state = NULL;
951 char *name, *ac_dev = NULL;
953 name = g_strdup_printf ("<invisible> %s", nm_active_connection_get_id (ac));
954 ac_path = nm_object_get_path (NM_OBJECT (ac));
955 ac_state = active_connection_state_to_string (nm_active_connection_get_state (ac));
956 ac_dev = get_ac_device_string (ac);
958 arr = nmc_dup_fields_array (nmc_fields_con_show,
959 sizeof (nmc_fields_con_show),
962 set_val_str (arr, 0, name);
963 set_val_strc (arr, 1, nm_active_connection_get_uuid (ac));
964 set_val_strc (arr, 2, nm_active_connection_get_connection_type (ac));
965 set_val_strc (arr, 3, NULL);
966 set_val_strc (arr, 4, NULL);
967 set_val_strc (arr, 5, NULL);
968 set_val_strc (arr, 6, NULL);
969 set_val_strc (arr, 7, NULL);
970 set_val_strc (arr, 8, NULL);
971 set_val_strc (arr, 9, _("yes"));
972 set_val_str (arr, 10, ac_dev);
973 set_val_strc (arr, 11, ac_state);
974 set_val_strc (arr, 12, ac_path);
976 set_val_color_fmt_all (arr, NMC_TERM_FORMAT_DIM);
978 g_ptr_array_add (nmc->output_data, arr);
982 fill_output_active_connection (NMActiveConnection *active,
987 NMRemoteConnection *con;
988 NMSettingConnection *s_con;
989 const GPtrArray *devices;
991 NMActiveConnectionState state;
993 const char *con_path = NULL, *con_zone = NULL;
995 NmcOutputField *tmpl, *arr;
997 int idx_start = with_group ? 0 : 1;
999 con = nm_active_connection_get_connection (active);
1001 con_path = nm_connection_get_path (NM_CONNECTION (con));
1002 s_con = nm_connection_get_setting_connection (NM_CONNECTION (con));
1003 g_assert (s_con != NULL);
1004 con_zone = nm_setting_connection_get_zone (s_con);
1007 state = nm_active_connection_get_state (active);
1008 master = nm_active_connection_get_master (active);
1010 /* Get devices of the active connection */
1011 dev_str = g_string_new (NULL);
1012 devices = nm_active_connection_get_devices (active);
1013 for (i = 0; i < devices->len; i++) {
1014 NMDevice *device = g_ptr_array_index (devices, i);
1015 const char *dev_iface = nm_device_get_iface (device);
1018 g_string_append (dev_str, dev_iface);
1019 g_string_append_c (dev_str, ',');
1022 if (dev_str->len > 0)
1023 g_string_truncate (dev_str, dev_str->len - 1); /* Cut off last ',' */
1025 tmpl = nmc_fields_con_active_details_general;
1026 tmpl_len = sizeof (nmc_fields_con_active_details_general);
1029 tmpl_len -= sizeof (NmcOutputField);
1032 /* Fill field values */
1033 arr = nmc_dup_fields_array (tmpl, tmpl_len, o_flags);
1035 set_val_strc (arr, 0, nmc_fields_con_active_details_groups[0].name);
1036 set_val_strc (arr, 1-idx_start, nm_active_connection_get_id (active));
1037 set_val_strc (arr, 2-idx_start, nm_active_connection_get_uuid (active));
1038 set_val_str (arr, 3-idx_start, dev_str->str);
1039 set_val_strc (arr, 4-idx_start, active_connection_state_to_string (state));
1040 set_val_strc (arr, 5-idx_start, nm_active_connection_get_default (active) ? _("yes") : _("no"));
1041 set_val_strc (arr, 6-idx_start, nm_active_connection_get_default6 (active) ? _("yes") : _("no"));
1042 set_val_strc (arr, 7-idx_start, nm_active_connection_get_specific_object_path (active));
1043 set_val_strc (arr, 8-idx_start, NM_IS_VPN_CONNECTION (active) ? _("yes") : _("no"));
1044 set_val_strc (arr, 9-idx_start, nm_object_get_path (NM_OBJECT (active)));
1045 set_val_strc (arr, 10-idx_start, con_path);
1046 set_val_strc (arr, 11-idx_start, con_zone);
1047 set_val_strc (arr, 12-idx_start, master ? nm_object_get_path (NM_OBJECT (master)) : NULL);
1049 g_ptr_array_add (nmc->output_data, arr);
1051 g_string_free (dev_str, FALSE);
1060 fill_vpn_data_item (const char *key, const char *value, gpointer user_data)
1062 FillVPNDataInfo *info = (FillVPNDataInfo *) user_data;
1064 info->array[info->idx++] = g_strdup_printf ("%s = %s", key, value);
1067 // FIXME: The same or similar code for VPN info appears also in nm-applet (applet-dialogs.c),
1068 // and in gnome-control-center as well. It could probably be shared somehow.
1070 get_vpn_connection_type (NMConnection *connection)
1072 const char *type, *p;
1074 /* The service type is in form of "org.freedesktop.NetworkManager.vpnc".
1075 * Extract end part after last dot, e.g. "vpnc"
1077 type = nm_setting_vpn_get_service_type (nm_connection_get_setting_vpn (connection));
1078 p = strrchr (type, '.');
1079 return g_strdup (p ? p + 1 : type);
1082 /* VPN parameters can be found at:
1083 * http://git.gnome.org/browse/network-manager-openvpn/tree/src/nm-openvpn-service.h
1084 * http://git.gnome.org/browse/network-manager-vpnc/tree/src/nm-vpnc-service.h
1085 * http://git.gnome.org/browse/network-manager-pptp/tree/src/nm-pptp-service.h
1086 * http://git.gnome.org/browse/network-manager-openconnect/tree/src/nm-openconnect-service.h
1087 * http://git.gnome.org/browse/network-manager-openswan/tree/src/nm-openswan-service.h
1088 * See also 'properties' directory in these plugins.
1090 static const gchar *
1091 find_vpn_gateway_key (const char *vpn_type)
1093 if (g_strcmp0 (vpn_type, "openvpn") == 0) return "remote";
1094 if (g_strcmp0 (vpn_type, "vpnc") == 0) return "IPSec gateway";
1095 if (g_strcmp0 (vpn_type, "pptp") == 0) return "gateway";
1096 if (g_strcmp0 (vpn_type, "openconnect") == 0) return "gateway";
1097 if (g_strcmp0 (vpn_type, "openswan") == 0) return "right";
1098 if (g_strcmp0 (vpn_type, "libreswan") == 0) return "right";
1099 if (g_strcmp0 (vpn_type, "ssh") == 0) return "remote";
1100 if (g_strcmp0 (vpn_type, "l2tp") == 0) return "gateway";
1104 static const gchar *
1105 find_vpn_username_key (const char *vpn_type)
1107 if (g_strcmp0 (vpn_type, "openvpn") == 0) return "username";
1108 if (g_strcmp0 (vpn_type, "vpnc") == 0) return "Xauth username";
1109 if (g_strcmp0 (vpn_type, "pptp") == 0) return "user";
1110 if (g_strcmp0 (vpn_type, "openconnect") == 0) return "username";
1111 if (g_strcmp0 (vpn_type, "openswan") == 0) return "leftxauthusername";
1112 if (g_strcmp0 (vpn_type, "libreswan") == 0) return "leftxauthusername";
1113 if (g_strcmp0 (vpn_type, "l2tp") == 0) return "user";
1118 VPN_DATA_ITEM_GATEWAY,
1119 VPN_DATA_ITEM_USERNAME
1122 static const gchar *
1123 get_vpn_data_item (NMConnection *connection, enum VpnDataItem vpn_data_item)
1126 char *type = get_vpn_connection_type (connection);
1128 switch (vpn_data_item) {
1129 case VPN_DATA_ITEM_GATEWAY:
1130 key = find_vpn_gateway_key (type);
1132 case VPN_DATA_ITEM_USERNAME:
1133 key = find_vpn_username_key (type);
1141 return nm_setting_vpn_get_data_item (nm_connection_get_setting_vpn (connection), key);
1146 nmc_active_connection_details (NMActiveConnection *acon, NmCli *nmc)
1148 GError *error = NULL;
1149 GArray *print_groups;
1150 GPtrArray *group_fields = NULL;
1153 char *fields_all = NMC_FIELDS_CON_ACTIVE_DETAILS_ALL;
1154 char *fields_common = NMC_FIELDS_CON_ACTIVE_DETAILS_ALL;
1155 NmcOutputField *tmpl, *arr;
1157 const char *base_hdr = _("Activate connection details");
1158 gboolean was_output = FALSE;
1160 if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0)
1161 fields_str = fields_common;
1162 else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0)
1163 fields_str = fields_all;
1165 fields_str = nmc->required_fields;
1167 print_groups = parse_output_fields (fields_str, nmc_fields_con_active_details_groups, TRUE, &group_fields, &error);
1169 g_string_printf (nmc->return_text, _("Error: 'connection show': %s"), error->message);
1170 g_error_free (error);
1171 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
1174 g_assert (print_groups);
1177 nmc->print_fields.header_name = (char *) construct_header_name (base_hdr, nm_active_connection_get_uuid (acon));
1178 nmc->print_fields.indices = parse_output_fields (NMC_FIELDS_CON_ACTIVE_DETAILS_ALL,
1179 nmc_fields_con_active_details_groups, FALSE, NULL, NULL);
1181 nmc_fields_con_active_details_groups[0].flags = NMC_OF_FLAG_MAIN_HEADER_ONLY;
1182 print_required_fields (nmc, nmc_fields_con_active_details_groups);
1184 /* Loop through the groups and print them. */
1185 for (i = 0; i < print_groups->len; i++) {
1186 int group_idx = g_array_index (print_groups, int, i);
1187 char *group_fld = (char *) g_ptr_array_index (group_fields, i);
1189 if (nmc->print_output != NMC_PRINT_TERSE && !nmc->multiline_output && was_output)
1190 g_print ("\n"); /* Empty line */
1194 /* Remove any previous data */
1195 nmc_empty_output_fields (nmc);
1198 if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name, nmc_fields_con_active_details_groups[0].name) == 0) {
1199 /* Add field names */
1200 tmpl = nmc_fields_con_active_details_general;
1201 tmpl_len = sizeof (nmc_fields_con_active_details_general);
1202 nmc->print_fields.indices = parse_output_fields (group_fld ? group_fld : NMC_FIELDS_CON_ACTIVE_DETAILS_GENERAL_ALL,
1203 tmpl, FALSE, NULL, NULL);
1204 arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES);
1205 g_ptr_array_add (nmc->output_data, arr);
1207 /* Fill in values */
1208 fill_output_active_connection (acon, nmc, TRUE, NMC_OF_FLAG_SECTION_PREFIX);
1210 print_data (nmc); /* Print all data */
1216 if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name, nmc_fields_con_active_details_groups[1].name) == 0) {
1217 gboolean b1 = FALSE;
1218 NMIPConfig *cfg4 = nm_active_connection_get_ip4_config (acon);
1220 b1 = print_ip4_config (cfg4, nmc, "IP4", group_fld);
1221 was_output = was_output || b1;
1225 if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name, nmc_fields_con_active_details_groups[2].name) == 0) {
1226 gboolean b1 = FALSE;
1227 NMDhcpConfig *dhcp4 = nm_active_connection_get_dhcp4_config (acon);
1229 b1 = print_dhcp4_config (dhcp4, nmc, "DHCP4", group_fld);
1230 was_output = was_output || b1;
1234 if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name, nmc_fields_con_active_details_groups[3].name) == 0) {
1235 gboolean b1 = FALSE;
1236 NMIPConfig *cfg6 = nm_active_connection_get_ip6_config (acon);
1238 b1 = print_ip6_config (cfg6, nmc, "IP6", group_fld);
1239 was_output = was_output || b1;
1243 if (strcasecmp (nmc_fields_con_active_details_groups[group_idx].name, nmc_fields_con_active_details_groups[4].name) == 0) {
1244 gboolean b1 = FALSE;
1245 NMDhcpConfig *dhcp6 = nm_active_connection_get_dhcp6_config (acon);
1247 b1 = print_dhcp6_config (dhcp6, nmc, "DHCP6", group_fld);
1248 was_output = was_output || b1;
1252 if (NM_IS_VPN_CONNECTION (acon) &&
1253 strcasecmp (nmc_fields_con_active_details_groups[group_idx].name, nmc_fields_con_active_details_groups[5].name) == 0) {
1255 NMSettingConnection *s_con;
1256 NMSettingVpn *s_vpn;
1257 NMVpnConnectionState vpn_state;
1258 char *type_str, *banner_str = NULL, *vpn_state_str;
1260 const char *username = NULL;
1261 char **vpn_data_array = NULL;
1264 con = NM_CONNECTION (nm_active_connection_get_connection (acon));
1266 s_con = nm_connection_get_setting_connection (con);
1267 g_assert (s_con != NULL);
1269 tmpl = nmc_fields_con_active_details_vpn;
1270 tmpl_len = sizeof (nmc_fields_con_active_details_vpn);
1271 nmc->print_fields.indices = parse_output_fields (group_fld ? group_fld : NMC_FIELDS_CON_ACTIVE_DETAILS_VPN_ALL,
1272 tmpl, FALSE, NULL, NULL);
1273 arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES);
1274 g_ptr_array_add (nmc->output_data, arr);
1276 s_vpn = nm_connection_get_setting_vpn (con);
1278 items_num = nm_setting_vpn_get_num_data_items (s_vpn);
1279 if (items_num > 0) {
1280 FillVPNDataInfo info;
1282 vpn_data_array = g_new (char *, items_num + 1);
1283 info.array = vpn_data_array;
1285 nm_setting_vpn_foreach_data_item (s_vpn, &fill_vpn_data_item, &info);
1286 vpn_data_array[items_num] = NULL;
1288 username = nm_setting_vpn_get_user_name (s_vpn);
1291 type_str = get_vpn_connection_type (con);
1292 banner = nm_vpn_connection_get_banner (NM_VPN_CONNECTION (acon));
1294 banner_str = g_strescape (banner, "");
1295 vpn_state = nm_vpn_connection_get_vpn_state (NM_VPN_CONNECTION (acon));
1296 vpn_state_str = g_strdup_printf ("%d - %s", vpn_state, vpn_connection_state_to_string (vpn_state));
1299 arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX);
1300 set_val_strc (arr, 0, nmc_fields_con_active_details_groups[5].name);
1301 set_val_str (arr, 1, type_str);
1302 set_val_strc (arr, 2, username ? username : get_vpn_data_item (con, VPN_DATA_ITEM_USERNAME));
1303 set_val_strc (arr, 3, get_vpn_data_item (con, VPN_DATA_ITEM_GATEWAY));
1304 set_val_str (arr, 4, banner_str);
1305 set_val_str (arr, 5, vpn_state_str);
1306 set_val_arr (arr, 6, vpn_data_array);
1307 g_ptr_array_add (nmc->output_data, arr);
1309 print_data (nmc); /* Print all data */
1314 g_array_free (print_groups, TRUE);
1316 g_ptr_array_free (group_fields, TRUE);
1322 split_required_fields_for_con_show (const char *input,
1323 char **profile_flds,
1327 char **fields, **iter;
1329 GString *str1, *str2;
1331 gboolean group_profile = FALSE;
1332 gboolean group_active = FALSE;
1333 gboolean success = TRUE;
1334 gboolean is_all, is_common;
1338 *profile_flds = NULL;
1339 *active_flds = NULL;
1343 str1 = g_string_new (NULL);
1344 str2 = g_string_new (NULL);
1346 /* Split supplied fields string */
1347 fields = g_strsplit_set (input, ",", -1);
1348 for (iter = fields; iter && *iter; iter++) {
1350 dot = strchr (*iter, '.');
1354 is_all = !dot && strcasecmp (*iter, "all") == 0;
1355 is_common = !dot && strcasecmp (*iter, "common") == 0;
1359 for (i = 0; nmc_fields_settings_names[i].name; i++) {
1360 if ( is_all || is_common
1361 || !strcasecmp (*iter, nmc_fields_settings_names[i].name)) {
1364 g_string_append (str1, *iter);
1365 g_string_append_c (str1, ',');
1372 for (i = 0; nmc_fields_con_active_details_groups[i].name; i++) {
1373 if ( is_all || is_common
1374 || !strcasecmp (*iter, nmc_fields_con_active_details_groups[i].name)) {
1377 g_string_append (str2, *iter);
1378 g_string_append_c (str2, ',');
1386 if (!strcasecmp (*iter, CON_SHOW_DETAIL_GROUP_PROFILE))
1387 group_profile = TRUE;
1388 else if (!strcasecmp (*iter, CON_SHOW_DETAIL_GROUP_ACTIVE))
1389 group_active = TRUE;
1391 char *allowed1 = nmc_get_allowed_fields (nmc_fields_settings_names, -1);
1392 char *allowed2 = nmc_get_allowed_fields (nmc_fields_con_active_details_groups, -1);
1393 g_set_error (error, NMCLI_ERROR, 0, _("invalid field '%s'; allowed fields: %s and %s, or %s,%s"),
1394 *iter, allowed1, allowed2, CON_SHOW_DETAIL_GROUP_PROFILE, CON_SHOW_DETAIL_GROUP_ACTIVE);
1403 g_strfreev (fields);
1405 /* Handle pseudo groups: profile, active */
1406 if (success && group_profile) {
1407 if (str1->len > 0) {
1408 g_set_error (error, NMCLI_ERROR, 0, _("'%s' has to be alone"),
1409 CON_SHOW_DETAIL_GROUP_PROFILE);
1412 g_string_assign (str1, "all,");
1414 if (success && group_active) {
1415 if (str2->len > 0) {
1416 g_set_error (error, NMCLI_ERROR, 0, _("'%s' has to be alone"),
1417 CON_SHOW_DETAIL_GROUP_ACTIVE);
1420 g_string_assign (str2, "all,");
1425 g_string_truncate (str1, str1->len - 1);
1427 g_string_truncate (str2, str2->len - 1);
1428 *profile_flds = g_string_free (str1, str1->len == 0);
1429 *active_flds = g_string_free (str2, str2->len == 0);
1431 g_string_free (str1, TRUE);
1432 g_string_free (str2, TRUE);
1438 NMC_SORT_ACTIVE = 1,
1439 NMC_SORT_ACTIVE_INV = -1,
1441 NMC_SORT_NAME_INV = -2,
1443 NMC_SORT_TYPE_INV = -3,
1445 NMC_SORT_PATH_INV = -4,
1450 const GArray *order;
1454 compare_connections (gconstpointer a, gconstpointer b, gpointer user_data)
1456 NMConnection *ca = *(NMConnection **)a;
1457 NMConnection *cb = *(NMConnection **)b;
1458 NMActiveConnection *aca, *acb;
1459 NmcSortInfo *info = (NmcSortInfo *) user_data;
1460 GArray *default_order = NULL;
1461 const GArray *order;
1464 const char *tmp1, *tmp2;
1465 unsigned long tmp1_int, tmp2_int;
1468 order = info->order;
1470 NmcSortOrder def[] = { NMC_SORT_ACTIVE, NMC_SORT_NAME, NMC_SORT_PATH };
1471 int num = G_N_ELEMENTS (def);
1472 default_order = g_array_sized_new (FALSE, FALSE, sizeof (NmcSortOrder), num);
1473 g_array_append_vals (default_order, def, num);
1474 order = default_order;
1477 for (i = 0; i < order->len; i++) {
1478 item = g_array_index (order, NmcSortOrder, i);
1480 case NMC_SORT_ACTIVE:
1481 case NMC_SORT_ACTIVE_INV:
1482 aca = get_ac_for_connection (nm_client_get_active_connections (info->nmc->client), ca);
1483 acb = get_ac_for_connection (nm_client_get_active_connections (info->nmc->client), cb);
1484 cmp = (aca && !acb) ? -1 : (!aca && acb) ? 1 : 0;
1485 if (item == NMC_SORT_ACTIVE_INV)
1489 case NMC_SORT_TYPE_INV:
1490 cmp = g_strcmp0 (nm_connection_get_connection_type (ca),
1491 nm_connection_get_connection_type (cb));
1492 if (item == NMC_SORT_TYPE_INV)
1496 case NMC_SORT_NAME_INV:
1497 cmp = g_strcmp0 (nm_connection_get_id (ca),
1498 nm_connection_get_id (cb));
1499 if (item == NMC_SORT_NAME_INV)
1503 case NMC_SORT_PATH_INV:
1504 tmp1 = nm_connection_get_path (ca);
1505 tmp2 = nm_connection_get_path (cb);
1506 tmp1 = tmp1 ? strrchr (tmp1, '/') : "0";
1507 tmp2 = tmp2 ? strrchr (tmp2, '/') : "0";
1508 nmc_string_to_uint (tmp1 ? tmp1+1 : "0", FALSE, 0, 0, &tmp1_int);
1509 nmc_string_to_uint (tmp2 ? tmp2+1 : "0", FALSE, 0, 0, &tmp2_int);
1510 cmp = (int) tmp1_int - tmp2_int;
1511 if (item == NMC_SORT_PATH_INV)
1523 g_array_unref (default_order);
1528 sort_connections (const GPtrArray *cons, NmCli *nmc, const GArray *order)
1532 NmcSortInfo compare_info;
1537 compare_info.nmc = nmc;
1538 compare_info.order = order;
1540 sorted = g_ptr_array_sized_new (cons->len);
1541 for (i = 0; i < cons->len; i++)
1542 g_ptr_array_add (sorted, cons->pdata[i]);
1543 g_ptr_array_sort_with_data (sorted, compare_connections, &compare_info);
1548 compare_ac_connections (gconstpointer a, gconstpointer b, gpointer user_data)
1550 NMActiveConnection *ca = *(NMActiveConnection **)a;
1551 NMActiveConnection *cb = *(NMActiveConnection **)b;
1554 /* Sort states first */
1555 cmp = nm_active_connection_get_state (cb) - nm_active_connection_get_state (ca);
1559 cmp = g_strcmp0 (nm_active_connection_get_id (ca),
1560 nm_active_connection_get_id (cb));
1564 return g_strcmp0 (nm_active_connection_get_connection_type (ca),
1565 nm_active_connection_get_connection_type (cb));
1569 get_invisible_active_connections (NmCli *nmc)
1571 const GPtrArray *acons;
1572 GPtrArray *invisibles;
1575 g_return_val_if_fail (nmc != NULL, NULL);
1577 invisibles = g_ptr_array_new ();
1578 acons = nm_client_get_active_connections (nmc->client);
1579 for (a = 0; a < acons->len; a++) {
1580 gboolean found = FALSE;
1581 NMActiveConnection *acon = g_ptr_array_index (acons, a);
1582 const char *a_uuid = nm_active_connection_get_uuid (acon);
1584 for (c = 0; c < nmc->connections->len; c++) {
1585 NMConnection *con = g_ptr_array_index (nmc->connections, c);
1586 const char *c_uuid = nm_connection_get_uuid (con);
1588 if (strcmp (a_uuid, c_uuid) == 0) {
1593 /* Active connection is not in connections array, add it to */
1595 g_ptr_array_add (invisibles, acon);
1597 g_ptr_array_sort_with_data (invisibles, compare_ac_connections, NULL);
1601 static NMCResultCode
1602 do_connections_show (NmCli *nmc, gboolean active_only, gboolean show_secrets,
1603 const GArray *order, int argc, char **argv)
1606 char *profile_flds = NULL, *active_flds = NULL;
1607 GPtrArray *invisibles, *sorted_cons;
1611 char *fields_all = NMC_FIELDS_CON_SHOW_ALL;
1612 char *fields_common = NMC_FIELDS_CON_SHOW_COMMON;
1613 NmcOutputField *tmpl, *arr;
1617 if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0)
1618 fields_str = fields_common;
1619 else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0)
1620 fields_str = fields_all;
1622 fields_str = nmc->required_fields;
1624 tmpl = nmc_fields_con_show;
1625 tmpl_len = sizeof (nmc_fields_con_show);
1626 nmc->print_fields.indices = parse_output_fields (fields_str, tmpl, FALSE, NULL, &err);
1630 if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &err))
1634 nmc->print_fields.header_name = active_only ? _("NetworkManager active profiles") :
1635 _("NetworkManager connection profiles");
1636 arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES);
1637 g_ptr_array_add (nmc->output_data, arr);
1639 /* There might be active connections not present in connection list
1640 * (e.g. private connections of a different user). Show them as well. */
1641 invisibles = get_invisible_active_connections (nmc);
1642 for (i = 0; i < invisibles->len; i++)
1643 fill_output_connection_for_invisible (invisibles->pdata[i], nmc);
1644 g_ptr_array_free (invisibles, TRUE);
1646 /* Sort the connections and fill the output data */
1647 sorted_cons = sort_connections (nmc->connections, nmc, order);
1648 for (i = 0; i < sorted_cons->len; i++)
1649 fill_output_connection (sorted_cons->pdata[i], nmc, active_only);
1650 g_ptr_array_free (sorted_cons, TRUE);
1652 print_data (nmc); /* Print all data */
1654 gboolean new_line = FALSE;
1655 gboolean without_fields = (nmc->required_fields == NULL);
1656 const GPtrArray *active_cons = nm_client_get_active_connections (nmc->client);
1659 /* multiline mode is default for 'connection show <ID>' */
1660 if (!nmc->mode_specified)
1661 nmc->multiline_output = TRUE;
1663 /* Split required fields into the settings and active ones. */
1664 if (!split_required_fields_for_con_show (nmc->required_fields, &profile_flds, &active_flds, &err))
1666 g_free (nmc->required_fields);
1667 nmc->required_fields = NULL;
1672 NMActiveConnection *acon = NULL;
1673 const char *selector = NULL;
1675 if ( strcmp (*argv, "id") == 0
1676 || strcmp (*argv, "uuid") == 0
1677 || strcmp (*argv, "path") == 0
1678 || strcmp (*argv, "apath") == 0) {
1680 if (next_arg (&argc, &argv) != 0) {
1681 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
1682 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
1687 /* Find connection by id, uuid, path or apath */
1688 con = nmc_find_connection (nmc->connections, selector, *argv, &pos);
1690 acon = find_active_connection (active_cons, nmc->connections, selector, *argv, NULL);
1692 con = NM_CONNECTION (nm_active_connection_get_connection (acon));
1695 if (!con && !acon) {
1696 g_string_printf (nmc->return_text, _("Error: %s - no such connection profile."), *argv);
1697 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
1701 /* Print connection details:
1702 * Usually we have both static and active connection.
1703 * But when a connection is private to a user, another user
1704 * may see only the active connection.
1707 /* Filter only active connections */
1709 acon = get_ac_for_connection (active_cons, con);
1710 if (active_only && !acon) {
1711 next_arg (&argc, &argv);
1715 /* Show an empty line between connections */
1719 /* Show profile configuration */
1720 if (without_fields || profile_flds) {
1722 nmc->required_fields = profile_flds;
1724 update_secrets_in_connection (NM_REMOTE_CONNECTION (con), con);
1725 res = nmc_connection_profile_details (con, nmc, show_secrets);
1726 nmc->required_fields = NULL;
1732 /* If the profile is active, print also active details */
1733 if (without_fields || active_flds) {
1735 nmc->required_fields = active_flds;
1736 res = nmc_active_connection_details (acon, nmc);
1737 nmc->required_fields = NULL;
1744 /* Take next argument.
1745 * But for pos != NULL we have more connections of the same name,
1746 * so process the same argument again.
1749 next_arg (&argc, &argv);
1755 g_string_printf (nmc->return_text, _("Error: %s."), err->message);
1756 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
1759 g_free (profile_flds);
1760 g_free (active_flds);
1761 return nmc->return_value;
1764 static NMActiveConnection *
1765 get_default_active_connection (NmCli *nmc, NMDevice **device)
1767 NMActiveConnection *default_ac = NULL;
1768 NMDevice *non_default_device = NULL;
1769 NMActiveConnection *non_default_ac = NULL;
1770 const GPtrArray *connections;
1773 g_return_val_if_fail (nmc != NULL, NULL);
1774 g_return_val_if_fail (device != NULL, NULL);
1775 g_return_val_if_fail (*device == NULL, NULL);
1777 connections = nm_client_get_active_connections (nmc->client);
1778 for (i = 0; i < connections->len; i++) {
1779 NMActiveConnection *candidate = g_ptr_array_index (connections, i);
1780 const GPtrArray *devices;
1782 devices = nm_active_connection_get_devices (candidate);
1786 if (nm_active_connection_get_default (candidate)) {
1788 *device = g_ptr_array_index (devices, 0);
1789 default_ac = candidate;
1792 if (!non_default_ac) {
1793 non_default_device = g_ptr_array_index (devices, 0);
1794 non_default_ac = candidate;
1799 /* Prefer the default connection if one exists, otherwise return the first
1800 * non-default connection.
1802 if (!default_ac && non_default_ac) {
1803 default_ac = non_default_ac;
1804 *device = non_default_device;
1809 /* Find a device to activate the connection on.
1810 * IN: connection: connection to activate
1811 * iface: device interface name to use (optional)
1812 * ap: access point to use (optional; valid just for 802-11-wireless)
1813 * nsp: Network Service Provider to use (option; valid only for wimax)
1814 * OUT: device: found device
1815 * spec_object: specific_object path of NMAccessPoint
1816 * RETURNS: TRUE when a device is found, FALSE otherwise.
1819 find_device_for_connection (NmCli *nmc,
1820 NMConnection *connection,
1825 const char **spec_object,
1828 NMSettingConnection *s_con;
1829 const char *con_type;
1832 g_return_val_if_fail (nmc != NULL, FALSE);
1833 g_return_val_if_fail (iface || ap || nsp, FALSE);
1834 g_return_val_if_fail (device != NULL && *device == NULL, FALSE);
1835 g_return_val_if_fail (spec_object != NULL && *spec_object == NULL, FALSE);
1836 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1838 s_con = nm_connection_get_setting_connection (connection);
1840 con_type = nm_setting_connection_get_connection_type (s_con);
1842 if (strcmp (con_type, NM_SETTING_VPN_SETTING_NAME) == 0) {
1843 /* VPN connections */
1844 NMActiveConnection *active = NULL;
1846 *device = nm_client_get_device_by_iface (nmc->client, iface);
1848 active = nm_device_get_active_connection (*device);
1851 g_set_error (error, NMCLI_ERROR, 0, _("no active connection on device '%s'"), iface);
1854 *spec_object = nm_object_get_path (NM_OBJECT (active));
1857 active = get_default_active_connection (nmc, device);
1859 g_set_error_literal (error, NMCLI_ERROR, 0, _("no active connection or device"));
1862 *spec_object = nm_object_get_path (NM_OBJECT (active));
1866 /* Other connections */
1867 NMDevice *found_device = NULL;
1868 const GPtrArray *devices = nm_client_get_devices (nmc->client);
1870 for (i = 0; i < devices->len && !found_device; i++) {
1871 NMDevice *dev = g_ptr_array_index (devices, i);
1874 const char *dev_iface = nm_device_get_iface (dev);
1875 if ( !g_strcmp0 (dev_iface, iface)
1876 && nm_device_connection_compatible (dev, connection, NULL)) {
1880 if (nm_device_connection_compatible (dev, connection, NULL)) {
1885 if (found_device && ap && !strcmp (con_type, NM_SETTING_WIRELESS_SETTING_NAME) && NM_IS_DEVICE_WIFI (dev)) {
1886 char *bssid_up = g_ascii_strup (ap, -1);
1887 const GPtrArray *aps = nm_device_wifi_get_access_points (NM_DEVICE_WIFI (dev));
1888 found_device = NULL; /* Mark as not found; set to the device again later, only if AP matches */
1890 for (j = 0; j < aps->len; j++) {
1891 NMAccessPoint *candidate_ap = g_ptr_array_index (aps, j);
1892 const char *candidate_bssid = nm_access_point_get_bssid (candidate_ap);
1894 if (!strcmp (bssid_up, candidate_bssid)) {
1896 *spec_object = nm_object_get_path (NM_OBJECT (candidate_ap));
1906 *device = found_device;
1910 g_set_error (error, NMCLI_ERROR, 0, _("device '%s' not compatible with connection '%s'"),
1911 iface, nm_setting_connection_get_id (s_con));
1913 g_set_error (error, NMCLI_ERROR, 0, _("no device found for connection '%s'"),
1914 nm_setting_connection_get_id (s_con));
1921 vpn_connection_state_reason_to_string (NMVpnConnectionStateReason reason)
1924 case NM_VPN_CONNECTION_STATE_REASON_UNKNOWN:
1925 return _("unknown reason");
1926 case NM_VPN_CONNECTION_STATE_REASON_NONE:
1928 case NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED:
1929 return _("the user was disconnected");
1930 case NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED:
1931 return _("the base network connection was interrupted");
1932 case NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED:
1933 return _("the VPN service stopped unexpectedly");
1934 case NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID:
1935 return _("the VPN service returned invalid configuration");
1936 case NM_VPN_CONNECTION_STATE_REASON_CONNECT_TIMEOUT:
1937 return _("the connection attempt timed out");
1938 case NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT:
1939 return _("the VPN service did not start in time");
1940 case NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED:
1941 return _("the VPN service failed to start");
1942 case NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS:
1943 return _("no valid VPN secrets");
1944 case NM_VPN_CONNECTION_STATE_REASON_LOGIN_FAILED:
1945 return _("invalid VPN secrets");
1946 case NM_VPN_CONNECTION_STATE_REASON_CONNECTION_REMOVED:
1947 return _("the connection was removed");
1949 return _("unknown");
1954 device_state_cb (NMDevice *device, GParamSpec *pspec, gpointer user_data)
1956 NmCli *nmc = (NmCli *) user_data;
1957 NMActiveConnection *active;
1958 NMDeviceState state;
1959 NMActiveConnectionState ac_state;
1961 active = nm_device_get_active_connection (device);
1962 state = nm_device_get_state (device);
1964 ac_state = active ? nm_active_connection_get_state (active) : NM_ACTIVE_CONNECTION_STATE_UNKNOWN;
1966 if (ac_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
1967 if (nmc->print_output == NMC_PRINT_PRETTY)
1968 nmc_terminal_erase_line ();
1969 g_print (_("Connection successfully activated (D-Bus active path: %s)\n"),
1970 nm_object_get_path (NM_OBJECT (active)));
1972 } else if ( ac_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATING
1973 && state >= NM_DEVICE_STATE_IP_CONFIG) {
1974 if (nmc->print_output == NMC_PRINT_PRETTY)
1975 nmc_terminal_erase_line ();
1976 g_print (_("Connection successfully activated (master waiting for slaves) (D-Bus active path: %s)\n"),
1977 nm_object_get_path (NM_OBJECT (active)));
1979 } else if (active && ac_state != NM_ACTIVE_CONNECTION_STATE_ACTIVATING) {
1980 g_string_printf (nmc->return_text, _("Error: Connection activation failed."));
1981 nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
1987 active_connection_state_cb (NMActiveConnection *active, GParamSpec *pspec, gpointer user_data)
1989 NmCli *nmc = (NmCli *) user_data;
1990 NMActiveConnectionState state;
1992 state = nm_active_connection_get_state (active);
1994 if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
1995 if (nmc->print_output == NMC_PRINT_PRETTY)
1996 nmc_terminal_erase_line ();
1997 g_print (_("Connection successfully activated (D-Bus active path: %s)\n"),
1998 nm_object_get_path (NM_OBJECT (active)));
1999 g_object_unref (active);
2001 } else if (state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED) {
2002 g_string_printf (nmc->return_text, _("Error: Connection activation failed."));
2003 nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
2004 g_object_unref (active);
2006 } else if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATING) {
2007 /* activating master connection does not automatically activate any slaves, so their
2008 * active connection state will not progress beyond ACTIVATING state.
2009 * Monitor the device instead. */
2010 const GPtrArray *devices;
2013 if (nmc->secret_agent) {
2014 NMRemoteConnection *connection = nm_active_connection_get_connection (active);
2016 nm_secret_agent_simple_enable (NM_SECRET_AGENT_SIMPLE (nmc->secret_agent),
2017 nm_connection_get_path (NM_CONNECTION (connection)));
2020 devices = nm_active_connection_get_devices (active);
2021 device = devices->len ? g_ptr_array_index (devices, 0) : NULL;
2023 && ( NM_IS_DEVICE_BOND (device)
2024 || NM_IS_DEVICE_TEAM (device)
2025 || NM_IS_DEVICE_BRIDGE (device))) {
2026 g_signal_handlers_disconnect_by_func (active, G_CALLBACK (active_connection_state_cb), nmc);
2027 g_signal_connect (device, "notify::" NM_DEVICE_STATE, G_CALLBACK (device_state_cb), nmc);
2029 device_state_cb (device, NULL, nmc);
2035 vpn_connection_state_cb (NMVpnConnection *vpn,
2036 NMVpnConnectionState state,
2037 NMVpnConnectionStateReason reason,
2040 NmCli *nmc = (NmCli *) user_data;
2043 case NM_VPN_CONNECTION_STATE_PREPARE:
2044 case NM_VPN_CONNECTION_STATE_NEED_AUTH:
2045 case NM_VPN_CONNECTION_STATE_CONNECT:
2046 case NM_VPN_CONNECTION_STATE_IP_CONFIG_GET:
2050 case NM_VPN_CONNECTION_STATE_ACTIVATED:
2051 if (nmc->print_output == NMC_PRINT_PRETTY)
2052 nmc_terminal_erase_line ();
2053 g_print (_("VPN connection successfully activated (D-Bus active path: %s)\n"),
2054 nm_object_get_path (NM_OBJECT (vpn)));
2055 g_object_unref (vpn);
2059 case NM_VPN_CONNECTION_STATE_FAILED:
2060 case NM_VPN_CONNECTION_STATE_DISCONNECTED:
2061 g_string_printf (nmc->return_text, _("Error: Connection activation failed: %s."),
2062 vpn_connection_state_reason_to_string (reason));
2063 nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
2064 g_object_unref (vpn);
2074 timeout_cb (gpointer user_data)
2076 /* Time expired -> exit nmcli */
2078 NmCli *nmc = (NmCli *) user_data;
2080 g_string_printf (nmc->return_text, _("Error: Timeout %d sec expired."), nmc->timeout);
2081 nmc->return_value = NMC_RESULT_ERROR_TIMEOUT_EXPIRED;
2087 progress_cb (gpointer user_data)
2089 const char *str = (const char *) user_data;
2091 nmc_terminal_show_progress (str);
2097 progress_device_cb (gpointer user_data)
2099 NMDevice *device = (NMDevice *) user_data;
2101 nmc_terminal_show_progress (device ? nmc_device_state_to_string (nm_device_get_state (device)) : "");
2107 progress_vpn_cb (gpointer user_data)
2109 NMVpnConnection *vpn = (NMVpnConnection *) user_data;
2112 str = NM_IS_VPN_CONNECTION (vpn) ?
2113 vpn_connection_state_to_string (nm_vpn_connection_get_vpn_state (vpn)) :
2116 nmc_terminal_show_progress (str);
2124 } ActivateConnectionInfo;
2127 activate_connection_cb (GObject *client, GAsyncResult *result, gpointer user_data)
2129 ActivateConnectionInfo *info = (ActivateConnectionInfo *) user_data;
2130 NmCli *nmc = info->nmc;
2131 NMDevice *device = info->device;
2132 NMActiveConnection *active;
2133 NMActiveConnectionState state;
2134 const GPtrArray *ac_devs;
2135 GError *error = NULL;
2137 active = nm_client_activate_connection_finish (NM_CLIENT (client), result, &error);
2140 g_string_printf (nmc->return_text, _("Error: Connection activation failed: %s"),
2142 g_error_free (error);
2143 nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
2146 state = nm_active_connection_get_state (active);
2148 /* device could be NULL for virtual devices. Fill it here. */
2149 ac_devs = nm_active_connection_get_devices (active);
2150 info->device = device = ac_devs->len > 0 ? g_ptr_array_index (ac_devs, 0) : NULL;
2153 if (nmc->nowait_flag || state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
2154 /* User doesn't want to wait or already activated */
2155 if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
2156 if (nmc->print_output == NMC_PRINT_PRETTY)
2157 nmc_terminal_erase_line ();
2158 g_print (_("Connection successfully activated (D-Bus active path: %s)\n"),
2159 nm_object_get_path (NM_OBJECT (active)));
2161 g_object_unref (active);
2164 if (NM_IS_VPN_CONNECTION (active)) {
2165 /* Monitor VPN state */
2166 g_signal_connect (G_OBJECT (active), "vpn-state-changed", G_CALLBACK (vpn_connection_state_cb), nmc);
2168 /* Start progress indication showing VPN states */
2169 if (nmc->print_output == NMC_PRINT_PRETTY) {
2171 g_source_remove (progress_id);
2172 progress_id = g_timeout_add (120, progress_vpn_cb, NM_VPN_CONNECTION (active));
2175 g_signal_connect (active, "notify::state", G_CALLBACK (active_connection_state_cb), nmc);
2176 active_connection_state_cb (active, NULL, nmc);
2178 /* Start progress indication showing device states */
2179 if (nmc->print_output == NMC_PRINT_PRETTY) {
2181 g_source_remove (progress_id);
2182 progress_id = g_timeout_add (120, progress_device_cb, device);
2186 /* Start timer not to loop forever when signals are not emitted */
2187 g_timeout_add_seconds (nmc->timeout, timeout_cb, nmc);
2195 * @passwd_file: file with passwords to parse
2196 * @error: location to store error, or %NULL
2198 * Parse passwords given in @passwd_file and insert them into a hash table.
2199 * Example of @passwd_file contents:
2200 * wifi.psk:tajne heslo
2201 * 802-1x.password:krakonos
2202 * 802-11-wireless-security:leap-password:my leap password
2204 * Returns: hash table with parsed passwords, or %NULL on an error
2207 parse_passwords (const char *passwd_file, GError **error)
2209 GHashTable *pwds_hash;
2210 char *contents = NULL;
2212 GError *local_err = NULL;
2213 char **lines, **iter;
2214 char *pwd_spec, *pwd, *prop;
2215 const char *setting;
2217 pwds_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
2222 /* Read the passwords file */
2223 if (!g_file_get_contents (passwd_file, &contents, &len, &local_err)) {
2224 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
2225 _("failed to read passwd-file '%s': %s"),
2226 passwd_file, local_err->message);
2227 g_error_free (local_err);
2228 g_hash_table_destroy (pwds_hash);
2232 lines = nmc_strsplit_set (contents, "\r\n", -1);
2233 for (iter = lines; *iter; iter++) {
2234 pwd = strchr (*iter, ':');
2236 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
2237 _("missing colon in 'password' entry '%s'"), *iter);
2242 prop = strchr (*iter, '.');
2244 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
2245 _("missing dot in 'password' entry '%s'"), *iter);
2251 while (g_ascii_isspace (*setting))
2253 /* Accept wifi-sec or wifi instead of cumbersome '802-11-wireless-security' */
2254 if (!strcmp (setting, "wifi-sec") || !strcmp (setting, "wifi"))
2255 setting = NM_SETTING_WIRELESS_SECURITY_SETTING_NAME;
2256 if (nm_setting_lookup_type (setting) == G_TYPE_INVALID) {
2257 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
2258 _("invalid setting name in 'password' entry '%s'"), setting);
2262 pwd_spec = g_strdup_printf ("%s.%s", setting, prop);
2263 g_hash_table_insert (pwds_hash, pwd_spec, g_strdup (pwd));
2272 g_hash_table_destroy (pwds_hash);
2279 nmc_activate_connection (NmCli *nmc,
2280 NMConnection *connection,
2285 GAsyncReadyCallback callback,
2288 ActivateConnectionInfo *info;
2290 GHashTable *pwds_hash;
2291 NMDevice *device = NULL;
2292 const char *spec_object = NULL;
2293 gboolean device_found;
2294 GError *local = NULL;
2296 g_return_val_if_fail (nmc != NULL, FALSE);
2297 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2299 if (connection && (ifname || ap || nsp)) {
2300 device_found = find_device_for_connection (nmc, connection, ifname, ap, nsp, &device, &spec_object, &local);
2302 /* Virtual connection may not have their interfaces created yet */
2303 if (!device_found && !nm_connection_is_virtual (connection)) {
2304 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_CON_ACTIVATION,
2305 "%s", local->message);
2306 g_clear_error (&local);
2309 g_clear_error (&local);
2310 } else if (ifname) {
2311 device = nm_client_get_device_by_iface (nmc->client, ifname);
2313 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_NOT_FOUND,
2314 _("unknown device '%s'."), ifname);
2317 } else if (!connection) {
2318 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_NOT_FOUND,
2319 _("neither a valid connection nor device given"));
2323 /* Parse passwords given in passwords file */
2324 pwds_hash = parse_passwords (pwds, &local);
2326 g_propagate_error (error, local);
2330 g_hash_table_destroy (nmc->pwds_hash);
2331 nmc->pwds_hash = pwds_hash;
2333 /* Create secret agent */
2334 nmc->secret_agent = nm_secret_agent_simple_new ("nmcli-connect");
2335 if (nmc->secret_agent) {
2336 g_signal_connect (nmc->secret_agent, "request-secrets", G_CALLBACK (nmc_secrets_requested), nmc);
2338 nm_secret_agent_simple_enable (NM_SECRET_AGENT_SIMPLE (nmc->secret_agent),
2339 nm_object_get_path (NM_OBJECT (connection)));
2343 info = g_malloc0 (sizeof (ActivateConnectionInfo));
2345 info->device = device;
2347 nm_client_activate_connection_async (nmc->client,
2357 static NMCResultCode
2358 do_connection_up (NmCli *nmc, int argc, char **argv)
2360 NMConnection *connection = NULL;
2361 const char *ifname = NULL;
2362 const char *ap = NULL;
2363 const char *nsp = NULL;
2364 const char *pwds = NULL;
2365 GError *error = NULL;
2366 const char *selector = NULL;
2367 const char *name = NULL;
2371 * Set default timeout for connection activation.
2372 * Activation can take quite a long time, use 90 seconds.
2374 if (nmc->timeout == -1)
2379 line = nmc_readline (PROMPT_CONNECTION);
2380 name = line ? line : "";
2382 } else if (strcmp (*argv, "ifname") != 0) {
2383 if ( strcmp (*argv, "id") == 0
2384 || strcmp (*argv, "uuid") == 0
2385 || strcmp (*argv, "path") == 0) {
2388 if (next_arg (&argc, &argv) != 0) {
2389 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector);
2390 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
2396 next_arg (&argc, &argv);
2400 connection = nmc_find_connection (nmc->connections, selector, name, NULL);
2402 g_string_printf (nmc->return_text, _("Error: Connection '%s' does not exist."), name);
2403 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
2409 if (strcmp (*argv, "ifname") == 0) {
2410 if (next_arg (&argc, &argv) != 0) {
2411 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
2412 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
2418 else if (strcmp (*argv, "ap") == 0) {
2419 if (next_arg (&argc, &argv) != 0) {
2420 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
2421 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
2427 else if (strcmp (*argv, "passwd-file") == 0) {
2428 if (next_arg (&argc, &argv) != 0) {
2429 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
2430 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
2437 g_printerr (_("Unknown parameter: %s\n"), *argv);
2444 /* Use nowait_flag instead of should_wait because exiting has to be postponed till
2445 * active_connection_state_cb() is called. That gives NM time to check our permissions
2446 * and we can follow activation progress.
2448 nmc->nowait_flag = (nmc->timeout == 0);
2451 if (!nmc_activate_connection (nmc, connection, ifname, ap, nsp, pwds, activate_connection_cb, &error)) {
2452 g_string_printf (nmc->return_text, _("Error: %s."),
2454 nmc->return_value = error->code;
2455 g_clear_error (&error);
2460 /* Start progress indication */
2461 if (nmc->print_output == NMC_PRINT_PRETTY)
2462 progress_id = g_timeout_add (120, progress_cb, _("preparing"));
2466 return nmc->return_value;
2475 static void connection_cb_info_finish (ConnectionCbInfo *info,
2476 gpointer connection);
2479 connection_removed_cb (NMClient *client, NMConnection *connection, ConnectionCbInfo *info)
2481 if (!g_slist_find (info->queue, connection))
2483 g_print (_("Connection '%s' (%s) successfully deleted.\n"),
2484 nm_connection_get_id (connection),
2485 nm_connection_get_uuid (connection));
2486 connection_cb_info_finish (info, connection);
2490 down_active_connection_state_cb (NMActiveConnection *active,
2492 ConnectionCbInfo *info)
2494 if (nm_active_connection_get_state (active) < NM_ACTIVE_CONNECTION_STATE_DEACTIVATED)
2497 if (info->nmc->print_output == NMC_PRINT_PRETTY)
2498 nmc_terminal_erase_line ();
2499 g_print (_("Connection '%s' successfully deactivated (D-Bus active path: %s)\n"),
2500 nm_active_connection_get_id (active), nm_object_get_path (NM_OBJECT (active)));
2502 g_signal_handlers_disconnect_by_func (G_OBJECT (active),
2503 down_active_connection_state_cb,
2505 connection_cb_info_finish (info, active);
2509 connection_op_timeout_cb (gpointer user_data)
2511 ConnectionCbInfo *info = user_data;
2513 timeout_cb (info->nmc);
2514 connection_cb_info_finish (info, NULL);
2515 return G_SOURCE_REMOVE;
2519 destroy_queue_element (gpointer data)
2521 g_signal_handlers_disconnect_matched (data, G_SIGNAL_MATCH_FUNC, 0, 0, 0,
2522 down_active_connection_state_cb, NULL);
2523 g_object_unref (data);
2527 connection_cb_info_finish (ConnectionCbInfo *info, gpointer connection)
2530 info->queue = g_slist_remove (info->queue, connection);
2531 g_object_unref (G_OBJECT (connection));
2533 g_slist_free_full (info->queue, destroy_queue_element);
2540 if (info->timeout_id)
2541 g_source_remove (info->timeout_id);
2542 g_signal_handlers_disconnect_by_func (info->nmc->client, connection_removed_cb, info);
2543 g_slice_free (ConnectionCbInfo, info);
2547 static NMCResultCode
2548 do_connection_down (NmCli *nmc, int argc, char **argv)
2550 NMActiveConnection *active;
2551 ConnectionCbInfo *info = NULL;
2552 const GPtrArray *active_cons;
2553 GSList *queue = NULL, *iter;
2554 char **arg_arr = NULL;
2555 char **arg_ptr = argv;
2559 if (nmc->timeout == -1)
2564 char *line = nmc_readline (PROMPT_ACTIVE_CONNECTIONS);
2565 nmc_string_to_arg_array (line, NULL, TRUE, &arg_arr, &arg_num);
2570 g_string_printf (nmc->return_text, _("Error: No connection specified."));
2571 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
2576 /* Get active connections */
2577 active_cons = nm_client_get_active_connections (nmc->client);
2578 while (arg_num > 0) {
2579 const char *selector = NULL;
2581 if ( strcmp (*arg_ptr, "id") == 0
2582 || strcmp (*arg_ptr, "uuid") == 0
2583 || strcmp (*arg_ptr, "path") == 0
2584 || strcmp (*arg_ptr, "apath") == 0) {
2586 selector = *arg_ptr;
2587 if (next_arg (&arg_num, &arg_ptr) != 0) {
2588 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector);
2589 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
2594 active = find_active_connection (active_cons, nmc->connections, selector, *arg_ptr, &idx);
2596 /* Check if the connection is unique. */
2597 /* Calling down for the same connection repeatedly would result in
2598 * NM responding for the last D-Bus call only and we would stall. */
2599 if (!g_slist_find (queue, active))
2600 queue = g_slist_prepend (queue, g_object_ref (active));
2602 g_printerr (_("Error: '%s' is not an active connection.\n"), *arg_ptr);
2603 g_string_printf (nmc->return_text, _("Error: not all active connections found."));
2604 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
2608 next_arg (&arg_num, &arg_ptr);
2612 g_string_printf (nmc->return_text, _("Error: no active connection provided."));
2613 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
2616 queue = g_slist_reverse (queue);
2618 if (nmc->timeout > 0) {
2621 info = g_slice_new0 (ConnectionCbInfo);
2623 info->queue = queue;
2624 info->timeout_id = g_timeout_add_seconds (nmc->timeout, connection_op_timeout_cb, info);
2627 for (iter = queue; iter; iter = g_slist_next (iter)) {
2628 active = iter->data;
2631 g_signal_connect (active,
2632 "notify::" NM_ACTIVE_CONNECTION_STATE,
2633 G_CALLBACK (down_active_connection_state_cb),
2636 /* Now deactivate the connection */
2637 nm_client_deactivate_connection (nmc->client, active, NULL, NULL);
2641 g_strfreev (arg_arr);
2642 return nmc->return_value;
2645 /*----------------------------------------------------------------------------*/
2647 typedef struct NameItem {
2650 const struct NameItem *settings;
2654 static const NameItem nmc_generic_settings [] = {
2655 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2656 { NULL, NULL, NULL, FALSE }
2659 static const NameItem nmc_ethernet_settings [] = {
2660 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2661 { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, TRUE },
2662 { NM_SETTING_802_1X_SETTING_NAME, NULL, NULL, FALSE },
2663 { NM_SETTING_DCB_SETTING_NAME, NULL, NULL, FALSE },
2664 { NULL, NULL, NULL, FALSE }
2667 static const NameItem nmc_infiniband_settings [] = {
2668 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2669 { NM_SETTING_INFINIBAND_SETTING_NAME, NULL, NULL, TRUE },
2670 { NULL, NULL, NULL, FALSE }
2673 static const NameItem nmc_wifi_settings [] = {
2674 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2675 { NM_SETTING_WIRELESS_SETTING_NAME, "wifi", NULL, TRUE },
2676 { NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, "wifi-sec", NULL, FALSE },
2677 { NM_SETTING_802_1X_SETTING_NAME, NULL, NULL, FALSE },
2678 { NULL, NULL, NULL, FALSE }
2681 static const NameItem nmc_wimax_settings [] = {
2682 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2683 { NM_SETTING_WIMAX_SETTING_NAME, NULL, NULL, TRUE },
2684 { NULL, NULL, NULL, FALSE }
2687 static const NameItem nmc_gsm_settings [] = {
2688 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2689 { NM_SETTING_GSM_SETTING_NAME, NULL, NULL, TRUE },
2690 { NM_SETTING_SERIAL_SETTING_NAME, NULL, NULL, FALSE },
2691 { NM_SETTING_PPP_SETTING_NAME, NULL, NULL, FALSE },
2692 { NULL, NULL, NULL, FALSE }
2695 static const NameItem nmc_cdma_settings [] = {
2696 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2697 { NM_SETTING_CDMA_SETTING_NAME, NULL, NULL, TRUE },
2698 { NM_SETTING_SERIAL_SETTING_NAME, NULL, NULL, FALSE },
2699 { NM_SETTING_PPP_SETTING_NAME, NULL, NULL, FALSE },
2700 { NULL, NULL, NULL, FALSE }
2703 static const NameItem nmc_bluetooth_settings [] = {
2704 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2705 { NM_SETTING_BLUETOOTH_SETTING_NAME, NULL, NULL, TRUE },
2706 { NULL, NULL, NULL, FALSE }
2709 static const NameItem nmc_adsl_settings [] = {
2710 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2711 { NM_SETTING_ADSL_SETTING_NAME, NULL, NULL, TRUE },
2712 { NULL, NULL, NULL, FALSE }
2715 /* PPPoE is a base connection type from historical reasons.
2716 * See libnm-core/nm-setting.c:_nm_setting_is_base_type()
2718 static const NameItem nmc_pppoe_settings [] = {
2719 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2720 { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, TRUE },
2721 { NM_SETTING_PPPOE_SETTING_NAME, NULL, NULL, TRUE },
2722 { NM_SETTING_PPP_SETTING_NAME, NULL, NULL, FALSE },
2723 { NM_SETTING_802_1X_SETTING_NAME, NULL, NULL, FALSE },
2724 { NULL, NULL, NULL, FALSE }
2727 static const NameItem nmc_olpc_mesh_settings [] = {
2728 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2729 { NM_SETTING_OLPC_MESH_SETTING_NAME, "olpc-mesh", NULL, TRUE },
2730 { NULL, NULL, NULL, FALSE }
2733 static const NameItem nmc_vpn_settings [] = {
2734 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2735 { NM_SETTING_VPN_SETTING_NAME, NULL, NULL, TRUE },
2736 { NULL, NULL, NULL, FALSE }
2739 static const NameItem nmc_vlan_settings [] = {
2740 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2741 { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, FALSE },
2742 { NM_SETTING_VLAN_SETTING_NAME, NULL, NULL, TRUE },
2743 { NULL, NULL, NULL, FALSE }
2746 static const NameItem nmc_bond_settings [] = {
2747 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2748 { NM_SETTING_BOND_SETTING_NAME, NULL, NULL, TRUE },
2749 { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, FALSE },
2750 { NULL, NULL, NULL, FALSE }
2753 static const NameItem nmc_team_settings [] = {
2754 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2755 { NM_SETTING_TEAM_SETTING_NAME, NULL, NULL, TRUE },
2756 { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, FALSE },
2757 { NULL, NULL, NULL, FALSE }
2760 static const NameItem nmc_bridge_settings [] = {
2761 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2762 { NM_SETTING_BRIDGE_SETTING_NAME, NULL, NULL, TRUE },
2763 { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, FALSE },
2764 { NULL, NULL, NULL, FALSE }
2767 static const NameItem nmc_bond_slave_settings [] = {
2768 { NULL, NULL, NULL, FALSE }
2771 static const NameItem nmc_team_slave_settings [] = {
2772 { NM_SETTING_TEAM_PORT_SETTING_NAME, NULL, NULL, TRUE },
2773 { NULL, NULL, NULL, FALSE }
2776 static const NameItem nmc_bridge_slave_settings [] = {
2777 { NM_SETTING_BRIDGE_PORT_SETTING_NAME, NULL, NULL, TRUE },
2778 { NULL, NULL, NULL, FALSE }
2781 static const NameItem nmc_no_slave_settings [] = {
2782 { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE },
2783 { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE },
2784 { NULL, NULL, NULL, FALSE }
2787 static const NameItem nmc_tun_settings [] = {
2788 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2789 { NM_SETTING_TUN_SETTING_NAME, NULL, NULL, TRUE },
2790 { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, FALSE },
2791 { NULL, NULL, NULL, FALSE }
2794 static const NameItem nmc_ip_tunnel_settings [] = {
2795 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2796 { NM_SETTING_IP_TUNNEL_SETTING_NAME, NULL, NULL, TRUE },
2797 { NULL, NULL, NULL, FALSE }
2800 static const NameItem nmc_macvlan_settings [] = {
2801 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2802 { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, FALSE },
2803 { NM_SETTING_MACVLAN_SETTING_NAME, NULL, NULL, TRUE },
2804 { NULL, NULL, NULL, FALSE }
2807 static const NameItem nmc_vxlan_settings [] = {
2808 { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
2809 { NM_SETTING_VXLAN_SETTING_NAME, NULL, NULL, TRUE },
2810 { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, FALSE },
2811 { NULL, NULL, NULL, FALSE }
2814 /* Available connection types */
2815 static const NameItem nmc_valid_connection_types[] = {
2816 { NM_SETTING_GENERIC_SETTING_NAME, NULL, nmc_generic_settings },
2817 { NM_SETTING_WIRED_SETTING_NAME, "ethernet", nmc_ethernet_settings },
2818 { NM_SETTING_PPPOE_SETTING_NAME, NULL, nmc_pppoe_settings },
2819 { NM_SETTING_WIRELESS_SETTING_NAME, "wifi", nmc_wifi_settings },
2820 { NM_SETTING_WIMAX_SETTING_NAME, NULL, nmc_wimax_settings },
2821 { NM_SETTING_GSM_SETTING_NAME, NULL, nmc_gsm_settings },
2822 { NM_SETTING_CDMA_SETTING_NAME, NULL, nmc_cdma_settings },
2823 { NM_SETTING_INFINIBAND_SETTING_NAME, NULL, nmc_infiniband_settings },
2824 { NM_SETTING_ADSL_SETTING_NAME, NULL, nmc_adsl_settings },
2825 { NM_SETTING_BLUETOOTH_SETTING_NAME, NULL, nmc_bluetooth_settings },
2826 { NM_SETTING_VPN_SETTING_NAME, NULL, nmc_vpn_settings },
2827 { NM_SETTING_OLPC_MESH_SETTING_NAME, "olpc-mesh", nmc_olpc_mesh_settings },
2828 { NM_SETTING_VLAN_SETTING_NAME, NULL, nmc_vlan_settings },
2829 { NM_SETTING_BOND_SETTING_NAME, NULL, nmc_bond_settings },
2830 { NM_SETTING_TEAM_SETTING_NAME, NULL, nmc_team_settings },
2831 { NM_SETTING_BRIDGE_SETTING_NAME, NULL, nmc_bridge_settings },
2832 { "bond-slave", NULL, nmc_bond_slave_settings },
2833 { "team-slave", NULL, nmc_team_slave_settings },
2834 { "bridge-slave", NULL, nmc_bridge_slave_settings },
2835 { "no-slave", NULL, nmc_no_slave_settings },
2836 { NM_SETTING_TUN_SETTING_NAME, NULL, nmc_tun_settings },
2837 { NM_SETTING_IP_TUNNEL_SETTING_NAME, NULL, nmc_ip_tunnel_settings },
2838 { NM_SETTING_MACVLAN_SETTING_NAME, NULL, nmc_macvlan_settings },
2839 { NM_SETTING_VXLAN_SETTING_NAME, NULL, nmc_vxlan_settings },
2840 { NULL, NULL, NULL }
2844 * Return an alias for the 'name' if exists, else return the 'name'.
2845 * The returned string must not be freed.
2848 get_name_alias (const char *name, const NameItem array[])
2850 const NameItem *iter = &array[0];
2855 while (iter && iter->name) {
2856 if (!strcmp (name, iter->name)) {
2868 * Construct a string with names and aliases from the arrays formatted as:
2869 * "name (alias), name, name (alias), name, name"
2871 * Returns: string; the caller is responsible for freeing it.
2874 get_valid_options_string (const NameItem *array, const NameItem *array_slv)
2876 const NameItem *iter = array;
2880 str = g_string_sized_new (150);
2882 for (i = 0; i < 2; i++, iter = array_slv) {
2883 while (iter && iter->name) {
2885 g_string_append (str, ", ");
2887 g_string_append_printf (str, "%s (%s)", iter->name, iter->alias);
2889 g_string_append (str, iter->name);
2893 return g_string_free (str, FALSE);
2896 static const NameItem *
2897 get_valid_settings_array (const char *con_type)
2904 num = G_N_ELEMENTS (nmc_valid_connection_types);
2905 for (i = 0; i < num; i++) {
2906 if (nm_streq0 (con_type, nmc_valid_connection_types[i].name))
2907 return nmc_valid_connection_types[i].settings;
2913 * Check if 'val' is valid string in either array->name or array->alias for
2914 * both array parameters (array & array_slv).
2915 * It accepts shorter string provided they are not ambiguous.
2916 * 'val' == NULL doesn't hurt.
2918 * Returns: pointer to array->name string or NULL on failure.
2919 * The returned string must not be freed.
2922 check_valid_name (const char *val, const NameItem *array, const NameItem *array_slv, GError **error)
2924 const NameItem *iter;
2925 gs_unref_ptrarray GPtrArray *tmp_arr = NULL;
2927 GError *tmp_err = NULL;
2930 g_return_val_if_fail (val, NULL);
2931 g_return_val_if_fail (array, NULL);
2933 /* Create a temporary array that can be used in nmc_string_is_valid() */
2934 tmp_arr = g_ptr_array_sized_new (32);
2936 for (i = 0; i < 2; i++, iter = array_slv) {
2937 while (iter && iter->name) {
2938 g_ptr_array_add (tmp_arr, (gpointer) iter->name);
2940 g_ptr_array_add (tmp_arr, (gpointer) iter->alias);
2944 g_ptr_array_add (tmp_arr, (gpointer) NULL);
2946 /* Check string validity */
2947 str = nmc_string_is_valid (val, (const char **) tmp_arr->pdata, &tmp_err);
2949 if (tmp_err->code == 1)
2950 g_propagate_error (error, tmp_err);
2952 /* We want to handle aliases, so construct own error message */
2953 char *err_str = get_valid_options_string (array, array_slv);
2955 g_set_error (error, 1, 0, _("'%s' not among [%s]"),
2958 g_clear_error (&tmp_err);
2963 /* Return a pointer to the found string in passed 'array' */
2965 for (i = 0; i < 2; i++, iter = array_slv) {
2966 while (iter && iter->name) {
2967 if ( nm_streq (iter->name, str)
2968 || nm_streq0 (iter->alias, str)) {
2975 /* We should not really come here */
2976 g_set_error (error, 1, 0, _("Unknown error"));
2981 is_setting_mandatory (NMConnection *connection, NMSetting *setting)
2983 NMSettingConnection *s_con;
2985 const NameItem *item;
2990 s_con = nm_connection_get_setting_connection (connection);
2992 c_type = nm_setting_connection_get_connection_type (s_con);
2994 name = nm_setting_get_name (setting);
2996 item = get_valid_settings_array (c_type);
2997 while (item && item->name) {
2998 if (!strcmp (name, item->name))
2999 return item->mandatory;
3003 /* Let's give a try to parameters related to slave type */
3004 s_type = nm_setting_connection_get_slave_type (s_con);
3005 slv_type = g_strdup_printf ("%s-slave", s_type ? s_type : "no");
3006 item = get_valid_settings_array (slv_type);
3008 while (item && item->name) {
3009 if (!strcmp (name, item->name))
3010 return item->mandatory;
3017 /*----------------------------------------------------------------------------*/
3020 check_mac (const char *mac,
3022 const char *keyword,
3025 g_return_val_if_fail (type == ARPHRD_ETHER || type == ARPHRD_INFINIBAND, FALSE);
3030 if (!nm_utils_hwaddr_valid (mac, nm_utils_hwaddr_len (type))) {
3031 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3032 _("Error: '%s': '%s' is not a valid %s MAC address."),
3033 keyword, mac, type == ARPHRD_INFINIBAND ? _("InfiniBand") : _("Ethernet"));
3041 check_and_convert_mtu (const char *mtu, guint32 *mtu_int, GError **error)
3043 unsigned long local_mtu_int;
3051 if (!nmc_string_to_uint (mtu, TRUE, 0, G_MAXUINT32, &local_mtu_int)) {
3052 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3053 _("Error: 'mtu': '%s' is not a valid MTU."), mtu);
3057 *mtu_int = (guint32) local_mtu_int;
3062 check_infiniband_parent (const char *parent, GError **error)
3067 if (!nm_utils_iface_valid_name (parent)) {
3068 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3069 _("Error: 'parent': '%s' is not a valid interface name."), parent);
3077 check_infiniband_p_key (const char *p_key, guint32 *p_key_int, GError **error)
3079 unsigned long local_p_key_int;
3080 gboolean p_key_valid = FALSE;
3084 if (!strncmp (p_key, "0x", 2))
3085 p_key_valid = nmc_string_to_uint_base (p_key + 2, 16, TRUE, 0, G_MAXUINT16, &local_p_key_int);
3087 p_key_valid = nmc_string_to_uint (p_key, TRUE, 0, G_MAXUINT16, &local_p_key_int);
3089 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3090 _("Error: 'p-key': '%s' is not a valid InfiniBand P_KEY."), p_key);
3094 *p_key_int = (guint32) local_p_key_int;
3099 check_user_group_id (const char *id, GError **error)
3101 unsigned long int value;
3103 if (!nmc_string_to_uint (id, FALSE, 0, 0, &value)) {
3104 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3105 _("Error: '%s' is not a valid UID/GID."), id);
3113 * check_valid_enumeration:
3114 * @str: string to check against string array @strings
3115 * @strings: string array to check @str againt
3116 * @what: what parameter @str belongs to (used in error message)
3117 * @what_desc: longer description of @what parameter (used in error message)
3118 * @error: location to store an error, or %NULL
3120 * Check whether @str is one of the string of @strings array. It accepts
3121 * shortcuts and normalizes them (@str argument is modified on success).
3123 * Returns: %TRUE on success, %FALSE on failure
3126 check_valid_enumeration (char **str,
3127 const char *strings[],
3129 const char *what_desc,
3133 const char *checked_str;
3138 tmp = g_strstrip (g_strdup (*str));
3139 checked_str = nmc_string_is_valid (tmp, strings, NULL);
3143 *str = g_strdup (checked_str);
3147 options = nmc_util_strv_for_display (strings, TRUE);
3148 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3149 _("Error: '%s': '%s' is not a valid %s %s."),
3150 what, *str, what_desc, options);
3153 return !!checked_str;
3156 /* Checks Wi-Fi mode. */
3158 check_wifi_mode (char **mode, GError **error)
3160 const char *modes[] = { "infrastructure", "ap", "adhoc", NULL };
3162 return check_valid_enumeration (mode, modes, "mode", _("Wi-Fi mode"), error);
3165 /* Checks InfiniBand mode. */
3167 check_infiniband_mode (char **mode, GError **error)
3169 const char *modes[] = { "datagram", "connected", NULL };
3171 return check_valid_enumeration (mode, modes, "mode", _("InfiniBand transport mode"), error);
3174 /* Checks ADSL protocol */
3176 check_adsl_protocol (char **protocol, GError **error)
3178 const char *protos[] = { NM_SETTING_ADSL_PROTOCOL_PPPOA,
3179 NM_SETTING_ADSL_PROTOCOL_PPPOE,
3180 NM_SETTING_ADSL_PROTOCOL_IPOATM,
3183 return check_valid_enumeration (protocol, protos, "protocol", _("ADSL protocol"), error);
3186 /* Checks ADSL encapsulation */
3188 check_adsl_encapsulation (char **encapsulation, GError **error)
3190 const char *modes[] = { NM_SETTING_ADSL_ENCAPSULATION_VCMUX,
3191 NM_SETTING_ADSL_ENCAPSULATION_LLC,
3194 return check_valid_enumeration (encapsulation, modes, "encapsulation", _("ADSL encapsulation"), error);
3197 /* Checks TUN mode. */
3199 check_tun_mode (char **mode, GError **error)
3201 const char *modes[] = { "tun", "tap", NULL };
3203 return check_valid_enumeration (mode, modes, "mode", _("TUN device mode"), error);
3207 check_and_convert_vlan_flags (const char *flags, guint32 *flags_int, GError **error)
3209 unsigned long local_flags_int;
3214 if (!nmc_string_to_uint (flags, TRUE, 0, 7, &local_flags_int)) {
3215 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3216 _("Error: 'flags': '%s' is not valid; use <0-7>."), flags);
3220 *flags_int = (guint32) local_flags_int;
3225 check_and_convert_vlan_prio_maps (const char *prio_map,
3226 NMVlanPriorityMap type,
3227 char ***prio_map_arr,
3230 char **local_prio_map_arr;
3231 GError *local_err = NULL;
3236 if (!(local_prio_map_arr = nmc_vlan_parse_priority_maps (prio_map, type, &local_err))) {
3237 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3238 _("Error: '%s': '%s' is not valid; %s "),
3239 type == NM_VLAN_INGRESS_MAP ? "ingress" : "egress",
3240 prio_map, local_err->message);
3245 *prio_map_arr = local_prio_map_arr;
3250 add_ip4_address_to_connection (NMIPAddress *ip4addr, NMConnection *connection)
3252 NMSettingIPConfig *s_ip4;
3258 s_ip4 = nm_connection_get_setting_ip4_config (connection);
3260 s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new ();
3261 nm_connection_add_setting (connection, NM_SETTING (s_ip4));
3262 g_object_set (s_ip4,
3263 NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL,
3266 ret = nm_setting_ip_config_add_address (s_ip4, ip4addr);
3267 nm_ip_address_unref (ip4addr);
3273 add_ip6_address_to_connection (NMIPAddress *ip6addr, NMConnection *connection)
3275 NMSettingIPConfig *s_ip6;
3281 s_ip6 = nm_connection_get_setting_ip6_config (connection);
3283 s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new ();
3284 nm_connection_add_setting (connection, NM_SETTING (s_ip6));
3285 g_object_set (s_ip6,
3286 NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_MANUAL,
3289 ret = nm_setting_ip_config_add_address (s_ip6, ip6addr);
3290 nm_ip_address_unref (ip6addr);
3296 unique_master_iface_ifname (const GPtrArray *connections,
3297 const char *try_name)
3299 NMConnection *connection;
3301 unsigned int num = 1;
3303 const char *ifname = NULL;
3305 new_name = g_strdup (try_name);
3306 while (i < connections->len) {
3307 connection = NM_CONNECTION (connections->pdata[i]);
3308 ifname = nm_connection_get_interface_name (connection);
3309 if (g_strcmp0 (new_name, ifname) == 0) {
3311 new_name = g_strdup_printf ("%s%d", try_name, num++);
3320 _strip_master_prefix (const char *master, const char *(**func)(NMConnection *))
3325 if (g_str_has_prefix (master, "ifname/")) {
3326 master = master + strlen ("ifname/");
3328 *func = nm_connection_get_interface_name;
3329 } else if (g_str_has_prefix (master, "uuid/")) {
3330 master = master + strlen ("uuid/");
3332 *func = nm_connection_get_uuid;
3333 } else if (g_str_has_prefix (master, "id/")) {
3334 master = master + strlen ("id/");
3336 *func = nm_connection_get_id;
3341 /* normalized_master_for_slave:
3342 * @connections: list af all connections
3343 * @master: UUID, ifname or ID of the master connection
3344 * @type: virtual connection type (bond, team, bridge, ...) or %NULL
3345 * @out_type: type of the connection that matched
3347 * Check whether master is a valid interface name, UUID or ID of some connection,
3348 * possibly of a specified @type.
3349 * First UUID and ifname are checked. If they don't match, ID is checked
3350 * and replaced by UUID on a match.
3352 * Returns: identifier of master connection if found, %NULL otherwise
3355 normalized_master_for_slave (const GPtrArray *connections,
3358 const char **out_type)
3360 NMConnection *connection;
3361 NMSettingConnection *s_con;
3362 const char *con_type = NULL, *id, *uuid, *ifname;
3364 const char *found_by_id = NULL;
3365 const char *out_type_by_id = NULL;
3366 const char *out_master = NULL;
3367 const char *(*func) (NMConnection *) = NULL;
3372 master = _strip_master_prefix (master, &func);
3373 for (i = 0; i < connections->len; i++) {
3374 connection = NM_CONNECTION (connections->pdata[i]);
3375 s_con = nm_connection_get_setting_connection (connection);
3377 con_type = nm_setting_connection_get_connection_type (s_con);
3378 if (type && g_strcmp0 (con_type, type) != 0)
3381 /* There was a prefix; only compare to that type. */
3382 if (g_strcmp0 (master, func (connection)) == 0) {
3384 *out_type = con_type;
3385 if (func == nm_connection_get_id)
3386 out_master = nm_connection_get_uuid (connection);
3388 out_master = master;
3392 id = nm_connection_get_id (connection);
3393 uuid = nm_connection_get_uuid (connection);
3394 ifname = nm_connection_get_interface_name (connection);
3395 if ( g_strcmp0 (master, uuid) == 0
3396 || g_strcmp0 (master, ifname) == 0) {
3397 out_master = master;
3399 *out_type = con_type;
3402 if (!found_by_id && g_strcmp0 (master, id) == 0) {
3403 out_type_by_id = con_type;
3410 out_master = found_by_id;
3412 *out_type = out_type_by_id;
3416 g_print (_("Warning: master='%s' doesn't refer to any existing profile.\n"), master);
3417 out_master = master;
3426 bridge_prop_string_to_uint (const char *str,
3427 const char *nmc_arg,
3429 const char *propname,
3430 unsigned long *out_val,
3433 GParamSpecUInt *pspec;
3435 pspec = (GParamSpecUInt *) g_object_class_find_property (g_type_class_peek (bridge_type),
3437 g_assert (G_IS_PARAM_SPEC_UINT (pspec));
3439 if (!nmc_string_to_uint (str, TRUE, pspec->minimum, pspec->maximum, out_val)) {
3440 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
3441 _("Error: '%s': '%s' is not valid; use <%u-%u>."),
3442 nmc_arg, str, pspec->minimum, pspec->maximum);
3448 #define WORD_YES "yes"
3449 #define WORD_NO "no"
3450 #define WORD_LOC_YES _("yes")
3451 #define WORD_LOC_NO _("no")
3453 prompt_yes_no (gboolean default_yes, char *delim)
3455 static char prompt[128] = { 0 };
3460 snprintf (prompt, sizeof (prompt), "(%s/%s) [%s]%s ",
3461 WORD_LOC_YES, WORD_LOC_NO,
3462 default_yes ? WORD_LOC_YES : WORD_LOC_NO, delim);
3468 normalize_yes_no (char **yes_no)
3471 const char *checked_yes_no;
3472 const char *strv[] = { WORD_LOC_YES, WORD_LOC_NO, NULL };
3474 if (!yes_no || !*yes_no)
3477 tmp = g_strstrip (g_strdup (*yes_no));
3478 checked_yes_no = nmc_string_is_valid (tmp, strv, NULL);
3480 if (g_strcmp0 (checked_yes_no, WORD_LOC_YES) == 0) {
3482 *yes_no = g_strdup (WORD_YES);
3483 } else if (g_strcmp0 (checked_yes_no, WORD_LOC_NO) == 0) {
3485 *yes_no = g_strdup (WORD_NO);
3487 return !!checked_yes_no;
3491 want_provide_opt_args (const char *type, int num)
3494 gboolean ret = TRUE;
3496 /* Ask for optional arguments. */
3497 g_print (ngettext ("There is %d optional argument for '%s' connection type.\n",
3498 "There are %d optional arguments for '%s' connection type.\n", num),
3500 answer = nmc_readline (ngettext ("Do you want to provide it? %s",
3501 "Do you want to provide them? %s", num),
3502 prompt_yes_no (TRUE, NULL));
3503 answer = answer ? g_strstrip (answer) : NULL;
3504 if (answer && matches (answer, WORD_LOC_YES) != 0)
3511 do_questionnaire_ethernet (gboolean ethernet, char **mtu, char **mac, char **cloned_mac)
3514 GError *error = NULL;
3516 /* Ask for optional arguments */
3517 if (ethernet && !want_provide_opt_args (_("ethernet"), 3))
3522 *mtu = nmc_readline (_("MTU [auto]: "));
3523 once_more = !check_and_convert_mtu (*mtu, NULL, &error);
3525 g_print ("%s\n", error->message);
3526 g_clear_error (&error);
3529 } while (once_more);
3533 *mac = nmc_readline (_("MAC [none]: "));
3534 once_more = !check_mac (*mac, ARPHRD_ETHER, "mac", &error);
3536 g_print ("%s\n", error->message);
3537 g_clear_error (&error);
3540 } while (once_more);
3544 *cloned_mac = nmc_readline (_("Cloned MAC [none]: "));
3545 once_more = !check_mac (*cloned_mac, ARPHRD_ETHER, "cloned-mac", &error);
3547 g_print ("%s\n", error->message);
3548 g_clear_error (&error);
3549 g_free (*cloned_mac);
3551 } while (once_more);
3555 #define WORD_DATAGRAM "datagram"
3556 #define WORD_CONNECTED "connected"
3557 #define PROMPT_IB_MODE "(" WORD_DATAGRAM "/" WORD_CONNECTED ") [" WORD_DATAGRAM "]: "
3559 do_questionnaire_infiniband (char **mtu, char **mac, char **mode, char **parent, char **p_key)
3562 GError *error = NULL;
3564 /* Ask for optional arguments */
3565 if (!want_provide_opt_args (_("InfiniBand"), 5))
3570 *mtu = nmc_readline (_("MTU [auto]: "));
3571 once_more = !check_and_convert_mtu (*mtu, NULL, &error);
3573 g_print ("%s\n", error->message);
3574 g_clear_error (&error);
3577 } while (once_more);
3581 *mac = nmc_readline (_("MAC [none]: "));
3582 once_more = !check_mac (*mac, ARPHRD_INFINIBAND, "mac", &error);
3584 g_print ("%s\n", error->message);
3585 g_clear_error (&error);
3588 } while (once_more);
3592 *mode = nmc_readline (_("Transport mode %s"), PROMPT_IB_MODE);
3594 *mode = g_strdup ("datagram");
3595 once_more = !check_infiniband_mode (mode, &error);
3597 g_print ("%s\n", error->message);
3598 g_clear_error (&error);
3601 } while (once_more);
3605 *parent = nmc_readline (_("Parent interface [none]: "));
3606 once_more = !check_infiniband_parent (*parent, &error);
3608 g_print ("%s\n", error->message);
3609 g_clear_error (&error);
3612 } while (once_more);
3616 *p_key = nmc_readline (_("P_KEY [none]: "));
3617 once_more = !check_infiniband_p_key (*p_key, NULL, &error);
3619 g_print ("%s\n", error->message);
3620 g_clear_error (&error);
3623 /* If parent is specified, so has to be P_KEY */
3624 if (!once_more && *parent && !*p_key) {
3626 g_print (_("Error: 'p-key' is mandatory when 'parent' is specified.\n"));
3628 } while (once_more);
3632 #define WORD_INFRA "infrastructure"
3633 #define WORD_AP "ap"
3634 #define WORD_ADHOC "adhoc"
3635 #define PROMPT_WIFI_MODE "(" WORD_INFRA "/" WORD_AP "/" WORD_ADHOC ") [" WORD_INFRA "]: "
3637 do_questionnaire_wifi (char **mtu, char **mac, char **cloned_mac, char **mode)
3640 GError *error = NULL;
3642 /* Ask for optional arguments */
3643 if (!want_provide_opt_args (_("Wi-Fi"), 4))
3646 /* Most optional Wi-Fi arguments are the same as for ethernet. */
3647 do_questionnaire_ethernet (FALSE, mtu, mac, cloned_mac);
3651 *mode = nmc_readline (_("Mode %s"), PROMPT_WIFI_MODE);
3653 *mode = g_strdup ("infrastructure");
3654 once_more = !check_wifi_mode (mode, &error);
3656 g_print ("%s\n", error->message);
3657 g_clear_error (&error);
3660 } while (once_more);
3665 do_questionnaire_wimax (char **mac)
3668 GError *error = NULL;
3670 /* Ask for optional 'wimax' arguments. */
3671 if (!want_provide_opt_args (_("WiMAX"), 1))
3676 *mac = nmc_readline (_("MAC [none]: "));
3677 once_more = !check_mac (*mac, ARPHRD_ETHER, "mac", &error);
3679 g_print ("%s\n", error->message);
3680 g_clear_error (&error);
3683 } while (once_more);
3688 do_questionnaire_pppoe (gboolean echo, char **password, char **service, char **mtu, char **mac)
3691 GError *error = NULL;
3693 /* Ask for optional 'pppoe' arguments. */
3694 if (!want_provide_opt_args (_("PPPoE"), 4))
3698 *password = nmc_readline_echo (echo, _("Password [none]: "));
3700 *service = nmc_readline (_("Service [none]: "));
3704 *mtu = nmc_readline (_("MTU [auto]: "));
3705 once_more = !check_and_convert_mtu (*mtu, NULL, &error);
3707 g_print ("%s\n", error->message);
3708 g_clear_error (&error);
3711 } while (once_more);
3715 *mac = nmc_readline (_("MAC [none]: "));
3716 once_more = !check_mac (*mac, ARPHRD_ETHER, "mac", &error);
3718 g_print ("%s\n", error->message);
3719 g_clear_error (&error);
3722 } while (once_more);
3727 do_questionnaire_mobile (gboolean echo, char **user, char **password)
3729 /* Ask for optional 'gsm' or 'cdma' arguments. */
3730 if (!want_provide_opt_args (_("mobile broadband"), 2))
3734 *user = nmc_readline (_("Username [none]: "));
3736 *password = nmc_readline_echo (echo, _("Password [none]: "));
3739 #define WORD_PANU "panu"
3740 #define WORD_DUN_GSM "dun-gsm"
3741 #define WORD_DUN_CDMA "dun-cdma"
3742 #define PROMPT_BT_TYPE "(" WORD_PANU "/" WORD_DUN_GSM "/" WORD_DUN_CDMA ") [" WORD_PANU "]: "
3744 do_questionnaire_bluetooth (char **bt_type)
3748 /* Ask for optional 'bluetooth' arguments. */
3749 if (!want_provide_opt_args (_("bluetooth"), 1))
3753 const char *types[] = { "dun", "dun-gsm", "dun-cdma", "panu", NULL };
3756 *bt_type = nmc_readline (_("Bluetooth type %s"), PROMPT_BT_TYPE);
3758 *bt_type = g_strdup ("panu");
3759 tmp = nmc_string_is_valid (*bt_type, types, NULL);
3762 g_print (_("Error: 'bt-type': '%s' is not a valid bluetooth type.\n"), *bt_type);
3765 } while (once_more);
3767 *bt_type = g_strdup (tmp);
3772 do_questionnaire_vlan (char **mtu, char **flags, char **ingress, char **egress)
3775 GError *error = NULL;
3777 /* Ask for optional 'vlan' arguments. */
3778 if (!want_provide_opt_args (_("VLAN"), 4))
3783 *mtu = nmc_readline (_("MTU [auto]: "));
3784 once_more = !check_and_convert_mtu (*mtu, NULL, &error);
3786 g_print ("%s\n", error->message);
3787 g_clear_error (&error);
3790 } while (once_more);
3794 *flags = nmc_readline (_("VLAN flags (<0-7>) [none]: "));
3795 once_more = !check_and_convert_vlan_flags (*flags, NULL, &error);
3797 g_print ("%s\n", error->message);
3798 g_clear_error (&error);
3801 } while (once_more);
3805 *ingress = nmc_readline (_("Ingress priority maps [none]: "));
3806 once_more = !check_and_convert_vlan_prio_maps (*ingress, NM_VLAN_INGRESS_MAP, NULL, &error);
3808 g_print ("%s\n", error->message);
3809 g_clear_error (&error);
3812 } while (once_more);
3816 *egress = nmc_readline (_("Egress priority maps [none]: "));
3817 once_more = !check_and_convert_vlan_prio_maps (*egress, NM_VLAN_EGRESS_MAP, NULL, &error);
3819 g_print ("%s\n", error->message);
3820 g_clear_error (&error);
3823 } while (once_more);
3827 #define PROMPT_BOND_MODE _("Bonding mode [balance-rr]: ")
3828 #define WORD_MIIMON "miimon"
3829 #define WORD_ARP "arp"
3830 #define PROMPT_BOND_MON_MODE "(" WORD_MIIMON "/" WORD_ARP ") [" WORD_MIIMON "]: "
3832 do_questionnaire_bond (char **mode, char **primary, char **miimon,
3833 char **downdelay, char **updelay,
3834 char **arpinterval, char **arpiptarget,
3840 GError *error = NULL;
3842 /* Ask for optional 'bond' arguments. */
3843 if (!want_provide_opt_args (_("bond"), 5))
3847 const char *mode_tmp;
3849 *mode = nmc_readline (PROMPT_BOND_MODE);
3851 *mode = g_strdup ("balance-rr");
3852 mode_tmp = nmc_bond_validate_mode (*mode, &error);
3855 *mode = g_strdup (mode_tmp);
3857 g_print ("%s\n", error->message);
3858 g_clear_error (&error);
3860 } while (!mode_tmp);
3863 if (g_strcmp0 (*mode, "active-backup") == 0 && !*primary) {
3865 *primary = nmc_readline (_("Bonding primary interface [none]: "));
3866 once_more = *primary && !nm_utils_iface_valid_name (*primary);
3868 g_print (_("Error: 'primary': '%s' is not a valid interface name.\n"),
3872 } while (once_more);
3876 monitor_mode = nmc_readline (_("Bonding monitoring mode %s"), PROMPT_BOND_MON_MODE);
3878 monitor_mode = g_strdup (WORD_MIIMON);
3879 g_strstrip (monitor_mode);
3880 once_more = matches (monitor_mode, WORD_MIIMON) != 0 && matches (monitor_mode, WORD_ARP) != 0;
3882 g_print (_("Error: '%s' is not a valid monitoring mode; use '%s' or '%s'.\n"),
3883 monitor_mode, WORD_MIIMON, WORD_ARP);
3884 g_free (monitor_mode);
3886 } while (once_more);
3888 if (matches (monitor_mode, WORD_MIIMON) == 0) {
3891 *miimon = nmc_readline (_("Bonding miimon [100]: "));
3892 once_more = *miimon && !nmc_string_to_uint (*miimon, TRUE, 0, G_MAXUINT32, &tmp);
3894 g_print (_("Error: 'miimon': '%s' is not a valid number <0-%u>.\n"),
3895 *miimon, G_MAXUINT32);
3898 } while (once_more);
3902 *downdelay = nmc_readline (_("Bonding downdelay [0]: "));
3903 once_more = *downdelay && !nmc_string_to_uint (*downdelay, TRUE, 0, G_MAXUINT32, &tmp);
3905 g_print (_("Error: 'downdelay': '%s' is not a valid number <0-%u>.\n"),
3906 *downdelay, G_MAXUINT32);
3907 g_free (*downdelay);
3909 } while (once_more);
3913 *updelay = nmc_readline (_("Bonding updelay [0]: "));
3914 once_more = *updelay && !nmc_string_to_uint (*updelay, TRUE, 0, G_MAXUINT32, &tmp);
3916 g_print (_("Error: 'updelay': '%s' is not a valid number <0-%u>.\n"),
3917 *updelay, G_MAXUINT32);
3920 } while (once_more);
3923 if (!*arpinterval) {
3925 *arpinterval = nmc_readline (_("Bonding arp-interval [0]: "));
3926 once_more = *arpinterval && !nmc_string_to_uint (*arpinterval, TRUE, 0, G_MAXUINT32, &tmp);
3928 g_print (_("Error: 'arp-interval': '%s' is not a valid number <0-%u>.\n"),
3929 *arpinterval, G_MAXUINT32);
3930 g_free (*arpinterval);
3932 } while (once_more);
3934 if (!*arpiptarget) {
3935 //FIXME: verify the string
3936 *arpiptarget = nmc_readline (_("Bonding arp-ip-target [none]: "));
3941 && (g_strcmp0 (*mode, "802.3ad") == 0 || g_strcmp0 (*mode, "4") == 0)) {
3943 *lacp_rate = nmc_readline (_("LACP rate ('slow' or 'fast') [slow]: "));
3944 once_more = *lacp_rate && (strcmp (*lacp_rate, "slow") &&
3945 strcmp (*lacp_rate, "0") &&
3946 strcmp (*lacp_rate, "fast") &&
3947 strcmp (*lacp_rate, "1"));
3949 printf (_("Error: 'lacp_rate': '%s' is invalid ('slow' or 'fast').\n"),
3951 g_free (*lacp_rate);
3953 } while (once_more);
3956 g_free (monitor_mode);
3960 do_questionnaire_team_common (const char *type_name, char **config)
3964 GError *error = NULL;
3966 /* Ask for optional arguments. */
3967 if (!want_provide_opt_args (type_name, 1))
3972 *config = nmc_readline (_("Team JSON configuration [none]: "));
3973 once_more = !nmc_team_check_config (*config, &json, &error);
3975 g_print ("Error: %s\n", error->message);
3976 g_clear_error (&error);
3979 } while (once_more);
3985 /* Both team and team-slave curently have just ithe same one optional argument */
3987 do_questionnaire_team (char **config)
3989 do_questionnaire_team_common (_("team"), config);
3993 do_questionnaire_team_slave (char **config)
3995 do_questionnaire_team_common (_("team-slave"), config);
3999 do_questionnaire_bridge (char **stp, char **priority, char **fwd_delay, char **hello_time,
4000 char **max_age, char **ageing_time, char **mcast_snoop, char **mac)
4004 GError *error = NULL;
4006 /* Ask for optional 'bridge' arguments. */
4007 if (!want_provide_opt_args (_("bridge"), 8))
4013 *stp = nmc_readline (_("Enable STP %s"), prompt_yes_no (TRUE, ":"));
4014 *stp = *stp ? *stp : g_strdup ("yes");
4015 normalize_yes_no (stp);
4016 once_more = !nmc_string_to_bool (*stp, &stp_bool, &error);
4018 g_print (_("Error: 'stp': %s.\n"), error->message);
4019 g_clear_error (&error);
4022 } while (once_more);
4026 *priority = nmc_readline (_("STP priority [32768]: "));
4027 *priority = *priority ? *priority : g_strdup ("32768");
4028 once_more = !nmc_string_to_uint (*priority, TRUE, 0, G_MAXUINT16, &tmp);
4030 g_print (_("Error: 'priority': '%s' is not a valid number <0-%d>.\n"),
4031 *priority, G_MAXUINT16);
4034 } while (once_more);
4038 *fwd_delay = nmc_readline (_("Forward delay [15]: "));
4039 *fwd_delay = *fwd_delay ? *fwd_delay : g_strdup ("15");
4040 once_more = !nmc_string_to_uint (*fwd_delay, TRUE, 2, 30, &tmp);
4042 g_print (_("Error: 'forward-delay': '%s' is not a valid number <2-30>.\n"),
4044 g_free (*fwd_delay);
4046 } while (once_more);
4051 *hello_time = nmc_readline (_("Hello time [2]: "));
4052 *hello_time = *hello_time ? *hello_time : g_strdup ("2");
4053 once_more = !nmc_string_to_uint (*hello_time, TRUE, 1, 10, &tmp);
4055 g_print (_("Error: 'hello-time': '%s' is not a valid number <1-10>.\n"),
4057 g_free (*hello_time);
4059 } while (once_more);
4063 *max_age = nmc_readline (_("Max age [20]: "));
4064 *max_age = *max_age ? *max_age : g_strdup ("20");
4065 once_more = !nmc_string_to_uint (*max_age, TRUE, 6, 40, &tmp);
4067 g_print (_("Error: 'max-age': '%s' is not a valid number <6-40>.\n"),
4071 } while (once_more);
4073 if (!*ageing_time) {
4075 *ageing_time = nmc_readline (_("MAC address ageing time [300]: "));
4076 *ageing_time = *ageing_time ? *ageing_time : g_strdup ("300");
4077 once_more = !nmc_string_to_uint (*ageing_time, TRUE, 0, 1000000, &tmp);
4079 g_print (_("Error: 'ageing-time': '%s' is not a valid number <0-1000000>.\n"),
4081 g_free (*ageing_time);
4083 } while (once_more);
4085 if (!*mcast_snoop) {
4086 gboolean mcast_snoop_bool;
4088 *mcast_snoop = nmc_readline (_("Enable IGMP snooping %s"), prompt_yes_no (TRUE, ":"));
4089 *mcast_snoop = *mcast_snoop ? *mcast_snoop : g_strdup ("yes");
4090 normalize_yes_no (mcast_snoop);
4091 once_more = !nmc_string_to_bool (*mcast_snoop, &mcast_snoop_bool, &error);
4093 g_print (_("Error: 'multicast-snooping': %s.\n"), error->message);
4094 g_clear_error (&error);
4095 g_free (*mcast_snoop);
4097 } while (once_more);
4101 *mac = nmc_get_user_input (_("MAC [none]: "));
4102 once_more = !check_mac (*mac, ARPHRD_ETHER, "mac", &error);
4104 g_print ("%s\n", error->message);
4105 g_clear_error (&error);
4108 } while (once_more);
4113 do_questionnaire_bridge_slave (char **priority, char **path_cost, char **hairpin)
4117 GError *error = NULL;
4119 /* Ask for optional 'bridge-slave' arguments. */
4120 if (!want_provide_opt_args (_("bridge-slave"), 3))
4125 *priority = nmc_readline (_("Bridge port priority [32]: "));
4126 *priority = *priority ? *priority : g_strdup ("32");
4127 once_more = !bridge_prop_string_to_uint (*priority, "priority", NM_TYPE_SETTING_BRIDGE_PORT,
4128 NM_SETTING_BRIDGE_PORT_PRIORITY, &tmp, &error);
4130 g_print ("%s\n", error->message);
4131 g_clear_error (&error);
4134 } while (once_more);
4138 *path_cost = nmc_readline (_("Bridge port STP path cost [100]: "));
4139 *path_cost = *path_cost ? *path_cost : g_strdup ("100");
4140 once_more = !bridge_prop_string_to_uint (*path_cost, "path-cost", NM_TYPE_SETTING_BRIDGE_PORT,
4141 NM_SETTING_BRIDGE_PORT_PATH_COST, &tmp, &error);
4143 g_print ("%s\n", error->message);
4144 g_clear_error (&error);
4145 g_free (*path_cost);
4147 } while (once_more);
4150 gboolean hairpin_bool;
4152 *hairpin = nmc_readline (_("Hairpin %s"), prompt_yes_no (TRUE, ":"));
4153 *hairpin = *hairpin ? *hairpin : g_strdup ("yes");
4154 normalize_yes_no (hairpin);
4155 once_more = !nmc_string_to_bool (*hairpin, &hairpin_bool, &error);
4157 g_print (_("Error: 'hairpin': %s.\n"), error->message);
4158 g_clear_error (&error);
4161 } while (once_more);
4166 do_questionnaire_vpn (char **user)
4168 /* Ask for optional 'vpn' arguments. */
4169 if (!want_provide_opt_args (_("VPN"), 1))
4173 *user = nmc_readline (_("Username [none]: "));
4177 do_questionnaire_olpc (char **channel, char **dhcp_anycast)
4181 GError *error = NULL;
4183 /* Ask for optional 'olpc' arguments. */
4184 if (!want_provide_opt_args (_("OLPC Mesh"), 2))
4189 *channel = nmc_readline (_("OLPC Mesh channel [1]: "));
4190 once_more = *channel && !nmc_string_to_uint (*channel, TRUE, 1, 13, &tmp);
4192 g_print (_("Error: 'channel': '%s' is not a valid number <1-13>.\n"),
4196 } while (once_more);
4198 if (!*dhcp_anycast) {
4200 *dhcp_anycast = nmc_readline (_("DHCP anycast MAC address [none]: "));
4201 once_more = !check_mac (*dhcp_anycast, ARPHRD_ETHER, "dhcp-anycast", &error);
4203 g_print ("%s\n", error->message);
4204 g_clear_error (&error);
4205 g_free (*dhcp_anycast);
4207 } while (once_more);
4211 #define PROMPT_ADSL_ENCAP "(" NM_SETTING_ADSL_ENCAPSULATION_VCMUX "/" NM_SETTING_ADSL_ENCAPSULATION_LLC ") [none]: "
4213 do_questionnaire_adsl (gboolean echo, char **password, char **encapsulation)
4216 GError *error = NULL;
4218 /* Ask for optional 'adsl' arguments. */
4219 if (!want_provide_opt_args (_("ADSL"), 2))
4223 *password = nmc_readline_echo (echo, _("Password [none]: "));
4225 if (!*encapsulation) {
4227 *encapsulation = nmc_readline (_("ADSL encapsulation %s"), PROMPT_ADSL_ENCAP);
4228 once_more = !check_adsl_encapsulation (encapsulation, &error);
4230 g_print ("%s\n", error->message);
4231 g_clear_error (&error);
4232 g_free (*encapsulation);
4234 } while (once_more);
4239 do_questionnaire_macvlan (char **tap)
4242 GError *error = NULL;
4244 /* Ask for optional 'macvlan' arguments. */
4245 if (!want_provide_opt_args (_("macvlan"), 1))
4251 *tap = nmc_readline (_("Tap %s"), prompt_yes_no (FALSE, ":"));
4252 *tap = *tap ? *tap : g_strdup ("yes");
4253 normalize_yes_no (tap);
4254 once_more = !nmc_string_to_bool (*tap, &tap_bool, &error);
4256 g_print (_("Error: 'tap': %s.\n"), error->message);
4257 g_clear_error (&error);
4260 } while (once_more);
4265 do_questionnaire_vxlan (char **parent, char **local, char **src_port_min,
4266 char **src_port_max, char **dst_port)
4271 /* Ask for optional 'vxlan' arguments. */
4272 if (!want_provide_opt_args (_("VXLAN"), 5))
4277 *parent = nmc_readline (_("Parent device [none]: "));
4279 && !nm_utils_is_uuid (*parent)
4280 && !nm_utils_iface_valid_name (*parent);
4282 g_print (_("Error: 'dev': '%s' is neither UUID nor interface name.\n"),
4286 } while (once_more);
4291 *local = nmc_readline (_("Local address [none]: "));
4293 && !nm_utils_ipaddr_valid (AF_INET, *local)
4294 && !nm_utils_ipaddr_valid (AF_INET6, *local);
4296 g_print (_("Error: 'local': '%s' is not a valid IP address.\n"),
4300 } while (once_more);
4303 if (!*src_port_min) {
4305 *src_port_min = nmc_readline (_("Minimum source port [0]: "));
4306 *src_port_min = *src_port_min ? *src_port_min : g_strdup ("0");
4307 once_more = !nmc_string_to_uint (*src_port_min, TRUE, 0, 65535, &tmp);
4309 g_print (_("Error: 'source-port-min': '%s' is not a valid number <0-65535>.\n"),
4311 g_free (*src_port_min);
4313 } while (once_more);
4316 if (!*src_port_max) {
4318 *src_port_max = nmc_readline (_("Maximum source port [0]: "));
4319 *src_port_max = *src_port_max ? *src_port_max : g_strdup ("0");
4320 once_more = !nmc_string_to_uint (*src_port_max, TRUE, 0, 65535, &tmp);
4322 g_print (_("Error: 'source-port-max': '%s' is not a valid number <0-65535>.\n"),
4324 g_free (*src_port_max);
4326 } while (once_more);
4331 *dst_port = nmc_readline (_("Destination port [8472]: "));
4332 *dst_port = *dst_port ? *dst_port : g_strdup ("8472");
4333 once_more = !nmc_string_to_uint (*dst_port, TRUE, 0, 65535, &tmp);
4335 g_print (_("Error: 'destination-port': '%s' is not a valid number <0-65535>.\n"),
4339 } while (once_more);
4344 split_address (char* str, char **ip, char **rest)
4352 n1 = strspn (str, " \t");
4353 n2 = strcspn (str+n1, " \t\0") + n1;
4354 n3 = strspn (str+n2, " \t") + n2;
4357 *ip = str[n1] ? str + n1 : NULL;
4358 *rest = str[n3] ? str + n3 : NULL;
4364 ask_for_ip_addresses (NMConnection *connection, int family)
4367 GError *error = NULL;
4368 char *str, *ip, *rest;
4371 NMIPAddress *ipaddr;
4373 if (family == AF_INET)
4374 prompt =_("IPv4 address (IP[/plen]) [none]: ");
4376 prompt =_("IPv6 address (IP[/plen]) [none]: ");
4380 str = nmc_readline ("%s", prompt);
4381 split_address (str, &ip, &rest);
4383 ipaddr = nmc_parse_and_build_address (family, ip, &error);
4385 if (family == AF_INET)
4386 added = add_ip4_address_to_connection (ipaddr, connection);
4388 added = add_ip6_address_to_connection (ipaddr, connection);
4390 g_print (_(" Address successfully added: %s\n"), ip);
4392 g_print (_(" Warning: address already present: %s\n"), ip);
4394 g_print (_(" Warning: ignoring garbage at the end: '%s'\n"), rest);
4396 g_prefix_error (&error, _("Error: "));
4397 g_print ("%s\n", error->message);
4398 g_clear_error (&error);
4408 maybe_ask_for_gateway (NMConnection *connection, int family)
4411 char *str, *gw, *rest;
4413 NMSettingIPConfig *s_ip;
4415 if (family == AF_INET) {
4416 prompt =_("IPv4 gateway [none]: ");
4417 s_ip = nm_connection_get_setting_ip4_config (connection);
4419 prompt =_("IPv6 gateway [none]: ");
4420 s_ip = nm_connection_get_setting_ip6_config (connection);
4424 if ( nm_setting_ip_config_get_num_addresses (s_ip) == 0
4425 || nm_setting_ip_config_get_gateway (s_ip) != NULL)
4430 str = nmc_readline ("%s", prompt);
4431 split_address (str, &gw, &rest);
4433 if (nm_utils_ipaddr_valid (family, gw)) {
4435 NM_SETTING_IP_CONFIG_GATEWAY, gw,
4439 g_print (_("Error: invalid gateway address '%s'\n"), gw);
4447 do_questionnaire_ip (NMConnection *connection)
4451 /* Ask for IP addresses */
4452 answer = nmc_readline (_("Do you want to add IP addresses? %s"), prompt_yes_no (TRUE, NULL));
4453 answer = answer ? g_strstrip (answer) : NULL;
4454 if (answer && matches (answer, WORD_LOC_YES) != 0) {
4460 g_print (_("Press <Enter> to finish adding addresses.\n"));
4462 ask_for_ip_addresses (connection, AF_INET);
4463 maybe_ask_for_gateway (connection, AF_INET);
4464 ask_for_ip_addresses (connection, AF_INET6);
4465 maybe_ask_for_gateway (connection, AF_INET6);
4469 is_setting_valid (NMConnection *connection, const NameItem *valid_settings_main, const NameItem *valid_settings_slave, char *setting)
4471 const char *setting_name;
4473 if (!(setting_name = check_valid_name (setting, valid_settings_main, valid_settings_slave, NULL)))
4475 return nm_connection_get_setting_by_name (connection, setting_name);
4479 is_property_valid (NMSetting *setting, const char *property, GError **error)
4481 char **valid_props = NULL;
4482 const char *prop_name;
4485 valid_props = nmc_setting_get_valid_properties (setting);
4486 prop_name = nmc_string_is_valid (property, (const char **) valid_props, error);
4487 ret = g_strdup (prop_name);
4488 g_strfreev (valid_props);
4492 #define WORD_TUN "tun"
4493 #define WORD_TAP "tap"
4494 #define PROMPT_TUN_MODE "(" WORD_TUN "/" WORD_TAP ") [" WORD_TUN "]: "
4496 do_questionnaire_tun (char **user, char **group,
4497 char **pi, char **vnet_hdr, char **multi_queue)
4500 GError *error = NULL;
4503 /* Ask for optional 'tun' arguments. */
4504 if (!want_provide_opt_args (_("Tun"), 5))
4509 *user = nmc_readline (_("User ID [none]: "));
4512 once_more = !check_user_group_id (*user, &error);
4514 g_print ("%s\n", error->message);
4515 g_clear_error (&error);
4518 } while (once_more);
4522 *group = nmc_readline (_("Group ID [none]: "));
4525 once_more = !check_user_group_id (*group, &error);
4527 g_print ("%s\n", error->message);
4528 g_clear_error (&error);
4531 } while (once_more);
4536 *pi = nmc_readline (_("Enable PI %s"), prompt_yes_no (FALSE, ":"));
4537 *pi = *pi ? *pi : g_strdup ("no");
4538 normalize_yes_no (pi);
4539 once_more = !nmc_string_to_bool (*pi, &b, &error);
4541 g_print (_("Error: 'pi': %s.\n"), error->message);
4542 g_clear_error (&error);
4545 } while (once_more);
4549 *vnet_hdr = nmc_readline (_("Enable VNET header %s"), prompt_yes_no (FALSE, ":"));
4550 *vnet_hdr = *vnet_hdr ? *vnet_hdr : g_strdup ("no");
4551 normalize_yes_no (vnet_hdr);
4552 once_more = !nmc_string_to_bool (*vnet_hdr, &b, &error);
4554 g_print (_("Error: 'vnet-hdr': %s.\n"), error->message);
4555 g_clear_error (&error);
4558 } while (once_more);
4560 if (!*multi_queue) {
4562 *multi_queue = nmc_readline (_("Enable multi queue %s"), prompt_yes_no (FALSE, ":"));
4563 *multi_queue = *multi_queue ? *multi_queue : g_strdup ("no");
4564 normalize_yes_no (multi_queue);
4565 once_more = !nmc_string_to_bool (*multi_queue, &b, &error);
4567 g_print (_("Error: 'multi-queue': %s.\n"), error->message);
4568 g_clear_error (&error);
4569 g_free (*multi_queue);
4571 } while (once_more);
4576 do_questionnaire_ip_tunnel (char **local, char **parent)
4580 /* Ask for optional 'ip-tunnel' arguments. */
4581 if (!want_provide_opt_args (_("IP Tunnel"), 2))
4586 *local = nmc_readline (_("Local endpoint [none]: "));
4589 once_more = !nm_utils_ipaddr_valid (AF_INET, *local)
4590 && !nm_utils_ipaddr_valid (AF_INET6, *local);
4592 g_print (_("Error: 'local': '%s' is not valid; must be an IP address\n"),
4596 } while (once_more);
4601 *parent = nmc_readline (_("Parent device [none]: "));
4603 && !nm_utils_is_uuid (*parent)
4604 && !nm_utils_iface_valid_name (*parent);
4606 g_print (_("Error: 'dev': '%s' is neither UUID nor interface name.\n"),
4610 } while (once_more);
4615 read_connection_properties (NMConnection *connection,
4621 NMSettingConnection *s_con;
4622 const char *con_type;
4623 const char *s_dot_p;
4626 char *slv_type = NULL;
4627 const char *setting_name;
4628 gboolean append = FALSE;
4629 gboolean remove = FALSE;
4630 gboolean success = FALSE;
4631 GError *local = NULL;
4633 s_con = nm_connection_get_setting_connection (connection);
4636 /* First check if we have a slave-type, as this would mean we will not
4637 * have ip properties but possibly others, slave-type specific.
4639 con_type = nm_setting_connection_get_slave_type (s_con);
4643 slv_type = g_strdup_printf ("%s-slave", con_type);
4645 con_type = nm_setting_connection_get_connection_type (s_con);
4647 /* Go through arguments and set properties */
4649 gs_free char *property_name = NULL;
4652 next_arg (&argc, &argv);
4654 next_arg (&argc, &argv);
4657 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4658 _("Error: <setting>.<property> argument is missing."));
4662 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4663 _("Error: value for '%s' is missing."), s_dot_p);
4666 /* Empty string will reset the value to default */
4667 if (value[0] == '\0')
4670 if (s_dot_p[0] == '+') {
4673 } else if (s_dot_p[0] == '-') {
4678 strv = g_strsplit (s_dot_p, ".", 2);
4679 if (g_strv_length (strv) != 2) {
4680 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4681 _("Error: invalid <setting>.<property> '%s'."), s_dot_p);
4685 setting_name = check_valid_name (strv[0], get_valid_settings_array (con_type),
4686 get_valid_settings_array (slv_type), &local);
4687 if (!setting_name) {
4688 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4689 _("Error: invalid or not allowed setting '%s': %s."),
4690 strv[0], local->message);
4691 g_clear_error (&local);
4694 setting = nm_connection_get_setting_by_name (connection, setting_name);
4696 setting = nmc_setting_new_for_name (setting_name);
4698 /* This should really not happen */
4699 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_UNKNOWN,
4700 _("Error: don't know how to create '%s' setting."),
4704 nm_connection_add_setting (connection, setting);
4707 property_name = is_property_valid (setting, strv[1], &local);
4708 if (!property_name) {
4709 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4710 _("Error: invalid property '%s': %s."),
4711 strv[1], local->message);
4712 g_clear_error (&local);
4719 nmc_setting_reset_property (setting, property_name, NULL);
4720 if (!nmc_setting_set_property (setting, property_name, value, &local)) {
4721 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4722 _("Error: failed to modify %s.%s: %s."),
4723 strv[0], strv[1], local->message);
4724 g_clear_error (&local);
4729 * - either empty: remove whole value
4730 * - or specified by index <0-n>: remove item at the index
4731 * - or option name: remove item with the option name
4735 if (nmc_string_to_uint (value, TRUE, 0, G_MAXUINT32, &idx))
4736 nmc_setting_remove_property_option (setting, property_name, NULL, idx, &local);
4738 nmc_setting_remove_property_option (setting, property_name, value, 0, &local);
4740 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4741 _("Error: failed to remove a value from %s.%s: %s."),
4742 strv[0], strv[1], local->message);
4743 g_clear_error (&local);
4747 nmc_setting_reset_property (setting, property_name, NULL);
4763 complete_slave (NMSettingConnection *s_con,
4764 const GPtrArray *all_connections,
4765 const char *slave_type,
4771 char *master_ask = NULL;
4772 const char *checked_master = NULL;
4775 g_print (_("Warning: 'type' is ignored. "
4776 "Use 'nmcli connection add \"%s\" ...' instead."),
4779 if (nm_setting_connection_get_master (s_con)) {
4780 /* Master already set. */
4782 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4783 _("Error: redundant 'master' option."));
4790 master = master_ask = nmc_readline (PROMPT_MASTER);
4792 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4793 _("Error: 'master' is required."));
4796 /* Verify master argument */
4797 checked_master = normalized_master_for_slave (all_connections, master, slave_type, NULL);
4799 /* Change properties in 'connection' setting */
4800 g_object_set (s_con,
4801 NM_SETTING_CONNECTION_MASTER, checked_master,
4804 g_free (master_ask);
4810 complete_connection_by_type (NMConnection *connection,
4811 const char *con_type,
4812 const GPtrArray *all_connections,
4814 gboolean show_secrets,
4819 NMSettingConnection *s_con;
4820 NMSettingGeneric *s_generic;
4821 NMSettingWired *s_wired;
4822 NMSettingInfiniband *s_infiniband;
4823 NMSettingWireless *s_wifi;
4824 NMSettingWimax *s_wimax;
4825 NMSettingPppoe *s_pppoe;
4826 NMSettingGsm *s_gsm;
4827 NMSettingCdma *s_cdma;
4828 NMSettingBluetooth *s_bt;
4829 NMSettingVlan *s_vlan;
4830 NMSettingBond *s_bond;
4831 NMSettingTeam *s_team;
4832 NMSettingTeamPort *s_team_port;
4833 NMSettingBridge *s_bridge;
4834 NMSettingBridgePort *s_bridge_port;
4835 NMSettingVpn *s_vpn;
4836 NMSettingOlpcMesh *s_olpc_mesh;
4837 NMSettingAdsl *s_adsl;
4838 NMSettingTun *s_tun;
4839 NMSettingIPTunnel *s_ip_tunnel;
4840 NMSettingMacvlan *s_macvlan;
4841 NMSettingVxlan *s_vxlan;
4842 const char *slave_type;
4844 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
4846 s_con = nm_connection_get_setting_connection (connection);
4849 if (!strcmp (con_type, NM_SETTING_WIRED_SETTING_NAME)) {
4850 /* Build up the settings required for 'ethernet' */
4851 gboolean success = FALSE;
4852 const char *mtu_c = NULL;
4854 guint32 mtu_int = 0;
4855 const char *mac_c = NULL;
4857 const char *cloned_mac_c = NULL;
4858 char *cloned_mac = NULL;
4859 nmc_arg_t exp_args[] = { {"mtu", TRUE, &mtu_c, FALSE},
4860 {"mac", TRUE, &mac_c, FALSE},
4861 {"cloned-mac", TRUE, &cloned_mac_c, FALSE},
4864 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
4867 /* Also ask for all optional arguments if '--ask' is specified. */
4868 mtu = g_strdup (mtu_c);
4869 mac = g_strdup (mac_c);
4870 cloned_mac = g_strdup (cloned_mac_c);
4872 do_questionnaire_ethernet (TRUE, &mtu, &mac, &cloned_mac);
4874 if (!check_and_convert_mtu (mtu, &mtu_int, error))
4876 if (!check_mac (mac, ARPHRD_ETHER, "mac", error))
4878 if (!check_mac (cloned_mac, ARPHRD_ETHER, "cloned-mac", error))
4881 /* Add ethernet setting */
4882 s_wired = (NMSettingWired *) nm_setting_wired_new ();
4883 nm_connection_add_setting (connection, NM_SETTING (s_wired));
4886 g_object_set (s_wired, NM_SETTING_WIRED_MTU, mtu_int, NULL);
4888 g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, mac, NULL);
4890 g_object_set (s_wired, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, cloned_mac, NULL);
4896 g_free (cloned_mac);
4900 } else if (!strcmp (con_type, NM_SETTING_INFINIBAND_SETTING_NAME)) {
4901 /* Build up the settings required for 'infiniband' */
4902 gboolean success = FALSE;
4903 const char *mtu_c = NULL;
4905 guint32 mtu_int = 0;
4906 const char *mac_c = NULL;
4908 const char *mode_c = NULL;
4910 const char *parent_c = NULL;
4911 char *parent = NULL;
4912 const char *p_key_c = NULL;
4914 guint32 p_key_int = 0;
4915 nmc_arg_t exp_args[] = { {"mtu", TRUE, &mtu_c, FALSE},
4916 {"mac", TRUE, &mac_c, FALSE},
4917 {"transport-mode", TRUE, &mode_c, FALSE},
4918 {"parent", TRUE, &parent_c, FALSE},
4919 {"p-key", TRUE, &p_key_c, FALSE},
4922 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
4925 /* Also ask for all optional arguments if '--ask' is specified. */
4926 mtu = g_strdup (mtu_c);
4927 mac = g_strdup (mac_c);
4928 mode = g_strdup (mode_c);
4929 parent = g_strdup (parent_c);
4930 p_key = g_strdup (p_key_c);
4932 do_questionnaire_infiniband (&mtu, &mac, &mode, &parent, &p_key);
4934 if (!check_and_convert_mtu (mtu, &mtu_int, error))
4936 if (!check_mac (mac, ARPHRD_INFINIBAND, "mac", error))
4938 if (!check_infiniband_mode (&mode, error))
4941 if (!check_infiniband_p_key (p_key, &p_key_int, error))
4943 if (!check_infiniband_parent (parent, error))
4945 } else if (parent) {
4946 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
4947 _("Error: 'parent': not valid without 'p-key'."));
4951 /* Add 'infiniband' setting */
4952 s_infiniband = (NMSettingInfiniband *) nm_setting_infiniband_new ();
4953 nm_connection_add_setting (connection, NM_SETTING (s_infiniband));
4955 g_object_set (s_infiniband, NM_SETTING_INFINIBAND_TRANSPORT_MODE, mode ? mode : "datagram", NULL);
4957 g_object_set (s_infiniband, NM_SETTING_INFINIBAND_MTU, mtu_int, NULL);
4959 g_object_set (s_infiniband, NM_SETTING_INFINIBAND_MAC_ADDRESS, mac, NULL);
4961 g_object_set (s_infiniband, NM_SETTING_INFINIBAND_P_KEY, p_key_int, NULL);
4963 g_object_set (s_infiniband, NM_SETTING_INFINIBAND_PARENT, parent, NULL);
4976 } else if (!strcmp (con_type, NM_SETTING_WIRELESS_SETTING_NAME)) {
4977 /* Build up the settings required for 'wifi' */
4978 gboolean success = FALSE;
4979 char *ssid_ask = NULL;
4980 const char *ssid = NULL;
4982 const char *mtu_c = NULL;
4984 guint32 mtu_int = 0;
4985 const char *mac_c = NULL;
4987 const char *cloned_mac_c = NULL;
4988 char *cloned_mac = NULL;
4989 const char *mode_c = NULL;
4991 nmc_arg_t exp_args[] = { {"ssid", TRUE, &ssid, !ask},
4992 {"mtu", TRUE, &mtu_c, FALSE},
4993 {"mac", TRUE, &mac_c, FALSE},
4994 {"cloned-mac", TRUE, &cloned_mac_c, FALSE},
4995 {"mode", TRUE, &mode_c, FALSE},
4998 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5002 ssid = ssid_ask = nmc_readline (_("SSID: "));
5004 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5005 _("Error: 'ssid' is required."));
5009 /* Also ask for all optional arguments if '--ask' is specified. */
5010 mtu = g_strdup (mtu_c);
5011 mac = g_strdup (mac_c);
5012 cloned_mac = g_strdup (cloned_mac_c);
5013 mode = g_strdup (mode_c);
5015 do_questionnaire_wifi (&mtu, &mac, &cloned_mac, &mode);
5017 if (!check_and_convert_mtu (mtu, &mtu_int, error))
5019 if (!check_mac (mac, ARPHRD_ETHER, "mac", error))
5021 if (!check_mac (cloned_mac, ARPHRD_ETHER, "cloned-mac", error))
5023 if (!check_wifi_mode (&mode, error))
5026 /* Add wifi setting */
5027 s_wifi = (NMSettingWireless *) nm_setting_wireless_new ();
5028 nm_connection_add_setting (connection, NM_SETTING (s_wifi));
5030 ssid_bytes = g_bytes_new (ssid, strlen (ssid));
5031 g_object_set (s_wifi, NM_SETTING_WIRELESS_SSID, ssid_bytes, NULL);
5034 g_object_set (s_wifi, NM_SETTING_WIRELESS_MTU, mtu_int, NULL);
5036 g_object_set (s_wifi, NM_SETTING_WIRELESS_MAC_ADDRESS, mac, NULL);
5038 g_object_set (s_wifi, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, cloned_mac, NULL);
5040 g_object_set (s_wifi, NM_SETTING_WIRELESS_MODE, mode, NULL);
5042 g_bytes_unref (ssid_bytes);
5049 g_free (cloned_mac);
5054 } else if (!strcmp (con_type, NM_SETTING_WIMAX_SETTING_NAME)) {
5055 /* Build up the settings required for 'wimax' */
5056 gboolean success = FALSE;
5057 const char *nsp_name = NULL;
5058 char *nsp_name_ask = NULL;
5059 const char *mac_c = NULL;
5061 nmc_arg_t exp_args[] = { {"nsp", TRUE, &nsp_name, !ask},
5062 {"mac", TRUE, &mac_c, FALSE},
5065 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5068 if (!nsp_name && ask)
5069 nsp_name = nsp_name_ask = nmc_readline (_("WiMAX NSP name: "));
5071 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5072 _("Error: 'nsp' is required."));
5076 /* Also ask for all optional arguments if '--ask' is specified. */
5077 mac = g_strdup (mac_c);
5079 do_questionnaire_wimax (&mac);
5081 if (!check_mac (mac, ARPHRD_ETHER, "mac", error))
5084 /* Add 'wimax' setting */
5085 s_wimax = (NMSettingWimax *) nm_setting_wimax_new ();
5086 nm_connection_add_setting (connection, NM_SETTING (s_wimax));
5087 g_object_set (s_wimax, NM_SETTING_WIMAX_NETWORK_NAME, nsp_name, NULL);
5090 g_object_set (s_wimax, NM_SETTING_WIMAX_MAC_ADDRESS, mac, NULL);
5094 g_free (nsp_name_ask);
5099 } else if (!strcmp (con_type, NM_SETTING_PPPOE_SETTING_NAME)) {
5100 /* Build up the settings required for 'pppoe' */
5101 gboolean success = FALSE;
5102 const char *username = NULL;
5103 char *username_ask = NULL;
5104 const char *password_c = NULL;
5105 char *password = NULL;
5106 const char *service_c = NULL;
5107 char *service = NULL;
5108 const char *mtu_c = NULL;
5110 guint32 mtu_int = 0;
5111 const char *mac_c = NULL;
5113 nmc_arg_t exp_args[] = { {"username", TRUE, &username, !ask},
5114 {"password", TRUE, &password_c, FALSE},
5115 {"service", TRUE, &service_c, FALSE},
5116 {"mtu", TRUE, &mtu_c, FALSE},
5117 {"mac", TRUE, &mac_c, FALSE},
5120 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5123 if (!username && ask)
5124 username = username_ask = nmc_readline (_("PPPoE username: "));
5126 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5127 _("Error: 'username' is required."));
5131 /* Also ask for all optional arguments if '--ask' is specified. */
5132 password = g_strdup (password_c);
5133 service = g_strdup (service_c);
5134 mtu = g_strdup (mtu_c);
5135 mac = g_strdup (mac_c);
5137 do_questionnaire_pppoe (show_secrets, &password, &service, &mtu, &mac);
5139 if (!check_and_convert_mtu (mtu, &mtu_int, error))
5141 if (!check_mac (mac, ARPHRD_ETHER, "mac", error))
5144 /* Add 'pppoe' setting */
5145 s_pppoe = (NMSettingPppoe *) nm_setting_pppoe_new ();
5146 nm_connection_add_setting (connection, NM_SETTING (s_pppoe));
5147 g_object_set (s_pppoe, NM_SETTING_PPPOE_USERNAME, username, NULL);
5148 g_object_set (s_pppoe, NM_SETTING_PPPOE_PASSWORD, password, NULL);
5149 g_object_set (s_pppoe, NM_SETTING_PPPOE_SERVICE, service, NULL);
5151 /* Add ethernet setting */
5152 s_wired = (NMSettingWired *) nm_setting_wired_new ();
5153 nm_connection_add_setting (connection, NM_SETTING (s_wired));
5155 g_object_set (s_wired, NM_SETTING_WIRED_MTU, mtu_int, NULL);
5157 g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, mac, NULL);
5161 g_free (username_ask);
5169 } else if ( !strcmp (con_type, NM_SETTING_GSM_SETTING_NAME)
5170 || !strcmp (con_type, NM_SETTING_CDMA_SETTING_NAME)) {
5171 /* Build up the settings required for 'gsm' or 'cdma' mobile broadband */
5172 gboolean success = FALSE;
5173 const char *apn = NULL;
5174 char *apn_ask = NULL;
5175 const char *user_c = NULL;
5177 const char *password_c = NULL;
5178 char *password = NULL;
5181 nmc_arg_t gsm_args[] = { {NULL}, {NULL}, {NULL}, /* placeholders */
5184 is_gsm = !strcmp (con_type, NM_SETTING_GSM_SETTING_NAME);
5187 gsm_args[i++] = (nmc_arg_t) {"apn", TRUE, &apn, !ask};
5188 gsm_args[i++] = (nmc_arg_t) {"user", TRUE, &user_c, FALSE};
5189 gsm_args[i++] = (nmc_arg_t) {"password", TRUE, &password_c, FALSE};
5190 gsm_args[i++] = (nmc_arg_t) {NULL};
5192 if (!nmc_parse_args (gsm_args, FALSE, &argc, &argv, error))
5195 if (!apn && ask && is_gsm)
5196 apn = apn_ask = nmc_readline (_("APN: "));
5197 if (!apn && is_gsm) {
5198 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5199 _("Error: 'apn' is required."));
5200 goto cleanup_mobile;
5203 /* Also ask for all optional arguments if '--ask' is specified. */
5204 user = g_strdup (user_c);
5205 password = g_strdup (password_c);
5207 do_questionnaire_mobile (show_secrets, &user, &password);
5210 g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_GSM_SETTING_NAME, NULL);
5212 /* Add 'gsm' setting */
5213 s_gsm = (NMSettingGsm *) nm_setting_gsm_new ();
5214 nm_connection_add_setting (connection, NM_SETTING (s_gsm));
5215 g_object_set (s_gsm,
5216 NM_SETTING_GSM_NUMBER, "*99#",
5217 NM_SETTING_GSM_APN, apn,
5218 NM_SETTING_GSM_USERNAME, user,
5219 NM_SETTING_GSM_PASSWORD, password,
5223 g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_CDMA_SETTING_NAME, NULL);
5225 /* Add 'cdma' setting */
5226 s_cdma = (NMSettingCdma *) nm_setting_cdma_new ();
5227 nm_connection_add_setting (connection, NM_SETTING (s_cdma));
5228 g_object_set (s_cdma,
5229 NM_SETTING_CDMA_NUMBER, "#777",
5230 NM_SETTING_CDMA_USERNAME, user,
5231 NM_SETTING_CDMA_PASSWORD, password,
5242 } else if (!strcmp (con_type, NM_SETTING_BLUETOOTH_SETTING_NAME)) {
5243 /* Build up the settings required for 'bluetooth' */
5244 gboolean success = FALSE;
5245 const char *addr = NULL;
5246 char *addr_ask = NULL;
5247 const char *bt_type_c = NULL;
5248 char *bt_type = NULL;
5249 nmc_arg_t exp_args[] = { {"addr", TRUE, &addr, !ask},
5250 {"bt-type", TRUE, &bt_type_c, FALSE},
5253 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5257 addr = addr_ask = nmc_readline (_("Bluetooth device address: "));
5259 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5260 _("Error: 'addr' is required."));
5263 if (!check_mac (addr, ARPHRD_ETHER, "addr", error))
5266 /* Also ask for all optional arguments if '--ask' is specified. */
5267 bt_type = g_strdup (bt_type_c);
5269 do_questionnaire_bluetooth (&bt_type);
5271 /* Default to 'panu' if bt-type is not provided. */
5273 bt_type = g_strdup (NM_SETTING_BLUETOOTH_TYPE_PANU);
5275 /* Add 'bluetooth' setting */
5276 s_bt = (NMSettingBluetooth *) nm_setting_bluetooth_new ();
5277 nm_connection_add_setting (connection, NM_SETTING (s_bt));
5280 g_object_set (s_bt, NM_SETTING_BLUETOOTH_BDADDR, addr, NULL);
5282 /* 'dun' type requires adding 'gsm' or 'cdma' setting */
5283 if ( !strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN)
5284 || !strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN"-gsm")) {
5285 bt_type = g_strdup (NM_SETTING_BLUETOOTH_TYPE_DUN);
5286 s_gsm = (NMSettingGsm *) nm_setting_gsm_new ();
5287 nm_connection_add_setting (connection, NM_SETTING (s_gsm));
5288 g_object_set (s_gsm, NM_SETTING_GSM_NUMBER, "*99#", NULL);
5289 // g_object_set (s_gsm, NM_SETTING_GSM_APN, "FIXME", NULL;
5291 } else if (!strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN"-cdma")) {
5292 bt_type = g_strdup (NM_SETTING_BLUETOOTH_TYPE_DUN);
5293 s_cdma = (NMSettingCdma *) nm_setting_cdma_new ();
5294 nm_connection_add_setting (connection, NM_SETTING (s_cdma));
5295 g_object_set (s_cdma, NM_SETTING_CDMA_NUMBER, "#777", NULL);
5297 } else if (!strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU)) {
5300 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5301 _("Error: 'bt-type': '%s' not valid; use [%s, %s (%s), %s]."),
5302 bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU, NM_SETTING_BLUETOOTH_TYPE_DUN,
5303 NM_SETTING_BLUETOOTH_TYPE_DUN"-gsm", NM_SETTING_BLUETOOTH_TYPE_DUN"-cdma");
5306 g_object_set (s_bt, NM_SETTING_BLUETOOTH_TYPE, bt_type, NULL);
5315 } else if (!strcmp (con_type, NM_SETTING_VLAN_SETTING_NAME)) {
5316 /* Build up the settings required for 'vlan' */
5317 gboolean success = FALSE;
5318 const char *parent = NULL;
5319 char *parent_ask = NULL;
5320 const char *vlan_id = NULL;
5321 char *vlan_id_ask = NULL;
5322 unsigned long id = 0;
5323 const char *flags_c = NULL;
5325 guint32 flags_int = 0;
5326 const char *ingress_c = NULL, *egress_c = NULL;
5327 char *ingress = NULL, *egress = NULL;
5328 char **ingress_arr = NULL, **egress_arr = NULL, **p;
5329 const char *mtu_c = NULL;
5332 gboolean valid_mac = FALSE;
5333 nmc_arg_t exp_args[] = { {"dev", TRUE, &parent, !ask},
5334 {"id", TRUE, &vlan_id, !ask},
5335 {"flags", TRUE, &flags_c, FALSE},
5336 {"ingress", TRUE, &ingress_c, FALSE},
5337 {"egress", TRUE, &egress_c, FALSE},
5338 {"mtu", TRUE, &mtu_c, FALSE},
5341 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5345 parent = parent_ask = nmc_readline (_("VLAN parent device or connection UUID: "));
5347 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5348 _("Error: 'dev' is required."));
5351 if (!vlan_id && ask)
5352 vlan_id = vlan_id_ask = nmc_readline (_("VLAN ID <0-4094>: "));
5354 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5355 _("Error: 'id' is required."));
5359 if (!nmc_string_to_uint (vlan_id, TRUE, 0, 4094, &id)) {
5360 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5361 _("Error: 'id': '%s' is not valid; use <0-4094>."),
5367 if ( !(valid_mac = nm_utils_hwaddr_valid (parent, ETH_ALEN))
5368 && !nm_utils_is_uuid (parent)
5369 && !nm_utils_iface_valid_name (parent)) {
5370 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5371 _("Error: 'dev': '%s' is neither UUID, interface name, nor MAC."),
5376 /* Also ask for all optional arguments if '--ask' is specified. */
5377 mtu = g_strdup (mtu_c);
5378 flags = g_strdup (flags_c);
5379 ingress = g_strdup (ingress_c);
5380 egress = g_strdup (egress_c);
5382 do_questionnaire_vlan (&mtu, &flags, &ingress, &egress);
5384 if (!check_and_convert_mtu (mtu, &mtu_int, error))
5386 if (!check_and_convert_vlan_flags (flags, &flags_int, error))
5388 if (!check_and_convert_vlan_prio_maps (ingress, NM_VLAN_INGRESS_MAP, &ingress_arr, error))
5390 if (!check_and_convert_vlan_prio_maps (egress, NM_VLAN_EGRESS_MAP, &egress_arr, error))
5393 /* Add 'vlan' setting */
5394 s_vlan = (NMSettingVlan *) nm_setting_vlan_new ();
5395 nm_connection_add_setting (connection, NM_SETTING (s_vlan));
5397 /* Add 'wired' setting if necessary */
5398 if (mtu || valid_mac) {
5399 s_wired = (NMSettingWired *) nm_setting_wired_new ();
5400 nm_connection_add_setting (connection, NM_SETTING (s_wired));
5403 g_object_set (s_wired, NM_SETTING_WIRED_MTU, mtu_int, NULL);
5405 g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, parent, NULL);
5408 /* Set 'vlan' properties */
5410 g_object_set (s_vlan, NM_SETTING_VLAN_PARENT, parent, NULL);
5412 g_object_set (s_vlan, NM_SETTING_VLAN_ID, id, NULL);
5415 g_object_set (s_vlan, NM_SETTING_VLAN_FLAGS, flags_int, NULL);
5416 for (p = ingress_arr; p && *p; p++)
5417 nm_setting_vlan_add_priority_str (s_vlan, NM_VLAN_INGRESS_MAP, *p);
5418 for (p = egress_arr; p && *p; p++)
5419 nm_setting_vlan_add_priority_str (s_vlan, NM_VLAN_EGRESS_MAP, *p);
5427 g_free (parent_ask);
5428 g_free (vlan_id_ask);
5429 g_strfreev (ingress_arr);
5430 g_strfreev (egress_arr);
5434 } else if (!strcmp (con_type, NM_SETTING_BOND_SETTING_NAME)) {
5435 /* Build up the settings required for 'bond' */
5436 gboolean success = FALSE;
5437 const char *ifname = NULL;
5438 const char *bond_mode_c = NULL;
5439 char *bond_mode = NULL;
5440 const char *bond_primary_c = NULL;
5441 char *bond_primary = NULL;
5442 const char *bond_miimon_c = NULL;
5443 char *bond_miimon = NULL;
5444 const char *bond_downdelay_c = NULL;
5445 char *bond_downdelay = NULL;
5446 const char *bond_updelay_c = NULL;
5447 char *bond_updelay = NULL;
5448 const char *bond_arpinterval_c = NULL;
5449 char *bond_arpinterval = NULL;
5450 const char *bond_arpiptarget_c = NULL;
5451 char *bond_arpiptarget = NULL;
5452 const char *bond_lacp_rate_c = NULL;
5453 char *bond_lacp_rate = NULL;
5454 nmc_arg_t exp_args[] = { {"mode", TRUE, &bond_mode_c, FALSE},
5455 {"primary", TRUE, &bond_primary_c, FALSE},
5456 {"miimon", TRUE, &bond_miimon_c, FALSE},
5457 {"downdelay", TRUE, &bond_downdelay_c, FALSE},
5458 {"updelay", TRUE, &bond_updelay_c, FALSE},
5459 {"arp-interval", TRUE, &bond_arpinterval_c, FALSE},
5460 {"arp-ip-target", TRUE, &bond_arpiptarget_c, FALSE},
5461 {"lacp-rate", TRUE, &bond_lacp_rate_c, FALSE},
5464 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5467 /* Also ask for all optional arguments if '--ask' is specified. */
5468 bond_mode = g_strdup (bond_mode_c);
5469 bond_primary = g_strdup (bond_primary_c);
5470 bond_miimon = g_strdup (bond_miimon_c);
5471 bond_downdelay = g_strdup (bond_downdelay_c);
5472 bond_updelay = g_strdup (bond_updelay_c);
5473 bond_arpinterval = g_strdup (bond_arpinterval_c);
5474 bond_arpiptarget = g_strdup (bond_arpiptarget_c);
5475 bond_lacp_rate = g_strdup (bond_lacp_rate_c);
5477 do_questionnaire_bond (&bond_mode, &bond_primary, &bond_miimon,
5478 &bond_downdelay, &bond_updelay,
5479 &bond_arpinterval, &bond_arpiptarget,
5482 /* Generate ifname if connection doesn't have one */
5483 ifname = nm_setting_connection_get_interface_name (s_con);
5485 char *bond_ifname = unique_master_iface_ifname (all_connections, "nm-bond");
5487 g_object_set (s_con,
5488 NM_SETTING_CONNECTION_INTERFACE_NAME, bond_ifname,
5490 g_free (bond_ifname);
5493 /* Add 'bond' setting */
5494 s_bond = (NMSettingBond *) nm_setting_bond_new ();
5495 nm_connection_add_setting (connection, NM_SETTING (s_bond));
5497 /* Set bond options */
5501 if (!(bm = nmc_bond_validate_mode (bond_mode, &err))) {
5502 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5503 _("Error: 'mode': %s."), err->message);
5504 g_clear_error (&err);
5507 nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_MODE, bm);
5510 if (!nm_utils_iface_valid_name (bond_primary)) {
5511 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5512 _("Error: 'primary': '%s' is not a valid interface name."),
5516 nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_PRIMARY, bond_primary);
5519 nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_MIIMON, bond_miimon);
5520 if (bond_downdelay && strcmp (bond_downdelay, "0") != 0)
5521 nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY, bond_downdelay);
5522 if (bond_updelay && strcmp (bond_updelay, "0") != 0)
5523 nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_UPDELAY, bond_updelay);
5524 if (bond_arpinterval && strcmp (bond_arpinterval, "0") != 0)
5525 nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_ARP_INTERVAL, bond_arpinterval);
5526 if (bond_arpiptarget)
5527 nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET, bond_arpiptarget);
5529 nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_LACP_RATE, bond_lacp_rate);
5534 g_free (bond_primary);
5535 g_free (bond_miimon);
5536 g_free (bond_downdelay);
5537 g_free (bond_updelay);
5538 g_free (bond_arpinterval);
5539 g_free (bond_arpiptarget);
5540 g_free (bond_lacp_rate);
5544 } else if (!strcmp (con_type, "bond-slave")) {
5545 /* Slave types without any specific settings ('bond-slave') */
5546 const char *master = NULL;
5547 const char *type = NULL;
5548 nmc_arg_t exp_args[] = { {"master", TRUE, &master, FALSE},
5549 {"type", TRUE, &type, FALSE},
5552 /* Set global variables for use in TAB completion */
5553 nmc_tab_completion.con_type = NM_SETTING_BOND_SETTING_NAME;
5555 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5558 if (!complete_slave (s_con, all_connections, slave_type, master, type, ask, error))
5561 /* Change properties in 'connection' setting */
5562 g_object_set (s_con,
5563 NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
5564 NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BOND_SETTING_NAME,
5567 /* Add ethernet setting */
5568 s_wired = (NMSettingWired *) nm_setting_wired_new ();
5569 nm_connection_add_setting (connection, NM_SETTING (s_wired));
5571 } else if (!strcmp (con_type, NM_SETTING_TEAM_SETTING_NAME)) {
5572 /* Build up the settings required for 'team' */
5573 gboolean success = FALSE;
5574 const char *ifname = NULL;
5575 const char *config_c = NULL;
5576 char *config = NULL;
5578 nmc_arg_t exp_args[] = { {"config", TRUE, &config_c, FALSE},
5581 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5584 /* Also ask for all optional arguments if '--ask' is specified. */
5585 config = g_strdup (config_c);
5587 do_questionnaire_team (&config);
5589 /* Generate ifname if conneciton doesn't have one */
5590 ifname = nm_setting_connection_get_interface_name (s_con);
5592 char *team_ifname = unique_master_iface_ifname (all_connections, "nm-team");
5594 g_object_set (s_con,
5595 NM_SETTING_CONNECTION_INTERFACE_NAME, team_ifname,
5597 g_free (team_ifname);
5600 /* Add 'team' setting */
5601 s_team = (NMSettingTeam *) nm_setting_team_new ();
5602 nm_connection_add_setting (connection, NM_SETTING (s_team));
5604 if (!nmc_team_check_config (config, &json, error)) {
5605 g_prefix_error (error, _("Error: "));
5609 /* Set team options */
5610 g_object_set (s_team, NM_SETTING_TEAM_CONFIG, json, NULL);
5619 } else if (!strcmp (con_type, "team-slave")) {
5620 /* Build up the settings required for 'team-slave' */
5621 gboolean success = FALSE;
5622 const char *master = NULL;
5623 char *master_ask = NULL;
5624 const char *type = NULL;
5625 const char *config_c = NULL;
5626 char *config = NULL;
5628 nmc_arg_t exp_args[] = { {"master", TRUE, &master, FALSE},
5629 {"type", TRUE, &type, FALSE},
5630 {"config", TRUE, &config_c, FALSE},
5633 /* Set global variables for use in TAB completion */
5634 nmc_tab_completion.con_type = NM_SETTING_TEAM_SETTING_NAME;
5636 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5639 if (!complete_slave (s_con, all_connections, slave_type, master, type, ask, error))
5642 /* Also ask for all optional arguments if '--ask' is specified. */
5643 config = g_strdup (config_c);
5645 do_questionnaire_team_slave (&config);
5647 /* Add 'team-port' setting */
5648 s_team_port = (NMSettingTeamPort *) nm_setting_team_port_new ();
5649 nm_connection_add_setting (connection, NM_SETTING (s_team_port));
5651 if (!nmc_team_check_config (config, &json, error)) {
5652 g_prefix_error (error, _("Error: "));
5653 goto cleanup_team_slave;
5656 /* Set team-port options */
5657 g_object_set (s_team_port, NM_SETTING_TEAM_PORT_CONFIG, json, NULL);
5661 g_free (master_ask);
5667 /* Change properties in 'connection' setting */
5668 g_object_set (s_con,
5669 NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
5670 NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_TEAM_SETTING_NAME,
5673 /* Add ethernet setting */
5674 s_wired = (NMSettingWired *) nm_setting_wired_new ();
5675 nm_connection_add_setting (connection, NM_SETTING (s_wired));
5677 } else if (!strcmp (con_type, NM_SETTING_BRIDGE_SETTING_NAME)) {
5678 /* Build up the settings required for 'bridge' */
5679 gboolean success = FALSE;
5680 const char *ifname = NULL;
5681 const char *stp_c = NULL;
5683 const char *priority_c = NULL;
5684 char *priority = NULL;
5685 const char *fwd_delay_c = NULL;
5686 char *fwd_delay = NULL;
5687 const char *hello_time_c = NULL;
5688 char *hello_time = NULL;
5689 const char *max_age_c = NULL;
5690 char *max_age = NULL;
5691 const char *ageing_time_c = NULL;
5692 char *ageing_time = NULL;
5693 const char *mcast_snoop_c = NULL;
5694 char *mcast_snoop = NULL;
5695 gboolean stp_bool, mcast_snoop_bool;
5696 unsigned long stp_prio_int, fwd_delay_int, hello_time_int,
5697 max_age_int, ageing_time_int;
5698 const char *mac_c = NULL;
5700 nmc_arg_t exp_args[] = { {"stp", TRUE, &stp_c, FALSE},
5701 {"priority", TRUE, &priority_c, FALSE},
5702 {"forward-delay", TRUE, &fwd_delay_c, FALSE},
5703 {"hello-time", TRUE, &hello_time_c, FALSE},
5704 {"max-age", TRUE, &max_age_c, FALSE},
5705 {"ageing-time", TRUE, &ageing_time_c, FALSE},
5706 {"multicast-snooping", TRUE, &mcast_snoop_c, FALSE},
5707 {"mac", TRUE, &mac_c, FALSE},
5710 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5713 /* Also ask for all optional arguments if '--ask' is specified. */
5714 stp = g_strdup (stp_c);
5715 priority = g_strdup (priority_c);
5716 fwd_delay = g_strdup (fwd_delay_c);
5717 hello_time = g_strdup (hello_time_c);
5718 max_age = g_strdup (max_age_c);
5719 ageing_time = g_strdup (ageing_time_c);
5720 mcast_snoop = g_strdup (mcast_snoop_c);
5721 mac = g_strdup (mac_c);
5723 do_questionnaire_bridge (&stp, &priority, &fwd_delay, &hello_time,
5724 &max_age, &ageing_time, &mcast_snoop, &mac);
5726 /* Generate ifname if conneciton doesn't have one */
5727 ifname = nm_setting_connection_get_interface_name (s_con);
5729 char *bridge_ifname = unique_master_iface_ifname (all_connections, "nm-bridge");
5731 g_object_set (s_con,
5732 NM_SETTING_CONNECTION_INTERFACE_NAME, bridge_ifname,
5734 g_free (bridge_ifname);
5738 GError *tmp_err = NULL;
5739 if (!nmc_string_to_bool (stp, &stp_bool, &tmp_err)) {
5740 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5741 _("Error: 'stp': %s."), tmp_err->message);
5742 g_clear_error (&tmp_err);
5743 goto cleanup_bridge;
5747 GError *tmp_err = NULL;
5748 if (!nmc_string_to_bool (mcast_snoop, &mcast_snoop_bool, &tmp_err)) {
5749 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5750 _("Error: 'multicast-snooping': %s."), tmp_err->message);
5751 g_clear_error (&tmp_err);
5752 goto cleanup_bridge;
5756 /* Add 'bond' setting */
5757 /* Must be done *before* bridge_prop_string_to_uint() so that the type is known */
5758 s_bridge = (NMSettingBridge *) nm_setting_bridge_new ();
5759 nm_connection_add_setting (connection, NM_SETTING (s_bridge));
5762 if (!bridge_prop_string_to_uint (priority, "priority", NM_TYPE_SETTING_BRIDGE,
5763 NM_SETTING_BRIDGE_PRIORITY, &stp_prio_int, error))
5764 goto cleanup_bridge;
5766 if (!bridge_prop_string_to_uint (fwd_delay, "forward-delay", NM_TYPE_SETTING_BRIDGE,
5767 NM_SETTING_BRIDGE_FORWARD_DELAY, &fwd_delay_int, error))
5768 goto cleanup_bridge;
5770 if (!bridge_prop_string_to_uint (hello_time, "hello-time", NM_TYPE_SETTING_BRIDGE,
5771 NM_SETTING_BRIDGE_HELLO_TIME, &hello_time_int, error))
5772 goto cleanup_bridge;
5774 if (!bridge_prop_string_to_uint (max_age, "max-age", NM_TYPE_SETTING_BRIDGE,
5775 NM_SETTING_BRIDGE_MAX_AGE, &max_age_int, error))
5776 goto cleanup_bridge;
5778 if (!bridge_prop_string_to_uint (ageing_time, "ageing-time", NM_TYPE_SETTING_BRIDGE,
5779 NM_SETTING_BRIDGE_AGEING_TIME, &ageing_time_int, error))
5780 goto cleanup_bridge;
5781 if (!check_mac (mac, ARPHRD_ETHER, "mac", error))
5782 goto cleanup_bridge;
5784 /* Set bridge options */
5786 g_object_set (s_bridge, NM_SETTING_BRIDGE_STP, stp_bool, NULL);
5788 g_object_set (s_bridge, NM_SETTING_BRIDGE_PRIORITY, stp_prio_int, NULL);
5790 g_object_set (s_bridge, NM_SETTING_BRIDGE_FORWARD_DELAY, fwd_delay_int, NULL);
5792 g_object_set (s_bridge, NM_SETTING_BRIDGE_HELLO_TIME, hello_time_int, NULL);
5794 g_object_set (s_bridge, NM_SETTING_BRIDGE_MAX_AGE, max_age_int, NULL);
5796 g_object_set (s_bridge, NM_SETTING_BRIDGE_AGEING_TIME, ageing_time_int, NULL);
5798 g_object_set (s_bridge, NM_SETTING_BRIDGE_MULTICAST_SNOOPING, mcast_snoop_bool, NULL);
5800 g_object_set (s_bridge, NM_SETTING_BRIDGE_MAC_ADDRESS, mac, NULL);
5807 g_free (hello_time);
5809 g_free (ageing_time);
5810 g_free (mcast_snoop);
5815 } else if (!strcmp (con_type, "bridge-slave")) {
5816 /* Build up the settings required for 'bridge-slave' */
5817 gboolean success = FALSE;
5818 const char *master = NULL;
5819 char *master_ask = NULL;
5820 const char *type = NULL;
5821 const char *priority_c = NULL;
5822 char *priority = NULL;
5823 const char *path_cost_c = NULL;
5824 char *path_cost = NULL;
5825 const char *hairpin_c = NULL;
5826 char *hairpin = NULL;
5827 unsigned long prio_int, path_cost_int;
5828 gboolean hairpin_bool;
5829 nmc_arg_t exp_args[] = { {"master", TRUE, &master, FALSE},
5830 {"type", TRUE, &type, FALSE},
5831 {"priority", TRUE, &priority_c, FALSE},
5832 {"path-cost", TRUE, &path_cost_c, FALSE},
5833 {"hairpin", TRUE, &hairpin_c, FALSE},
5836 /* Set global variables for use in TAB completion */
5837 nmc_tab_completion.con_type = NM_SETTING_BRIDGE_SETTING_NAME;
5839 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5842 if (!complete_slave (s_con, all_connections, slave_type, master, type, ask, error))
5845 /* Add 'bridge-port' setting */
5846 /* Must be done *before* bridge_prop_string_to_uint() so that the type is known */
5847 s_bridge_port = (NMSettingBridgePort *) nm_setting_bridge_port_new ();
5848 nm_connection_add_setting (connection, NM_SETTING (s_bridge_port));
5850 /* Also ask for all optional arguments if '--ask' is specified. */
5851 priority = g_strdup (priority_c);
5852 path_cost = g_strdup (path_cost_c);
5853 hairpin = g_strdup (hairpin_c);
5855 do_questionnaire_bridge_slave (&priority, &path_cost, &hairpin);
5858 if (!bridge_prop_string_to_uint (priority, "priority", NM_TYPE_SETTING_BRIDGE_PORT,
5859 NM_SETTING_BRIDGE_PORT_PRIORITY, &prio_int, error))
5860 goto cleanup_bridge_slave;
5862 if (!bridge_prop_string_to_uint (path_cost, "path-cost", NM_TYPE_SETTING_BRIDGE_PORT,
5863 NM_SETTING_BRIDGE_PORT_PATH_COST, &path_cost_int, error))
5864 goto cleanup_bridge_slave;
5866 GError *tmp_err = NULL;
5867 if (!nmc_string_to_bool (hairpin, &hairpin_bool, &tmp_err)) {
5868 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5869 _("Error: 'hairpin': %s."), tmp_err->message);
5870 g_clear_error (&tmp_err);
5871 goto cleanup_bridge_slave;
5876 g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_PRIORITY, prio_int, NULL);
5878 g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_PATH_COST, path_cost_int, NULL);
5880 g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE, hairpin_bool, NULL);
5883 cleanup_bridge_slave:
5884 g_free (master_ask);
5891 /* Change properties in 'connection' setting */
5892 g_object_set (s_con,
5893 NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
5894 NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BRIDGE_SETTING_NAME,
5897 /* Add ethernet setting */
5898 s_wired = (NMSettingWired *) nm_setting_wired_new ();
5899 nm_connection_add_setting (connection, NM_SETTING (s_wired));
5901 } else if (!strcmp (con_type, NM_SETTING_VPN_SETTING_NAME)) {
5902 /* Build up the settings required for 'vpn' */
5903 gboolean success = FALSE;
5904 const char *vpn_type = NULL;
5905 char *vpn_type_ask = NULL;
5906 const char *user_c = NULL;
5909 char *service_type = NULL;
5910 nmc_arg_t exp_args[] = { {"vpn-type", TRUE, &vpn_type, !ask},
5911 {"user", TRUE, &user_c, FALSE},
5914 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5917 if (!vpn_type && ask)
5918 vpn_type = vpn_type_ask = nmc_readline (PROMPT_VPN_TYPE);
5920 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5921 _("Error: 'vpn-type' is required."));
5925 vpn_type = g_strstrip (vpn_type_ask);
5927 if (!(st = nmc_string_is_valid (vpn_type, nmc_known_vpns, NULL))) {
5928 g_print (_("Warning: 'vpn-type': %s not known.\n"), vpn_type);
5931 service_type = g_strdup_printf ("%s.%s", NM_DBUS_INTERFACE, st);
5933 /* Also ask for all optional arguments if '--ask' is specified. */
5934 user = g_strdup (user_c);
5936 do_questionnaire_vpn (&user);
5938 /* Add 'vpn' setting */
5939 s_vpn = (NMSettingVpn *) nm_setting_vpn_new ();
5940 nm_connection_add_setting (connection, NM_SETTING (s_vpn));
5942 g_object_set (s_vpn, NM_SETTING_VPN_SERVICE_TYPE, service_type, NULL);
5943 g_object_set (s_vpn, NM_SETTING_VPN_USER_NAME, user, NULL);
5947 g_free (vpn_type_ask);
5948 g_free (service_type);
5953 } else if (!strcmp (con_type, NM_SETTING_OLPC_MESH_SETTING_NAME)) {
5954 /* Build up the settings required for 'olpc' */
5955 gboolean success = FALSE;
5956 char *ssid_ask = NULL;
5957 const char *ssid = NULL;
5959 const char *channel_c = NULL;
5960 char *channel = NULL;
5962 const char *dhcp_anycast_c = NULL;
5963 char *dhcp_anycast = NULL;
5964 nmc_arg_t exp_args[] = { {"ssid", TRUE, &ssid, !ask},
5965 {"channel", TRUE, &channel_c, FALSE},
5966 {"dhcp-anycast", TRUE, &dhcp_anycast_c, FALSE},
5969 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5973 ssid = ssid_ask = nmc_readline (_("SSID: "));
5975 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5976 _("Error: 'ssid' is required."));
5980 /* Also ask for all optional arguments if '--ask' is specified. */
5981 channel = g_strdup (channel_c);
5982 dhcp_anycast = g_strdup (dhcp_anycast_c);
5984 do_questionnaire_olpc (&channel, &dhcp_anycast);
5987 if (!nmc_string_to_uint (channel, TRUE, 1, 13, &chan)) {
5988 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5989 _("Error: 'channel': '%s' is not valid; use <1-13>."),
5994 if (!check_mac (dhcp_anycast, ARPHRD_ETHER, "dhcp-anycast", error))
5997 /* Add OLPC mesh setting */
5998 s_olpc_mesh = (NMSettingOlpcMesh *) nm_setting_olpc_mesh_new ();
5999 nm_connection_add_setting (connection, NM_SETTING (s_olpc_mesh));
6001 ssid_bytes = g_bytes_new (ssid, strlen (ssid));
6002 g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_SSID, ssid_bytes, NULL);
6004 g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_CHANNEL, chan, NULL);
6006 g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_CHANNEL, 1, NULL);
6008 g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS, dhcp_anycast, NULL);
6009 g_bytes_unref (ssid_bytes);
6015 g_free (dhcp_anycast);
6019 } else if (!strcmp (con_type, NM_SETTING_ADSL_SETTING_NAME)) {
6020 /* Build up the settings required for 'adsl' */
6021 gboolean success = FALSE;
6022 char *username_ask = NULL;
6023 const char *username = NULL;
6024 char *protocol_ask = NULL, *protocol = NULL;
6025 const char *protocol_c = NULL;
6026 const char *password_c = NULL;
6027 char *password = NULL;
6028 const char *encapsulation_c = NULL;
6029 char *encapsulation = NULL;
6030 nmc_arg_t exp_args[] = { {"username", TRUE, &username, !ask},
6031 {"protocol", TRUE, &protocol_c, !ask},
6032 {"password", TRUE, &password_c, FALSE},
6033 {"encapsulation", TRUE, &encapsulation_c, FALSE},
6036 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
6039 if (!username && ask)
6040 username = username_ask = nmc_readline (_("Username: "));
6042 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6043 _("Error: 'username' is required."));
6047 #define PROMPT_ADSL_PROTO "(" NM_SETTING_ADSL_PROTOCOL_PPPOA "/" NM_SETTING_ADSL_PROTOCOL_PPPOE "/" NM_SETTING_ADSL_PROTOCOL_IPOATM "): "
6048 if (!protocol_c && ask)
6049 protocol_c = protocol_ask = nmc_readline (_("Protocol %s"), PROMPT_ADSL_PROTO);
6051 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6052 _("Error: 'protocol' is required."));
6055 protocol = g_strdup (protocol_c);
6056 if (!check_adsl_protocol (&protocol, error))
6059 /* Also ask for all optional arguments if '--ask' is specified. */
6060 password = g_strdup (password_c);
6061 encapsulation = g_strdup (encapsulation_c);
6063 do_questionnaire_adsl (show_secrets, &password, &encapsulation);
6065 if (!check_adsl_encapsulation (&encapsulation, error))
6068 /* Add ADSL setting */
6069 s_adsl = (NMSettingAdsl *) nm_setting_adsl_new ();
6070 nm_connection_add_setting (connection, NM_SETTING (s_adsl));
6072 g_object_set (s_adsl,
6073 NM_SETTING_ADSL_USERNAME, username,
6074 NM_SETTING_ADSL_PROTOCOL, protocol,
6075 NM_SETTING_ADSL_PASSWORD, password,
6076 NM_SETTING_ADSL_ENCAPSULATION, encapsulation,
6081 g_free (username_ask);
6084 g_free (protocol_ask);
6085 g_free (encapsulation);
6090 } else if (!strcmp (con_type, NM_SETTING_MACVLAN_SETTING_NAME)) {
6091 /* Build up the settings required for 'macvlan' */
6092 gboolean success = FALSE;
6093 const char *parent = NULL;
6094 char *parent_ask = NULL;
6095 const char *mode = NULL;
6096 char *mode_ask = NULL;
6097 const char *tap_c = NULL;
6099 NMSettingMacvlanMode mode_enum;
6100 gboolean valid_mac = FALSE;
6101 gboolean tap_bool = FALSE;
6102 nmc_arg_t exp_args[] = { {"dev", TRUE, &parent, !ask},
6103 {"mode", TRUE, &mode, !ask},
6104 {"tap", TRUE, &tap_c, FALSE},
6107 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
6111 parent = parent_ask = nmc_readline (_("MACVLAN parent device or connection UUID: "));
6113 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6114 _("Error: 'dev' is required."));
6118 if ( !(valid_mac = nm_utils_hwaddr_valid (parent, ETH_ALEN))
6119 && !nm_utils_is_uuid (parent)
6120 && !nm_utils_iface_valid_name (parent)) {
6121 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6122 _("Error: 'dev': '%s' is neither UUID, interface name, nor MAC."),
6124 goto cleanup_macvlan;
6128 mode = mode_ask = nmc_readline (PROMPT_MACVLAN_MODE);
6130 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6131 _("Error: 'mode' is required."));
6135 if (!nm_utils_enum_from_str (nm_setting_macvlan_mode_get_type(), mode, (int *) &mode_enum, NULL)) {
6136 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6137 _("Error: 'mode' is not valid."));
6141 /* Also ask for all optional arguments if '--ask' is specified. */
6142 tap = g_strdup (tap_c);
6144 do_questionnaire_macvlan (&tap);
6147 GError *tmp_err = NULL;
6148 if (!nmc_string_to_bool (tap, &tap_bool, &tmp_err)) {
6149 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6150 _("Error: 'tap': %s."), tmp_err->message);
6151 g_clear_error (&tmp_err);
6152 goto cleanup_macvlan;
6156 /* Add 'macvlan' setting */
6157 s_macvlan = (NMSettingMacvlan *) nm_setting_macvlan_new ();
6158 nm_connection_add_setting (connection, NM_SETTING (s_macvlan));
6160 /* Add 'wired' setting if necessary */
6162 s_wired = (NMSettingWired *) nm_setting_wired_new ();
6163 nm_connection_add_setting (connection, NM_SETTING (s_wired));
6164 g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, parent, NULL);
6167 /* Set 'macvlan' properties */
6169 g_object_set (s_macvlan, NM_SETTING_MACVLAN_PARENT, parent, NULL);
6170 g_object_set (s_macvlan, NM_SETTING_MACVLAN_MODE, mode_enum, NULL);
6171 g_object_set (s_macvlan, NM_SETTING_MACVLAN_TAP, tap_bool, NULL);
6175 g_free (parent_ask);
6182 } else if (!strcmp (con_type, NM_SETTING_TUN_SETTING_NAME)) {
6183 /* Build up the settings required for 'tun' */
6184 gboolean success = FALSE;
6185 const char *mode_c = NULL;
6186 char *mode_ask = NULL, *mode = NULL;
6187 NMSettingTunMode mode_enum;
6188 const char *owner_c = NULL, *group_c = NULL;
6189 char *owner = NULL, *group = NULL;
6190 const char *pi_c = NULL, *vnet_hdr_c = NULL, *multi_queue_c = NULL;
6191 char *pi = NULL, *vnet_hdr = NULL, *multi_queue = NULL;
6192 gboolean pi_bool, vnet_hdr_bool, multi_queue_bool;
6193 nmc_arg_t exp_args[] = { {"mode", TRUE, &mode_c, !ask},
6194 {"owner", TRUE, &owner_c, FALSE},
6195 {"group", TRUE, &group_c, FALSE},
6196 {"pi", TRUE, &pi_c, FALSE},
6197 {"vnet-hdr", TRUE, &vnet_hdr_c, FALSE},
6198 {"multi-queue", TRUE, &multi_queue_c, FALSE},
6201 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
6204 if (!mode_c && ask) {
6205 mode_ask = nmc_readline (_("Mode %s"), PROMPT_TUN_MODE);
6206 mode_ask = mode_ask ? mode_ask : g_strdup ("tun");
6210 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6211 _("Error: 'mode' is required."));
6214 mode = g_strdup (mode_c);
6215 if (!check_tun_mode (&mode, error))
6218 if (owner && !check_user_group_id (owner, error))
6220 if (group && !check_user_group_id (group, error))
6223 owner = g_strdup (owner_c);
6224 group = g_strdup (group_c);
6225 pi = g_strdup (pi_c);
6226 vnet_hdr = g_strdup (vnet_hdr_c);
6227 multi_queue = g_strdup (multi_queue_c);
6229 do_questionnaire_tun (&owner, &group, &pi, &vnet_hdr, &multi_queue);
6232 GError *tmp_err = NULL;
6234 if (!nmc_string_to_bool (pi, &pi_bool, &tmp_err)) {
6235 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6236 _("Error: 'pi': %s."), tmp_err->message);
6237 g_clear_error (&tmp_err);
6243 GError *tmp_err = NULL;
6245 if (!nmc_string_to_bool (vnet_hdr, &vnet_hdr_bool, &tmp_err)) {
6246 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6247 _("Error: 'vnet-hdr': %s."), tmp_err->message);
6248 g_clear_error (&tmp_err);
6254 GError *tmp_err = NULL;
6256 if (!nmc_string_to_bool (multi_queue, &multi_queue_bool, &tmp_err)) {
6257 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6258 _("Error: 'multi-queue': %s."), tmp_err->message);
6259 g_clear_error (&tmp_err);
6263 /* Add 'tun' setting */
6264 s_tun = (NMSettingTun *) nm_setting_tun_new ();
6265 nm_connection_add_setting (connection, NM_SETTING (s_tun));
6266 mode_enum = !strcmp (mode, "tun") ? NM_SETTING_TUN_MODE_TUN : NM_SETTING_TUN_MODE_TAP;
6268 g_object_set (s_tun,
6269 NM_SETTING_TUN_MODE, mode_enum,
6270 NM_SETTING_TUN_OWNER, owner,
6271 NM_SETTING_TUN_GROUP, group,
6274 g_object_set (s_tun, NM_SETTING_TUN_PI, pi_bool, NULL);
6276 g_object_set (s_tun, NM_SETTING_TUN_VNET_HDR, vnet_hdr_bool, NULL);
6278 g_object_set (s_tun, NM_SETTING_TUN_MULTI_QUEUE, multi_queue_bool, NULL);
6288 g_free (multi_queue);
6292 } else if (!strcmp (con_type, NM_SETTING_IP_TUNNEL_SETTING_NAME)) {
6293 /* Build up the settings required for 'ip-tunnel' */
6294 const char *mode_c = NULL, *local_c = NULL, *remote_c = NULL;
6295 char *mode_ask = NULL, *remote_ask = NULL, *local = NULL;
6296 const char *parent_c = NULL;
6297 char *parent = NULL;
6298 gboolean success = FALSE;
6299 NMIPTunnelMode mode_enum;
6300 nmc_arg_t exp_args[] = { {"mode", TRUE, &mode_c, !ask},
6301 {"local", TRUE, &local_c, FALSE},
6302 {"remote", TRUE, &remote_c, !ask},
6303 {"dev", TRUE, &parent_c, FALSE},
6306 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
6310 mode_c = mode_ask = nmc_readline (PROMPT_IP_TUNNEL_MODE);
6312 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6313 _("Error: 'mode' is required."));
6314 goto cleanup_tunnel;
6317 if (!nm_utils_enum_from_str (nm_ip_tunnel_mode_get_type (),
6318 mode_c, (int *) &mode_enum, NULL)) {
6319 gs_free const char **values = NULL;
6320 gs_free char *values_str = NULL;
6322 values = nm_utils_enum_get_values (nm_ip_tunnel_mode_get_type (),
6323 NM_IP_TUNNEL_MODE_UNKNOWN + 1,
6325 values_str = g_strjoinv (",", (char **) values);
6326 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6327 _("Error: 'mode': '%s' is not valid, use one of %s"),
6328 mode_c, values_str);
6329 goto cleanup_tunnel;
6332 if (!remote_c && ask)
6333 remote_c = remote_ask = nmc_readline (_("Remote endpoint: "));
6335 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6336 _("Error: 'remote' is required."));
6337 goto cleanup_tunnel;
6340 if ( !nm_utils_ipaddr_valid (AF_INET, remote_c)
6341 && !nm_utils_ipaddr_valid (AF_INET6, remote_c)) {
6342 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6343 _("Error: 'remote': '%s' is not valid; must be an IP address"),
6345 goto cleanup_tunnel;
6348 local = g_strdup (local_c);
6349 parent = g_strdup (parent_c);
6351 do_questionnaire_ip_tunnel (&local, &parent);
6354 && !nm_utils_ipaddr_valid (AF_INET, local)
6355 && !nm_utils_ipaddr_valid (AF_INET6, local)) {
6356 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6357 _("Error: 'local': '%s' is not valid; must be an IP address"),
6359 goto cleanup_tunnel;
6363 if ( !nm_utils_is_uuid (parent)
6364 && !nm_utils_iface_valid_name (parent)) {
6365 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6366 _("Error: 'dev': '%s' is neither UUID nor interface name."),
6368 goto cleanup_tunnel;
6372 /* Add 'tunnel' setting */
6373 s_ip_tunnel = (NMSettingIPTunnel *) nm_setting_ip_tunnel_new ();
6374 nm_connection_add_setting (connection, NM_SETTING (s_ip_tunnel));
6376 /* Set 'tunnel' properties */
6377 g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_MODE, mode_enum, NULL);
6378 g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_REMOTE, remote_c, NULL);
6380 g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_LOCAL, local, NULL);
6382 g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_PARENT, parent, NULL);
6384 /* Set default values for IPv6 tunnels */
6385 if (nm_utils_ipaddr_valid (AF_INET6, remote_c)) {
6386 g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_TOS, 64, NULL);
6387 g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_ENCAPSULATION_LIMIT, 4, NULL);
6392 g_free (remote_ask);
6399 } else if (!strcmp (con_type, NM_SETTING_VXLAN_SETTING_NAME)) {
6400 /* Build up the settings required for 'vxlan' */
6401 gboolean success = FALSE;
6402 char *id_ask = NULL;
6403 const char *id = NULL;
6404 char *remote_ask = NULL;
6405 const char *remote = NULL;
6406 const char *parent_c = NULL, *local_c = NULL;
6407 const char *src_port_min_c = NULL, *src_port_max_c = NULL;
6408 const char *dst_port_c = NULL;
6409 char *parent = NULL, *local = NULL;
6410 char *src_port_min = NULL, *src_port_max = NULL, *dst_port = NULL;
6411 unsigned long int vni;
6412 unsigned long sport_min = G_MAXULONG, sport_max = G_MAXULONG;
6413 unsigned long dport = G_MAXULONG;
6414 nmc_arg_t exp_args[] = { {"id", TRUE, &id, !ask},
6415 {"remote", TRUE, &remote, !ask},
6416 {"dev", TRUE, &parent_c, FALSE},
6417 {"local", TRUE, &local_c, FALSE},
6418 {"source-port-min", TRUE, &src_port_min_c, FALSE},
6419 {"source-port-max", TRUE, &src_port_max_c, FALSE},
6420 {"destination-port", TRUE, &dst_port_c, FALSE},
6423 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
6427 id = id_ask = nmc_readline (_("VXLAN ID: "));
6429 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6430 _("Error: 'id' is required."));
6435 remote = remote_ask = nmc_readline (_("Remote: "));
6437 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6438 _("Error: 'remote' is required."));
6442 if (!nmc_string_to_uint (id, TRUE, 0, (1UL << 24) - 1, &vni)) {
6443 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6444 _("Error: 'id': '%s' is not valid; use <0-16777215>."), id);
6448 parent = g_strdup (parent_c);
6449 local = g_strdup (local_c);
6450 src_port_min = g_strdup (src_port_min_c);
6451 src_port_max = g_strdup (src_port_max_c);
6452 dst_port = g_strdup (dst_port_c);
6455 do_questionnaire_vxlan (&parent, &local, &src_port_min, &src_port_max, &dst_port);
6458 if ( !nm_utils_is_uuid (parent)
6459 && !nm_utils_iface_valid_name (parent)) {
6460 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6461 _("Error: 'dev': '%s' is neither UUID nor interface name."),
6467 if ( !nm_utils_ipaddr_valid (AF_INET, remote)
6468 && !nm_utils_ipaddr_valid (AF_INET6, remote)) {
6469 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6470 _("Error: 'remote': '%s' is not a valid IP address"),
6476 if ( !nm_utils_ipaddr_valid (AF_INET, local)
6477 && !nm_utils_ipaddr_valid (AF_INET6, local)) {
6478 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6479 _("Error: 'local': '%s' is not a valid IP address"),
6486 if (!nmc_string_to_uint (src_port_min, TRUE, 0, 65535, &sport_min)) {
6487 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6488 _("Error: 'source-port-min': %s is not valid; use <0-65535>."),
6495 if (!nmc_string_to_uint (src_port_max, TRUE, 0, 65535, &sport_max)) {
6496 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6497 _("Error: 'source-port-max': %s is not valid; use <0-65535>."),
6504 if (!nmc_string_to_uint (dst_port, TRUE, 0, 65535, &dport)) {
6505 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6506 _("Error: 'destination-port': %s is not valid; use <0-65535>."),
6512 /* Add 'vxlan' setting */
6513 s_vxlan = (NMSettingVxlan *) nm_setting_vxlan_new ();
6514 nm_connection_add_setting (connection, NM_SETTING (s_vxlan));
6516 g_object_set (s_vxlan, NM_SETTING_VXLAN_ID, (guint) vni, NULL);
6517 g_object_set (s_vxlan, NM_SETTING_VXLAN_REMOTE, remote, NULL);
6518 g_object_set (s_vxlan, NM_SETTING_VXLAN_LOCAL, local, NULL);
6519 g_object_set (s_vxlan, NM_SETTING_VXLAN_PARENT, parent, NULL);
6521 if (sport_min != G_MAXULONG)
6522 g_object_set (s_vxlan, NM_SETTING_VXLAN_SOURCE_PORT_MIN, sport_min, NULL);
6523 if (sport_max != G_MAXULONG)
6524 g_object_set (s_vxlan, NM_SETTING_VXLAN_SOURCE_PORT_MAX, sport_max, NULL);
6525 if (dport != G_MAXULONG)
6526 g_object_set (s_vxlan, NM_SETTING_VXLAN_DESTINATION_PORT, dport, NULL);
6532 g_free (remote_ask);
6535 g_free (src_port_min);
6536 g_free (src_port_max);
6540 } else if (!strcmp (con_type, NM_SETTING_GENERIC_SETTING_NAME)) {
6541 /* Add 'generic' setting */
6542 s_generic = (NMSettingGeneric *) nm_setting_generic_new ();
6543 nm_connection_add_setting (connection, NM_SETTING (s_generic));
6545 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6546 _("Error: '%s' is not a valid connection type."),
6551 slave_type = nm_setting_connection_get_slave_type (s_con);
6553 /* Read and add IP configuration */
6554 NMIPAddress *ip4addr = NULL, *ip6addr = NULL;
6555 const char *ip4 = NULL, *gw4 = NULL, *ip6 = NULL, *gw6 = NULL;
6556 nmc_arg_t exp_args[] = { {"ip4", TRUE, &ip4, FALSE}, {"gw4", TRUE, &gw4, FALSE},
6557 {"ip6", TRUE, &ip6, FALSE}, {"gw6", TRUE, &gw6, FALSE},
6563 /* reset 'found' flag */
6564 for (p = exp_args; p->name; p++)
6567 ip4 = gw4 = ip6 = gw6 = NULL;
6569 if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, NULL))
6572 /* coverity[dead_error_begin] */
6574 ip4addr = nmc_parse_and_build_address (AF_INET, ip4, error);
6576 g_prefix_error (error, _("Error: "));
6579 add_ip4_address_to_connection (ip4addr, connection);
6582 /* coverity[dead_error_begin] */
6584 NMSettingIPConfig *s_ip = nm_connection_get_setting_ip4_config (connection);
6587 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6588 _("Error: IPv4 gateway specified without IPv4 addresses"));
6590 } else if (nm_setting_ip_config_get_gateway (s_ip)) {
6591 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6592 _("Error: multiple IPv4 gateways specified"));
6594 } else if (!nm_utils_ipaddr_valid (AF_INET, gw4)) {
6595 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6596 _("Error: Invalid IPv4 gateway '%s'"),
6601 NM_SETTING_IP_CONFIG_GATEWAY, gw4,
6605 /* coverity[dead_error_begin] */
6607 ip6addr = nmc_parse_and_build_address (AF_INET6, ip6, error);
6609 g_prefix_error (error, _("Error: "));
6612 add_ip6_address_to_connection (ip6addr, connection);
6615 /* coverity[dead_error_begin] */
6617 NMSettingIPConfig *s_ip = nm_connection_get_setting_ip6_config (connection);
6620 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6621 _("Error: IPv6 gateway specified without IPv6 addresses"));
6623 } else if (nm_setting_ip_config_get_gateway (s_ip)) {
6624 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6625 _("Error: multiple IPv6 gateways specified"));
6627 } else if (!nm_utils_ipaddr_valid (AF_INET, gw6)) {
6628 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6629 _("Error: Invalid IPv6 gateway '%s'"),
6634 NM_SETTING_IP_CONFIG_GATEWAY, gw6,
6639 /* Ask for addresses if '--ask' is specified. */
6641 do_questionnaire_ip (connection);
6645 /* Set extra connection properties. */
6646 nmc_arg_t exp_args[] = { {"--", FALSE, NULL, TRUE},
6649 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
6652 if (!read_connection_properties (connection, argc, argv, error))
6662 } AddConnectionInfo;
6665 add_connection_cb (GObject *client,
6666 GAsyncResult *result,
6669 AddConnectionInfo *info = (AddConnectionInfo *) user_data;
6670 NmCli *nmc = info->nmc;
6671 NMRemoteConnection *connection;
6672 GError *error = NULL;
6674 connection = nm_client_add_connection_finish (NM_CLIENT (client), result, &error);
6676 g_string_printf (nmc->return_text,
6677 _("Error: Failed to add '%s' connection: %s"),
6678 info->con_name, error->message);
6679 g_error_free (error);
6680 nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
6682 g_print (_("Connection '%s' (%s) successfully added.\n"),
6683 nm_connection_get_id (NM_CONNECTION (connection)),
6684 nm_connection_get_uuid (NM_CONNECTION (connection)));
6685 g_object_unref (connection);
6688 g_free (info->con_name);
6694 add_new_connection (gboolean persistent,
6696 NMConnection *connection,
6697 GAsyncReadyCallback callback,
6700 nm_client_add_connection_async (client, connection, persistent,
6701 NULL, callback, user_data);
6705 update_connection (gboolean persistent,
6706 NMRemoteConnection *connection,
6707 GAsyncReadyCallback callback,
6710 nm_remote_connection_commit_changes_async (connection, persistent,
6711 NULL, callback, user_data);
6715 gen_func_vpn_types (const char *text, int state)
6717 return nmc_rl_gen_func_basic (text, state, nmc_known_vpns);
6721 gen_func_bool_values_l10n (const char *text, int state)
6723 const char *words[] = { WORD_LOC_YES, WORD_LOC_NO, NULL };
6724 return nmc_rl_gen_func_basic (text, state, words);
6728 gen_func_wifi_mode (const char *text, int state)
6730 const char *words[] = { "infrastructure", "ap", "adhoc", NULL };
6731 return nmc_rl_gen_func_basic (text, state, words);
6735 gen_func_ib_type (const char *text, int state)
6737 const char *words[] = { "datagram", "connected", NULL };
6738 return nmc_rl_gen_func_basic (text, state, words);
6742 gen_func_bt_type (const char *text, int state)
6744 const char *words[] = { "panu", "dun-gsm", "dun-cdma", NULL };
6745 return nmc_rl_gen_func_basic (text, state, words);
6749 gen_func_bond_mode (const char *text, int state)
6751 const char *words[] = { "balance-rr", "active-backup", "balance-xor", "broadcast",
6752 "802.3ad", "balance-tlb", "balance-alb", NULL };
6753 return nmc_rl_gen_func_basic (text, state, words);
6756 gen_func_bond_mon_mode (const char *text, int state)
6758 const char *words[] = { "miimon", "arp", NULL };
6759 return nmc_rl_gen_func_basic (text, state, words);
6763 gen_func_adsl_proto (const char *text, int state)
6765 const char *words[] = { "pppoe", "pppoa", "ipoatm", NULL };
6766 return nmc_rl_gen_func_basic (text, state, words);
6770 gen_func_adsl_encap (const char *text, int state)
6772 const char *words[] = { "vcmux", "llc", NULL };
6773 return nmc_rl_gen_func_basic (text, state, words);
6777 gen_func_tun_mode (const char *text, int state)
6779 const char *words[] = { "tun", "tap", NULL };
6780 return nmc_rl_gen_func_basic (text, state, words);
6784 gen_func_ip_tunnel_mode (const char *text, int state)
6786 gs_free const char **words = NULL;
6788 words = nm_utils_enum_get_values (nm_ip_tunnel_mode_get_type (),
6789 NM_IP_TUNNEL_MODE_UNKNOWN + 1,
6791 return nmc_rl_gen_func_basic (text, state, words);
6795 gen_func_macvlan_mode (const char *text, int state)
6797 gs_free const char **words = NULL;
6799 words = nm_utils_enum_get_values (nm_setting_macvlan_mode_get_type(),
6800 NM_SETTING_MACVLAN_MODE_UNKNOWN + 1,
6802 return nmc_rl_gen_func_basic (text, state, words);
6806 gen_func_master_ifnames (const char *text, int state)
6812 NMSettingConnection *s_con;
6813 const char *con_type, *ifname;
6815 if (!nm_cli.connections)
6818 /* Disable appending space after completion */
6819 rl_completion_append_character = '\0';
6821 ifnames = g_ptr_array_sized_new (20);
6822 for (i = 0; i < nm_cli.connections->len; i++) {
6823 con = NM_CONNECTION (nm_cli.connections->pdata[i]);
6824 s_con = nm_connection_get_setting_connection (con);
6826 con_type = nm_setting_connection_get_connection_type (s_con);
6827 if (g_strcmp0 (con_type, nmc_tab_completion.con_type) != 0)
6829 ifname = nm_connection_get_interface_name (con);
6830 g_ptr_array_add (ifnames, (gpointer) ifname);
6832 g_ptr_array_add (ifnames, (gpointer) NULL);
6834 ret = nmc_rl_gen_func_basic (text, state, (const char **) ifnames->pdata);
6836 g_ptr_array_free (ifnames, TRUE);
6841 is_single_word (const char* line)
6845 n1 = strspn (line, " \t");
6846 n2 = strcspn (line+n1, " \t\0") + n1;
6847 n3 = strspn (line+n2, " \t");
6856 nmcli_con_add_tab_completion (const char *text, int start, int end)
6858 char **match_array = NULL;
6859 rl_compentry_func_t *generator_func = NULL;
6861 /* Disable readline's default filename completion */
6862 rl_attempted_completion_over = 1;
6864 /* Restore standard append character to space */
6865 rl_completion_append_character = ' ';
6867 if (!is_single_word (rl_line_buffer))
6870 if (g_strcmp0 (rl_prompt, PROMPT_CON_TYPE) == 0)
6871 generator_func = gen_connection_types;
6872 else if (g_strcmp0 (rl_prompt, PROMPT_VPN_TYPE) == 0)
6873 generator_func = gen_func_vpn_types;
6874 else if (g_strcmp0 (rl_prompt, PROMPT_MASTER) == 0)
6875 generator_func = gen_func_master_ifnames;
6876 else if ( g_str_has_suffix (rl_prompt, prompt_yes_no (TRUE, NULL))
6877 || g_str_has_suffix (rl_prompt, prompt_yes_no (TRUE, ":"))
6878 || g_str_has_suffix (rl_prompt, prompt_yes_no (FALSE, NULL))
6879 || g_str_has_suffix (rl_prompt, prompt_yes_no (FALSE, ":")))
6880 generator_func = gen_func_bool_values_l10n;
6881 else if (g_str_has_suffix (rl_prompt, PROMPT_WIFI_MODE))
6882 generator_func = gen_func_wifi_mode;
6883 else if (g_str_has_suffix (rl_prompt, PROMPT_IB_MODE))
6884 generator_func = gen_func_ib_type;
6885 else if (g_str_has_suffix (rl_prompt, PROMPT_BT_TYPE))
6886 generator_func = gen_func_bt_type;
6887 else if (g_str_has_prefix (rl_prompt, PROMPT_BOND_MODE))
6888 generator_func = gen_func_bond_mode;
6889 else if (g_str_has_suffix (rl_prompt, PROMPT_BOND_MON_MODE))
6890 generator_func = gen_func_bond_mon_mode;
6891 else if (g_str_has_suffix (rl_prompt, PROMPT_ADSL_PROTO))
6892 generator_func = gen_func_adsl_proto;
6893 else if (g_str_has_suffix (rl_prompt, PROMPT_ADSL_ENCAP))
6894 generator_func = gen_func_adsl_encap;
6895 else if (g_str_has_suffix (rl_prompt, PROMPT_TUN_MODE))
6896 generator_func = gen_func_tun_mode;
6897 else if (g_str_has_suffix (rl_prompt, PROMPT_IP_TUNNEL_MODE))
6898 generator_func = gen_func_ip_tunnel_mode;
6899 else if (g_str_has_suffix (rl_prompt, PROMPT_MACVLAN_MODE))
6900 generator_func = gen_func_macvlan_mode;
6903 match_array = rl_completion_matches (text, generator_func);
6908 static NMCResultCode
6909 do_connection_add (NmCli *nmc, int argc, char **argv)
6911 NMConnection *connection = NULL;
6912 NMSettingConnection *s_con;
6914 char *default_name = NULL;
6915 const char *type = NULL;
6916 char *type_ask = NULL;
6917 const char *con_name = NULL;
6918 const char *autoconnect = NULL;
6919 gboolean auto_bool = TRUE;
6920 const char *ifname = NULL;
6921 char *ifname_ask = NULL;
6922 gboolean ifname_mandatory = TRUE;
6923 const char *save = NULL;
6924 gboolean save_bool = TRUE;
6925 const char *master = NULL;
6926 const char *checked_master = NULL;
6927 const char *slave_type = NULL;
6928 AddConnectionInfo *info = NULL;
6929 const char *setting_name;
6930 GError *error = NULL;
6931 nmc_arg_t exp_args[] = { {"type", TRUE, &type, !nmc->ask},
6932 {"con-name", TRUE, &con_name, FALSE},
6933 {"autoconnect", TRUE, &autoconnect, FALSE},
6934 {"ifname", TRUE, &ifname, FALSE},
6935 {"save", TRUE, &save, FALSE},
6936 {"master", TRUE, &master, FALSE},
6937 {"slave-type", TRUE, &slave_type, FALSE},
6940 rl_attempted_completion_function = (rl_completion_func_t *) nmcli_con_add_tab_completion;
6942 nmc->return_value = NMC_RESULT_SUCCESS;
6944 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, &error)) {
6945 g_string_assign (nmc->return_text, error->message);
6946 nmc->return_value = error->code;
6947 g_clear_error (&error);
6951 if (!type && nmc->ask) {
6952 char *types_tmp = get_valid_options_string (nmc_valid_connection_types, NULL);
6953 g_print ("Valid types: [%s]\n", types_tmp);
6954 type = type_ask = nmc_readline (PROMPT_CON_TYPE);
6958 g_string_printf (nmc->return_text, _("Error: 'type' argument is required."));
6959 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6963 type = g_strstrip (type_ask);
6965 if (!(setting_name = check_valid_name (type, nmc_valid_connection_types, NULL, &error))) {
6966 g_string_printf (nmc->return_text, _("Error: invalid connection type; %s."),
6968 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6969 g_clear_error (&error);
6973 GError *tmp_err = NULL;
6974 if (!nmc_string_to_bool (autoconnect, &auto_bool, &tmp_err)) {
6975 g_string_printf (nmc->return_text, _("Error: 'autoconnect': %s."),
6977 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6978 g_clear_error (&tmp_err);
6983 GError *tmp_err = NULL;
6984 if (!nmc_string_to_bool (save, &save_bool, &tmp_err)) {
6985 g_string_printf (nmc->return_text, _("Error: 'save': %s."),
6987 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6988 g_clear_error (&tmp_err);
6993 /* ifname is mandatory for all connection types except virtual ones (bond, team, bridge, vlan) */
6994 if ( strcmp (type, NM_SETTING_BOND_SETTING_NAME) == 0
6995 || strcmp (type, NM_SETTING_TEAM_SETTING_NAME) == 0
6996 || strcmp (type, NM_SETTING_BRIDGE_SETTING_NAME) == 0
6997 || strcmp (type, NM_SETTING_VLAN_SETTING_NAME) == 0)
6998 ifname_mandatory = FALSE;
7000 if (!ifname && ifname_mandatory) {
7002 ifname = ifname_ask = nmc_readline (_("Interface name [*]: "));
7004 ifname = ifname_ask = g_strdup ("*");
7007 g_string_printf (nmc->return_text, _("Error: 'ifname' argument is required."));
7009 g_string_printf (nmc->return_text, _("Error: mandatory 'ifname' not seen before '%s'."),
7011 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
7016 if (!nm_utils_iface_valid_name (ifname) && strcmp (ifname, "*") != 0) {
7017 g_string_printf (nmc->return_text,
7018 _("Error: 'ifname': '%s' is not a valid interface nor '*'."),
7020 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
7023 /* Special value of '*' means no specific interface name */
7024 if (strcmp (ifname, "*") == 0)
7028 /* Create a new connection object */
7029 connection = nm_simple_connection_new ();
7031 /* Build up the 'connection' setting */
7032 s_con = (NMSettingConnection *) nm_setting_connection_new ();
7033 uuid = nm_utils_uuid_generate ();
7035 default_name = g_strdup (con_name);
7037 char *try_name = ifname ?
7038 g_strdup_printf ("%s-%s", get_name_alias (setting_name, nmc_valid_connection_types), ifname)
7039 : g_strdup (get_name_alias (setting_name, nmc_valid_connection_types));
7040 default_name = nmc_unique_connection_name (nmc->connections, try_name);
7045 /* Verify master argument */
7046 checked_master = normalized_master_for_slave (nmc->connections, master, slave_type, &slave_type);
7048 g_object_set (s_con,
7049 NM_SETTING_CONNECTION_ID, default_name,
7050 NM_SETTING_CONNECTION_UUID, uuid,
7051 NM_SETTING_CONNECTION_TYPE, setting_name,
7052 NM_SETTING_CONNECTION_AUTOCONNECT, auto_bool,
7053 NM_SETTING_CONNECTION_INTERFACE_NAME, ifname,
7054 NM_SETTING_CONNECTION_MASTER, checked_master,
7055 NM_SETTING_CONNECTION_SLAVE_TYPE, slave_type,
7058 g_free (default_name);
7059 nm_connection_add_setting (connection, NM_SETTING (s_con));
7061 if (!complete_connection_by_type (connection,
7069 g_string_assign (nmc->return_text, error->message);
7070 nmc->return_value = error->code;
7071 g_clear_error (&error);
7077 info = g_malloc0 (sizeof (AddConnectionInfo));
7079 info->con_name = g_strdup (nm_connection_get_id (connection));
7081 /* Tell the settings service to add the new connection */
7082 add_new_connection (save_bool,
7089 g_object_unref (connection);
7091 return nmc->return_value;
7095 g_object_unref (connection);
7097 g_free (ifname_ask);
7099 return nmc->return_value;
7103 /*----------------------------------------------------------------------------*/
7104 /* Functions for readline TAB completion in editor */
7107 uuid_display_hook (char **array, int len, int max_len)
7113 for (i = 1; i <= len; i++) {
7114 con = nmc_find_connection (nmc_tab_completion.nmc->connections, "uuid", array[i], NULL);
7115 id = con ? nm_connection_get_id (con) : NULL;
7117 tmp = g_strdup_printf ("%s (%s)", array[i], id);
7120 if (max < strlen (id))
7124 rl_display_match_list (array, len, max_len + max + 3);
7125 rl_forced_update_display ();
7129 gen_nmcli_cmds_menu (const char *text, int state)
7131 const char *words[] = { "goto", "set", "remove", "describe", "print", "verify",
7132 "save", "activate", "back", "help", "quit", "nmcli",
7134 return nmc_rl_gen_func_basic (text, state, words);
7138 gen_nmcli_cmds_submenu (const char *text, int state)
7140 const char *words[] = { "set", "add", "change", "remove", "describe",
7141 "print", "back", "help", "quit",
7143 return nmc_rl_gen_func_basic (text, state, words);
7147 gen_cmd_nmcli (const char *text, int state)
7149 const char *words[] = { "status-line", "save-confirmation", "show-secrets", "prompt-color", NULL };
7150 return nmc_rl_gen_func_basic (text, state, words);
7154 gen_cmd_nmcli_prompt_color (const char *text, int state)
7156 const char *words[] = { "normal", "black", "red", "green", "yellow",
7157 "blue", "magenta", "cyan", "white", NULL };
7158 return nmc_rl_gen_func_basic (text, state, words);
7162 gen_func_bool_values (const char *text, int state)
7164 const char *words[] = { "yes", "no", NULL };
7165 return nmc_rl_gen_func_basic (text, state, words);
7169 gen_cmd_verify0 (const char *text, int state)
7171 const char *words[] = { "all", "fix", NULL };
7172 return nmc_rl_gen_func_basic (text, state, words);
7176 gen_cmd_print0 (const char *text, int state)
7178 static char **words = NULL;
7184 const char *setting_name;
7187 settings = nm_connection_to_dbus (nmc_tab_completion.connection, NM_CONNECTION_SERIALIZE_NO_SECRETS);
7188 words = g_new (char *, g_variant_n_children (settings) + 2);
7189 g_variant_iter_init (&iter, settings);
7190 while (g_variant_iter_next (&iter, "{&s@a{sv}}", &setting_name, NULL))
7191 words [i++] = g_strdup (setting_name);
7192 words[i++] = g_strdup ("all");
7194 g_variant_unref (settings);
7198 ret = nmc_rl_gen_func_basic (text, state, (const char **) words);
7208 gen_cmd_print2 (const char *text, int state)
7210 const char *words[] = { "setting", "connection", "all", NULL };
7211 return nmc_rl_gen_func_basic (text, state, words);
7215 gen_cmd_save (const char *text, int state)
7217 const char *words[] = { "persistent", "temporary", NULL };
7218 return nmc_rl_gen_func_basic (text, state, words);
7222 gen_connection_types (const char *text, int state)
7224 static int list_idx, len;
7225 const char *c_type, *a_type;
7229 len = strlen (text);
7232 while (nmc_valid_connection_types[list_idx].name) {
7233 a_type = nmc_valid_connection_types[list_idx].alias;
7234 c_type = nmc_valid_connection_types[list_idx].name;
7236 if (a_type && !strncmp (text, a_type, len))
7237 return g_strdup (a_type);
7238 if (c_type && !strncmp (text, c_type, len))
7239 return g_strdup (c_type);
7246 gen_setting_names (const char *text, int state)
7248 static int list_idx, len, is_slv;
7249 const char *s_name, *a_name;
7250 const NameItem *valid_settings_arr;
7251 NMSettingConnection *s_con;
7252 const char *s_type = NULL;
7257 len = strlen (text);
7262 valid_settings_arr = get_valid_settings_array (nmc_tab_completion.con_type);
7263 if (!valid_settings_arr)
7265 while (valid_settings_arr[list_idx].name) {
7266 a_name = valid_settings_arr[list_idx].alias;
7267 s_name = valid_settings_arr[list_idx].name;
7269 if (len == 0 && a_name)
7270 return g_strdup_printf ("%s (%s)", s_name, a_name);
7271 if (a_name && !strncmp (text, a_name, len))
7272 return g_strdup (a_name);
7273 if (s_name && !strncmp (text, s_name, len))
7274 return g_strdup (s_name);
7277 /* Let's give a try to parameters related to slave type */
7283 s_con = nm_connection_get_setting_connection (nmc_tab_completion.connection);
7285 s_type = nm_setting_connection_get_slave_type (s_con);
7286 slv_type = g_strdup_printf ("%s-slave", s_type ? s_type : "no");
7287 valid_settings_arr = get_valid_settings_array (slv_type);
7290 while (valid_settings_arr[list_idx].name) {
7291 a_name = valid_settings_arr[list_idx].alias;
7292 s_name = valid_settings_arr[list_idx].name;
7294 if (len == 0 && a_name)
7295 return g_strdup_printf ("%s (%s)", s_name, a_name);
7296 if (a_name && !strncmp (text, a_name, len))
7297 return g_strdup (a_name);
7298 if (s_name && !strncmp (text, s_name, len))
7299 return g_strdup (s_name);
7306 gen_property_names (const char *text, int state)
7308 NMSetting *setting = NULL;
7309 char **valid_props = NULL;
7311 const char *line = rl_line_buffer;
7312 const char *setting_name;
7314 const NameItem *valid_settings_main;
7315 const NameItem *valid_settings_slave;
7317 const char *slv_type;
7319 /* Try to get the setting from 'line' - setting_name.property */
7320 p1 = strchr (line, '.');
7322 while (p1 > line && !g_ascii_isspace (*p1))
7325 strv = g_strsplit (p1+1, ".", 2);
7327 valid_settings_main = get_valid_settings_array (nmc_tab_completion.con_type);
7329 /* Support autocompletion of slave-connection parameters
7330 * guessing the slave type from the setting name already
7331 * typed (or autocompleted) */
7332 if (nm_streq0 (strv[0], NM_SETTING_TEAM_PORT_SETTING_NAME))
7333 slv_type = "team-slave";
7334 else if (nm_streq0 (strv[0], NM_SETTING_BRIDGE_PORT_SETTING_NAME))
7335 slv_type = "bridge-slave";
7337 slv_type = "no-slave";
7338 valid_settings_slave = get_valid_settings_array (slv_type);
7340 setting_name = check_valid_name (strv[0],
7341 valid_settings_main,
7342 valid_settings_slave,
7344 setting = nmc_setting_new_for_name (setting_name);
7346 /* Else take the current setting, if any */
7347 setting = nmc_tab_completion.setting ? g_object_ref (nmc_tab_completion.setting) : NULL;
7351 valid_props = nmc_setting_get_valid_properties (setting);
7352 ret = nmc_rl_gen_func_basic (text, state, (const char **) valid_props);
7356 g_strfreev (valid_props);
7358 g_object_unref (setting);
7363 gen_compat_devices (const char *text, int state)
7366 const GPtrArray *devices;
7367 const char **compatible_devices;
7370 devices = nm_client_get_devices (nmc_tab_completion.nmc->client);
7371 if (devices->len == 0)
7374 compatible_devices = g_new (const char *, devices->len + 1);
7375 for (i = 0; i < devices->len; i++) {
7376 NMDevice *dev = g_ptr_array_index (devices, i);
7377 const char *ifname = nm_device_get_iface (dev);
7378 NMDevice *device = NULL;
7379 const char *spec_object = NULL;
7381 if (find_device_for_connection (nmc_tab_completion.nmc, nmc_tab_completion.connection,
7382 ifname, NULL, NULL, &device, &spec_object, NULL)) {
7383 compatible_devices[j++] = ifname;
7386 compatible_devices[j] = NULL;
7388 ret = nmc_rl_gen_func_basic (text, state, compatible_devices);
7390 g_free (compatible_devices);
7394 static const char **
7395 _create_vpn_array (const GPtrArray *connections, gboolean uuid)
7400 if (connections->len < 1)
7403 array = g_new (const char *, connections->len + 1);
7404 for (c = 0; c < connections->len; c++) {
7405 NMConnection *connection = NM_CONNECTION (connections->pdata[c]);
7406 const char *type = nm_connection_get_connection_type (connection);
7408 if (g_strcmp0 (type, NM_SETTING_VPN_SETTING_NAME) == 0)
7409 array[idx++] = uuid ? nm_connection_get_uuid (connection) : nm_connection_get_id (connection);
7416 gen_vpn_uuids (const char *text, int state)
7418 const GPtrArray *connections = nm_cli.connections;
7422 if (connections->len < 1)
7425 uuids = _create_vpn_array (connections, TRUE);
7426 ret = nmc_rl_gen_func_basic (text, state, uuids);
7432 gen_vpn_ids (const char *text, int state)
7434 const GPtrArray *connections = nm_cli.connections;
7438 if (connections->len < 1)
7441 ids = _create_vpn_array (connections, FALSE);
7442 ret = nmc_rl_gen_func_basic (text, state, ids);
7447 static rl_compentry_func_t *
7448 get_gen_func_cmd_nmcli (const char *str)
7452 if (matches (str, "status-line") == 0)
7453 return gen_func_bool_values;
7454 if (matches (str, "save-confirmation") == 0)
7455 return gen_func_bool_values;
7456 if (matches (str, "show-secrets") == 0)
7457 return gen_func_bool_values;
7458 if (matches (str, "prompt-color") == 0)
7459 return gen_cmd_nmcli_prompt_color;
7464 * Helper function parsing line for completion.
7466 * line : the whole line to be parsed
7467 * end : the position of cursor in the line
7468 * cmd : command to match
7470 * cw_num : is set to the word number being completed (1, 2, 3, 4).
7471 * prev_word : returns the previous word (so that we have some context).
7473 * Returns TRUE when the first word of the 'line' matches 'cmd'.
7476 * line="rem" cmd="remove" -> TRUE cw_num=1
7477 * line="set con" cmd="set" -> TRUE cw_num=2
7478 * line="go ipv4.method" cmd="goto" -> TRUE cw_num=2
7479 * line=" des eth.mtu " cmd="describe" -> TRUE cw_num=3
7480 * line=" bla ipv4.method" cmd="goto" -> FALSE
7483 should_complete_cmd (const char *line, int end, const char *cmd,
7484 int *cw_num, char **prev_word)
7487 const char *word1, *word2, *word3;
7488 size_t n1, n2, n3, n4, n5, n6;
7489 gboolean word1_done, word2_done, word3_done;
7490 gboolean ret = FALSE;
7495 tmp = g_strdup (line);
7497 n1 = strspn (tmp, " \t");
7498 n2 = strcspn (tmp+n1, " \t\0") + n1;
7499 n3 = strspn (tmp+n2, " \t") + n2;
7500 n4 = strcspn (tmp+n3, " \t\0") + n3;
7501 n5 = strspn (tmp+n4, " \t") + n4;
7502 n6 = strcspn (tmp+n5, " \t\0") + n5;
7504 word1_done = end > n2;
7505 word2_done = end > n4;
7506 word3_done = end > n6;
7507 tmp[n2] = tmp[n4] = tmp[n6] = '\0';
7509 word1 = tmp[n1] ? tmp + n1 : NULL;
7510 word2 = tmp[n3] ? tmp + n3 : NULL;
7511 word3 = tmp[n5] ? tmp + n5 : NULL;
7518 } else if (!word2_done) {
7522 *prev_word = g_strdup (word1);
7523 } else if (!word3_done) {
7527 *prev_word = g_strdup (word2);
7532 *prev_word = g_strdup (word3);
7535 if (word1 && matches (word1, cmd) == 0)
7543 * extract_setting_and_property:
7544 * prompt: (in) (allow-none): prompt string, or NULL
7545 * line: (in) (allow-none): line, or NULL
7546 * setting: (out) (transfer full) (array zero-terminated=1):
7547 * return location for setting name
7548 * property: (out) (transfer full) (array zero-terminated=1):
7549 * return location for property name
7551 * Extract setting and property names from prompt and/or line.
7554 extract_setting_and_property (const char *prompt, const char *line,
7555 char **setting, char **property)
7561 /* prompt looks like this:
7562 "nmcli 802-1x>" or "nmcli 802-1x.pac-file>" */
7563 const char *p1, *p2, *dot;
7565 p1 = strchr (prompt, ' ');
7567 dot = strchr (++p1, '.');
7570 num1 = strcspn (p1, ".");
7571 num2 = strcspn (p2, ">");
7572 sett = num1 > 0 ? g_strndup (p1, num1) : NULL;
7573 prop = num2 > 0 ? g_strndup (p2, num2) : NULL;
7575 num1 = strcspn (p1, ">");
7576 sett = num1 > 0 ? g_strndup (p1, num1) : NULL;
7582 /* line looks like this:
7583 " set 802-1x.pac-file ..." or " set pac-file ..." */
7584 const char *p1, *p2, *dot;
7585 size_t n1, n2, n3, n4;
7586 size_t num1, num2, len;
7587 n1 = strspn (line, " \t"); /* white-space */
7588 n2 = strcspn (line+n1, " \t\0") + n1; /* command */
7589 n3 = strspn (line+n2, " \t") + n2; /* white-space */
7590 n4 = strcspn (line+n3, " \t\0") + n3; /* setting/property */
7594 dot = strchr (p1, '.');
7595 if (dot && dot < p1 + len) {
7597 num1 = strcspn (p1, ".");
7598 num2 = len > num1 + 1 ? len - num1 - 1 : 0;
7599 sett = num1 > 0 ? g_strndup (p1, num1) : sett;
7600 prop = num2 > 0 ? g_strndup (p2, num2) : prop;
7603 prop = len > 0 ? g_strndup (p1, len) : NULL;
7618 get_setting_and_property (const char *prompt, const char *line,
7619 NMSetting **setting_out, char**property_out)
7621 const NameItem *valid_settings_main;
7622 const NameItem *valid_settings_slave;
7623 const char *setting_name;
7624 NMSetting *setting = NULL;
7625 char *property = NULL;
7626 char *sett = NULL, *prop = NULL;
7627 NMSettingConnection *s_con;
7628 const char *s_type = NULL;
7631 extract_setting_and_property (prompt, line, &sett, &prop);
7633 /* Is this too much (and useless?) effort for an unlikely case? */
7634 s_con = nm_connection_get_setting_connection (nmc_tab_completion.connection);
7636 s_type = nm_setting_connection_get_slave_type (s_con);
7637 slv_type = g_strdup_printf ("%s-slave", s_type ? s_type : "no");
7639 valid_settings_main = get_valid_settings_array (nmc_tab_completion.con_type);
7640 valid_settings_slave = get_valid_settings_array (slv_type);
7643 setting_name = check_valid_name (sett, valid_settings_main,
7644 valid_settings_slave, NULL);
7645 setting = nmc_setting_new_for_name (setting_name);
7647 setting = nmc_tab_completion.setting ? g_object_ref (nmc_tab_completion.setting) : NULL;
7649 if (setting && prop)
7650 property = is_property_valid (setting, prop, NULL);
7652 property = g_strdup (nmc_tab_completion.property);
7654 *setting_out = setting;
7655 *property_out = property;
7662 _get_and_check_property (const char *prompt,
7665 const char **array_multi,
7669 gboolean found = FALSE;
7671 extract_setting_and_property (prompt, line, NULL, &prop);
7674 found = !!nmc_string_is_valid (prop, array, NULL);
7675 if (array_multi && multi)
7676 *multi = !!nmc_string_is_valid (prop, array_multi, NULL);
7683 should_complete_files (const char *prompt, const char *line)
7685 const char *file_properties[] = {
7686 /* '802-1x' properties */
7693 "phase2-client-cert",
7695 "phase2-private-key",
7696 /* 'team' and 'team-port' properties */
7700 return _get_and_check_property (prompt, line, file_properties, NULL, NULL);
7704 should_complete_vpn_uuids (const char *prompt, const char *line)
7706 const char *uuid_properties[] = {
7707 /* 'connection' properties */
7711 return _get_and_check_property (prompt, line, uuid_properties, NULL, NULL);
7714 static const char **
7715 get_allowed_property_values (void)
7719 const char **avals = NULL;
7721 get_setting_and_property (rl_prompt, rl_line_buffer, &setting, &property);
7722 if (setting && property)
7723 avals = nmc_setting_get_property_allowed_values (setting, property);
7726 g_object_unref (setting);
7733 should_complete_property_values (const char *prompt, const char *line, gboolean *multi)
7735 /* properties allowing multiple values */
7736 const char *multi_props[] = {
7737 /* '802-1x' properties */
7738 NM_SETTING_802_1X_EAP,
7739 /* '802-11-wireless-security' properties */
7740 NM_SETTING_WIRELESS_SECURITY_PROTO,
7741 NM_SETTING_WIRELESS_SECURITY_PAIRWISE,
7742 NM_SETTING_WIRELESS_SECURITY_GROUP,
7743 /* 'bond' properties */
7744 NM_SETTING_BOND_OPTIONS,
7745 /* 'ethernet' properties */
7746 NM_SETTING_WIRED_S390_OPTIONS,
7749 _get_and_check_property (prompt, line, NULL, multi_props, multi);
7750 return get_allowed_property_values () != NULL;
7753 //FIXME: this helper should go to libnm later
7755 _setting_property_is_boolean (NMSetting *setting, const char *property_name)
7759 g_return_val_if_fail (NM_IS_SETTING (setting), FALSE);
7760 g_return_val_if_fail (property_name, FALSE);
7762 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), property_name);
7763 if (pspec && pspec->value_type == G_TYPE_BOOLEAN)
7769 should_complete_boolean (const char *prompt, const char *line)
7773 gboolean is_boolean = FALSE;
7775 get_setting_and_property (prompt, line, &setting, &property);
7776 if (setting && property)
7777 is_boolean = _setting_property_is_boolean (setting, property);
7780 g_object_unref (setting);
7787 gen_property_values (const char *text, int state)
7792 avals = get_allowed_property_values ();
7794 ret = nmc_rl_gen_func_basic (text, state, avals);
7799 extern int rl_complete_with_tilde_expansion;
7802 * Attempt to complete on the contents of TEXT. START and END show the
7803 * region of TEXT that contains the word to complete. We can use the
7804 * entire line in case we want to do some simple parsing. Return the
7805 * array of matches, or NULL if there aren't any.
7808 nmcli_editor_tab_completion (const char *text, int start, int end)
7810 char **match_array = NULL;
7811 const char *line = rl_line_buffer;
7812 rl_compentry_func_t *generator_func = NULL;
7818 /* Restore standard append character to space */
7819 rl_completion_append_character = ' ';
7821 /* Restore standard function for displaying matches */
7822 rl_completion_display_matches_hook = NULL;
7824 /* Disable default filename completion */
7825 rl_attempted_completion_over = 1;
7827 /* Enable tilde expansion when filenames are completed */
7828 rl_complete_with_tilde_expansion = 1;
7830 /* Filter out possible ANSI color escape sequences */
7831 prompt_tmp = nmc_filter_out_colors ((const char *) rl_prompt);
7833 /* Find the first non-space character */
7834 n1 = strspn (line, " \t");
7836 /* Choose the right generator function */
7837 if (strcmp (prompt_tmp, EDITOR_PROMPT_CON_TYPE) == 0)
7838 generator_func = gen_connection_types;
7839 else if (strcmp (prompt_tmp, EDITOR_PROMPT_SETTING) == 0)
7840 generator_func = gen_setting_names;
7841 else if (strcmp (prompt_tmp, EDITOR_PROMPT_PROPERTY) == 0)
7842 generator_func = gen_property_names;
7843 else if ( g_str_has_suffix (rl_prompt, prompt_yes_no (TRUE, NULL))
7844 || g_str_has_suffix (rl_prompt, prompt_yes_no (FALSE, NULL)))
7845 generator_func = gen_func_bool_values_l10n;
7846 else if (g_str_has_prefix (prompt_tmp, "nmcli")) {
7847 if (!strchr (prompt_tmp, '.')) {
7848 int level = g_str_has_prefix (prompt_tmp, "nmcli>") ? 0 : 1;
7849 const char *dot = strchr (line, '.');
7852 /* Main menu - level 0,1 */
7854 generator_func = gen_nmcli_cmds_menu;
7856 if (should_complete_cmd (line, end, "goto", &num, NULL) && num <= 2) {
7857 if (level == 0 && (!dot || dot >= line + end))
7858 generator_func = gen_setting_names;
7860 generator_func = gen_property_names;
7861 } else if (should_complete_cmd (line, end, "set", &num, NULL)) {
7863 if (level == 0 && (!dot || dot >= line + end)) {
7864 generator_func = gen_setting_names;
7865 rl_completion_append_character = '.';
7867 generator_func = gen_property_names;
7868 } else if (num >= 3) {
7869 if (num == 3 && should_complete_files (NULL, line))
7870 rl_attempted_completion_over = 0;
7871 else if (should_complete_vpn_uuids (NULL, line)) {
7872 rl_completion_display_matches_hook = uuid_display_hook;
7873 generator_func = gen_vpn_uuids;
7874 } else if ( should_complete_property_values (NULL, line, &multi)
7875 && (num == 3 || multi)) {
7876 generator_func = gen_property_values;
7877 } else if (should_complete_boolean (NULL, line) && num == 3)
7878 generator_func = gen_func_bool_values;
7880 } else if ( ( should_complete_cmd (line, end, "remove", &num, NULL)
7881 || should_complete_cmd (line, end, "describe", &num, NULL))
7883 if (level == 0 && (!dot || dot >= line + end)) {
7884 generator_func = gen_setting_names;
7885 rl_completion_append_character = '.';
7887 generator_func = gen_property_names;
7888 } else if (should_complete_cmd (line, end, "nmcli", &num, &word)) {
7890 generator_func = gen_cmd_nmcli;
7892 generator_func = get_gen_func_cmd_nmcli (word);
7893 } else if (should_complete_cmd (line, end, "print", &num, NULL) && num <= 2) {
7894 if (level == 0 && (!dot || dot >= line + end))
7895 generator_func = gen_cmd_print0;
7897 generator_func = gen_property_names;
7898 } else if (should_complete_cmd (line, end, "verify", &num, NULL) && num <= 2) {
7899 generator_func = gen_cmd_verify0;
7900 } else if (should_complete_cmd (line, end, "activate", &num, NULL) && num <= 2) {
7901 generator_func = gen_compat_devices;
7902 } else if (should_complete_cmd (line, end, "save", &num, NULL) && num <= 2) {
7903 generator_func = gen_cmd_save;
7904 } else if (should_complete_cmd (line, end, "help", &num, NULL) && num <= 2)
7905 generator_func = gen_nmcli_cmds_menu;
7908 /* Submenu - level 2 */
7910 generator_func = gen_nmcli_cmds_submenu;
7914 if ( should_complete_cmd (line, end, "add", &num, NULL)
7915 || should_complete_cmd (line, end, "set", &num, NULL)) {
7916 if (num <= 2 && should_complete_files (prompt_tmp, line))
7917 rl_attempted_completion_over = 0;
7918 else if (should_complete_vpn_uuids (prompt_tmp, line)) {
7919 rl_completion_display_matches_hook = uuid_display_hook;
7920 generator_func = gen_vpn_uuids;
7921 } else if ( should_complete_property_values (prompt_tmp, NULL, &multi)
7922 && (num <= 2 || multi)) {
7923 generator_func = gen_property_values;
7924 } else if (should_complete_boolean (prompt_tmp, NULL) && num <= 2)
7925 generator_func = gen_func_bool_values;
7927 if (should_complete_cmd (line, end, "print", &num, NULL) && num <= 2)
7928 generator_func = gen_cmd_print2;
7929 else if (should_complete_cmd (line, end, "help", &num, NULL) && num <= 2)
7930 generator_func = gen_nmcli_cmds_submenu;
7936 match_array = rl_completion_matches (text, generator_func);
7938 g_free (prompt_tmp);
7943 #define NMCLI_EDITOR_HISTORY ".nmcli-history"
7946 load_history_cmds (const char *uuid)
7955 filename = g_build_filename (g_get_home_dir (), NMCLI_EDITOR_HISTORY, NULL);
7956 kf = g_key_file_new ();
7957 if (!g_key_file_load_from_file (kf, filename, G_KEY_FILE_KEEP_COMMENTS, &err)) {
7958 if (g_error_matches (err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_PARSE))
7959 g_print ("Warning: %s parse error: %s\n", filename, err->message);
7960 g_key_file_free (kf);
7964 keys = g_key_file_get_keys (kf, uuid, NULL, NULL);
7965 for (i = 0; keys && keys[i]; i++) {
7966 line = g_key_file_get_string (kf, uuid, keys[i], NULL);
7972 g_key_file_free (kf);
7977 save_history_cmds (const char *uuid)
7979 HIST_ENTRY **hist = NULL;
7988 hist = history_list ();
7990 filename = g_build_filename (g_get_home_dir (), NMCLI_EDITOR_HISTORY, NULL);
7991 kf = g_key_file_new ();
7992 if (!g_key_file_load_from_file (kf, filename, G_KEY_FILE_KEEP_COMMENTS, &err)) {
7993 if ( !g_error_matches (err, G_FILE_ERROR, G_FILE_ERROR_NOENT)
7994 && !g_error_matches (err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND)) {
7995 g_print ("Warning: %s parse error: %s\n", filename, err->message);
7996 g_key_file_free (kf);
7998 g_clear_error (&err);
8001 g_clear_error (&err);
8004 /* Remove previous history group and save new history entries */
8005 g_key_file_remove_group (kf, uuid, NULL);
8006 for (i = 0; hist[i]; i++)
8008 key = g_strdup_printf ("%zd", i);
8009 g_key_file_set_string (kf, uuid, key, hist[i]->line);
8013 /* Write history to file */
8014 data = g_key_file_to_data (kf, &len, NULL);
8016 g_file_set_contents (filename, data, len, NULL);
8019 g_key_file_free (kf);
8024 /*----------------------------------------------------------------------------*/
8027 editor_show_connection (NMConnection *connection, NmCli *nmc)
8029 nmc->print_output = NMC_PRINT_PRETTY;
8030 nmc->multiline_output = TRUE;
8031 nmc->escape_values = 0;
8033 /* Remove any previous data */
8034 nmc_empty_output_fields (nmc);
8036 nmc_connection_profile_details (connection, nmc, nmc->editor_show_secrets);
8040 editor_show_setting (NMSetting *setting, NmCli *nmc)
8042 g_print (_("['%s' setting values]\n"),
8043 nm_setting_get_name (setting));
8045 nmc->print_output = NMC_PRINT_NORMAL;
8046 nmc->multiline_output = TRUE;
8047 nmc->escape_values = 0;
8049 /* Remove any previous data */
8050 nmc_empty_output_fields (nmc);
8052 setting_details (setting, nmc, NULL, nmc->editor_show_secrets);
8056 NMC_EDITOR_MAIN_CMD_UNKNOWN = 0,
8057 NMC_EDITOR_MAIN_CMD_GOTO,
8058 NMC_EDITOR_MAIN_CMD_REMOVE,
8059 NMC_EDITOR_MAIN_CMD_SET,
8060 NMC_EDITOR_MAIN_CMD_DESCRIBE,
8061 NMC_EDITOR_MAIN_CMD_PRINT,
8062 NMC_EDITOR_MAIN_CMD_VERIFY,
8063 NMC_EDITOR_MAIN_CMD_SAVE,
8064 NMC_EDITOR_MAIN_CMD_ACTIVATE,
8065 NMC_EDITOR_MAIN_CMD_BACK,
8066 NMC_EDITOR_MAIN_CMD_HELP,
8067 NMC_EDITOR_MAIN_CMD_NMCLI,
8068 NMC_EDITOR_MAIN_CMD_QUIT,
8071 static NmcEditorMainCmd
8072 parse_editor_main_cmd (const char *cmd, char **cmd_arg)
8074 NmcEditorMainCmd editor_cmd = NMC_EDITOR_MAIN_CMD_UNKNOWN;
8077 vec = nmc_strsplit_set (cmd, " \t", 2);
8078 if (g_strv_length (vec) < 1) {
8081 return NMC_EDITOR_MAIN_CMD_UNKNOWN;
8084 if (matches (vec[0], "goto") == 0)
8085 editor_cmd = NMC_EDITOR_MAIN_CMD_GOTO;
8086 else if (matches (vec[0], "remove") == 0)
8087 editor_cmd = NMC_EDITOR_MAIN_CMD_REMOVE;
8088 else if (matches (vec[0], "set") == 0)
8089 editor_cmd = NMC_EDITOR_MAIN_CMD_SET;
8090 else if (matches (vec[0], "describe") == 0)
8091 editor_cmd = NMC_EDITOR_MAIN_CMD_DESCRIBE;
8092 else if (matches (vec[0], "print") == 0)
8093 editor_cmd = NMC_EDITOR_MAIN_CMD_PRINT;
8094 else if (matches (vec[0], "verify") == 0)
8095 editor_cmd = NMC_EDITOR_MAIN_CMD_VERIFY;
8096 else if (matches (vec[0], "save") == 0)
8097 editor_cmd = NMC_EDITOR_MAIN_CMD_SAVE;
8098 else if (matches (vec[0], "activate") == 0)
8099 editor_cmd = NMC_EDITOR_MAIN_CMD_ACTIVATE;
8100 else if (matches (vec[0], "back") == 0)
8101 editor_cmd = NMC_EDITOR_MAIN_CMD_BACK;
8102 else if (matches (vec[0], "help") == 0 || strcmp (vec[0], "?") == 0)
8103 editor_cmd = NMC_EDITOR_MAIN_CMD_HELP;
8104 else if (matches (vec[0], "quit") == 0)
8105 editor_cmd = NMC_EDITOR_MAIN_CMD_QUIT;
8106 else if (matches (vec[0], "nmcli") == 0)
8107 editor_cmd = NMC_EDITOR_MAIN_CMD_NMCLI;
8109 /* set pointer to command argument */
8111 *cmd_arg = vec[1] ? g_strstrip (g_strdup (vec[1])) : NULL;
8118 editor_main_usage (void)
8120 g_print ("------------------------------------------------------------------------------\n");
8121 /* TRANSLATORS: do not translate command names and keywords before ::
8122 * However, you should translate terms enclosed in <>.
8124 g_print (_("---[ Main menu ]---\n"
8125 "goto [<setting> | <prop>] :: go to a setting or property\n"
8126 "remove <setting>[.<prop>] | <prop> :: remove setting or reset property value\n"
8127 "set [<setting>.<prop> <value>] :: set property value\n"
8128 "describe [<setting>.<prop>] :: describe property\n"
8129 "print [all | <setting>[.<prop>]] :: print the connection\n"
8130 "verify [all | fix] :: verify the connection\n"
8131 "save [persistent|temporary] :: save the connection\n"
8132 "activate [<ifname>] [/<ap>|<nsp>] :: activate the connection\n"
8133 "back :: go one level up (back)\n"
8134 "help/? [<command>] :: print this help\n"
8135 "nmcli <conf-option> <value> :: nmcli configuration\n"
8136 "quit :: exit nmcli\n"));
8137 g_print ("------------------------------------------------------------------------------\n");
8141 editor_main_help (const char *command)
8144 editor_main_usage ();
8146 /* detailed command descriptions */
8147 NmcEditorMainCmd cmd = parse_editor_main_cmd (command, NULL);
8150 case NMC_EDITOR_MAIN_CMD_GOTO:
8151 g_print (_("goto <setting>[.<prop>] | <prop> :: enter setting/property for editing\n\n"
8152 "This command enters into a setting or property for editing it.\n\n"
8153 "Examples: nmcli> goto connection\n"
8154 " nmcli connection> goto secondaries\n"
8155 " nmcli> goto ipv4.addresses\n"));
8157 case NMC_EDITOR_MAIN_CMD_REMOVE:
8158 g_print (_("remove <setting>[.<prop>] :: remove setting or reset property value\n\n"
8159 "This command removes an entire setting from the connection, or if a property\n"
8160 "is given, resets that property to the default value.\n\n"
8161 "Examples: nmcli> remove wifi-sec\n"
8162 " nmcli> remove eth.mtu\n"));
8164 case NMC_EDITOR_MAIN_CMD_SET:
8165 g_print (_("set [<setting>.<prop> <value>] :: set property value\n\n"
8166 "This command sets property value.\n\n"
8167 "Example: nmcli> set con.id My connection\n"));
8169 case NMC_EDITOR_MAIN_CMD_DESCRIBE:
8170 g_print (_("describe [<setting>.<prop>] :: describe property\n\n"
8171 "Shows property description. You can consult nm-settings(5) "
8172 "manual page to see all NM settings and properties.\n"));
8174 case NMC_EDITOR_MAIN_CMD_PRINT:
8175 g_print (_("print [all] :: print setting or connection values\n\n"
8176 "Shows current property or the whole connection.\n\n"
8177 "Example: nmcli ipv4> print all\n"));
8179 case NMC_EDITOR_MAIN_CMD_VERIFY:
8180 g_print (_("verify [all | fix] :: verify setting or connection validity\n\n"
8181 "Verifies whether the setting or connection is valid and can be saved later.\n"
8182 "It indicates invalid values on error. Some errors may be fixed automatically\n"
8183 "by 'fix' option.\n\n"
8184 "Examples: nmcli> verify\n"
8185 " nmcli> verify fix\n"
8186 " nmcli bond> verify\n"));
8188 case NMC_EDITOR_MAIN_CMD_SAVE:
8189 g_print (_("save [persistent|temporary] :: save the connection\n\n"
8190 "Sends the connection profile to NetworkManager that either will save it\n"
8191 "persistently, or will only keep it in memory. 'save' without an argument\n"
8192 "means 'save persistent'.\n"
8193 "Note that once you save the profile persistently those settings are saved\n"
8194 "across reboot or restart. Subsequent changes can also be temporary or\n"
8195 "persistent, but any temporary changes will not persist across reboot or\n"
8196 "restart. If you want to fully remove the persistent connection, the connection\n"
8197 "profile must be deleted.\n"));
8199 case NMC_EDITOR_MAIN_CMD_ACTIVATE:
8200 g_print (_("activate [<ifname>] [/<ap>|<nsp>] :: activate the connection\n\n"
8201 "Activates the connection.\n\n"
8202 "Available options:\n"
8203 "<ifname> - device the connection will be activated on\n"
8204 "/<ap>|<nsp> - AP (Wi-Fi) or NSP (WiMAX) (prepend with / when <ifname> is not specified)\n"));
8206 case NMC_EDITOR_MAIN_CMD_BACK:
8207 g_print (_("back :: go to upper menu level\n\n"));
8209 case NMC_EDITOR_MAIN_CMD_HELP:
8210 g_print (_("help/? [<command>] :: help for the nmcli commands\n\n"));
8212 case NMC_EDITOR_MAIN_CMD_NMCLI:
8213 g_print (_("nmcli [<conf-option> <value>] :: nmcli configuration\n\n"
8214 "Configures nmcli. The following options are available:\n"
8215 "status-line yes | no [default: no]\n"
8216 "save-confirmation yes | no [default: yes]\n"
8217 "show-secrets yes | no [default: no]\n"
8218 "prompt-color <color> | <0-8> [default: 0]\n"
8219 "%s" /* color table description */
8221 "Examples: nmcli> nmcli status-line yes\n"
8222 " nmcli> nmcli save-confirmation no\n"
8223 " nmcli> nmcli prompt-color 3\n"),
8225 " 1 = \33[30mblack\33[0m\n"
8226 " 2 = \33[31mred\33[0m\n"
8227 " 3 = \33[32mgreen\33[0m\n"
8228 " 4 = \33[33myellow\33[0m\n"
8229 " 5 = \33[34mblue\33[0m\n"
8230 " 6 = \33[35mmagenta\33[0m\n"
8231 " 7 = \33[36mcyan\33[0m\n"
8232 " 8 = \33[37mwhite\33[0m\n");
8234 case NMC_EDITOR_MAIN_CMD_QUIT:
8235 g_print (_("quit :: exit nmcli\n\n"
8236 "This command exits nmcli. When the connection being edited "
8237 "is not saved, the user is asked to confirm the action.\n"));
8240 g_print (_("Unknown command: '%s'\n"), command);
8247 NMC_EDITOR_SUB_CMD_UNKNOWN = 0,
8248 NMC_EDITOR_SUB_CMD_SET,
8249 NMC_EDITOR_SUB_CMD_ADD,
8250 NMC_EDITOR_SUB_CMD_CHANGE,
8251 NMC_EDITOR_SUB_CMD_REMOVE,
8252 NMC_EDITOR_SUB_CMD_DESCRIBE,
8253 NMC_EDITOR_SUB_CMD_PRINT,
8254 NMC_EDITOR_SUB_CMD_BACK,
8255 NMC_EDITOR_SUB_CMD_HELP,
8256 NMC_EDITOR_SUB_CMD_QUIT
8259 static NmcEditorSubCmd
8260 parse_editor_sub_cmd (const char *cmd, char **cmd_arg)
8262 NmcEditorSubCmd editor_cmd = NMC_EDITOR_SUB_CMD_UNKNOWN;
8265 vec = nmc_strsplit_set (cmd, " \t", 2);
8266 if (g_strv_length (vec) < 1) {
8269 return NMC_EDITOR_SUB_CMD_UNKNOWN;
8272 if (matches (vec[0], "set") == 0)
8273 editor_cmd = NMC_EDITOR_SUB_CMD_SET;
8274 else if (matches (vec[0], "add") == 0)
8275 editor_cmd = NMC_EDITOR_SUB_CMD_ADD;
8276 else if (matches (vec[0], "change") == 0)
8277 editor_cmd = NMC_EDITOR_SUB_CMD_CHANGE;
8278 else if (matches (vec[0], "remove") == 0)
8279 editor_cmd = NMC_EDITOR_SUB_CMD_REMOVE;
8280 else if (matches (vec[0], "describe") == 0)
8281 editor_cmd = NMC_EDITOR_SUB_CMD_DESCRIBE;
8282 else if (matches (vec[0], "print") == 0)
8283 editor_cmd = NMC_EDITOR_SUB_CMD_PRINT;
8284 else if (matches (vec[0], "back") == 0)
8285 editor_cmd = NMC_EDITOR_SUB_CMD_BACK;
8286 else if (matches (vec[0], "help") == 0 || strcmp (vec[0], "?") == 0)
8287 editor_cmd = NMC_EDITOR_SUB_CMD_HELP;
8288 else if (matches (vec[0], "quit") == 0)
8289 editor_cmd = NMC_EDITOR_SUB_CMD_QUIT;
8291 /* set pointer to command argument */
8293 *cmd_arg = g_strdup (vec[1]);
8300 editor_sub_help (void)
8302 g_print ("------------------------------------------------------------------------------\n");
8303 /* TRANSLATORS: do not translate command names and keywords before ::
8304 * However, you should translate terms enclosed in <>.
8306 g_print (_("---[ Property menu ]---\n"
8307 "set [<value>] :: set new value\n"
8308 "add [<value>] :: add new option to the property\n"
8309 "change :: change current value\n"
8310 "remove [<index> | <option>] :: delete the value\n"
8311 "describe :: describe property\n"
8312 "print [setting | connection] :: print property (setting/connection) value(s)\n"
8313 "back :: go to upper level\n"
8314 "help/? [<command>] :: print this help or command description\n"
8315 "quit :: exit nmcli\n"));
8316 g_print ("------------------------------------------------------------------------------\n");
8320 editor_sub_usage (const char *command)
8326 /* detailed command descriptions */
8327 NmcEditorSubCmd cmdsub = parse_editor_sub_cmd (command, NULL);
8330 case NMC_EDITOR_SUB_CMD_SET:
8331 g_print (_("set [<value>] :: set new value\n\n"
8332 "This command sets provided <value> to this property\n"));
8334 case NMC_EDITOR_SUB_CMD_ADD:
8335 g_print (_("add [<value>] :: append new value to the property\n\n"
8336 "This command adds provided <value> to this property, if "
8337 "the property is of a container type. For single-valued "
8338 "properties the property value is replaced (same as 'set').\n"));
8340 case NMC_EDITOR_SUB_CMD_CHANGE:
8341 g_print (_("change :: change current value\n\n"
8342 "Displays current value and allows editing it.\n"));
8344 case NMC_EDITOR_SUB_CMD_REMOVE:
8345 g_print (_("remove [<value>|<index>|<option name>] :: delete the value\n\n"
8346 "Removes the property value. For single-valued properties, this sets the\n"
8347 "property back to its default value. For container-type properties, this removes\n"
8348 "all the values of that property, or you can specify an argument to remove just\n"
8349 "a single item or option. The argument is either a value or index of the item to\n"
8350 "remove, or an option name (for properties with named options).\n\n"
8351 "Examples: nmcli ipv4.dns> remove 8.8.8.8\n"
8352 " nmcli ipv4.dns> remove 2\n"
8353 " nmcli bond.options> remove downdelay\n\n"));
8355 case NMC_EDITOR_SUB_CMD_DESCRIBE:
8356 g_print (_("describe :: describe property\n\n"
8357 "Shows property description. You can consult nm-settings(5) "
8358 "manual page to see all NM settings and properties.\n"));
8360 case NMC_EDITOR_SUB_CMD_PRINT:
8361 g_print (_("print [property|setting|connection] :: print property (setting, connection) value(s)\n\n"
8362 "Shows property value. Providing an argument you can also display "
8363 "values for the whole setting or connection.\n"));
8365 case NMC_EDITOR_SUB_CMD_BACK:
8366 g_print (_("back :: go to upper menu level\n\n"));
8368 case NMC_EDITOR_SUB_CMD_HELP:
8369 g_print (_("help/? [<command>] :: help for nmcli commands\n\n"));
8371 case NMC_EDITOR_SUB_CMD_QUIT:
8372 g_print (_("quit :: exit nmcli\n\n"
8373 "This command exits nmcli. When the connection being edited "
8374 "is not saved, the user is asked to confirm the action.\n"));
8377 g_print (_("Unknown command: '%s'\n"), command);
8383 /*----------------------------------------------------------------------------*/
8387 NMActiveConnection *ac;
8391 static gboolean nmc_editor_cb_called;
8392 static GError *nmc_editor_error;
8393 static MonitorACInfo *nmc_editor_monitor_ac;
8394 static GMutex nmc_editor_mutex;
8395 static GCond nmc_editor_cond;
8398 * Store 'error' to shared 'nmc_editor_error' and monitoring info to
8399 * 'nmc_editor_monitor_ac' and signal the condition so that
8400 * the 'editor-thread' thread could process that.
8403 set_info_and_signal_editor_thread (GError *error, MonitorACInfo *monitor_ac_info)
8405 g_mutex_lock (&nmc_editor_mutex);
8406 nmc_editor_cb_called = TRUE;
8407 nmc_editor_error = error ? g_error_copy (error) : NULL;
8408 nmc_editor_monitor_ac = monitor_ac_info;
8409 g_cond_signal (&nmc_editor_cond);
8410 g_mutex_unlock (&nmc_editor_mutex);
8414 add_connection_editor_cb (GObject *client,
8415 GAsyncResult *result,
8418 NMRemoteConnection *connection;
8419 GError *error = NULL;
8421 connection = nm_client_add_connection_finish (NM_CLIENT (client), result, &error);
8422 set_info_and_signal_editor_thread (error, NULL);
8424 g_clear_object (&connection);
8425 g_clear_error (&error);
8429 update_connection_editor_cb (GObject *connection,
8430 GAsyncResult *result,
8433 GError *error = NULL;
8435 nm_remote_connection_commit_changes_finish (NM_REMOTE_CONNECTION (connection),
8437 set_info_and_signal_editor_thread (error, NULL);
8438 g_clear_error (&error);
8442 progress_activation_editor_cb (gpointer user_data)
8444 MonitorACInfo *info = (MonitorACInfo *) user_data;
8445 NMDevice *device = info->device;
8446 NMActiveConnection *ac = info->ac;
8447 NMActiveConnectionState ac_state;
8448 NMDeviceState dev_state;
8453 ac_state = nm_active_connection_get_state (ac);
8454 dev_state = nm_device_get_state (device);
8456 nmc_terminal_show_progress (nmc_device_state_to_string (dev_state));
8458 if ( ac_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED
8459 || dev_state == NM_DEVICE_STATE_ACTIVATED) {
8460 nmc_terminal_erase_line ();
8461 g_print (_("Connection successfully activated (D-Bus active path: %s)\n"),
8462 nm_object_get_path (NM_OBJECT (ac)));
8463 goto finish; /* we are done */
8464 } else if ( ac_state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED
8465 || dev_state == NM_DEVICE_STATE_FAILED) {
8466 nmc_terminal_erase_line ();
8467 g_print (_("Error: Connection activation failed.\n"));
8468 goto finish; /* we are done */
8475 g_object_unref (device);
8477 g_object_unref (ac);
8482 activate_connection_editor_cb (GObject *client,
8483 GAsyncResult *result,
8486 ActivateConnectionInfo *info = (ActivateConnectionInfo *) user_data;
8487 NMDevice *device = info->device;
8488 const GPtrArray *ac_devs;
8489 MonitorACInfo *monitor_ac_info = NULL;
8490 NMActiveConnection *active;
8491 GError *error = NULL;
8493 active = nm_client_activate_connection_finish (NM_CLIENT (client), result, &error);
8497 ac_devs = nm_active_connection_get_devices (active);
8498 device = ac_devs->len > 0 ? g_ptr_array_index (ac_devs, 0) : NULL;
8501 monitor_ac_info = g_malloc0 (sizeof (AddConnectionInfo));
8502 monitor_ac_info->device = g_object_ref (device);
8503 monitor_ac_info->ac = active;
8504 monitor_ac_info->monitor_id = g_timeout_add (120, progress_activation_editor_cb, monitor_ac_info);
8506 g_object_unref (active);
8508 set_info_and_signal_editor_thread (error, monitor_ac_info);
8509 g_clear_error (&error);
8512 /*----------------------------------------------------------------------------*/
8515 print_property_description (NMSetting *setting, const char *prop_name)
8519 desc = nmc_setting_get_property_desc (setting, prop_name);
8520 g_print ("\n=== [%s] ===\n%s\n", prop_name, desc);
8525 print_setting_description (NMSetting *setting)
8527 /* Show description of all properties */
8531 all_props = nmc_setting_get_valid_properties (setting);
8532 g_print (("<<< %s >>>\n"), nm_setting_get_name (setting));
8533 for (i = 0; all_props && all_props[i]; i++)
8534 print_property_description (setting, all_props[i]);
8535 g_strfreev (all_props);
8539 connection_remove_setting (NMConnection *connection, NMSetting *setting)
8543 g_return_val_if_fail (setting, FALSE);
8545 mandatory = is_setting_mandatory (connection, setting);
8547 nm_connection_remove_setting (connection, G_OBJECT_TYPE (setting));
8550 g_print (_("Error: setting '%s' is mandatory and cannot be removed.\n"),
8551 nm_setting_get_name (setting));
8556 editor_show_status_line (NMConnection *connection, gboolean dirty, gboolean temp)
8558 NMSettingConnection *s_con;
8559 const char *con_type, *con_id, *con_uuid;
8561 s_con = nm_connection_get_setting_connection (connection);
8563 con_type = nm_setting_connection_get_connection_type (s_con);
8564 con_id = nm_connection_get_id (connection);
8565 con_uuid = nm_connection_get_uuid (connection);
8567 /* TRANSLATORS: status line in nmcli connection editor */
8568 g_print (_("[ Type: %s | Name: %s | UUID: %s | Dirty: %s | Temp: %s ]\n"),
8569 con_type, con_id, con_uuid,
8570 dirty ? _("yes") : _("no"),
8571 temp ? _("yes") : _("no"));
8575 refresh_remote_connection (GWeakRef *weak, NMRemoteConnection **remote)
8579 g_return_val_if_fail (remote != NULL, FALSE);
8581 previous = (*remote != NULL);
8583 g_object_unref (*remote);
8584 *remote = g_weak_ref_get (weak);
8586 return (previous && !*remote);
8590 is_connection_dirty (NMConnection *connection, NMRemoteConnection *remote)
8592 return !nm_connection_compare (connection,
8593 remote ? NM_CONNECTION (remote) : NULL,
8594 NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS |
8595 NM_SETTING_COMPARE_FLAG_IGNORE_TIMESTAMP);
8602 gboolean want_quit = FALSE;
8604 answer = nmc_readline (_("The connection is not saved. "
8605 "Do you really want to quit? %s"),
8606 prompt_yes_no (FALSE, NULL));
8607 answer = answer ? g_strstrip (answer) : NULL;
8608 if (answer && matches (answer, WORD_LOC_YES) == 0)
8616 * Submenu for detailed property editing
8617 * Return: TRUE - continue; FALSE - should quit
8620 property_edit_submenu (NmCli *nmc,
8621 NMConnection *connection,
8622 NMRemoteConnection **rem_con,
8623 GWeakRef *rem_con_weak,
8624 NMSetting *curr_setting,
8625 const char *prop_name)
8627 NmcEditorSubCmd cmdsub;
8628 gboolean cmd_property_loop = TRUE;
8629 gboolean should_quit = FALSE;
8630 char *prop_val_user;
8631 gboolean set_result;
8632 GError *tmp_err = NULL;
8635 GValue prop_g_value = G_VALUE_INIT;
8636 gboolean temp_changes;
8639 /* Set global variable for use in TAB completion */
8640 nmc_tab_completion.property = prop_name;
8642 prompt = nmc_colorize (nmc, nmc->editor_prompt_color, NMC_TERM_FORMAT_NORMAL,
8644 nm_setting_get_name (curr_setting), prop_name);
8646 while (cmd_property_loop) {
8647 char *cmd_property_user;
8648 char *cmd_property_arg;
8650 /* Get the remote connection again, it may have disapeared */
8651 removed = refresh_remote_connection (rem_con_weak, rem_con);
8653 g_print (_("The connection profile has been removed from another client. "
8654 "You may type 'save' in the main menu to restore it.\n"));
8656 /* Connection is dirty? (not saved or differs from the saved) */
8657 dirty = is_connection_dirty (connection, *rem_con);
8658 temp_changes = *rem_con ? nm_remote_connection_get_unsaved (*rem_con) : TRUE;
8659 if (nmc->editor_status_line)
8660 editor_show_status_line (connection, dirty, temp_changes);
8662 cmd_property_user = nmc_readline ("%s", prompt);
8663 if (!cmd_property_user || *cmd_property_user == '\0')
8665 cmdsub = parse_editor_sub_cmd (g_strstrip (cmd_property_user), &cmd_property_arg);
8668 case NMC_EDITOR_SUB_CMD_SET:
8669 case NMC_EDITOR_SUB_CMD_ADD:
8670 /* list, arrays,...: SET replaces the whole property value
8671 * ADD adds the new value(s)
8672 * single values: : both SET and ADD sets the new value
8674 if (!cmd_property_arg) {
8675 const char **avals = nmc_setting_get_property_allowed_values (curr_setting, prop_name);
8677 char *avals_str = nmc_util_strv_for_display (avals, FALSE);
8678 g_print (_("Allowed values for '%s' property: %s\n"),
8679 prop_name, avals_str);
8682 prop_val_user = nmc_readline (_("Enter '%s' value: "), prop_name);
8684 prop_val_user = g_strdup (cmd_property_arg);
8686 /* nmc_setting_set_property() only adds new value, thus we have to
8687 * remove the original value and save it for error cases.
8689 if (cmdsub == NMC_EDITOR_SUB_CMD_SET) {
8690 nmc_property_get_gvalue (curr_setting, prop_name, &prop_g_value);
8691 nmc_property_set_default_value (curr_setting, prop_name);
8694 set_result = nmc_setting_set_property (curr_setting, prop_name, prop_val_user, &tmp_err);
8695 g_free (prop_val_user);
8697 g_print (_("Error: failed to set '%s' property: %s\n"), prop_name, tmp_err->message);
8698 g_clear_error (&tmp_err);
8699 if (cmdsub == NMC_EDITOR_SUB_CMD_SET) {
8700 /* Block change signals and restore original value */
8701 g_signal_handlers_block_matched (curr_setting, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL);
8702 nmc_property_set_gvalue (curr_setting, prop_name, &prop_g_value);
8703 g_signal_handlers_unblock_matched (curr_setting, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL);
8706 if (G_IS_VALUE (&prop_g_value))
8707 g_value_unset (&prop_g_value);
8710 case NMC_EDITOR_SUB_CMD_CHANGE:
8711 rl_startup_hook = nmc_rl_set_deftext;
8712 nmc_rl_pre_input_deftext = nmc_setting_get_property_parsable (curr_setting, prop_name, NULL);
8713 prop_val_user = nmc_readline (_("Edit '%s' value: "), prop_name);
8715 nmc_property_get_gvalue (curr_setting, prop_name, &prop_g_value);
8716 nmc_property_set_default_value (curr_setting, prop_name);
8718 if (!nmc_setting_set_property (curr_setting, prop_name, prop_val_user, &tmp_err)) {
8719 g_print (_("Error: failed to set '%s' property: %s\n"), prop_name, tmp_err->message);
8720 g_clear_error (&tmp_err);
8721 g_signal_handlers_block_matched (curr_setting, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL);
8722 nmc_property_set_gvalue (curr_setting, prop_name, &prop_g_value);
8723 g_signal_handlers_unblock_matched (curr_setting, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL);
8725 g_free (prop_val_user);
8726 if (G_IS_VALUE (&prop_g_value))
8727 g_value_unset (&prop_g_value);
8730 case NMC_EDITOR_SUB_CMD_REMOVE:
8731 if (cmd_property_arg) {
8732 unsigned long val_int = G_MAXUINT32;
8733 char *option = NULL;
8735 if (!nmc_string_to_uint (cmd_property_arg, TRUE, 0, G_MAXUINT32, &val_int))
8736 option = g_strdup (cmd_property_arg);
8738 if (!nmc_setting_remove_property_option (curr_setting, prop_name,
8739 option ? g_strstrip (option) : NULL,
8742 g_print (_("Error: %s\n"), tmp_err->message);
8743 g_clear_error (&tmp_err);
8747 if (!nmc_setting_reset_property (curr_setting, prop_name, &tmp_err)) {
8748 g_print (_("Error: failed to remove value of '%s': %s\n"), prop_name,
8750 g_clear_error (&tmp_err);
8755 case NMC_EDITOR_SUB_CMD_DESCRIBE:
8756 /* Show property description */
8757 print_property_description (curr_setting, prop_name);
8760 case NMC_EDITOR_SUB_CMD_PRINT:
8761 /* Print current connection settings/properties */
8762 if (cmd_property_arg) {
8763 if (matches (cmd_property_arg, "setting") == 0)
8764 editor_show_setting (curr_setting, nmc);
8765 else if ( matches (cmd_property_arg, "connection") == 0
8766 || matches (cmd_property_arg, "all") == 0)
8767 editor_show_connection (connection, nmc);
8769 g_print (_("Unknown command argument: '%s'\n"), cmd_property_arg);
8771 char *prop_val = nmc_setting_get_property (curr_setting, prop_name, NULL);
8772 g_print ("%s: %s\n", prop_name, prop_val);
8777 case NMC_EDITOR_SUB_CMD_BACK:
8778 /* Set global variable for use in TAB completion */
8779 nmc_tab_completion.property = NULL;
8780 cmd_property_loop = FALSE;
8783 case NMC_EDITOR_SUB_CMD_HELP:
8784 editor_sub_usage (cmd_property_arg);
8787 case NMC_EDITOR_SUB_CMD_QUIT:
8788 if (is_connection_dirty (connection, *rem_con)) {
8789 if (confirm_quit ()) {
8790 cmd_property_loop = FALSE;
8791 should_quit = TRUE; /* we will quit nmcli */
8794 cmd_property_loop = FALSE;
8795 should_quit = TRUE; /* we will quit nmcli */
8799 case NMC_EDITOR_SUB_CMD_UNKNOWN:
8801 g_print (_("Unknown command: '%s'\n"), cmd_property_user);
8804 g_free (cmd_property_user);
8805 g_free (cmd_property_arg);
8809 return !should_quit;
8813 * Split 'str' in the following format: [[[setting.]property] [value]]
8814 * and return the components in 'setting', 'property' and 'value'
8815 * Use g_free() to deallocate the returned strings.
8818 split_editor_main_cmd_args (const char *str, char **setting, char **property, char **value)
8820 char **args, **items;
8825 args = nmc_strsplit_set (str, " \t", 2);
8827 items = nmc_strsplit_set (args[0], ".", 2);
8828 if (g_strv_length (items) == 2) {
8830 *setting = g_strdup (items[0]);
8832 *property = g_strdup (items[1]);
8835 *property = g_strdup (items[0]);
8839 if (value && args[1])
8840 *value = g_strstrip (g_strdup (args[1]));
8846 create_setting_by_name (const char *name, const NameItem *valid_settings_main, const NameItem *valid_settings_slave)
8848 const char *setting_name;
8849 NMSetting *setting = NULL;
8851 /* Get a valid setting name */
8852 setting_name = check_valid_name (name, valid_settings_main, valid_settings_slave, NULL);
8855 setting = nmc_setting_new_for_name (setting_name);
8857 return NULL; /* This should really not happen */
8858 nmc_setting_custom_init (setting);
8864 ask_check_setting (const char *arg,
8865 const NameItem *valid_settings_main,
8866 const NameItem *valid_settings_slave,
8867 const char *valid_settings_str)
8869 char *setting_name_user;
8870 const char *setting_name;
8874 g_print (_("Available settings: %s\n"), valid_settings_str);
8875 setting_name_user = nmc_readline (EDITOR_PROMPT_SETTING);
8877 setting_name_user = g_strdup (arg);
8879 if (setting_name_user)
8880 g_strstrip (setting_name_user);
8882 if (!(setting_name = check_valid_name (setting_name_user,
8883 valid_settings_main,
8884 valid_settings_slave,
8886 g_print (_("Error: invalid setting name; %s\n"), err->message);
8887 g_clear_error (&err);
8889 g_free (setting_name_user);
8890 return setting_name;
8894 ask_check_property (const char *arg,
8895 const char **valid_props,
8896 const char *valid_props_str)
8898 char *prop_name_user;
8899 const char *prop_name;
8900 GError *tmp_err = NULL;
8903 g_print (_("Available properties: %s\n"), valid_props_str);
8904 prop_name_user = nmc_readline (EDITOR_PROMPT_PROPERTY);
8906 g_strstrip (prop_name_user);
8908 prop_name_user = g_strdup (arg);
8910 if (!(prop_name = nmc_string_is_valid (prop_name_user, valid_props, &tmp_err))) {
8911 g_print (_("Error: property %s\n"), tmp_err->message);
8912 g_clear_error (&tmp_err);
8914 g_free (prop_name_user);
8918 /* Copy timestamp from src do dst */
8920 update_connection_timestamp (NMConnection *src, NMConnection *dst)
8922 NMSettingConnection *s_con_src, *s_con_dst;
8924 s_con_src = nm_connection_get_setting_connection (src);
8925 s_con_dst = nm_connection_get_setting_connection (dst);
8926 if (s_con_src && s_con_dst) {
8927 guint64 timestamp = nm_setting_connection_get_timestamp (s_con_src);
8928 g_object_set (s_con_dst, NM_SETTING_CONNECTION_TIMESTAMP, timestamp, NULL);
8933 confirm_connection_saving (NMConnection *local, NMConnection *remote)
8935 NMSettingConnection *s_con_loc, *s_con_rem;
8936 gboolean ac_local, ac_remote;
8937 gboolean confirmed = TRUE;
8939 s_con_loc = nm_connection_get_setting_connection (local);
8940 g_assert (s_con_loc);
8941 ac_local = nm_setting_connection_get_autoconnect (s_con_loc);
8944 s_con_rem = nm_connection_get_setting_connection (remote);
8945 g_assert (s_con_rem);
8946 ac_remote = nm_setting_connection_get_autoconnect (s_con_rem);
8950 if (ac_local && !ac_remote) {
8952 answer = nmc_readline (_("Saving the connection with 'autoconnect=yes'. "
8953 "That might result in an immediate activation of the connection.\n"
8954 "Do you still want to save? %s"), prompt_yes_no (TRUE, NULL));
8955 answer = answer ? g_strstrip (answer) : NULL;
8956 if (!answer || matches (answer, WORD_LOC_YES) == 0)
8968 NMSetting *curr_setting;
8970 char *valid_props_str;
8971 } NmcEditorMenuContext;
8974 menu_switch_to_level0 (NmCli *nmc,
8975 NmcEditorMenuContext *menu_ctx,
8977 NmcTermColor prompt_color)
8979 menu_ctx->level = 0;
8980 g_free (menu_ctx->main_prompt);
8981 menu_ctx->main_prompt = nmc_colorize (nmc, prompt_color, NMC_TERM_FORMAT_NORMAL, "%s", prompt);
8982 menu_ctx->curr_setting = NULL;
8983 g_strfreev (menu_ctx->valid_props);
8984 menu_ctx->valid_props = NULL;
8985 g_free (menu_ctx->valid_props_str);
8986 menu_ctx->valid_props_str = NULL;
8990 menu_switch_to_level1 (NmCli *nmc,
8991 NmcEditorMenuContext *menu_ctx,
8993 const char *setting_name,
8994 NmcTermColor prompt_color)
8996 menu_ctx->level = 1;
8997 g_free (menu_ctx->main_prompt);
8998 menu_ctx->main_prompt = nmc_colorize (nmc, prompt_color, NMC_TERM_FORMAT_NORMAL,
8999 "nmcli %s> ", setting_name);
9000 menu_ctx->curr_setting = setting;
9001 g_strfreev (menu_ctx->valid_props);
9002 menu_ctx->valid_props = nmc_setting_get_valid_properties (menu_ctx->curr_setting);
9003 g_free (menu_ctx->valid_props_str);
9004 menu_ctx->valid_props_str = g_strjoinv (", ", menu_ctx->valid_props);
9008 editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_type)
9010 NMSettingConnection *s_con;
9011 NMRemoteConnection *rem_con;
9012 NMRemoteConnection *con_tmp;
9013 GWeakRef weak = { { NULL } };
9015 NmcEditorMainCmd cmd;
9017 gboolean cmd_loop = TRUE;
9018 char *cmd_arg = NULL;
9019 char *cmd_arg_s, *cmd_arg_p, *cmd_arg_v;
9020 const char *BASE_PROMPT = "nmcli> ";
9021 const NameItem *valid_settings_main = NULL;
9022 const NameItem *valid_settings_slave = NULL;
9023 char *valid_settings_str = NULL;
9024 const char *s_type = NULL;
9026 AddConnectionInfo *info = NULL;
9028 gboolean temp_changes;
9029 GError *err1 = NULL;
9030 NmcEditorMenuContext menu_ctx;
9032 s_con = nm_connection_get_setting_connection (connection);
9034 s_type = nm_setting_connection_get_slave_type (s_con);
9035 slv_type = g_strdup_printf ("%s-slave", s_type ? s_type : "no");
9037 valid_settings_main = get_valid_settings_array (connection_type);
9038 valid_settings_slave = get_valid_settings_array (slv_type);
9041 valid_settings_str = get_valid_options_string (valid_settings_main, valid_settings_slave);
9042 g_print (_("You may edit the following settings: %s\n"), valid_settings_str);
9045 menu_ctx.main_prompt = nmc_colorize (nmc, nmc->editor_prompt_color, NMC_TERM_FORMAT_NORMAL,
9047 menu_ctx.curr_setting = NULL;
9048 menu_ctx.valid_props = NULL;
9049 menu_ctx.valid_props_str = NULL;
9051 /* Get remote connection */
9052 con_tmp = nm_client_get_connection_by_uuid (nmc->client,
9053 nm_connection_get_uuid (connection));
9054 g_weak_ref_init (&weak, con_tmp);
9055 rem_con = g_weak_ref_get (&weak);
9058 /* Connection is dirty? (not saved or differs from the saved) */
9059 dirty = is_connection_dirty (connection, rem_con);
9060 temp_changes = rem_con ? nm_remote_connection_get_unsaved (rem_con) : TRUE;
9061 if (nmc->editor_status_line)
9062 editor_show_status_line (connection, dirty, temp_changes);
9064 /* Read user input */
9065 cmd_user = nmc_readline ("%s", menu_ctx.main_prompt);
9067 /* Get the remote connection again, it may have disapeared */
9068 removed = refresh_remote_connection (&weak, &rem_con);
9070 g_print (_("The connection profile has been removed from another client. "
9071 "You may type 'save' to restore it.\n"));
9073 if (!cmd_user || *cmd_user == '\0')
9075 cmd = parse_editor_main_cmd (g_strstrip (cmd_user), &cmd_arg);
9080 split_editor_main_cmd_args (cmd_arg, &cmd_arg_s, &cmd_arg_p, &cmd_arg_v);
9082 case NMC_EDITOR_MAIN_CMD_SET:
9083 /* Set property value */
9085 if (menu_ctx.level == 1) {
9086 const char *prop_name;
9087 char *prop_val_user = NULL;
9089 GError *tmp_err = NULL;
9091 prop_name = ask_check_property (cmd_arg,
9092 (const char **) menu_ctx.valid_props,
9093 menu_ctx.valid_props_str);
9097 avals = nmc_setting_get_property_allowed_values (menu_ctx.curr_setting, prop_name);
9099 char *avals_str = nmc_util_strv_for_display (avals, FALSE);
9100 g_print (_("Allowed values for '%s' property: %s\n"),
9101 prop_name, avals_str);
9104 prop_val_user = nmc_readline (_("Enter '%s' value: "), prop_name);
9106 /* Set property value */
9107 if (!nmc_setting_set_property (menu_ctx.curr_setting, prop_name, prop_val_user, &tmp_err)) {
9108 g_print (_("Error: failed to set '%s' property: %s\n"), prop_name, tmp_err->message);
9109 g_clear_error (&tmp_err);
9112 g_print (_("Error: no setting selected; valid are [%s]\n"), valid_settings_str);
9113 g_print (_("use 'goto <setting>' first, or 'set <setting>.<property>'\n"));
9116 NMSetting *ss = NULL;
9117 gboolean created_ss = FALSE;
9119 GError *tmp_err = NULL;
9122 /* setting provided as "setting.property" */
9123 ss = is_setting_valid (connection, valid_settings_main, valid_settings_slave, cmd_arg_s);
9125 ss = create_setting_by_name (cmd_arg_s, valid_settings_main, valid_settings_slave);
9127 g_print (_("Error: invalid setting argument '%s'; valid are [%s]\n"),
9128 cmd_arg_s, valid_settings_str);
9134 if (menu_ctx.curr_setting)
9135 ss = menu_ctx.curr_setting;
9137 g_print (_("Error: missing setting for '%s' property\n"), cmd_arg_p);
9142 prop_name = is_property_valid (ss, cmd_arg_p, &tmp_err);
9144 g_print (_("Error: invalid property: %s\n"), tmp_err->message);
9145 g_clear_error (&tmp_err);
9147 g_object_unref (ss);
9155 const char **avals = nmc_setting_get_property_allowed_values (ss, prop_name);
9157 char *avals_str = nmc_util_strv_for_display (avals, FALSE);
9158 g_print (_("Allowed values for '%s' property: %s\n"),
9159 prop_name, avals_str);
9162 cmd_arg_v = nmc_readline (_("Enter '%s' value: "), prop_name);
9165 /* Set property value */
9166 if (!nmc_setting_set_property (ss, prop_name, cmd_arg_v, &tmp_err)) {
9167 g_print (_("Error: failed to set '%s' property: %s\n"),
9168 prop_name, tmp_err->message);
9169 g_clear_error (&tmp_err);
9173 nm_connection_add_setting (connection, ss);
9178 case NMC_EDITOR_MAIN_CMD_GOTO:
9179 /* cmd_arg_s != NULL means 'setting.property' argument */
9180 if (menu_ctx.level == 0 || cmd_arg_s) {
9181 /* in top level - no setting selected yet */
9182 const char *setting_name;
9184 const char *user_arg = cmd_arg_s ? cmd_arg_s : cmd_arg_p;
9186 setting_name = ask_check_setting (user_arg,
9187 valid_settings_main,
9188 valid_settings_slave,
9189 valid_settings_str);
9193 setting = nm_connection_get_setting_by_name (connection, setting_name);
9195 setting = nmc_setting_new_for_name (setting_name);
9197 g_print (_("Error: unknown setting '%s'\n"), setting_name);
9200 nmc_setting_custom_init (setting);
9201 nm_connection_add_setting (connection, setting);
9203 /* Set global variable for use in TAB completion */
9204 nmc_tab_completion.setting = setting;
9206 /* Switch to level 1 */
9207 menu_switch_to_level1 (nmc, &menu_ctx, setting, setting_name, nmc->editor_prompt_color);
9210 g_print (_("You may edit the following properties: %s\n"), menu_ctx.valid_props_str);
9214 if (menu_ctx.level == 1 || cmd_arg_s) {
9215 /* level 1 - setting selected */
9216 const char *prop_name;
9218 prop_name = ask_check_property (cmd_arg_p,
9219 (const char **) menu_ctx.valid_props,
9220 menu_ctx.valid_props_str);
9224 /* submenu - level 2 - editing properties */
9225 cmd_loop = property_edit_submenu (nmc,
9229 menu_ctx.curr_setting,
9234 case NMC_EDITOR_MAIN_CMD_REMOVE:
9235 /* Remove setting from connection, or delete value of a property */
9237 if (menu_ctx.level == 1) {
9238 GError *tmp_err = NULL;
9239 const char *prop_name;
9241 prop_name = ask_check_property (cmd_arg,
9242 (const char **) menu_ctx.valid_props,
9243 menu_ctx.valid_props_str);
9247 /* Delete property value */
9248 if (!nmc_setting_reset_property (menu_ctx.curr_setting, prop_name, &tmp_err)) {
9249 g_print (_("Error: failed to remove value of '%s': %s\n"), prop_name,
9251 g_clear_error (&tmp_err);
9254 g_print (_("Error: no argument given; valid are [%s]\n"), valid_settings_str);
9256 NMSetting *ss = NULL;
9260 /* cmd_arg_s != NULL means argument is "setting.property" */
9261 descr_all = !cmd_arg_s && !menu_ctx.curr_setting;
9262 user_s = descr_all ? cmd_arg_p : cmd_arg_s ? cmd_arg_s : NULL;
9264 ss = is_setting_valid (connection,
9265 valid_settings_main,
9266 valid_settings_slave,
9269 if (check_valid_name (user_s,
9270 valid_settings_main,
9271 valid_settings_slave,
9273 g_print (_("Setting '%s' is not present in the connection.\n"),
9276 g_print (_("Error: invalid setting argument '%s'; valid are [%s]\n"),
9277 user_s, valid_settings_str);
9281 ss = menu_ctx.curr_setting;
9284 /* Remove setting from the connection */
9285 connection_remove_setting (connection, ss);
9286 if (ss == menu_ctx.curr_setting) {
9287 /* If we removed the setting we are in, go up */
9288 menu_switch_to_level0 (nmc, &menu_ctx, BASE_PROMPT, nmc->editor_prompt_color);
9289 nmc_tab_completion.setting = NULL; /* for TAB completion */
9292 GError *tmp_err = NULL;
9293 char *prop_name = is_property_valid (ss, cmd_arg_p, &tmp_err);
9295 /* Delete property value */
9296 if (!nmc_setting_reset_property (ss, prop_name, &tmp_err)) {
9297 g_print (_("Error: failed to remove value of '%s': %s\n"), prop_name,
9299 g_clear_error (&tmp_err);
9302 /* If the string is not a property, try it as a setting */
9304 s_tmp = is_setting_valid (connection,
9305 valid_settings_main,
9306 valid_settings_slave,
9309 /* Remove setting from the connection */
9310 connection_remove_setting (connection, s_tmp);
9311 /* coverity[copy_paste_error] - suppress Coverity COPY_PASTE_ERROR defect */
9312 if (ss == menu_ctx.curr_setting) {
9313 /* If we removed the setting we are in, go up */
9314 menu_switch_to_level0 (nmc, &menu_ctx, BASE_PROMPT, nmc->editor_prompt_color);
9315 nmc_tab_completion.setting = NULL; /* for TAB completion */
9318 g_print (_("Error: %s properties, nor it is a setting name.\n"),
9320 g_clear_error (&tmp_err);
9327 case NMC_EDITOR_MAIN_CMD_DESCRIBE:
9328 /* Print property description */
9330 if (menu_ctx.level == 1) {
9331 const char *prop_name;
9333 prop_name = ask_check_property (cmd_arg,
9334 (const char **) menu_ctx.valid_props,
9335 menu_ctx.valid_props_str);
9339 /* Show property description */
9340 print_property_description (menu_ctx.curr_setting, prop_name);
9342 g_print (_("Error: no setting selected; valid are [%s]\n"), valid_settings_str);
9343 g_print (_("use 'goto <setting>' first, or 'describe <setting>.<property>'\n"));
9346 NMSetting *ss = NULL;
9347 gboolean unref_ss = FALSE;
9351 /* cmd_arg_s != NULL means argument is "setting.property" */
9352 descr_all = !cmd_arg_s && !menu_ctx.curr_setting;
9353 user_s = descr_all ? cmd_arg_p : cmd_arg_s ? cmd_arg_s : NULL;
9355 ss = is_setting_valid (connection,
9356 valid_settings_main,
9357 valid_settings_slave,
9360 ss = create_setting_by_name (user_s,
9361 valid_settings_main,
9362 valid_settings_slave);
9364 g_print (_("Error: invalid setting argument '%s'; valid are [%s]\n"),
9365 user_s, valid_settings_str);
9371 ss = menu_ctx.curr_setting;
9374 /* Show description for all properties */
9375 print_setting_description (ss);
9377 GError *tmp_err = NULL;
9378 char *prop_name = is_property_valid (ss, cmd_arg_p, &tmp_err);
9380 /* Show property description */
9381 print_property_description (ss, prop_name);
9383 /* If the string is not a property, try it as a setting */
9385 s_tmp = is_setting_valid (connection,
9386 valid_settings_main,
9387 valid_settings_slave,
9390 print_setting_description (s_tmp);
9392 g_print (_("Error: invalid property: %s, "
9393 "neither a valid setting name.\n"),
9395 g_clear_error (&tmp_err);
9400 g_object_unref (ss);
9404 case NMC_EDITOR_MAIN_CMD_PRINT:
9405 /* Print current connection settings/properties */
9407 if (strcmp (cmd_arg, "all") == 0)
9408 editor_show_connection (connection, nmc);
9410 NMSetting *ss = NULL;
9411 gboolean whole_setting;
9414 /* cmd_arg_s != NULL means argument is "setting.property" */
9415 whole_setting = !cmd_arg_s && !menu_ctx.curr_setting;
9416 user_s = whole_setting ? cmd_arg_p : cmd_arg_s ? cmd_arg_s : NULL;
9419 s_name = check_valid_name (user_s,
9420 valid_settings_main,
9421 valid_settings_slave,
9424 g_print (_("Error: unknown setting: '%s'\n"), user_s);
9427 ss = nm_connection_get_setting_by_name (connection, s_name);
9429 g_print (_("Error: '%s' setting not present in the connection\n"), s_name);
9433 ss = menu_ctx.curr_setting;
9435 if (whole_setting) {
9436 /* Print the whole setting */
9437 editor_show_setting (ss, nmc);
9440 char *prop_name = is_property_valid (ss, cmd_arg_p, &err);
9442 /* Print one property */
9443 char *prop_val = nmc_setting_get_property (ss, prop_name, NULL);
9444 g_print ("%s.%s: %s\n", nm_setting_get_name (ss),prop_name , prop_val);
9447 /* If the string is not a property, try it as a setting */
9449 s_tmp = is_setting_valid (connection,
9450 valid_settings_main,
9451 valid_settings_slave,
9454 /* Print the whole setting */
9455 editor_show_setting (s_tmp, nmc);
9457 g_print (_("Error: invalid property: %s%s\n"),
9459 cmd_arg_s ? "" : _(", neither a valid setting name"));
9460 g_clear_error (&err);
9466 if (menu_ctx.curr_setting)
9467 editor_show_setting (menu_ctx.curr_setting, nmc);
9469 editor_show_connection (connection, nmc);
9473 case NMC_EDITOR_MAIN_CMD_VERIFY:
9474 /* Verify current setting or the whole connection */
9475 if (cmd_arg && strcmp (cmd_arg, "all") && strcmp (cmd_arg, "fix")) {
9476 g_print (_("Invalid verify option: %s\n"), cmd_arg);
9480 if ( menu_ctx.curr_setting
9481 && (!cmd_arg || strcmp (cmd_arg, "all") != 0)) {
9482 GError *tmp_err = NULL;
9483 (void) nm_setting_verify (menu_ctx.curr_setting, NULL, &tmp_err);
9484 g_print (_("Verify setting '%s': %s\n"),
9485 nm_setting_get_name (menu_ctx.curr_setting),
9486 tmp_err ? tmp_err->message : "OK");
9487 g_clear_error (&tmp_err);
9489 GError *tmp_err = NULL;
9490 gboolean valid, modified;
9491 gboolean fixed = TRUE;
9493 valid = nm_connection_verify (connection, &tmp_err);
9494 if (!valid && (g_strcmp0 (cmd_arg, "fix") == 0)) {
9495 /* Try to fix normalizable errors */
9496 g_clear_error (&tmp_err);
9497 fixed = nm_connection_normalize (connection, NULL, &modified, &tmp_err);
9499 g_print (_("Verify connection: %s\n"),
9500 tmp_err ? tmp_err->message : "OK");
9502 g_print (_("The error cannot be fixed automatically.\n"));
9503 g_clear_error (&tmp_err);
9507 case NMC_EDITOR_MAIN_CMD_SAVE:
9508 /* Save the connection */
9509 if (nm_connection_verify (connection, &err1)) {
9510 gboolean persistent = TRUE;
9512 /* parse argument */
9514 if (matches (cmd_arg, "temporary") == 0)
9516 else if (matches (cmd_arg, "persistent") == 0)
9519 g_print (_("Error: invalid argument '%s'\n"), cmd_arg);
9524 /* Ask for save confirmation if the connection changes to autoconnect=yes */
9525 if (nmc->editor_save_confirmation)
9526 if (!confirm_connection_saving (connection, NM_CONNECTION (rem_con)))
9530 /* Tell the settings service to add the new connection */
9531 info = g_malloc0 (sizeof (AddConnectionInfo));
9533 info->con_name = g_strdup (nm_connection_get_id (connection));
9534 add_new_connection (persistent,
9537 add_connection_editor_cb,
9540 /* Save/update already saved (existing) connection */
9541 nm_connection_replace_settings_from_connection (NM_CONNECTION (rem_con),
9543 update_connection (persistent, rem_con, update_connection_editor_cb, NULL);
9546 g_mutex_lock (&nmc_editor_mutex);
9547 //FIXME: add also a timeout for cases the callback is not called
9548 while (!nmc_editor_cb_called)
9549 g_cond_wait (&nmc_editor_cond, &nmc_editor_mutex);
9551 if (nmc_editor_error) {
9552 g_print (_("Error: Failed to save '%s' (%s) connection: %s\n"),
9553 nm_connection_get_id (connection),
9554 nm_connection_get_uuid (connection),
9555 nmc_editor_error->message);
9556 g_error_free (nmc_editor_error);
9559 _("Connection '%s' (%s) successfully saved.\n") :
9560 _("Connection '%s' (%s) successfully updated.\n"),
9561 nm_connection_get_id (connection),
9562 nm_connection_get_uuid (connection));
9564 con_tmp = nm_client_get_connection_by_uuid (nmc->client,
9565 nm_connection_get_uuid (connection));
9566 g_weak_ref_set (&weak, con_tmp);
9567 refresh_remote_connection (&weak, &rem_con);
9569 /* Replace local connection with the remote one to be sure they are equal.
9570 * This mitigates problems with plugins not preserving some properties or
9571 * adding ipv{4,6} settings when not present.
9574 char *s_name = NULL;
9575 if (menu_ctx.curr_setting)
9576 s_name = g_strdup (nm_setting_get_name (menu_ctx.curr_setting));
9578 /* Update settings in the local connection */
9579 nm_connection_replace_settings_from_connection (connection,
9580 NM_CONNECTION (con_tmp));
9582 /* Also update setting for menu context and TAB-completion */
9583 menu_ctx.curr_setting = s_name ? nm_connection_get_setting_by_name (connection, s_name) : NULL;
9584 nmc_tab_completion.setting = menu_ctx.curr_setting;
9589 nmc_editor_cb_called = FALSE;
9590 nmc_editor_error = NULL;
9591 g_mutex_unlock (&nmc_editor_mutex);
9593 g_print (_("Error: connection verification failed: %s\n"),
9594 err1 ? err1->message : _("(unknown error)"));
9595 g_print (_("You may try running 'verify fix' to fix errors.\n"));
9598 g_clear_error (&err1);
9601 case NMC_EDITOR_MAIN_CMD_ACTIVATE:
9603 GError *tmp_err = NULL;
9604 const char *ifname = cmd_arg_p;
9605 const char *ap_nsp = cmd_arg_v;
9607 /* When only AP/NSP is specified it is prepended with '/' */
9609 if (ifname && ifname[0] == '/') {
9610 ap_nsp = ifname + 1;
9614 ap_nsp = ap_nsp && ap_nsp[0] == '/' ? ap_nsp + 1 : ap_nsp;
9616 if (is_connection_dirty (connection, rem_con)) {
9617 g_print (_("Error: connection is not saved. Type 'save' first.\n"));
9620 if (!nm_connection_verify (NM_CONNECTION (rem_con), &tmp_err)) {
9621 g_print (_("Error: connection is not valid: %s\n"), tmp_err->message);
9622 g_clear_error (&tmp_err);
9626 nmc->nowait_flag = FALSE;
9628 nmc->print_output = NMC_PRINT_PRETTY;
9629 if (!nmc_activate_connection (nmc, NM_CONNECTION (rem_con), ifname, ap_nsp, ap_nsp, NULL,
9630 activate_connection_editor_cb, &tmp_err)) {
9631 g_print (_("Error: Cannot activate connection: %s.\n"), tmp_err->message);
9632 g_clear_error (&tmp_err);
9636 g_mutex_lock (&nmc_editor_mutex);
9637 while (!nmc_editor_cb_called)
9638 g_cond_wait (&nmc_editor_cond, &nmc_editor_mutex);
9640 if (nmc_editor_error) {
9641 g_print (_("Error: Failed to activate '%s' (%s) connection: %s\n"),
9642 nm_connection_get_id (connection),
9643 nm_connection_get_uuid (connection),
9644 nmc_editor_error->message);
9645 g_error_free (nmc_editor_error);
9647 g_print (_("Monitoring connection activation (press any key to continue)\n"));
9648 nmc_get_user_input ("");
9651 if (nmc_editor_monitor_ac) {
9652 if (nmc_editor_monitor_ac->monitor_id)
9653 g_source_remove (nmc_editor_monitor_ac->monitor_id);
9654 g_free (nmc_editor_monitor_ac);
9656 nmc_editor_cb_called = FALSE;
9657 nmc_editor_error = NULL;
9658 nmc_editor_monitor_ac = NULL;
9659 g_mutex_unlock (&nmc_editor_mutex);
9661 /* Update timestamp in local connection */
9662 update_connection_timestamp (NM_CONNECTION (rem_con), connection);
9667 case NMC_EDITOR_MAIN_CMD_BACK:
9668 /* Go back (up) an the menu */
9669 if (menu_ctx.level == 1) {
9670 menu_switch_to_level0 (nmc, &menu_ctx, BASE_PROMPT, nmc->editor_prompt_color);
9671 nmc_tab_completion.setting = NULL; /* for TAB completion */
9675 case NMC_EDITOR_MAIN_CMD_HELP:
9676 /* Print command help */
9677 editor_main_help (cmd_arg);
9680 case NMC_EDITOR_MAIN_CMD_NMCLI:
9681 if (cmd_arg_p && matches (cmd_arg_p, "status-line") == 0) {
9682 GError *tmp_err = NULL;
9684 if (!nmc_string_to_bool (cmd_arg_v ? g_strstrip (cmd_arg_v) : "", &bb, &tmp_err)) {
9685 g_print (_("Error: status-line: %s\n"), tmp_err->message);
9686 g_clear_error (&tmp_err);
9688 nmc->editor_status_line = bb;
9689 } else if (cmd_arg_p && matches (cmd_arg_p, "save-confirmation") == 0) {
9690 GError *tmp_err = NULL;
9692 if (!nmc_string_to_bool (cmd_arg_v ? g_strstrip (cmd_arg_v) : "", &bb, &tmp_err)) {
9693 g_print (_("Error: save-confirmation: %s\n"), tmp_err->message);
9694 g_clear_error (&tmp_err);
9696 nmc->editor_save_confirmation = bb;
9697 } else if (cmd_arg_p && matches (cmd_arg_p, "show-secrets") == 0) {
9698 GError *tmp_err = NULL;
9700 if (!nmc_string_to_bool (cmd_arg_v ? g_strstrip (cmd_arg_v) : "", &bb, &tmp_err)) {
9701 g_print (_("Error: show-secrets: %s\n"), tmp_err->message);
9702 g_clear_error (&tmp_err);
9704 nmc->editor_show_secrets = bb;
9705 } else if (cmd_arg_p && matches (cmd_arg_p, "prompt-color") == 0) {
9706 GError *tmp_err = NULL;
9708 color = nmc_term_color_parse_string (cmd_arg_v ? g_strstrip (cmd_arg_v) : " ", &tmp_err);
9710 g_print (_("Error: bad color: %s\n"), tmp_err->message);
9711 g_clear_error (&tmp_err);
9713 nmc->editor_prompt_color = color;
9714 g_free (menu_ctx.main_prompt);
9715 if (menu_ctx.level == 0)
9716 menu_ctx.main_prompt = nmc_colorize (nmc, nmc->editor_prompt_color, NMC_TERM_FORMAT_NORMAL,
9719 menu_ctx.main_prompt = nmc_colorize (nmc, nmc->editor_prompt_color, NMC_TERM_FORMAT_NORMAL,
9721 nm_setting_get_name (menu_ctx.curr_setting));
9723 } else if (!cmd_arg_p) {
9724 g_print (_("Current nmcli configuration:\n"));
9725 g_print ("status-line: %s\n"
9726 "save-confirmation: %s\n"
9727 "show-secrets: %s\n"
9728 "prompt-color: %d\n",
9729 nmc->editor_status_line ? "yes" : "no",
9730 nmc->editor_save_confirmation ? "yes" : "no",
9731 nmc->editor_show_secrets ? "yes" : "no",
9732 nmc->editor_prompt_color);
9734 g_print (_("Invalid configuration option '%s'; allowed [%s]\n"),
9735 cmd_arg_v ? cmd_arg_v : "", "status-line, save-confirmation, show-secrets, prompt-color");
9739 case NMC_EDITOR_MAIN_CMD_QUIT:
9740 if (is_connection_dirty (connection, rem_con)) {
9741 if (confirm_quit ())
9742 cmd_loop = FALSE; /* quit command loop */
9744 cmd_loop = FALSE; /* quit command loop */
9747 case NMC_EDITOR_MAIN_CMD_UNKNOWN:
9749 g_print (_("Unknown command: '%s'\n"), cmd_user);
9759 g_free (valid_settings_str);
9760 g_free (menu_ctx.main_prompt);
9761 g_strfreev (menu_ctx.valid_props);
9762 g_free (menu_ctx.valid_props_str);
9764 g_object_unref (rem_con);
9765 g_weak_ref_clear (&weak);
9767 /* Save history file */
9768 save_history_cmds (nm_connection_get_uuid (connection));
9774 get_ethernet_device_name (NmCli *nmc)
9776 const GPtrArray *devices;
9779 devices = nm_client_get_devices (nmc->client);
9780 for (i = 0; i < devices->len; i++) {
9781 NMDevice *dev = g_ptr_array_index (devices, i);
9782 if (NM_IS_DEVICE_ETHERNET (dev))
9783 return nm_device_get_iface (dev);
9789 editor_init_new_connection (NmCli *nmc, NMConnection *connection)
9791 NMSetting *setting, *base_setting;
9792 NMSettingConnection *s_con;
9793 const char *con_type;
9794 const char *slave_type = NULL;
9796 s_con = nm_connection_get_setting_connection (connection);
9798 con_type = nm_setting_connection_get_connection_type (s_con);
9800 /* Initialize new connection according to its type using sensible defaults. */
9802 nmc_setting_connection_connect_handlers (s_con, connection);
9804 if (g_strcmp0 (con_type, "bond-slave") == 0)
9805 slave_type = NM_SETTING_BOND_SETTING_NAME;
9806 if (g_strcmp0 (con_type, "team-slave") == 0)
9807 slave_type = NM_SETTING_TEAM_SETTING_NAME;
9808 if (g_strcmp0 (con_type, "bridge-slave") == 0)
9809 slave_type = NM_SETTING_BRIDGE_SETTING_NAME;
9812 const char *dev_ifname = get_ethernet_device_name (nmc);
9814 /* For bond/team/bridge slaves add 'wired' setting */
9815 setting = nm_setting_wired_new ();
9816 nm_connection_add_setting (connection, setting);
9818 g_object_set (s_con,
9819 NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
9820 NM_SETTING_CONNECTION_MASTER, dev_ifname ? dev_ifname : "eth0",
9821 NM_SETTING_CONNECTION_SLAVE_TYPE, slave_type,
9824 /* Add a "base" setting to the connection by default */
9825 base_setting = nmc_setting_new_for_name (con_type);
9828 nm_connection_add_setting (connection, base_setting);
9830 /* Set a sensible bond/team/bridge interface name by default */
9831 if (g_strcmp0 (con_type, NM_SETTING_BOND_SETTING_NAME) == 0)
9832 g_object_set (s_con,
9833 NM_SETTING_CONNECTION_INTERFACE_NAME, "nm-bond",
9835 if (g_strcmp0 (con_type, NM_SETTING_TEAM_SETTING_NAME) == 0)
9836 g_object_set (s_con,
9837 NM_SETTING_CONNECTION_INTERFACE_NAME, "nm-team",
9839 if (g_strcmp0 (con_type, NM_SETTING_BRIDGE_SETTING_NAME) == 0)
9840 g_object_set (s_con,
9841 NM_SETTING_CONNECTION_INTERFACE_NAME, "nm-bridge",
9844 /* Set sensible initial VLAN values */
9845 if (g_strcmp0 (con_type, NM_SETTING_VLAN_SETTING_NAME) == 0) {
9846 const char *dev_ifname = get_ethernet_device_name (nmc);
9848 g_object_set (NM_SETTING_VLAN (base_setting),
9849 NM_SETTING_VLAN_PARENT, dev_ifname ? dev_ifname : "eth0",
9850 NM_SETTING_VLAN_ID, 1,
9852 g_object_set (s_con,
9853 NM_SETTING_CONNECTION_MASTER, dev_ifname ? dev_ifname : "eth0",
9854 NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_VLAN_SETTING_NAME,
9858 /* Initialize 'transport-mode' so that 'infiniband' is valid */
9859 if (g_strcmp0 (con_type, NM_SETTING_INFINIBAND_SETTING_NAME) == 0)
9860 g_object_set (NM_SETTING_INFINIBAND (base_setting),
9861 NM_SETTING_INFINIBAND_TRANSPORT_MODE, "datagram",
9864 /* Initialize 'number' so that 'cdma' is valid */
9865 if (g_strcmp0 (con_type, NM_SETTING_CDMA_SETTING_NAME) == 0)
9866 g_object_set (NM_SETTING_CDMA (base_setting),
9867 NM_SETTING_CDMA_NUMBER, "#777",
9870 /* Initialize 'number' so that 'gsm' is valid */
9871 if (g_strcmp0 (con_type, NM_SETTING_GSM_SETTING_NAME) == 0)
9872 g_object_set (NM_SETTING_GSM (base_setting),
9873 NM_SETTING_GSM_NUMBER, "*99#",
9877 if (g_strcmp0 (con_type, NM_SETTING_WIRELESS_SETTING_NAME) == 0) {
9878 /* For Wi-Fi set mode to "infrastructure". Even though mode == NULL
9879 * is regarded as "infrastructure", explicit value makes no doubts.
9881 g_object_set (NM_SETTING_WIRELESS (base_setting),
9882 NM_SETTING_WIRELESS_MODE, NM_SETTING_WIRELESS_MODE_INFRA,
9885 /* Do custom initialization for wifi setting */
9886 nmc_setting_custom_init (base_setting);
9890 if (g_strcmp0 (con_type, NM_SETTING_ADSL_SETTING_NAME) == 0) {
9891 /* Initialize a protocol */
9892 g_object_set (NM_SETTING_ADSL (base_setting),
9893 NM_SETTING_ADSL_PROTOCOL, NM_SETTING_ADSL_PROTOCOL_PPPOE,
9897 /* Always add IPv4 and IPv6 settings for non-slave connections */
9898 setting = nm_setting_ip4_config_new ();
9899 nmc_setting_custom_init (setting);
9900 nm_connection_add_setting (connection, setting);
9902 setting = nm_setting_ip6_config_new ();
9903 nmc_setting_custom_init (setting);
9904 nm_connection_add_setting (connection, setting);
9909 editor_init_existing_connection (NMConnection *connection)
9911 NMSettingIPConfig *s_ip4, *s_ip6;
9912 NMSettingWireless *s_wireless;
9913 NMSettingConnection *s_con;
9915 s_ip4 = nm_connection_get_setting_ip4_config (connection);
9916 s_ip6 = nm_connection_get_setting_ip6_config (connection);
9917 s_wireless = nm_connection_get_setting_wireless (connection);
9918 s_con = nm_connection_get_setting_connection (connection);
9921 nmc_setting_ip4_connect_handlers (s_ip4);
9923 nmc_setting_ip6_connect_handlers (s_ip6);
9925 nmc_setting_wireless_connect_handlers (s_wireless);
9927 nmc_setting_connection_connect_handlers (s_con, connection);
9930 static NMCResultCode
9931 do_connection_edit (NmCli *nmc, int argc, char **argv)
9933 NMConnection *connection = NULL;
9934 NMSettingConnection *s_con;
9935 const char *connection_type;
9937 char *default_name = NULL;
9938 const char *type = NULL;
9939 char *type_ask = NULL;
9940 const char *con_name = NULL;
9941 const char *con = NULL;
9942 const char *con_id = NULL;
9943 const char *con_uuid = NULL;
9944 const char *con_path = NULL;
9945 const char *selector = NULL;
9947 GError *error = NULL;
9948 GError *err1 = NULL;
9949 nmc_arg_t exp_args[] = { {"type", TRUE, &type, FALSE},
9950 {"con-name", TRUE, &con_name, FALSE},
9951 {"id", TRUE, &con_id, FALSE},
9952 {"uuid", TRUE, &con_uuid, FALSE},
9953 {"path", TRUE, &con_path, FALSE},
9956 nmc->return_value = NMC_RESULT_SUCCESS;
9961 if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, &error)) {
9962 g_string_assign (nmc->return_text, error->message);
9963 nmc->return_value = error->code;
9964 g_clear_error (&error);
9969 /* Setup some readline completion stuff */
9970 /* Set a pointer to an alternative function to create matches */
9971 rl_attempted_completion_function = (rl_completion_func_t *) nmcli_editor_tab_completion;
9972 /* Use ' ' and '.' as word break characters */
9973 rl_completer_word_break_characters = ". ";
9976 if (con_id && !con_uuid && !con_path) {
9979 } else if (con_uuid && !con_id && !con_path) {
9982 } else if (con_path && !con_id && !con_uuid) {
9985 } else if (!con_path && !con_id && !con_uuid) {
9988 g_string_printf (nmc->return_text,
9989 _("Error: only one of 'id', uuid, or 'path' can be provided."));
9990 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
9996 /* Existing connection */
9997 NMConnection *found_con;
9999 found_con = nmc_find_connection (nmc->connections, selector, con, NULL);
10001 g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), con);
10002 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10006 /* Duplicate the connection and use that so that we need not
10007 * differentiate existing vs. new later
10009 connection = nm_simple_connection_new_clone (found_con);
10011 /* Merge secrets into the connection */
10012 update_secrets_in_connection (NM_REMOTE_CONNECTION (found_con), connection);
10014 s_con = nm_connection_get_setting_connection (connection);
10016 connection_type = nm_setting_connection_get_connection_type (s_con);
10019 g_print (_("Warning: editing existing connection '%s'; 'type' argument is ignored\n"),
10020 nm_connection_get_id (connection));
10022 g_print (_("Warning: editing existing connection '%s'; 'con-name' argument is ignored\n"),
10023 nm_connection_get_id (connection));
10025 /* Load previously saved history commands for the connection */
10026 load_history_cmds (nm_connection_get_uuid (connection));
10028 editor_init_existing_connection (connection);
10030 /* New connection */
10031 connection_type = check_valid_name (type, nmc_valid_connection_types, NULL, &err1);
10032 tmp_str = get_valid_options_string (nmc_valid_connection_types, NULL);
10034 while (!connection_type) {
10036 g_print (_("Valid connection types: %s\n"), tmp_str);
10038 g_print (_("Error: invalid connection type; %s\n"), err1->message);
10039 g_clear_error (&err1);
10041 type_ask = nmc_readline (EDITOR_PROMPT_CON_TYPE);
10042 type = type_ask = type_ask ? g_strstrip (type_ask) : NULL;
10043 connection_type = check_valid_name (type_ask, nmc_valid_connection_types, NULL, &err1);
10048 /* Create a new connection object */
10049 connection = nm_simple_connection_new ();
10051 /* Build up the 'connection' setting */
10052 s_con = (NMSettingConnection *) nm_setting_connection_new ();
10053 uuid = nm_utils_uuid_generate ();
10055 default_name = g_strdup (con_name);
10057 default_name = nmc_unique_connection_name (nmc->connections,
10058 get_name_alias (connection_type, nmc_valid_connection_types));
10060 g_object_set (s_con,
10061 NM_SETTING_CONNECTION_ID, default_name,
10062 NM_SETTING_CONNECTION_UUID, uuid,
10063 NM_SETTING_CONNECTION_TYPE, connection_type,
10066 g_free (default_name);
10067 nm_connection_add_setting (connection, NM_SETTING (s_con));
10069 /* Initialize the new connection so that it is valid from the start */
10070 editor_init_new_connection (nmc, connection);
10073 /* nmcli runs the editor */
10074 nmc->in_editor = TRUE;
10077 g_print (_("===| nmcli interactive connection editor |==="));
10080 g_print (_("Editing existing '%s' connection: '%s'"), connection_type, con);
10082 g_print (_("Adding a new '%s' connection"), connection_type);
10084 g_print (_("Type 'help' or '?' for available commands."));
10086 g_print (_("Type 'describe [<setting>.<prop>]' for detailed property description."));
10089 /* Set global variables for use in TAB completion */
10090 nmc_tab_completion.nmc = nmc;
10091 nmc_tab_completion.con_type = g_strdup (connection_type);
10092 nmc_tab_completion.connection = connection;
10094 /* Run menu loop */
10095 editor_menu_main (nmc, connection, connection_type);
10098 g_object_unref (connection);
10099 g_free (nmc_tab_completion.con_type);
10101 nmc->should_wait++;
10102 return nmc->return_value;
10105 g_assert (!connection);
10108 nmc->should_wait++;
10109 return nmc->return_value;
10114 modify_connection_cb (GObject *connection,
10115 GAsyncResult *result,
10116 gpointer user_data)
10118 NmCli *nmc = (NmCli *) user_data;
10119 GError *error = NULL;
10121 if (!nm_remote_connection_commit_changes_finish (NM_REMOTE_CONNECTION (connection),
10123 g_string_printf (nmc->return_text,
10124 _("Error: Failed to modify connection '%s': %s"),
10125 nm_connection_get_id (NM_CONNECTION (connection)),
10127 g_error_free (error);
10128 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10130 if (nmc->print_output == NMC_PRINT_PRETTY)
10131 g_print (_("Connection '%s' (%s) successfully modified.\n"),
10132 nm_connection_get_id (NM_CONNECTION (connection)),
10133 nm_connection_get_uuid (NM_CONNECTION (connection)));
10138 static NMCResultCode
10139 do_connection_modify (NmCli *nmc,
10140 gboolean temporary,
10144 NMConnection *connection = NULL;
10145 NMRemoteConnection *rc = NULL;
10147 const char *selector = NULL;
10148 GError *error = NULL;
10150 nmc->return_value = NMC_RESULT_SUCCESS;
10153 g_string_printf (nmc->return_text, _("Error: No arguments provided."));
10154 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10157 if ( strcmp (*argv, "id") == 0
10158 || strcmp (*argv, "uuid") == 0
10159 || strcmp (*argv, "path") == 0) {
10162 if (next_arg (&argc, &argv) != 0) {
10163 g_string_printf (nmc->return_text, _("Error: %s argument is missing."),
10165 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10172 g_string_printf (nmc->return_text, _("Error: connection ID is missing."));
10173 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10176 connection = nmc_find_connection (nmc->connections, selector, name, NULL);
10178 g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), name);
10179 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10182 rc = nm_client_get_connection_by_uuid (nmc->client,
10183 nm_connection_get_uuid (connection));
10185 g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), name);
10186 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10190 if (next_arg (&argc, &argv) != 0) {
10191 g_string_printf (nmc->return_text, _("Error: <setting>.<property> argument is missing."));
10192 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10196 if (!read_connection_properties (NM_CONNECTION (rc), argc, argv, &error)) {
10197 g_string_assign (nmc->return_text, error->message);
10198 nmc->return_value = error->code;
10199 g_clear_error (&error);
10203 update_connection (!temporary, rc, modify_connection_cb, nmc);
10205 nmc->should_wait++;
10207 return nmc->return_value;
10215 } CloneConnectionInfo;
10218 clone_connection_cb (GObject *client,
10219 GAsyncResult *result,
10220 gpointer user_data)
10222 CloneConnectionInfo *info = (CloneConnectionInfo *) user_data;
10223 NmCli *nmc = info->nmc;
10224 NMRemoteConnection *connection;
10225 GError *error = NULL;
10227 connection = nm_client_add_connection_finish (NM_CLIENT (client), result, &error);
10229 g_string_printf (nmc->return_text,
10230 _("Error: Failed to add '%s' connection: %s"),
10231 info->con_id, error->message);
10232 g_error_free (error);
10233 nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
10235 g_print (_("%s (%s) cloned as %s (%s).\n"),
10238 nm_connection_get_id (NM_CONNECTION (connection)),
10239 nm_connection_get_uuid (NM_CONNECTION (connection)));
10240 g_object_unref (connection);
10243 g_free (info->con_id);
10244 g_free (info->orig_id);
10245 g_free (info->orig_uuid);
10246 g_slice_free (CloneConnectionInfo, info);
10250 static NMCResultCode
10251 do_connection_clone (NmCli *nmc, gboolean temporary, int argc, char **argv)
10253 NMConnection *connection = NULL;
10254 NMConnection *new_connection = NULL;
10255 NMSettingConnection *s_con;
10256 CloneConnectionInfo *info;
10258 const char *new_name;
10259 char *name_ask = NULL;
10260 char *new_name_ask = NULL;
10261 const char *selector = NULL;
10266 name = name_ask = nmc_readline (PROMPT_CONNECTION);
10267 new_name = new_name_ask = nmc_readline (_("New connection name: "));
10269 g_string_printf (nmc->return_text, _("Error: No arguments provided."));
10270 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10274 if ( strcmp (*argv, "id") == 0
10275 || strcmp (*argv, "uuid") == 0
10276 || strcmp (*argv, "path") == 0) {
10279 if (next_arg (&argc, &argv) != 0) {
10280 g_string_printf (nmc->return_text, _("Error: %s argument is missing."),
10282 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10287 if (next_arg (&argc, &argv) != 0) {
10288 g_string_printf (nmc->return_text, _("Error: <new name> argument is missing."));
10289 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10293 if (next_arg (&argc, &argv) == 0) {
10294 g_string_printf (nmc->return_text, _("Error: unexpected extra argument '%s'."), *argv);
10295 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10301 g_string_printf (nmc->return_text, _("Error: connection ID is missing."));
10302 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10306 g_string_printf (nmc->return_text, _("Error: <new name> argument is missing."));
10307 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10311 connection = nmc_find_connection (nmc->connections, selector, name, NULL);
10313 g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), name);
10314 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10318 /* Copy the connection */
10319 new_connection = nm_simple_connection_new_clone (connection);
10321 s_con = nm_connection_get_setting_connection (new_connection);
10323 uuid = nm_utils_uuid_generate ();
10324 g_object_set (s_con,
10325 NM_SETTING_CONNECTION_ID, new_name,
10326 NM_SETTING_CONNECTION_UUID, uuid,
10330 /* Merge secrets into the new connection */
10331 update_secrets_in_connection (NM_REMOTE_CONNECTION (connection), new_connection);
10333 info = g_slice_new0 (CloneConnectionInfo);
10335 info->orig_id = g_strdup (nm_connection_get_id (connection));
10336 info->orig_uuid = g_strdup (nm_connection_get_uuid (connection));
10337 info->con_id = g_strdup (nm_connection_get_id (new_connection));
10339 /* Add the new cloned connection to NetworkManager */
10340 add_new_connection (!temporary,
10343 clone_connection_cb,
10346 nmc->should_wait = TRUE;
10348 if (new_connection)
10349 g_object_unref (new_connection);
10351 g_free (new_name_ask);
10353 return nmc->return_value;
10357 delete_cb (GObject *con, GAsyncResult *result, gpointer user_data)
10359 ConnectionCbInfo *info = (ConnectionCbInfo *) user_data;
10360 GError *error = NULL;
10362 if (!nm_remote_connection_delete_finish (NM_REMOTE_CONNECTION (con), result, &error)) {
10363 g_string_printf (info->nmc->return_text, _("Error: not all connections deleted."));
10364 g_printerr (_("Error: Connection deletion failed: %s"),
10366 g_error_free (error);
10367 info->nmc->return_value = NMC_RESULT_ERROR_CON_DEL;
10368 connection_cb_info_finish (info, con);
10370 if (info->nmc->nowait_flag)
10371 connection_cb_info_finish (info, con);
10375 static NMCResultCode
10376 do_connection_delete (NmCli *nmc, int argc, char **argv)
10378 NMConnection *connection;
10379 ConnectionCbInfo *info = NULL;
10380 GSList *queue = NULL, *iter;
10381 char **arg_arr = NULL;
10382 char **arg_ptr = argv;
10383 int arg_num = argc;
10384 GString *invalid_cons = NULL;
10387 if (nmc->timeout == -1)
10392 char *line = nmc_readline (PROMPT_CONNECTIONS);
10393 nmc_string_to_arg_array (line, NULL, TRUE, &arg_arr, &arg_num);
10397 if (arg_num == 0) {
10398 g_string_printf (nmc->return_text, _("Error: No connection specified."));
10399 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10404 while (arg_num > 0) {
10405 const char *selector = NULL;
10407 if ( strcmp (*arg_ptr, "id") == 0
10408 || strcmp (*arg_ptr, "uuid") == 0
10409 || strcmp (*arg_ptr, "path") == 0) {
10410 selector = *arg_ptr;
10411 if (next_arg (&arg_num, &arg_ptr) != 0) {
10412 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector);
10413 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10418 connection = nmc_find_connection (nmc->connections, selector, *arg_ptr, &pos);
10420 /* Check if the connection is unique. */
10421 /* Calling delete for the same connection repeatedly would result in
10422 * NM responding for the last D-Bus call only and we would stall. */
10423 if (!g_slist_find (queue, connection))
10424 queue = g_slist_prepend (queue, g_object_ref (connection));
10426 g_printerr (_("Error: unknown connection '%s'\n"), *arg_ptr);
10427 g_string_printf (nmc->return_text, _("Error: not all active connections found."));
10428 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10430 invalid_cons = g_string_new (NULL);
10431 g_string_append_printf (invalid_cons, "'%s', ", *arg_ptr);
10434 /* Take next argument (if there's no other connection of the same name) */
10436 next_arg (&arg_num, &arg_ptr);
10440 g_string_printf (nmc->return_text, _("Error: no connection provided."));
10441 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10444 queue = g_slist_reverse (queue);
10446 info = g_slice_new0 (ConnectionCbInfo);
10448 info->queue = queue;
10449 info->timeout_id = g_timeout_add_seconds (nmc->timeout, connection_op_timeout_cb, info);
10451 nmc->nowait_flag = (nmc->timeout == 0);
10452 nmc->should_wait++;
10454 g_signal_connect (nmc->client, NM_CLIENT_CONNECTION_REMOVED,
10455 G_CALLBACK (connection_removed_cb), info);
10457 /* Now delete the connections */
10458 for (iter = queue; iter; iter = g_slist_next (iter))
10459 nm_remote_connection_delete_async (NM_REMOTE_CONNECTION (iter->data),
10460 NULL, delete_cb, info);
10463 if (invalid_cons) {
10464 g_string_truncate (invalid_cons, invalid_cons->len-2); /* truncate trailing ", " */
10465 g_string_printf (nmc->return_text, _("Error: cannot delete unknown connection(s): %s."),
10466 invalid_cons->str);
10467 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10468 g_string_free (invalid_cons, TRUE);
10470 g_strfreev (arg_arr);
10471 return nmc->return_value;
10475 connection_changed (NMConnection *connection, NmCli *nmc)
10477 g_print (_("%s: connection profile changed\n"), nm_connection_get_id (connection));
10481 connection_watch (NmCli *nmc, NMConnection *connection)
10483 nmc->should_wait++;
10484 g_signal_connect (connection, NM_CONNECTION_CHANGED, G_CALLBACK (connection_changed), nmc);
10488 connection_unwatch (NmCli *nmc, NMConnection *connection)
10490 if (g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_changed), nmc))
10491 nmc->should_wait--;
10493 /* Terminate if all the watched connections disappeared. */
10494 if (!nmc->should_wait)
10499 connection_added (NMClient *client, NMRemoteConnection *con, NmCli *nmc)
10501 NMConnection *connection = NM_CONNECTION (con);
10503 g_print (_("%s: connection profile created\n"), nm_connection_get_id (connection));
10504 connection_watch (nmc, connection);
10508 connection_removed (NMClient *client, NMRemoteConnection *con, NmCli *nmc)
10510 NMConnection *connection = NM_CONNECTION (con);
10512 g_print (_("%s: connection profile removed\n"), nm_connection_get_id (connection));
10513 connection_unwatch (nmc, connection);
10516 static NMCResultCode
10517 do_connection_monitor (NmCli *nmc, int argc, char **argv)
10520 /* No connections specified. Monitor all. */
10523 nmc->connections = nm_client_get_connections (nmc->client);
10524 for (i = 0; i < nmc->connections->len; i++)
10525 connection_watch (nmc, g_ptr_array_index (nmc->connections, i));
10527 /* We'll watch the connection additions too, never exit. */
10528 nmc->should_wait++;
10529 g_signal_connect (nmc->client, NM_CLIENT_CONNECTION_ADDED, G_CALLBACK (connection_added), nmc);
10531 /* Look up the specified connections and watch them. */
10532 NMConnection *connection;
10533 char **arg_ptr = argv;
10534 int arg_num = argc;
10538 const char *selector = NULL;
10540 if ( strcmp (*arg_ptr, "id") == 0
10541 || strcmp (*arg_ptr, "uuid") == 0
10542 || strcmp (*arg_ptr, "path") == 0) {
10543 selector = *arg_ptr;
10544 if (next_arg (&arg_num, &arg_ptr) != 0) {
10545 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector);
10546 return NMC_RESULT_ERROR_USER_INPUT;
10550 connection = nmc_find_connection (nmc->connections, selector, *arg_ptr, &pos);
10552 connection_watch (nmc, connection);
10554 g_printerr (_("Error: unknown connection '%s'\n"), *arg_ptr);
10555 g_string_printf (nmc->return_text, _("Error: not all connections found."));
10556 return NMC_RESULT_ERROR_NOT_FOUND;
10559 /* Take next argument (if there's no other connection of the same name) */
10561 next_arg (&arg_num, &arg_ptr);
10562 } while (arg_num > 0);
10565 g_signal_connect (nmc->client, NM_CLIENT_CONNECTION_REMOVED, G_CALLBACK (connection_removed), nmc);
10567 return NMC_RESULT_SUCCESS;
10570 static NMCResultCode
10571 do_connection_reload (NmCli *nmc, int argc, char **argv)
10573 GError *error = NULL;
10575 nmc->return_value = NMC_RESULT_SUCCESS;
10577 if (!nm_client_reload_connections (nmc->client, NULL, &error)) {
10578 g_string_printf (nmc->return_text, _("Error: failed to reload connections: %s."),
10580 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10581 g_clear_error (&error);
10584 return nmc->return_value;
10587 static NMCResultCode
10588 do_connection_load (NmCli *nmc, int argc, char **argv)
10590 GError *error = NULL;
10591 char **filenames, **failures = NULL;
10594 nmc->return_value = NMC_RESULT_SUCCESS;
10597 g_string_printf (nmc->return_text, _("Error: No connection specified."));
10598 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10599 return nmc->return_value;
10602 filenames = g_new (char *, argc + 1);
10603 for (i = 0; i < argc; i++)
10604 filenames[i] = argv[i];
10605 filenames[i] = NULL;
10607 nm_client_load_connections (nmc->client, filenames, &failures, NULL, &error);
10608 g_free (filenames);
10610 g_string_printf (nmc->return_text, _("Error: failed to load connection: %s."),
10612 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10613 g_error_free (error);
10617 for (i = 0; failures[i]; i++)
10618 g_printerr (_("Could not load file '%s'\n"), failures[i]);
10619 g_strfreev (failures);
10622 return nmc->return_value;
10625 // FIXME: change the text when non-VPN connection types are supported
10626 #define PROMPT_IMPORT_TYPE PROMPT_VPN_TYPE
10627 #define PROMPT_IMPORT_FILE _("File to import: ")
10629 static NMCResultCode
10630 do_connection_import (NmCli *nmc, gboolean temporary, int argc, char **argv)
10632 GError *error = NULL;
10633 const char *type = NULL, *filename = NULL;
10634 char *type_ask = NULL, *filename_ask = NULL;
10635 AddConnectionInfo *info;
10636 NMConnection *connection = NULL;
10637 NMVpnEditorPlugin *plugin;
10641 type_ask = nmc_readline (PROMPT_IMPORT_TYPE);
10642 filename_ask = nmc_readline (PROMPT_IMPORT_FILE);
10643 type = type_ask = type_ask ? g_strstrip (type_ask) : NULL;
10644 filename = filename_ask = filename_ask ? g_strstrip (filename_ask) : NULL;
10646 g_string_printf (nmc->return_text, _("Error: No arguments provided."));
10647 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10653 if (strcmp (*argv, "type") == 0) {
10654 if (next_arg (&argc, &argv) != 0) {
10655 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
10656 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10662 g_printerr (_("Warning: 'type' already specified, ignoring extra one.\n"));
10664 } else if (strcmp (*argv, "file") == 0) {
10665 if (next_arg (&argc, &argv) != 0) {
10666 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
10667 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10673 g_printerr (_("Warning: 'file' already specified, ignoring extra one.\n"));
10675 g_string_printf (nmc->return_text, _("Unknown parameter: %s"), *argv);
10676 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10685 g_string_printf (nmc->return_text, _("Error: 'type' argument is required."));
10686 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10690 g_string_printf (nmc->return_text, _("Error: 'file' argument is required."));
10691 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10695 /* Import VPN configuration */
10696 plugin = nm_vpn_get_plugin_by_service (type, &error);
10698 g_string_printf (nmc->return_text, _("Error: failed to load VPN plugin: %s."),
10700 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10704 connection = nm_vpn_editor_plugin_import (plugin, filename, &error);
10706 g_string_printf (nmc->return_text, _("Error: failed to import '%s': %s."),
10707 filename, error->message);
10708 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10712 info = g_malloc0 (sizeof (AddConnectionInfo));
10714 info->con_name = g_strdup (nm_connection_get_id (connection));
10716 /* Add the new imported connection to NetworkManager */
10717 add_new_connection (!temporary,
10723 nmc->should_wait = TRUE;
10726 g_object_unref (connection);
10727 g_clear_error (&error);
10729 g_free (filename_ask);
10730 return nmc->return_value;
10733 static NMCResultCode
10734 do_connection_export (NmCli *nmc, int argc, char **argv)
10736 NMConnection *connection = NULL;
10738 const char *out_name = NULL;
10739 char *name_ask = NULL;
10740 char *out_name_ask = NULL;
10741 const char *path = NULL;
10742 const char *selector = NULL;
10743 const char *type = NULL;
10744 NMVpnEditorPlugin *plugin;
10745 GError *error = NULL;
10746 char tmpfile[] = "/tmp/nmcli-export-temp-XXXXXX";
10750 name_ask = nmc_readline (PROMPT_VPN_CONNECTION);
10751 name = name_ask = name_ask ? g_strstrip (name_ask) : NULL;
10752 out_name = out_name_ask = nmc_readline (_("Output file name: "));
10754 g_string_printf (nmc->return_text, _("Error: No arguments provided."));
10755 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10759 if ( strcmp (*argv, "id") == 0
10760 || strcmp (*argv, "uuid") == 0
10761 || strcmp (*argv, "path") == 0) {
10764 if (next_arg (&argc, &argv) != 0) {
10765 g_string_printf (nmc->return_text, _("Error: %s argument is missing."),
10767 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10772 if (next_arg (&argc, &argv) == 0)
10775 if (next_arg (&argc, &argv) == 0) {
10776 g_string_printf (nmc->return_text, _("Error: unknown extra argument: '%s'."), *argv);
10777 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10783 g_string_printf (nmc->return_text, _("Error: connection ID is missing."));
10784 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10787 connection = nmc_find_connection (nmc->connections, selector, name, NULL);
10789 g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), name);
10790 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10794 type = nm_connection_get_connection_type (connection);
10795 if (g_strcmp0 (type, NM_SETTING_VPN_SETTING_NAME) != 0) {
10796 g_string_printf (nmc->return_text, _("Error: the connection is not VPN."));
10797 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10800 type = nm_setting_vpn_get_service_type (nm_connection_get_setting_vpn (connection));
10802 /* Export VPN configuration */
10803 plugin = nm_vpn_get_plugin_by_service (type, &error);
10805 g_string_printf (nmc->return_text, _("Error: failed to load VPN plugin: %s."),
10807 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10815 fd = g_mkstemp (tmpfile);
10817 g_string_printf (nmc->return_text, _("Error: failed to create temporary file %s."), tmpfile);
10818 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10825 if (!nm_vpn_editor_plugin_export (plugin, path, connection, &error)) {
10826 g_string_printf (nmc->return_text, _("Error: failed to export '%s': %s."),
10827 nm_connection_get_id (connection), error->message);
10828 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10832 /* No output file -> copy data to stdout */
10834 char *contents = NULL;
10836 if (!g_file_get_contents (path, &contents, &len, &error)) {
10837 g_string_printf (nmc->return_text, _("Error: failed to read temporary file '%s': %s."),
10838 path, error->message);
10839 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10842 g_print ("%s", contents);
10847 if (!out_name && path)
10849 g_clear_error (&error);
10851 g_free (out_name_ask);
10852 return nmc->return_value;
10860 } NmcEditorThreadData;
10862 static GThread *editor_thread;
10863 static NmcEditorThreadData editor_thread_data;
10866 * We need to run do_connection_edit() in a thread so that
10867 * glib main loop is not blocked and could receive and process D-Bus
10871 connection_editor_thread_func (gpointer data)
10873 NmcEditorThreadData *td = (NmcEditorThreadData *) data;
10875 /* run editor for editing/adding connections */
10876 td->nmc->return_value = do_connection_edit (td->nmc, td->argc, td->argv);
10878 /* quit glib main loop now that we are done with this thread */
10885 gen_func_connection_names (const char *text, int state)
10888 const char **connections;
10891 if (nm_cli.connections->len == 0)
10894 connections = g_new (const char *, nm_cli.connections->len + 1);
10895 for (i = 0; i < nm_cli.connections->len; i++) {
10896 NMConnection *con = NM_CONNECTION (nm_cli.connections->pdata[i]);
10897 const char *id = nm_connection_get_id (con);
10898 connections[i] = id;
10900 connections[i] = NULL;
10902 ret = nmc_rl_gen_func_basic (text, state, connections);
10904 g_free (connections);
10909 gen_func_active_connection_names (const char *text, int state)
10912 const GPtrArray *acs;
10913 const char **connections;
10916 if (!nm_cli.client)
10919 acs = nm_client_get_active_connections (nm_cli.client);
10920 if (!acs || acs->len == 0)
10923 connections = g_new (const char *, acs->len + 1);
10924 for (i = 0; i < acs->len; i++)
10925 connections[i] = nm_active_connection_get_id (acs->pdata[i]);
10926 connections[i] = NULL;
10928 ret = nmc_rl_gen_func_basic (text, state, connections);
10930 g_free (connections);
10935 nmcli_con_tab_completion (const char *text, int start, int end)
10937 char **match_array = NULL;
10938 rl_compentry_func_t *generator_func = NULL;
10940 /* Disable readline's default filename completion */
10941 rl_attempted_completion_over = 1;
10943 if (g_strcmp0 (rl_prompt, PROMPT_CONNECTION) == 0) {
10944 /* Disable appending space after completion */
10945 rl_completion_append_character = '\0';
10947 if (!is_single_word (rl_line_buffer))
10950 generator_func = gen_func_connection_names;
10951 } else if (g_strcmp0 (rl_prompt, PROMPT_CONNECTIONS) == 0) {
10952 generator_func = gen_func_connection_names;
10953 } else if (g_strcmp0 (rl_prompt, PROMPT_ACTIVE_CONNECTIONS) == 0) {
10954 generator_func = gen_func_active_connection_names;
10955 } else if (g_strcmp0 (rl_prompt, PROMPT_IMPORT_TYPE) == 0) {
10956 generator_func = gen_func_vpn_types;
10957 } else if (g_strcmp0 (rl_prompt, PROMPT_IMPORT_FILE) == 0) {
10958 rl_attempted_completion_over = 0;
10959 rl_complete_with_tilde_expansion = 1;
10960 } else if (g_strcmp0 (rl_prompt, PROMPT_VPN_CONNECTION) == 0) {
10961 generator_func = gen_vpn_ids;
10964 if (generator_func)
10965 match_array = rl_completion_matches (text, generator_func);
10967 return match_array;
10971 parse_preferred_connection_order (const char *order, GError **error)
10973 char **strv, **iter;
10977 gboolean inverse, unique;
10980 strv = nmc_strsplit_set (order, ":", -1);
10981 if (!strv || !*strv) {
10982 g_set_error (error, NMCLI_ERROR, 0,
10983 _("incorrect string '%s' of '--order' option"), order);
10988 order_arr = g_array_sized_new (FALSE, FALSE, sizeof (NmcSortOrder), 4);
10989 for (iter = strv; iter && *iter; iter++) {
10994 if (str[0] == '+' || str[0] == '-')
10997 if (matches (str, "active") == 0)
10998 val = inverse ? NMC_SORT_ACTIVE_INV : NMC_SORT_ACTIVE;
10999 else if (matches (str, "name") == 0)
11000 val = inverse ? NMC_SORT_NAME_INV : NMC_SORT_NAME;
11001 else if (matches (str, "type") == 0)
11002 val = inverse ? NMC_SORT_TYPE_INV : NMC_SORT_TYPE;
11003 else if (matches (str, "path") == 0)
11004 val = inverse ? NMC_SORT_PATH_INV : NMC_SORT_PATH;
11006 g_array_unref (order_arr);
11008 g_set_error (error, NMCLI_ERROR, 0,
11009 _("incorrect item '%s' in '--order' option"), *iter);
11012 /* Check for duplicates and ignore them. */
11014 for (i = 0; i < order_arr->len; i++) {
11015 if (abs (g_array_index (order_arr, NmcSortOrder, i)) - abs (val) == 0) {
11021 /* Value is ok and unique, add it to the array */
11023 g_array_append_val (order_arr, val);
11030 /* Entry point function for connections-related commands: 'nmcli connection' */
11032 do_connections (NmCli *nmc, int argc, char **argv)
11034 GError *error = NULL;
11036 /* Register polkit agent */
11037 nmc_start_polkit_agent_start_try (nmc);
11039 /* Set completion function for 'nmcli con' */
11040 rl_attempted_completion_function = (rl_completion_func_t *) nmcli_con_tab_completion;
11042 /* Exit early on help */
11043 if (nmc_arg_is_help (*argv)) {
11045 return nmc->return_value;
11047 if (argc != 0 && nmc_arg_is_help (*(argv+1))) {
11048 if (usage_connection_second_level (*argv))
11049 return nmc->return_value;
11052 /* Get NMClient object early */
11053 nmc->get_client (nmc);
11055 /* Check whether NetworkManager is running */
11056 if (!nm_client_get_nm_running (nmc->client)) {
11057 g_string_printf (nmc->return_text, _("Error: NetworkManager is not running."));
11058 nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING;
11059 return nmc->return_value;
11061 /* Compare NM and nmcli versions */
11062 if (!nmc_versions_match (nmc))
11063 return nmc->return_value;
11065 /* Get the connection list */
11066 nmc->connections = nm_client_get_connections (nmc->client);
11068 /* Now parse the command line and perform the required operation */
11070 if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error))
11072 nmc->return_value = do_connections_show (nmc, FALSE, FALSE, NULL, argc, argv);
11074 if (matches (*argv, "show") == 0) {
11075 gboolean active = FALSE;
11076 gboolean show_secrets = FALSE;
11077 GArray *order = NULL;
11080 next_arg (&argc, &argv);
11081 /* check connection show options [--active] [--show-secrets] */
11082 for (i = 0; i < 3; i++) {
11083 if (!active && nmc_arg_is_option (*argv, "active")) {
11085 next_arg (&argc, &argv);
11087 /* --show-secrets is deprecated in favour of global --show-secrets */
11088 /* Keep it here for backwards compatibility */
11089 if (!show_secrets && nmc_arg_is_option (*argv, "show-secrets")) {
11090 show_secrets = TRUE;
11091 next_arg (&argc, &argv);
11093 if (!order && nmc_arg_is_option (*argv, "order")) {
11094 if (next_arg (&argc, &argv) != 0) {
11095 g_set_error_literal (&error, NMCLI_ERROR, 0,
11096 _("'--order' argument is missing"));
11099 order = parse_preferred_connection_order (*argv, &error);
11102 next_arg (&argc, &argv);
11105 show_secrets = nmc->show_secrets || show_secrets;
11106 nmc->return_value = do_connections_show (nmc, active, show_secrets, order, argc, argv);
11108 g_array_unref (order);
11109 } else if (matches(*argv, "up") == 0) {
11110 nmc->return_value = do_connection_up (nmc, argc-1, argv+1);
11111 } else if (matches(*argv, "down") == 0) {
11112 nmc->return_value = do_connection_down (nmc, argc-1, argv+1);
11113 } else if (matches(*argv, "add") == 0) {
11114 nmc->return_value = do_connection_add (nmc, argc-1, argv+1);
11115 } else if (matches(*argv, "edit") == 0) {
11116 nmc->should_wait++;
11117 editor_thread_data.nmc = nmc;
11118 editor_thread_data.argc = argc - 1;
11119 editor_thread_data.argv = argv + 1;
11120 editor_thread = g_thread_new ("editor-thread", connection_editor_thread_func, &editor_thread_data);
11121 g_thread_unref (editor_thread);
11122 } else if (matches(*argv, "delete") == 0) {
11123 nmc->return_value = do_connection_delete (nmc, argc-1, argv+1);
11124 } else if (matches(*argv, "reload") == 0) {
11125 nmc->return_value = do_connection_reload (nmc, argc-1, argv+1);
11126 } else if (matches(*argv, "load") == 0) {
11127 nmc->return_value = do_connection_load (nmc, argc-1, argv+1);
11128 } else if (matches (*argv, "modify") == 0) {
11129 gboolean temporary = FALSE;
11131 next_arg (&argc, &argv);
11132 if (nmc_arg_is_option (*argv, "temporary")) {
11134 next_arg (&argc, &argv);
11136 nmc->return_value = do_connection_modify (nmc, temporary, argc, argv);
11137 } else if (matches (*argv, "clone") == 0) {
11138 gboolean temporary = FALSE;
11140 next_arg (&argc, &argv);
11141 if (nmc_arg_is_option (*argv, "temporary")) {
11143 next_arg (&argc, &argv);
11145 nmc->return_value = do_connection_clone (nmc, temporary, argc, argv);
11146 } else if (matches(*argv, "import") == 0) {
11147 gboolean temporary = FALSE;
11149 next_arg (&argc, &argv);
11150 if (nmc_arg_is_option (*argv, "temporary")) {
11152 next_arg (&argc, &argv);
11154 nmc->return_value = do_connection_import (nmc, temporary, argc, argv);
11155 } else if (matches(*argv, "export") == 0) {
11156 nmc->return_value = do_connection_export (nmc, argc-1, argv+1);
11157 } else if (matches(*argv, "monitor") == 0) {
11158 nmc->return_value = do_connection_monitor (nmc, argc-1, argv+1);
11161 g_string_printf (nmc->return_text, _("Error: '%s' is not valid 'connection' command."), *argv);
11162 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
11166 return nmc->return_value;
11169 g_string_printf (nmc->return_text, _("Error: %s."), error->message);
11170 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
11171 g_error_free (error);
11172 return nmc->return_value;
11176 monitor_connections (NmCli *nmc)
11178 do_connection_monitor (nmc, 0, NULL);