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")) {
5546 /* Change properties in 'connection' setting */
5547 g_object_set (s_con,
5548 NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
5549 NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BOND_SETTING_NAME,
5552 /* Add ethernet setting */
5553 s_wired = (NMSettingWired *) nm_setting_wired_new ();
5554 nm_connection_add_setting (connection, NM_SETTING (s_wired));
5556 } else if (!strcmp (con_type, NM_SETTING_TEAM_SETTING_NAME)) {
5557 /* Build up the settings required for 'team' */
5558 gboolean success = FALSE;
5559 const char *ifname = NULL;
5560 const char *config_c = NULL;
5561 char *config = NULL;
5563 nmc_arg_t exp_args[] = { {"config", TRUE, &config_c, FALSE},
5566 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5569 /* Also ask for all optional arguments if '--ask' is specified. */
5570 config = g_strdup (config_c);
5572 do_questionnaire_team (&config);
5574 /* Generate ifname if conneciton doesn't have one */
5575 ifname = nm_setting_connection_get_interface_name (s_con);
5577 char *team_ifname = unique_master_iface_ifname (all_connections, "nm-team");
5579 g_object_set (s_con,
5580 NM_SETTING_CONNECTION_INTERFACE_NAME, team_ifname,
5582 g_free (team_ifname);
5585 /* Add 'team' setting */
5586 s_team = (NMSettingTeam *) nm_setting_team_new ();
5587 nm_connection_add_setting (connection, NM_SETTING (s_team));
5589 if (!nmc_team_check_config (config, &json, error)) {
5590 g_prefix_error (error, _("Error: "));
5594 /* Set team options */
5595 g_object_set (s_team, NM_SETTING_TEAM_CONFIG, json, NULL);
5604 } else if (!strcmp (con_type, "team-slave")) {
5606 /* Change properties in 'connection' setting */
5607 g_object_set (s_con,
5608 NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
5609 NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_TEAM_SETTING_NAME,
5612 /* Add ethernet setting */
5613 s_wired = (NMSettingWired *) nm_setting_wired_new ();
5614 nm_connection_add_setting (connection, NM_SETTING (s_wired));
5616 } else if (!strcmp (con_type, NM_SETTING_BRIDGE_SETTING_NAME)) {
5617 /* Build up the settings required for 'bridge' */
5618 gboolean success = FALSE;
5619 const char *ifname = NULL;
5620 const char *stp_c = NULL;
5622 const char *priority_c = NULL;
5623 char *priority = NULL;
5624 const char *fwd_delay_c = NULL;
5625 char *fwd_delay = NULL;
5626 const char *hello_time_c = NULL;
5627 char *hello_time = NULL;
5628 const char *max_age_c = NULL;
5629 char *max_age = NULL;
5630 const char *ageing_time_c = NULL;
5631 char *ageing_time = NULL;
5632 const char *mcast_snoop_c = NULL;
5633 char *mcast_snoop = NULL;
5634 gboolean stp_bool, mcast_snoop_bool;
5635 unsigned long stp_prio_int, fwd_delay_int, hello_time_int,
5636 max_age_int, ageing_time_int;
5637 const char *mac_c = NULL;
5639 nmc_arg_t exp_args[] = { {"stp", TRUE, &stp_c, FALSE},
5640 {"priority", TRUE, &priority_c, FALSE},
5641 {"forward-delay", TRUE, &fwd_delay_c, FALSE},
5642 {"hello-time", TRUE, &hello_time_c, FALSE},
5643 {"max-age", TRUE, &max_age_c, FALSE},
5644 {"ageing-time", TRUE, &ageing_time_c, FALSE},
5645 {"multicast-snooping", TRUE, &mcast_snoop_c, FALSE},
5646 {"mac", TRUE, &mac_c, FALSE},
5649 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5652 /* Also ask for all optional arguments if '--ask' is specified. */
5653 stp = g_strdup (stp_c);
5654 priority = g_strdup (priority_c);
5655 fwd_delay = g_strdup (fwd_delay_c);
5656 hello_time = g_strdup (hello_time_c);
5657 max_age = g_strdup (max_age_c);
5658 ageing_time = g_strdup (ageing_time_c);
5659 mcast_snoop = g_strdup (mcast_snoop_c);
5660 mac = g_strdup (mac_c);
5662 do_questionnaire_bridge (&stp, &priority, &fwd_delay, &hello_time,
5663 &max_age, &ageing_time, &mcast_snoop, &mac);
5665 /* Generate ifname if conneciton doesn't have one */
5666 ifname = nm_setting_connection_get_interface_name (s_con);
5668 char *bridge_ifname = unique_master_iface_ifname (all_connections, "nm-bridge");
5670 g_object_set (s_con,
5671 NM_SETTING_CONNECTION_INTERFACE_NAME, bridge_ifname,
5673 g_free (bridge_ifname);
5677 GError *tmp_err = NULL;
5678 if (!nmc_string_to_bool (stp, &stp_bool, &tmp_err)) {
5679 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5680 _("Error: 'stp': %s."), tmp_err->message);
5681 g_clear_error (&tmp_err);
5682 goto cleanup_bridge;
5686 GError *tmp_err = NULL;
5687 if (!nmc_string_to_bool (mcast_snoop, &mcast_snoop_bool, &tmp_err)) {
5688 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5689 _("Error: 'multicast-snooping': %s."), tmp_err->message);
5690 g_clear_error (&tmp_err);
5691 goto cleanup_bridge;
5695 /* Add 'bond' setting */
5696 /* Must be done *before* bridge_prop_string_to_uint() so that the type is known */
5697 s_bridge = (NMSettingBridge *) nm_setting_bridge_new ();
5698 nm_connection_add_setting (connection, NM_SETTING (s_bridge));
5701 if (!bridge_prop_string_to_uint (priority, "priority", NM_TYPE_SETTING_BRIDGE,
5702 NM_SETTING_BRIDGE_PRIORITY, &stp_prio_int, error))
5703 goto cleanup_bridge;
5705 if (!bridge_prop_string_to_uint (fwd_delay, "forward-delay", NM_TYPE_SETTING_BRIDGE,
5706 NM_SETTING_BRIDGE_FORWARD_DELAY, &fwd_delay_int, error))
5707 goto cleanup_bridge;
5709 if (!bridge_prop_string_to_uint (hello_time, "hello-time", NM_TYPE_SETTING_BRIDGE,
5710 NM_SETTING_BRIDGE_HELLO_TIME, &hello_time_int, error))
5711 goto cleanup_bridge;
5713 if (!bridge_prop_string_to_uint (max_age, "max-age", NM_TYPE_SETTING_BRIDGE,
5714 NM_SETTING_BRIDGE_MAX_AGE, &max_age_int, error))
5715 goto cleanup_bridge;
5717 if (!bridge_prop_string_to_uint (ageing_time, "ageing-time", NM_TYPE_SETTING_BRIDGE,
5718 NM_SETTING_BRIDGE_AGEING_TIME, &ageing_time_int, error))
5719 goto cleanup_bridge;
5720 if (!check_mac (mac, ARPHRD_ETHER, "mac", error))
5721 goto cleanup_bridge;
5723 /* Set bridge options */
5725 g_object_set (s_bridge, NM_SETTING_BRIDGE_STP, stp_bool, NULL);
5727 g_object_set (s_bridge, NM_SETTING_BRIDGE_PRIORITY, stp_prio_int, NULL);
5729 g_object_set (s_bridge, NM_SETTING_BRIDGE_FORWARD_DELAY, fwd_delay_int, NULL);
5731 g_object_set (s_bridge, NM_SETTING_BRIDGE_HELLO_TIME, hello_time_int, NULL);
5733 g_object_set (s_bridge, NM_SETTING_BRIDGE_MAX_AGE, max_age_int, NULL);
5735 g_object_set (s_bridge, NM_SETTING_BRIDGE_AGEING_TIME, ageing_time_int, NULL);
5737 g_object_set (s_bridge, NM_SETTING_BRIDGE_MULTICAST_SNOOPING, mcast_snoop_bool, NULL);
5739 g_object_set (s_bridge, NM_SETTING_BRIDGE_MAC_ADDRESS, mac, NULL);
5746 g_free (hello_time);
5748 g_free (ageing_time);
5749 g_free (mcast_snoop);
5754 } else if (!strcmp (con_type, "bridge-slave")) {
5756 /* Change properties in 'connection' setting */
5757 g_object_set (s_con,
5758 NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
5759 NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_BRIDGE_SETTING_NAME,
5762 /* Add ethernet setting */
5763 s_wired = (NMSettingWired *) nm_setting_wired_new ();
5764 nm_connection_add_setting (connection, NM_SETTING (s_wired));
5766 } else if (!strcmp (con_type, NM_SETTING_VPN_SETTING_NAME)) {
5767 /* Build up the settings required for 'vpn' */
5768 gboolean success = FALSE;
5769 const char *vpn_type = NULL;
5770 char *vpn_type_ask = NULL;
5771 const char *user_c = NULL;
5774 char *service_type = NULL;
5775 nmc_arg_t exp_args[] = { {"vpn-type", TRUE, &vpn_type, !ask},
5776 {"user", TRUE, &user_c, FALSE},
5779 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5782 if (!vpn_type && ask)
5783 vpn_type = vpn_type_ask = nmc_readline (PROMPT_VPN_TYPE);
5785 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5786 _("Error: 'vpn-type' is required."));
5790 vpn_type = g_strstrip (vpn_type_ask);
5792 if (!(st = nmc_string_is_valid (vpn_type, nmc_known_vpns, NULL))) {
5793 g_print (_("Warning: 'vpn-type': %s not known.\n"), vpn_type);
5796 service_type = g_strdup_printf ("%s.%s", NM_DBUS_INTERFACE, st);
5798 /* Also ask for all optional arguments if '--ask' is specified. */
5799 user = g_strdup (user_c);
5801 do_questionnaire_vpn (&user);
5803 /* Add 'vpn' setting */
5804 s_vpn = (NMSettingVpn *) nm_setting_vpn_new ();
5805 nm_connection_add_setting (connection, NM_SETTING (s_vpn));
5807 g_object_set (s_vpn, NM_SETTING_VPN_SERVICE_TYPE, service_type, NULL);
5808 g_object_set (s_vpn, NM_SETTING_VPN_USER_NAME, user, NULL);
5812 g_free (vpn_type_ask);
5813 g_free (service_type);
5818 } else if (!strcmp (con_type, NM_SETTING_OLPC_MESH_SETTING_NAME)) {
5819 /* Build up the settings required for 'olpc' */
5820 gboolean success = FALSE;
5821 char *ssid_ask = NULL;
5822 const char *ssid = NULL;
5824 const char *channel_c = NULL;
5825 char *channel = NULL;
5827 const char *dhcp_anycast_c = NULL;
5828 char *dhcp_anycast = NULL;
5829 nmc_arg_t exp_args[] = { {"ssid", TRUE, &ssid, !ask},
5830 {"channel", TRUE, &channel_c, FALSE},
5831 {"dhcp-anycast", TRUE, &dhcp_anycast_c, FALSE},
5834 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5838 ssid = ssid_ask = nmc_readline (_("SSID: "));
5840 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5841 _("Error: 'ssid' is required."));
5845 /* Also ask for all optional arguments if '--ask' is specified. */
5846 channel = g_strdup (channel_c);
5847 dhcp_anycast = g_strdup (dhcp_anycast_c);
5849 do_questionnaire_olpc (&channel, &dhcp_anycast);
5852 if (!nmc_string_to_uint (channel, TRUE, 1, 13, &chan)) {
5853 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5854 _("Error: 'channel': '%s' is not valid; use <1-13>."),
5859 if (!check_mac (dhcp_anycast, ARPHRD_ETHER, "dhcp-anycast", error))
5862 /* Add OLPC mesh setting */
5863 s_olpc_mesh = (NMSettingOlpcMesh *) nm_setting_olpc_mesh_new ();
5864 nm_connection_add_setting (connection, NM_SETTING (s_olpc_mesh));
5866 ssid_bytes = g_bytes_new (ssid, strlen (ssid));
5867 g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_SSID, ssid_bytes, NULL);
5869 g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_CHANNEL, chan, NULL);
5871 g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_CHANNEL, 1, NULL);
5873 g_object_set (s_olpc_mesh, NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS, dhcp_anycast, NULL);
5874 g_bytes_unref (ssid_bytes);
5880 g_free (dhcp_anycast);
5884 } else if (!strcmp (con_type, NM_SETTING_ADSL_SETTING_NAME)) {
5885 /* Build up the settings required for 'adsl' */
5886 gboolean success = FALSE;
5887 char *username_ask = NULL;
5888 const char *username = NULL;
5889 char *protocol_ask = NULL, *protocol = NULL;
5890 const char *protocol_c = NULL;
5891 const char *password_c = NULL;
5892 char *password = NULL;
5893 const char *encapsulation_c = NULL;
5894 char *encapsulation = NULL;
5895 nmc_arg_t exp_args[] = { {"username", TRUE, &username, !ask},
5896 {"protocol", TRUE, &protocol_c, !ask},
5897 {"password", TRUE, &password_c, FALSE},
5898 {"encapsulation", TRUE, &encapsulation_c, FALSE},
5901 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5904 if (!username && ask)
5905 username = username_ask = nmc_readline (_("Username: "));
5907 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5908 _("Error: 'username' is required."));
5912 #define PROMPT_ADSL_PROTO "(" NM_SETTING_ADSL_PROTOCOL_PPPOA "/" NM_SETTING_ADSL_PROTOCOL_PPPOE "/" NM_SETTING_ADSL_PROTOCOL_IPOATM "): "
5913 if (!protocol_c && ask)
5914 protocol_c = protocol_ask = nmc_readline (_("Protocol %s"), PROMPT_ADSL_PROTO);
5916 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5917 _("Error: 'protocol' is required."));
5920 protocol = g_strdup (protocol_c);
5921 if (!check_adsl_protocol (&protocol, error))
5924 /* Also ask for all optional arguments if '--ask' is specified. */
5925 password = g_strdup (password_c);
5926 encapsulation = g_strdup (encapsulation_c);
5928 do_questionnaire_adsl (show_secrets, &password, &encapsulation);
5930 if (!check_adsl_encapsulation (&encapsulation, error))
5933 /* Add ADSL setting */
5934 s_adsl = (NMSettingAdsl *) nm_setting_adsl_new ();
5935 nm_connection_add_setting (connection, NM_SETTING (s_adsl));
5937 g_object_set (s_adsl,
5938 NM_SETTING_ADSL_USERNAME, username,
5939 NM_SETTING_ADSL_PROTOCOL, protocol,
5940 NM_SETTING_ADSL_PASSWORD, password,
5941 NM_SETTING_ADSL_ENCAPSULATION, encapsulation,
5946 g_free (username_ask);
5949 g_free (protocol_ask);
5950 g_free (encapsulation);
5955 } else if (!strcmp (con_type, NM_SETTING_MACVLAN_SETTING_NAME)) {
5956 /* Build up the settings required for 'macvlan' */
5957 gboolean success = FALSE;
5958 const char *parent = NULL;
5959 char *parent_ask = NULL;
5960 const char *mode = NULL;
5961 char *mode_ask = NULL;
5962 const char *tap_c = NULL;
5964 NMSettingMacvlanMode mode_enum;
5965 gboolean valid_mac = FALSE;
5966 gboolean tap_bool = FALSE;
5967 nmc_arg_t exp_args[] = { {"dev", TRUE, &parent, !ask},
5968 {"mode", TRUE, &mode, !ask},
5969 {"tap", TRUE, &tap_c, FALSE},
5972 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
5976 parent = parent_ask = nmc_readline (_("MACVLAN parent device or connection UUID: "));
5978 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5979 _("Error: 'dev' is required."));
5983 if ( !(valid_mac = nm_utils_hwaddr_valid (parent, ETH_ALEN))
5984 && !nm_utils_is_uuid (parent)
5985 && !nm_utils_iface_valid_name (parent)) {
5986 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5987 _("Error: 'dev': '%s' is neither UUID, interface name, nor MAC."),
5989 goto cleanup_macvlan;
5993 mode = mode_ask = nmc_readline (PROMPT_MACVLAN_MODE);
5995 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
5996 _("Error: 'mode' is required."));
6000 if (!nm_utils_enum_from_str (nm_setting_macvlan_mode_get_type(), mode, (int *) &mode_enum, NULL)) {
6001 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6002 _("Error: 'mode' is not valid."));
6006 /* Also ask for all optional arguments if '--ask' is specified. */
6007 tap = g_strdup (tap_c);
6009 do_questionnaire_macvlan (&tap);
6012 GError *tmp_err = NULL;
6013 if (!nmc_string_to_bool (tap, &tap_bool, &tmp_err)) {
6014 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6015 _("Error: 'tap': %s."), tmp_err->message);
6016 g_clear_error (&tmp_err);
6017 goto cleanup_macvlan;
6021 /* Add 'macvlan' setting */
6022 s_macvlan = (NMSettingMacvlan *) nm_setting_macvlan_new ();
6023 nm_connection_add_setting (connection, NM_SETTING (s_macvlan));
6025 /* Add 'wired' setting if necessary */
6027 s_wired = (NMSettingWired *) nm_setting_wired_new ();
6028 nm_connection_add_setting (connection, NM_SETTING (s_wired));
6029 g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, parent, NULL);
6032 /* Set 'macvlan' properties */
6034 g_object_set (s_macvlan, NM_SETTING_MACVLAN_PARENT, parent, NULL);
6035 g_object_set (s_macvlan, NM_SETTING_MACVLAN_MODE, mode_enum, NULL);
6036 g_object_set (s_macvlan, NM_SETTING_MACVLAN_TAP, tap_bool, NULL);
6040 g_free (parent_ask);
6047 } else if (!strcmp (con_type, NM_SETTING_TUN_SETTING_NAME)) {
6048 /* Build up the settings required for 'tun' */
6049 gboolean success = FALSE;
6050 const char *mode_c = NULL;
6051 char *mode_ask = NULL, *mode = NULL;
6052 NMSettingTunMode mode_enum;
6053 const char *owner_c = NULL, *group_c = NULL;
6054 char *owner = NULL, *group = NULL;
6055 const char *pi_c = NULL, *vnet_hdr_c = NULL, *multi_queue_c = NULL;
6056 char *pi = NULL, *vnet_hdr = NULL, *multi_queue = NULL;
6057 gboolean pi_bool, vnet_hdr_bool, multi_queue_bool;
6058 nmc_arg_t exp_args[] = { {"mode", TRUE, &mode_c, !ask},
6059 {"owner", TRUE, &owner_c, FALSE},
6060 {"group", TRUE, &group_c, FALSE},
6061 {"pi", TRUE, &pi_c, FALSE},
6062 {"vnet-hdr", TRUE, &vnet_hdr_c, FALSE},
6063 {"multi-queue", TRUE, &multi_queue_c, FALSE},
6066 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
6069 if (!mode_c && ask) {
6070 mode_ask = nmc_readline (_("Mode %s"), PROMPT_TUN_MODE);
6071 mode_ask = mode_ask ? mode_ask : g_strdup ("tun");
6075 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6076 _("Error: 'mode' is required."));
6079 mode = g_strdup (mode_c);
6080 if (!check_tun_mode (&mode, error))
6083 if (owner && !check_user_group_id (owner, error))
6085 if (group && !check_user_group_id (group, error))
6088 owner = g_strdup (owner_c);
6089 group = g_strdup (group_c);
6090 pi = g_strdup (pi_c);
6091 vnet_hdr = g_strdup (vnet_hdr_c);
6092 multi_queue = g_strdup (multi_queue_c);
6094 do_questionnaire_tun (&owner, &group, &pi, &vnet_hdr, &multi_queue);
6097 GError *tmp_err = NULL;
6099 if (!nmc_string_to_bool (pi, &pi_bool, &tmp_err)) {
6100 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6101 _("Error: 'pi': %s."), tmp_err->message);
6102 g_clear_error (&tmp_err);
6108 GError *tmp_err = NULL;
6110 if (!nmc_string_to_bool (vnet_hdr, &vnet_hdr_bool, &tmp_err)) {
6111 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6112 _("Error: 'vnet-hdr': %s."), tmp_err->message);
6113 g_clear_error (&tmp_err);
6119 GError *tmp_err = NULL;
6121 if (!nmc_string_to_bool (multi_queue, &multi_queue_bool, &tmp_err)) {
6122 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6123 _("Error: 'multi-queue': %s."), tmp_err->message);
6124 g_clear_error (&tmp_err);
6128 /* Add 'tun' setting */
6129 s_tun = (NMSettingTun *) nm_setting_tun_new ();
6130 nm_connection_add_setting (connection, NM_SETTING (s_tun));
6131 mode_enum = !strcmp (mode, "tun") ? NM_SETTING_TUN_MODE_TUN : NM_SETTING_TUN_MODE_TAP;
6133 g_object_set (s_tun,
6134 NM_SETTING_TUN_MODE, mode_enum,
6135 NM_SETTING_TUN_OWNER, owner,
6136 NM_SETTING_TUN_GROUP, group,
6139 g_object_set (s_tun, NM_SETTING_TUN_PI, pi_bool, NULL);
6141 g_object_set (s_tun, NM_SETTING_TUN_VNET_HDR, vnet_hdr_bool, NULL);
6143 g_object_set (s_tun, NM_SETTING_TUN_MULTI_QUEUE, multi_queue_bool, NULL);
6153 g_free (multi_queue);
6157 } else if (!strcmp (con_type, NM_SETTING_IP_TUNNEL_SETTING_NAME)) {
6158 /* Build up the settings required for 'ip-tunnel' */
6159 const char *mode_c = NULL, *local_c = NULL, *remote_c = NULL;
6160 char *mode_ask = NULL, *remote_ask = NULL, *local = NULL;
6161 const char *parent_c = NULL;
6162 char *parent = NULL;
6163 gboolean success = FALSE;
6164 NMIPTunnelMode mode_enum;
6165 nmc_arg_t exp_args[] = { {"mode", TRUE, &mode_c, !ask},
6166 {"local", TRUE, &local_c, FALSE},
6167 {"remote", TRUE, &remote_c, !ask},
6168 {"dev", TRUE, &parent_c, FALSE},
6171 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
6175 mode_c = mode_ask = nmc_readline (PROMPT_IP_TUNNEL_MODE);
6177 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6178 _("Error: 'mode' is required."));
6179 goto cleanup_tunnel;
6182 if (!nm_utils_enum_from_str (nm_ip_tunnel_mode_get_type (),
6183 mode_c, (int *) &mode_enum, NULL)) {
6184 gs_free const char **values = NULL;
6185 gs_free char *values_str = NULL;
6187 values = nm_utils_enum_get_values (nm_ip_tunnel_mode_get_type (),
6188 NM_IP_TUNNEL_MODE_UNKNOWN + 1,
6190 values_str = g_strjoinv (",", (char **) values);
6191 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6192 _("Error: 'mode': '%s' is not valid, use one of %s"),
6193 mode_c, values_str);
6194 goto cleanup_tunnel;
6197 if (!remote_c && ask)
6198 remote_c = remote_ask = nmc_readline (_("Remote endpoint: "));
6200 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6201 _("Error: 'remote' is required."));
6202 goto cleanup_tunnel;
6205 if ( !nm_utils_ipaddr_valid (AF_INET, remote_c)
6206 && !nm_utils_ipaddr_valid (AF_INET6, remote_c)) {
6207 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6208 _("Error: 'remote': '%s' is not valid; must be an IP address"),
6210 goto cleanup_tunnel;
6213 local = g_strdup (local_c);
6214 parent = g_strdup (parent_c);
6216 do_questionnaire_ip_tunnel (&local, &parent);
6219 && !nm_utils_ipaddr_valid (AF_INET, local)
6220 && !nm_utils_ipaddr_valid (AF_INET6, local)) {
6221 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6222 _("Error: 'local': '%s' is not valid; must be an IP address"),
6224 goto cleanup_tunnel;
6228 if ( !nm_utils_is_uuid (parent)
6229 && !nm_utils_iface_valid_name (parent)) {
6230 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6231 _("Error: 'dev': '%s' is neither UUID nor interface name."),
6233 goto cleanup_tunnel;
6237 /* Add 'tunnel' setting */
6238 s_ip_tunnel = (NMSettingIPTunnel *) nm_setting_ip_tunnel_new ();
6239 nm_connection_add_setting (connection, NM_SETTING (s_ip_tunnel));
6241 /* Set 'tunnel' properties */
6242 g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_MODE, mode_enum, NULL);
6243 g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_REMOTE, remote_c, NULL);
6245 g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_LOCAL, local, NULL);
6247 g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_PARENT, parent, NULL);
6249 /* Set default values for IPv6 tunnels */
6250 if (nm_utils_ipaddr_valid (AF_INET6, remote_c)) {
6251 g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_TOS, 64, NULL);
6252 g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_ENCAPSULATION_LIMIT, 4, NULL);
6257 g_free (remote_ask);
6264 } else if (!strcmp (con_type, NM_SETTING_VXLAN_SETTING_NAME)) {
6265 /* Build up the settings required for 'vxlan' */
6266 gboolean success = FALSE;
6267 char *id_ask = NULL;
6268 const char *id = NULL;
6269 char *remote_ask = NULL;
6270 const char *remote = NULL;
6271 const char *parent_c = NULL, *local_c = NULL;
6272 const char *src_port_min_c = NULL, *src_port_max_c = NULL;
6273 const char *dst_port_c = NULL;
6274 char *parent = NULL, *local = NULL;
6275 char *src_port_min = NULL, *src_port_max = NULL, *dst_port = NULL;
6276 unsigned long int vni;
6277 unsigned long sport_min = G_MAXULONG, sport_max = G_MAXULONG;
6278 unsigned long dport = G_MAXULONG;
6279 nmc_arg_t exp_args[] = { {"id", TRUE, &id, !ask},
6280 {"remote", TRUE, &remote, !ask},
6281 {"dev", TRUE, &parent_c, FALSE},
6282 {"local", TRUE, &local_c, FALSE},
6283 {"source-port-min", TRUE, &src_port_min_c, FALSE},
6284 {"source-port-max", TRUE, &src_port_max_c, FALSE},
6285 {"destination-port", TRUE, &dst_port_c, FALSE},
6288 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
6292 id = id_ask = nmc_readline (_("VXLAN ID: "));
6294 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6295 _("Error: 'id' is required."));
6300 remote = remote_ask = nmc_readline (_("Remote: "));
6302 g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6303 _("Error: 'remote' is required."));
6307 if (!nmc_string_to_uint (id, TRUE, 0, (1UL << 24) - 1, &vni)) {
6308 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6309 _("Error: 'id': '%s' is not valid; use <0-16777215>."), id);
6313 parent = g_strdup (parent_c);
6314 local = g_strdup (local_c);
6315 src_port_min = g_strdup (src_port_min_c);
6316 src_port_max = g_strdup (src_port_max_c);
6317 dst_port = g_strdup (dst_port_c);
6320 do_questionnaire_vxlan (&parent, &local, &src_port_min, &src_port_max, &dst_port);
6323 if ( !nm_utils_is_uuid (parent)
6324 && !nm_utils_iface_valid_name (parent)) {
6325 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6326 _("Error: 'dev': '%s' is neither UUID nor interface name."),
6332 if ( !nm_utils_ipaddr_valid (AF_INET, remote)
6333 && !nm_utils_ipaddr_valid (AF_INET6, remote)) {
6334 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6335 _("Error: 'remote': '%s' is not a valid IP address"),
6341 if ( !nm_utils_ipaddr_valid (AF_INET, local)
6342 && !nm_utils_ipaddr_valid (AF_INET6, local)) {
6343 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6344 _("Error: 'local': '%s' is not a valid IP address"),
6351 if (!nmc_string_to_uint (src_port_min, TRUE, 0, 65535, &sport_min)) {
6352 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6353 _("Error: 'source-port-min': %s is not valid; use <0-65535>."),
6360 if (!nmc_string_to_uint (src_port_max, TRUE, 0, 65535, &sport_max)) {
6361 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6362 _("Error: 'source-port-max': %s is not valid; use <0-65535>."),
6369 if (!nmc_string_to_uint (dst_port, TRUE, 0, 65535, &dport)) {
6370 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6371 _("Error: 'destination-port': %s is not valid; use <0-65535>."),
6377 /* Add 'vxlan' setting */
6378 s_vxlan = (NMSettingVxlan *) nm_setting_vxlan_new ();
6379 nm_connection_add_setting (connection, NM_SETTING (s_vxlan));
6381 g_object_set (s_vxlan, NM_SETTING_VXLAN_ID, (guint) vni, NULL);
6382 g_object_set (s_vxlan, NM_SETTING_VXLAN_REMOTE, remote, NULL);
6383 g_object_set (s_vxlan, NM_SETTING_VXLAN_LOCAL, local, NULL);
6384 g_object_set (s_vxlan, NM_SETTING_VXLAN_PARENT, parent, NULL);
6386 if (sport_min != G_MAXULONG)
6387 g_object_set (s_vxlan, NM_SETTING_VXLAN_SOURCE_PORT_MIN, sport_min, NULL);
6388 if (sport_max != G_MAXULONG)
6389 g_object_set (s_vxlan, NM_SETTING_VXLAN_SOURCE_PORT_MAX, sport_max, NULL);
6390 if (dport != G_MAXULONG)
6391 g_object_set (s_vxlan, NM_SETTING_VXLAN_DESTINATION_PORT, dport, NULL);
6397 g_free (remote_ask);
6400 g_free (src_port_min);
6401 g_free (src_port_max);
6405 } else if (!strcmp (con_type, NM_SETTING_GENERIC_SETTING_NAME)) {
6406 /* Add 'generic' setting */
6407 s_generic = (NMSettingGeneric *) nm_setting_generic_new ();
6408 nm_connection_add_setting (connection, NM_SETTING (s_generic));
6410 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6411 _("Error: '%s' is not a valid connection type."),
6416 slave_type = nm_setting_connection_get_slave_type (s_con);
6419 /* Set global variables for use in TAB completion */
6420 nmc_tab_completion.con_type = (char *)slave_type;
6422 if (!strcmp (slave_type, NM_SETTING_TEAM_SETTING_NAME)) {
6423 /* Build up the settings required for 'team-slave' */
6424 gboolean success = FALSE;
6425 const char *master = NULL;
6426 char *master_ask = NULL;
6427 const char *type = NULL;
6428 const char *config_c = NULL;
6429 char *config = NULL;
6431 nmc_arg_t exp_args[] = { {"master", TRUE, &master, FALSE},
6432 {"type", TRUE, &type, FALSE},
6433 {"config", TRUE, &config_c, FALSE},
6436 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
6439 if (!complete_slave (s_con, all_connections, slave_type, master, type, ask, error))
6442 /* Also ask for all optional arguments if '--ask' is specified. */
6443 config = g_strdup (config_c);
6445 do_questionnaire_team_slave (&config);
6447 /* Add 'team-port' setting */
6448 s_team_port = (NMSettingTeamPort *) nm_setting_team_port_new ();
6449 nm_connection_add_setting (connection, NM_SETTING (s_team_port));
6451 if (!nmc_team_check_config (config, &json, error)) {
6452 g_prefix_error (error, _("Error: "));
6453 goto cleanup_team_slave;
6456 /* Set team-port options */
6457 g_object_set (s_team_port, NM_SETTING_TEAM_PORT_CONFIG, json, NULL);
6461 g_free (master_ask);
6467 } else if (!strcmp (slave_type, NM_SETTING_BRIDGE_SETTING_NAME)) {
6468 /* Build up the settings required for 'bridge-slave' */
6469 gboolean success = FALSE;
6470 const char *master = NULL;
6471 char *master_ask = NULL;
6472 const char *type = NULL;
6473 const char *priority_c = NULL;
6474 char *priority = NULL;
6475 const char *path_cost_c = NULL;
6476 char *path_cost = NULL;
6477 const char *hairpin_c = NULL;
6478 char *hairpin = NULL;
6479 unsigned long prio_int, path_cost_int;
6480 gboolean hairpin_bool;
6481 nmc_arg_t exp_args[] = { {"master", TRUE, &master, FALSE},
6482 {"type", TRUE, &type, FALSE},
6483 {"priority", TRUE, &priority_c, FALSE},
6484 {"path-cost", TRUE, &path_cost_c, FALSE},
6485 {"hairpin", TRUE, &hairpin_c, FALSE},
6488 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
6491 if (!complete_slave (s_con, all_connections, slave_type, master, type, ask, error))
6494 /* Add 'bridge-port' setting */
6495 /* Must be done *before* bridge_prop_string_to_uint() so that the type is known */
6496 s_bridge_port = (NMSettingBridgePort *) nm_setting_bridge_port_new ();
6497 nm_connection_add_setting (connection, NM_SETTING (s_bridge_port));
6499 /* Also ask for all optional arguments if '--ask' is specified. */
6500 priority = g_strdup (priority_c);
6501 path_cost = g_strdup (path_cost_c);
6502 hairpin = g_strdup (hairpin_c);
6504 do_questionnaire_bridge_slave (&priority, &path_cost, &hairpin);
6507 if (!bridge_prop_string_to_uint (priority, "priority", NM_TYPE_SETTING_BRIDGE_PORT,
6508 NM_SETTING_BRIDGE_PORT_PRIORITY, &prio_int, error))
6509 goto cleanup_bridge_slave;
6511 if (!bridge_prop_string_to_uint (path_cost, "path-cost", NM_TYPE_SETTING_BRIDGE_PORT,
6512 NM_SETTING_BRIDGE_PORT_PATH_COST, &path_cost_int, error))
6513 goto cleanup_bridge_slave;
6515 GError *tmp_err = NULL;
6516 if (!nmc_string_to_bool (hairpin, &hairpin_bool, &tmp_err)) {
6517 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6518 _("Error: 'hairpin': %s."), tmp_err->message);
6519 g_clear_error (&tmp_err);
6520 goto cleanup_bridge_slave;
6525 g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_PRIORITY, prio_int, NULL);
6527 g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_PATH_COST, path_cost_int, NULL);
6529 g_object_set (s_bridge_port, NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE, hairpin_bool, NULL);
6532 cleanup_bridge_slave:
6533 g_free (master_ask);
6540 /* Slave types without any specific settings ('bond-slave') */
6541 const char *master = NULL;
6542 const char *type = NULL;
6543 nmc_arg_t exp_args[] = { {"master", TRUE, &master, FALSE},
6544 {"type", TRUE, &type, FALSE},
6547 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
6550 if (!complete_slave (s_con, all_connections, slave_type, master, type, ask, error))
6555 /* Read and add IP configuration */
6556 NMIPAddress *ip4addr = NULL, *ip6addr = NULL;
6557 const char *ip4 = NULL, *gw4 = NULL, *ip6 = NULL, *gw6 = NULL;
6558 nmc_arg_t exp_args[] = { {"ip4", TRUE, &ip4, FALSE}, {"gw4", TRUE, &gw4, FALSE},
6559 {"ip6", TRUE, &ip6, FALSE}, {"gw6", TRUE, &gw6, FALSE},
6565 /* reset 'found' flag */
6566 for (p = exp_args; p->name; p++)
6569 ip4 = gw4 = ip6 = gw6 = NULL;
6571 if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, NULL))
6574 /* coverity[dead_error_begin] */
6576 ip4addr = nmc_parse_and_build_address (AF_INET, ip4, error);
6578 g_prefix_error (error, _("Error: "));
6581 add_ip4_address_to_connection (ip4addr, connection);
6584 /* coverity[dead_error_begin] */
6586 NMSettingIPConfig *s_ip = nm_connection_get_setting_ip4_config (connection);
6589 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6590 _("Error: IPv4 gateway specified without IPv4 addresses"));
6592 } else if (nm_setting_ip_config_get_gateway (s_ip)) {
6593 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6594 _("Error: multiple IPv4 gateways specified"));
6596 } else if (!nm_utils_ipaddr_valid (AF_INET, gw4)) {
6597 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6598 _("Error: Invalid IPv4 gateway '%s'"),
6603 NM_SETTING_IP_CONFIG_GATEWAY, gw4,
6607 /* coverity[dead_error_begin] */
6609 ip6addr = nmc_parse_and_build_address (AF_INET6, ip6, error);
6611 g_prefix_error (error, _("Error: "));
6614 add_ip6_address_to_connection (ip6addr, connection);
6617 /* coverity[dead_error_begin] */
6619 NMSettingIPConfig *s_ip = nm_connection_get_setting_ip6_config (connection);
6622 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6623 _("Error: IPv6 gateway specified without IPv6 addresses"));
6625 } else if (nm_setting_ip_config_get_gateway (s_ip)) {
6626 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6627 _("Error: multiple IPv6 gateways specified"));
6629 } else if (!nm_utils_ipaddr_valid (AF_INET, gw6)) {
6630 g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
6631 _("Error: Invalid IPv6 gateway '%s'"),
6636 NM_SETTING_IP_CONFIG_GATEWAY, gw6,
6641 /* Ask for addresses if '--ask' is specified. */
6643 do_questionnaire_ip (connection);
6647 /* Set extra connection properties. */
6648 nmc_arg_t exp_args[] = { {"--", FALSE, NULL, TRUE},
6651 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
6654 if (!read_connection_properties (connection, argc, argv, error))
6664 } AddConnectionInfo;
6667 add_connection_cb (GObject *client,
6668 GAsyncResult *result,
6671 AddConnectionInfo *info = (AddConnectionInfo *) user_data;
6672 NmCli *nmc = info->nmc;
6673 NMRemoteConnection *connection;
6674 GError *error = NULL;
6676 connection = nm_client_add_connection_finish (NM_CLIENT (client), result, &error);
6678 g_string_printf (nmc->return_text,
6679 _("Error: Failed to add '%s' connection: %s"),
6680 info->con_name, error->message);
6681 g_error_free (error);
6682 nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
6684 g_print (_("Connection '%s' (%s) successfully added.\n"),
6685 nm_connection_get_id (NM_CONNECTION (connection)),
6686 nm_connection_get_uuid (NM_CONNECTION (connection)));
6687 g_object_unref (connection);
6690 g_free (info->con_name);
6696 add_new_connection (gboolean persistent,
6698 NMConnection *connection,
6699 GAsyncReadyCallback callback,
6702 nm_client_add_connection_async (client, connection, persistent,
6703 NULL, callback, user_data);
6707 update_connection (gboolean persistent,
6708 NMRemoteConnection *connection,
6709 GAsyncReadyCallback callback,
6712 nm_remote_connection_commit_changes_async (connection, persistent,
6713 NULL, callback, user_data);
6717 gen_func_vpn_types (const char *text, int state)
6719 return nmc_rl_gen_func_basic (text, state, nmc_known_vpns);
6723 gen_func_bool_values_l10n (const char *text, int state)
6725 const char *words[] = { WORD_LOC_YES, WORD_LOC_NO, NULL };
6726 return nmc_rl_gen_func_basic (text, state, words);
6730 gen_func_wifi_mode (const char *text, int state)
6732 const char *words[] = { "infrastructure", "ap", "adhoc", NULL };
6733 return nmc_rl_gen_func_basic (text, state, words);
6737 gen_func_ib_type (const char *text, int state)
6739 const char *words[] = { "datagram", "connected", NULL };
6740 return nmc_rl_gen_func_basic (text, state, words);
6744 gen_func_bt_type (const char *text, int state)
6746 const char *words[] = { "panu", "dun-gsm", "dun-cdma", NULL };
6747 return nmc_rl_gen_func_basic (text, state, words);
6751 gen_func_bond_mode (const char *text, int state)
6753 const char *words[] = { "balance-rr", "active-backup", "balance-xor", "broadcast",
6754 "802.3ad", "balance-tlb", "balance-alb", NULL };
6755 return nmc_rl_gen_func_basic (text, state, words);
6758 gen_func_bond_mon_mode (const char *text, int state)
6760 const char *words[] = { "miimon", "arp", NULL };
6761 return nmc_rl_gen_func_basic (text, state, words);
6765 gen_func_adsl_proto (const char *text, int state)
6767 const char *words[] = { "pppoe", "pppoa", "ipoatm", NULL };
6768 return nmc_rl_gen_func_basic (text, state, words);
6772 gen_func_adsl_encap (const char *text, int state)
6774 const char *words[] = { "vcmux", "llc", NULL };
6775 return nmc_rl_gen_func_basic (text, state, words);
6779 gen_func_tun_mode (const char *text, int state)
6781 const char *words[] = { "tun", "tap", NULL };
6782 return nmc_rl_gen_func_basic (text, state, words);
6786 gen_func_ip_tunnel_mode (const char *text, int state)
6788 gs_free const char **words = NULL;
6790 words = nm_utils_enum_get_values (nm_ip_tunnel_mode_get_type (),
6791 NM_IP_TUNNEL_MODE_UNKNOWN + 1,
6793 return nmc_rl_gen_func_basic (text, state, words);
6797 gen_func_macvlan_mode (const char *text, int state)
6799 gs_free const char **words = NULL;
6801 words = nm_utils_enum_get_values (nm_setting_macvlan_mode_get_type(),
6802 NM_SETTING_MACVLAN_MODE_UNKNOWN + 1,
6804 return nmc_rl_gen_func_basic (text, state, words);
6808 gen_func_master_ifnames (const char *text, int state)
6814 NMSettingConnection *s_con;
6815 const char *con_type, *ifname;
6817 if (!nm_cli.connections)
6820 /* Disable appending space after completion */
6821 rl_completion_append_character = '\0';
6823 ifnames = g_ptr_array_sized_new (20);
6824 for (i = 0; i < nm_cli.connections->len; i++) {
6825 con = NM_CONNECTION (nm_cli.connections->pdata[i]);
6826 s_con = nm_connection_get_setting_connection (con);
6828 con_type = nm_setting_connection_get_connection_type (s_con);
6829 if (g_strcmp0 (con_type, nmc_tab_completion.con_type) != 0)
6831 ifname = nm_connection_get_interface_name (con);
6832 g_ptr_array_add (ifnames, (gpointer) ifname);
6834 g_ptr_array_add (ifnames, (gpointer) NULL);
6836 ret = nmc_rl_gen_func_basic (text, state, (const char **) ifnames->pdata);
6838 g_ptr_array_free (ifnames, TRUE);
6843 is_single_word (const char* line)
6847 n1 = strspn (line, " \t");
6848 n2 = strcspn (line+n1, " \t\0") + n1;
6849 n3 = strspn (line+n2, " \t");
6858 nmcli_con_add_tab_completion (const char *text, int start, int end)
6860 char **match_array = NULL;
6861 rl_compentry_func_t *generator_func = NULL;
6863 /* Disable readline's default filename completion */
6864 rl_attempted_completion_over = 1;
6866 /* Restore standard append character to space */
6867 rl_completion_append_character = ' ';
6869 if (!is_single_word (rl_line_buffer))
6872 if (g_strcmp0 (rl_prompt, PROMPT_CON_TYPE) == 0)
6873 generator_func = gen_connection_types;
6874 else if (g_strcmp0 (rl_prompt, PROMPT_VPN_TYPE) == 0)
6875 generator_func = gen_func_vpn_types;
6876 else if (g_strcmp0 (rl_prompt, PROMPT_MASTER) == 0)
6877 generator_func = gen_func_master_ifnames;
6878 else if ( g_str_has_suffix (rl_prompt, prompt_yes_no (TRUE, NULL))
6879 || g_str_has_suffix (rl_prompt, prompt_yes_no (TRUE, ":"))
6880 || g_str_has_suffix (rl_prompt, prompt_yes_no (FALSE, NULL))
6881 || g_str_has_suffix (rl_prompt, prompt_yes_no (FALSE, ":")))
6882 generator_func = gen_func_bool_values_l10n;
6883 else if (g_str_has_suffix (rl_prompt, PROMPT_WIFI_MODE))
6884 generator_func = gen_func_wifi_mode;
6885 else if (g_str_has_suffix (rl_prompt, PROMPT_IB_MODE))
6886 generator_func = gen_func_ib_type;
6887 else if (g_str_has_suffix (rl_prompt, PROMPT_BT_TYPE))
6888 generator_func = gen_func_bt_type;
6889 else if (g_str_has_prefix (rl_prompt, PROMPT_BOND_MODE))
6890 generator_func = gen_func_bond_mode;
6891 else if (g_str_has_suffix (rl_prompt, PROMPT_BOND_MON_MODE))
6892 generator_func = gen_func_bond_mon_mode;
6893 else if (g_str_has_suffix (rl_prompt, PROMPT_ADSL_PROTO))
6894 generator_func = gen_func_adsl_proto;
6895 else if (g_str_has_suffix (rl_prompt, PROMPT_ADSL_ENCAP))
6896 generator_func = gen_func_adsl_encap;
6897 else if (g_str_has_suffix (rl_prompt, PROMPT_TUN_MODE))
6898 generator_func = gen_func_tun_mode;
6899 else if (g_str_has_suffix (rl_prompt, PROMPT_IP_TUNNEL_MODE))
6900 generator_func = gen_func_ip_tunnel_mode;
6901 else if (g_str_has_suffix (rl_prompt, PROMPT_MACVLAN_MODE))
6902 generator_func = gen_func_macvlan_mode;
6905 match_array = rl_completion_matches (text, generator_func);
6910 static NMCResultCode
6911 do_connection_add (NmCli *nmc, int argc, char **argv)
6913 NMConnection *connection = NULL;
6914 NMSettingConnection *s_con;
6916 char *default_name = NULL;
6917 const char *type = NULL;
6918 char *type_ask = NULL;
6919 const char *con_name = NULL;
6920 const char *autoconnect = NULL;
6921 gboolean auto_bool = TRUE;
6922 const char *ifname = NULL;
6923 char *ifname_ask = NULL;
6924 gboolean ifname_mandatory = TRUE;
6925 const char *save = NULL;
6926 gboolean save_bool = TRUE;
6927 const char *master = NULL;
6928 const char *checked_master = NULL;
6929 const char *slave_type = NULL;
6930 AddConnectionInfo *info = NULL;
6931 const char *setting_name;
6932 GError *error = NULL;
6933 nmc_arg_t exp_args[] = { {"type", TRUE, &type, !nmc->ask},
6934 {"con-name", TRUE, &con_name, FALSE},
6935 {"autoconnect", TRUE, &autoconnect, FALSE},
6936 {"ifname", TRUE, &ifname, FALSE},
6937 {"save", TRUE, &save, FALSE},
6938 {"master", TRUE, &master, FALSE},
6939 {"slave-type", TRUE, &slave_type, FALSE},
6942 rl_attempted_completion_function = (rl_completion_func_t *) nmcli_con_add_tab_completion;
6944 nmc->return_value = NMC_RESULT_SUCCESS;
6946 if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, &error)) {
6947 g_string_assign (nmc->return_text, error->message);
6948 nmc->return_value = error->code;
6949 g_clear_error (&error);
6953 if (!type && nmc->ask) {
6954 char *types_tmp = get_valid_options_string (nmc_valid_connection_types, NULL);
6955 g_print ("Valid types: [%s]\n", types_tmp);
6956 type = type_ask = nmc_readline (PROMPT_CON_TYPE);
6960 g_string_printf (nmc->return_text, _("Error: 'type' argument is required."));
6961 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6965 type = g_strstrip (type_ask);
6967 if (!(setting_name = check_valid_name (type, nmc_valid_connection_types, NULL, &error))) {
6968 g_string_printf (nmc->return_text, _("Error: invalid connection type; %s."),
6970 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6971 g_clear_error (&error);
6975 GError *tmp_err = NULL;
6976 if (!nmc_string_to_bool (autoconnect, &auto_bool, &tmp_err)) {
6977 g_string_printf (nmc->return_text, _("Error: 'autoconnect': %s."),
6979 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6980 g_clear_error (&tmp_err);
6985 GError *tmp_err = NULL;
6986 if (!nmc_string_to_bool (save, &save_bool, &tmp_err)) {
6987 g_string_printf (nmc->return_text, _("Error: 'save': %s."),
6989 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
6990 g_clear_error (&tmp_err);
6995 /* ifname is mandatory for all connection types except virtual ones (bond, team, bridge, vlan) */
6996 if ( strcmp (type, NM_SETTING_BOND_SETTING_NAME) == 0
6997 || strcmp (type, NM_SETTING_TEAM_SETTING_NAME) == 0
6998 || strcmp (type, NM_SETTING_BRIDGE_SETTING_NAME) == 0
6999 || strcmp (type, NM_SETTING_VLAN_SETTING_NAME) == 0)
7000 ifname_mandatory = FALSE;
7002 if (!ifname && ifname_mandatory) {
7004 ifname = ifname_ask = nmc_readline (_("Interface name [*]: "));
7006 ifname = ifname_ask = g_strdup ("*");
7009 g_string_printf (nmc->return_text, _("Error: 'ifname' argument is required."));
7011 g_string_printf (nmc->return_text, _("Error: mandatory 'ifname' not seen before '%s'."),
7013 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
7018 if (!nm_utils_iface_valid_name (ifname) && strcmp (ifname, "*") != 0) {
7019 g_string_printf (nmc->return_text,
7020 _("Error: 'ifname': '%s' is not a valid interface nor '*'."),
7022 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
7025 /* Special value of '*' means no specific interface name */
7026 if (strcmp (ifname, "*") == 0)
7030 /* Create a new connection object */
7031 connection = nm_simple_connection_new ();
7033 /* Build up the 'connection' setting */
7034 s_con = (NMSettingConnection *) nm_setting_connection_new ();
7035 uuid = nm_utils_uuid_generate ();
7037 default_name = g_strdup (con_name);
7039 char *try_name = ifname ?
7040 g_strdup_printf ("%s-%s", get_name_alias (setting_name, nmc_valid_connection_types), ifname)
7041 : g_strdup (get_name_alias (setting_name, nmc_valid_connection_types));
7042 default_name = nmc_unique_connection_name (nmc->connections, try_name);
7047 /* Verify master argument */
7048 checked_master = normalized_master_for_slave (nmc->connections, master, slave_type, &slave_type);
7050 g_object_set (s_con,
7051 NM_SETTING_CONNECTION_ID, default_name,
7052 NM_SETTING_CONNECTION_UUID, uuid,
7053 NM_SETTING_CONNECTION_TYPE, setting_name,
7054 NM_SETTING_CONNECTION_AUTOCONNECT, auto_bool,
7055 NM_SETTING_CONNECTION_INTERFACE_NAME, ifname,
7056 NM_SETTING_CONNECTION_MASTER, checked_master,
7057 NM_SETTING_CONNECTION_SLAVE_TYPE, slave_type,
7060 g_free (default_name);
7061 nm_connection_add_setting (connection, NM_SETTING (s_con));
7063 if (!complete_connection_by_type (connection,
7071 g_string_assign (nmc->return_text, error->message);
7072 nmc->return_value = error->code;
7073 g_clear_error (&error);
7079 info = g_malloc0 (sizeof (AddConnectionInfo));
7081 info->con_name = g_strdup (nm_connection_get_id (connection));
7083 /* Tell the settings service to add the new connection */
7084 add_new_connection (save_bool,
7091 g_object_unref (connection);
7093 return nmc->return_value;
7097 g_object_unref (connection);
7099 g_free (ifname_ask);
7101 return nmc->return_value;
7105 /*----------------------------------------------------------------------------*/
7106 /* Functions for readline TAB completion in editor */
7109 uuid_display_hook (char **array, int len, int max_len)
7115 for (i = 1; i <= len; i++) {
7116 con = nmc_find_connection (nmc_tab_completion.nmc->connections, "uuid", array[i], NULL);
7117 id = con ? nm_connection_get_id (con) : NULL;
7119 tmp = g_strdup_printf ("%s (%s)", array[i], id);
7122 if (max < strlen (id))
7126 rl_display_match_list (array, len, max_len + max + 3);
7127 rl_forced_update_display ();
7131 gen_nmcli_cmds_menu (const char *text, int state)
7133 const char *words[] = { "goto", "set", "remove", "describe", "print", "verify",
7134 "save", "activate", "back", "help", "quit", "nmcli",
7136 return nmc_rl_gen_func_basic (text, state, words);
7140 gen_nmcli_cmds_submenu (const char *text, int state)
7142 const char *words[] = { "set", "add", "change", "remove", "describe",
7143 "print", "back", "help", "quit",
7145 return nmc_rl_gen_func_basic (text, state, words);
7149 gen_cmd_nmcli (const char *text, int state)
7151 const char *words[] = { "status-line", "save-confirmation", "show-secrets", "prompt-color", NULL };
7152 return nmc_rl_gen_func_basic (text, state, words);
7156 gen_cmd_nmcli_prompt_color (const char *text, int state)
7158 const char *words[] = { "normal", "black", "red", "green", "yellow",
7159 "blue", "magenta", "cyan", "white", NULL };
7160 return nmc_rl_gen_func_basic (text, state, words);
7164 gen_func_bool_values (const char *text, int state)
7166 const char *words[] = { "yes", "no", NULL };
7167 return nmc_rl_gen_func_basic (text, state, words);
7171 gen_cmd_verify0 (const char *text, int state)
7173 const char *words[] = { "all", "fix", NULL };
7174 return nmc_rl_gen_func_basic (text, state, words);
7178 gen_cmd_print0 (const char *text, int state)
7180 static char **words = NULL;
7186 const char *setting_name;
7189 settings = nm_connection_to_dbus (nmc_tab_completion.connection, NM_CONNECTION_SERIALIZE_NO_SECRETS);
7190 words = g_new (char *, g_variant_n_children (settings) + 2);
7191 g_variant_iter_init (&iter, settings);
7192 while (g_variant_iter_next (&iter, "{&s@a{sv}}", &setting_name, NULL))
7193 words [i++] = g_strdup (setting_name);
7194 words[i++] = g_strdup ("all");
7196 g_variant_unref (settings);
7200 ret = nmc_rl_gen_func_basic (text, state, (const char **) words);
7210 gen_cmd_print2 (const char *text, int state)
7212 const char *words[] = { "setting", "connection", "all", NULL };
7213 return nmc_rl_gen_func_basic (text, state, words);
7217 gen_cmd_save (const char *text, int state)
7219 const char *words[] = { "persistent", "temporary", NULL };
7220 return nmc_rl_gen_func_basic (text, state, words);
7224 gen_connection_types (const char *text, int state)
7226 static int list_idx, len;
7227 const char *c_type, *a_type;
7231 len = strlen (text);
7234 while (nmc_valid_connection_types[list_idx].name) {
7235 a_type = nmc_valid_connection_types[list_idx].alias;
7236 c_type = nmc_valid_connection_types[list_idx].name;
7238 if (a_type && !strncmp (text, a_type, len))
7239 return g_strdup (a_type);
7240 if (c_type && !strncmp (text, c_type, len))
7241 return g_strdup (c_type);
7248 gen_setting_names (const char *text, int state)
7250 static int list_idx, len, is_slv;
7251 const char *s_name, *a_name;
7252 const NameItem *valid_settings_arr;
7253 NMSettingConnection *s_con;
7254 const char *s_type = NULL;
7259 len = strlen (text);
7264 valid_settings_arr = get_valid_settings_array (nmc_tab_completion.con_type);
7265 if (!valid_settings_arr)
7267 while (valid_settings_arr[list_idx].name) {
7268 a_name = valid_settings_arr[list_idx].alias;
7269 s_name = valid_settings_arr[list_idx].name;
7271 if (len == 0 && a_name)
7272 return g_strdup_printf ("%s (%s)", s_name, a_name);
7273 if (a_name && !strncmp (text, a_name, len))
7274 return g_strdup (a_name);
7275 if (s_name && !strncmp (text, s_name, len))
7276 return g_strdup (s_name);
7279 /* Let's give a try to parameters related to slave type */
7285 s_con = nm_connection_get_setting_connection (nmc_tab_completion.connection);
7287 s_type = nm_setting_connection_get_slave_type (s_con);
7288 slv_type = g_strdup_printf ("%s-slave", s_type ? s_type : "no");
7289 valid_settings_arr = get_valid_settings_array (slv_type);
7292 while (valid_settings_arr[list_idx].name) {
7293 a_name = valid_settings_arr[list_idx].alias;
7294 s_name = valid_settings_arr[list_idx].name;
7296 if (len == 0 && a_name)
7297 return g_strdup_printf ("%s (%s)", s_name, a_name);
7298 if (a_name && !strncmp (text, a_name, len))
7299 return g_strdup (a_name);
7300 if (s_name && !strncmp (text, s_name, len))
7301 return g_strdup (s_name);
7308 gen_property_names (const char *text, int state)
7310 NMSetting *setting = NULL;
7311 char **valid_props = NULL;
7313 const char *line = rl_line_buffer;
7314 const char *setting_name;
7316 const NameItem *valid_settings_main;
7317 const NameItem *valid_settings_slave;
7319 const char *slv_type;
7321 /* Try to get the setting from 'line' - setting_name.property */
7322 p1 = strchr (line, '.');
7324 while (p1 > line && !g_ascii_isspace (*p1))
7327 strv = g_strsplit (p1+1, ".", 2);
7329 valid_settings_main = get_valid_settings_array (nmc_tab_completion.con_type);
7331 /* Support autocompletion of slave-connection parameters
7332 * guessing the slave type from the setting name already
7333 * typed (or autocompleted) */
7334 if (nm_streq0 (strv[0], NM_SETTING_TEAM_PORT_SETTING_NAME))
7335 slv_type = "team-slave";
7336 else if (nm_streq0 (strv[0], NM_SETTING_BRIDGE_PORT_SETTING_NAME))
7337 slv_type = "bridge-slave";
7339 slv_type = "no-slave";
7340 valid_settings_slave = get_valid_settings_array (slv_type);
7342 setting_name = check_valid_name (strv[0],
7343 valid_settings_main,
7344 valid_settings_slave,
7346 setting = nmc_setting_new_for_name (setting_name);
7348 /* Else take the current setting, if any */
7349 setting = nmc_tab_completion.setting ? g_object_ref (nmc_tab_completion.setting) : NULL;
7353 valid_props = nmc_setting_get_valid_properties (setting);
7354 ret = nmc_rl_gen_func_basic (text, state, (const char **) valid_props);
7358 g_strfreev (valid_props);
7360 g_object_unref (setting);
7365 gen_compat_devices (const char *text, int state)
7368 const GPtrArray *devices;
7369 const char **compatible_devices;
7372 devices = nm_client_get_devices (nmc_tab_completion.nmc->client);
7373 if (devices->len == 0)
7376 compatible_devices = g_new (const char *, devices->len + 1);
7377 for (i = 0; i < devices->len; i++) {
7378 NMDevice *dev = g_ptr_array_index (devices, i);
7379 const char *ifname = nm_device_get_iface (dev);
7380 NMDevice *device = NULL;
7381 const char *spec_object = NULL;
7383 if (find_device_for_connection (nmc_tab_completion.nmc, nmc_tab_completion.connection,
7384 ifname, NULL, NULL, &device, &spec_object, NULL)) {
7385 compatible_devices[j++] = ifname;
7388 compatible_devices[j] = NULL;
7390 ret = nmc_rl_gen_func_basic (text, state, compatible_devices);
7392 g_free (compatible_devices);
7396 static const char **
7397 _create_vpn_array (const GPtrArray *connections, gboolean uuid)
7402 if (connections->len < 1)
7405 array = g_new (const char *, connections->len + 1);
7406 for (c = 0; c < connections->len; c++) {
7407 NMConnection *connection = NM_CONNECTION (connections->pdata[c]);
7408 const char *type = nm_connection_get_connection_type (connection);
7410 if (g_strcmp0 (type, NM_SETTING_VPN_SETTING_NAME) == 0)
7411 array[idx++] = uuid ? nm_connection_get_uuid (connection) : nm_connection_get_id (connection);
7418 gen_vpn_uuids (const char *text, int state)
7420 const GPtrArray *connections = nm_cli.connections;
7424 if (connections->len < 1)
7427 uuids = _create_vpn_array (connections, TRUE);
7428 ret = nmc_rl_gen_func_basic (text, state, uuids);
7434 gen_vpn_ids (const char *text, int state)
7436 const GPtrArray *connections = nm_cli.connections;
7440 if (connections->len < 1)
7443 ids = _create_vpn_array (connections, FALSE);
7444 ret = nmc_rl_gen_func_basic (text, state, ids);
7449 static rl_compentry_func_t *
7450 get_gen_func_cmd_nmcli (const char *str)
7454 if (matches (str, "status-line") == 0)
7455 return gen_func_bool_values;
7456 if (matches (str, "save-confirmation") == 0)
7457 return gen_func_bool_values;
7458 if (matches (str, "show-secrets") == 0)
7459 return gen_func_bool_values;
7460 if (matches (str, "prompt-color") == 0)
7461 return gen_cmd_nmcli_prompt_color;
7466 * Helper function parsing line for completion.
7468 * line : the whole line to be parsed
7469 * end : the position of cursor in the line
7470 * cmd : command to match
7472 * cw_num : is set to the word number being completed (1, 2, 3, 4).
7473 * prev_word : returns the previous word (so that we have some context).
7475 * Returns TRUE when the first word of the 'line' matches 'cmd'.
7478 * line="rem" cmd="remove" -> TRUE cw_num=1
7479 * line="set con" cmd="set" -> TRUE cw_num=2
7480 * line="go ipv4.method" cmd="goto" -> TRUE cw_num=2
7481 * line=" des eth.mtu " cmd="describe" -> TRUE cw_num=3
7482 * line=" bla ipv4.method" cmd="goto" -> FALSE
7485 should_complete_cmd (const char *line, int end, const char *cmd,
7486 int *cw_num, char **prev_word)
7489 const char *word1, *word2, *word3;
7490 size_t n1, n2, n3, n4, n5, n6;
7491 gboolean word1_done, word2_done, word3_done;
7492 gboolean ret = FALSE;
7497 tmp = g_strdup (line);
7499 n1 = strspn (tmp, " \t");
7500 n2 = strcspn (tmp+n1, " \t\0") + n1;
7501 n3 = strspn (tmp+n2, " \t") + n2;
7502 n4 = strcspn (tmp+n3, " \t\0") + n3;
7503 n5 = strspn (tmp+n4, " \t") + n4;
7504 n6 = strcspn (tmp+n5, " \t\0") + n5;
7506 word1_done = end > n2;
7507 word2_done = end > n4;
7508 word3_done = end > n6;
7509 tmp[n2] = tmp[n4] = tmp[n6] = '\0';
7511 word1 = tmp[n1] ? tmp + n1 : NULL;
7512 word2 = tmp[n3] ? tmp + n3 : NULL;
7513 word3 = tmp[n5] ? tmp + n5 : NULL;
7520 } else if (!word2_done) {
7524 *prev_word = g_strdup (word1);
7525 } else if (!word3_done) {
7529 *prev_word = g_strdup (word2);
7534 *prev_word = g_strdup (word3);
7537 if (word1 && matches (word1, cmd) == 0)
7545 * extract_setting_and_property:
7546 * prompt: (in) (allow-none): prompt string, or NULL
7547 * line: (in) (allow-none): line, or NULL
7548 * setting: (out) (transfer full) (array zero-terminated=1):
7549 * return location for setting name
7550 * property: (out) (transfer full) (array zero-terminated=1):
7551 * return location for property name
7553 * Extract setting and property names from prompt and/or line.
7556 extract_setting_and_property (const char *prompt, const char *line,
7557 char **setting, char **property)
7563 /* prompt looks like this:
7564 "nmcli 802-1x>" or "nmcli 802-1x.pac-file>" */
7565 const char *p1, *p2, *dot;
7567 p1 = strchr (prompt, ' ');
7569 dot = strchr (++p1, '.');
7572 num1 = strcspn (p1, ".");
7573 num2 = strcspn (p2, ">");
7574 sett = num1 > 0 ? g_strndup (p1, num1) : NULL;
7575 prop = num2 > 0 ? g_strndup (p2, num2) : NULL;
7577 num1 = strcspn (p1, ">");
7578 sett = num1 > 0 ? g_strndup (p1, num1) : NULL;
7584 /* line looks like this:
7585 " set 802-1x.pac-file ..." or " set pac-file ..." */
7586 const char *p1, *p2, *dot;
7587 size_t n1, n2, n3, n4;
7588 size_t num1, num2, len;
7589 n1 = strspn (line, " \t"); /* white-space */
7590 n2 = strcspn (line+n1, " \t\0") + n1; /* command */
7591 n3 = strspn (line+n2, " \t") + n2; /* white-space */
7592 n4 = strcspn (line+n3, " \t\0") + n3; /* setting/property */
7596 dot = strchr (p1, '.');
7597 if (dot && dot < p1 + len) {
7599 num1 = strcspn (p1, ".");
7600 num2 = len > num1 + 1 ? len - num1 - 1 : 0;
7601 sett = num1 > 0 ? g_strndup (p1, num1) : sett;
7602 prop = num2 > 0 ? g_strndup (p2, num2) : prop;
7605 prop = len > 0 ? g_strndup (p1, len) : NULL;
7620 get_setting_and_property (const char *prompt, const char *line,
7621 NMSetting **setting_out, char**property_out)
7623 const NameItem *valid_settings_main;
7624 const NameItem *valid_settings_slave;
7625 const char *setting_name;
7626 NMSetting *setting = NULL;
7627 char *property = NULL;
7628 char *sett = NULL, *prop = NULL;
7629 NMSettingConnection *s_con;
7630 const char *s_type = NULL;
7633 extract_setting_and_property (prompt, line, &sett, &prop);
7635 /* Is this too much (and useless?) effort for an unlikely case? */
7636 s_con = nm_connection_get_setting_connection (nmc_tab_completion.connection);
7638 s_type = nm_setting_connection_get_slave_type (s_con);
7639 slv_type = g_strdup_printf ("%s-slave", s_type ? s_type : "no");
7641 valid_settings_main = get_valid_settings_array (nmc_tab_completion.con_type);
7642 valid_settings_slave = get_valid_settings_array (slv_type);
7645 setting_name = check_valid_name (sett, valid_settings_main,
7646 valid_settings_slave, NULL);
7647 setting = nmc_setting_new_for_name (setting_name);
7649 setting = nmc_tab_completion.setting ? g_object_ref (nmc_tab_completion.setting) : NULL;
7651 if (setting && prop)
7652 property = is_property_valid (setting, prop, NULL);
7654 property = g_strdup (nmc_tab_completion.property);
7656 *setting_out = setting;
7657 *property_out = property;
7664 _get_and_check_property (const char *prompt,
7667 const char **array_multi,
7671 gboolean found = FALSE;
7673 extract_setting_and_property (prompt, line, NULL, &prop);
7676 found = !!nmc_string_is_valid (prop, array, NULL);
7677 if (array_multi && multi)
7678 *multi = !!nmc_string_is_valid (prop, array_multi, NULL);
7685 should_complete_files (const char *prompt, const char *line)
7687 const char *file_properties[] = {
7688 /* '802-1x' properties */
7695 "phase2-client-cert",
7697 "phase2-private-key",
7698 /* 'team' and 'team-port' properties */
7702 return _get_and_check_property (prompt, line, file_properties, NULL, NULL);
7706 should_complete_vpn_uuids (const char *prompt, const char *line)
7708 const char *uuid_properties[] = {
7709 /* 'connection' properties */
7713 return _get_and_check_property (prompt, line, uuid_properties, NULL, NULL);
7716 static const char **
7717 get_allowed_property_values (void)
7721 const char **avals = NULL;
7723 get_setting_and_property (rl_prompt, rl_line_buffer, &setting, &property);
7724 if (setting && property)
7725 avals = nmc_setting_get_property_allowed_values (setting, property);
7728 g_object_unref (setting);
7735 should_complete_property_values (const char *prompt, const char *line, gboolean *multi)
7737 /* properties allowing multiple values */
7738 const char *multi_props[] = {
7739 /* '802-1x' properties */
7740 NM_SETTING_802_1X_EAP,
7741 /* '802-11-wireless-security' properties */
7742 NM_SETTING_WIRELESS_SECURITY_PROTO,
7743 NM_SETTING_WIRELESS_SECURITY_PAIRWISE,
7744 NM_SETTING_WIRELESS_SECURITY_GROUP,
7745 /* 'bond' properties */
7746 NM_SETTING_BOND_OPTIONS,
7747 /* 'ethernet' properties */
7748 NM_SETTING_WIRED_S390_OPTIONS,
7751 _get_and_check_property (prompt, line, NULL, multi_props, multi);
7752 return get_allowed_property_values () != NULL;
7755 //FIXME: this helper should go to libnm later
7757 _setting_property_is_boolean (NMSetting *setting, const char *property_name)
7761 g_return_val_if_fail (NM_IS_SETTING (setting), FALSE);
7762 g_return_val_if_fail (property_name, FALSE);
7764 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), property_name);
7765 if (pspec && pspec->value_type == G_TYPE_BOOLEAN)
7771 should_complete_boolean (const char *prompt, const char *line)
7775 gboolean is_boolean = FALSE;
7777 get_setting_and_property (prompt, line, &setting, &property);
7778 if (setting && property)
7779 is_boolean = _setting_property_is_boolean (setting, property);
7782 g_object_unref (setting);
7789 gen_property_values (const char *text, int state)
7794 avals = get_allowed_property_values ();
7796 ret = nmc_rl_gen_func_basic (text, state, avals);
7801 extern int rl_complete_with_tilde_expansion;
7804 * Attempt to complete on the contents of TEXT. START and END show the
7805 * region of TEXT that contains the word to complete. We can use the
7806 * entire line in case we want to do some simple parsing. Return the
7807 * array of matches, or NULL if there aren't any.
7810 nmcli_editor_tab_completion (const char *text, int start, int end)
7812 char **match_array = NULL;
7813 const char *line = rl_line_buffer;
7814 rl_compentry_func_t *generator_func = NULL;
7820 /* Restore standard append character to space */
7821 rl_completion_append_character = ' ';
7823 /* Restore standard function for displaying matches */
7824 rl_completion_display_matches_hook = NULL;
7826 /* Disable default filename completion */
7827 rl_attempted_completion_over = 1;
7829 /* Enable tilde expansion when filenames are completed */
7830 rl_complete_with_tilde_expansion = 1;
7832 /* Filter out possible ANSI color escape sequences */
7833 prompt_tmp = nmc_filter_out_colors ((const char *) rl_prompt);
7835 /* Find the first non-space character */
7836 n1 = strspn (line, " \t");
7838 /* Choose the right generator function */
7839 if (strcmp (prompt_tmp, EDITOR_PROMPT_CON_TYPE) == 0)
7840 generator_func = gen_connection_types;
7841 else if (strcmp (prompt_tmp, EDITOR_PROMPT_SETTING) == 0)
7842 generator_func = gen_setting_names;
7843 else if (strcmp (prompt_tmp, EDITOR_PROMPT_PROPERTY) == 0)
7844 generator_func = gen_property_names;
7845 else if ( g_str_has_suffix (rl_prompt, prompt_yes_no (TRUE, NULL))
7846 || g_str_has_suffix (rl_prompt, prompt_yes_no (FALSE, NULL)))
7847 generator_func = gen_func_bool_values_l10n;
7848 else if (g_str_has_prefix (prompt_tmp, "nmcli")) {
7849 if (!strchr (prompt_tmp, '.')) {
7850 int level = g_str_has_prefix (prompt_tmp, "nmcli>") ? 0 : 1;
7851 const char *dot = strchr (line, '.');
7854 /* Main menu - level 0,1 */
7856 generator_func = gen_nmcli_cmds_menu;
7858 if (should_complete_cmd (line, end, "goto", &num, NULL) && num <= 2) {
7859 if (level == 0 && (!dot || dot >= line + end))
7860 generator_func = gen_setting_names;
7862 generator_func = gen_property_names;
7863 } else if (should_complete_cmd (line, end, "set", &num, NULL)) {
7865 if (level == 0 && (!dot || dot >= line + end)) {
7866 generator_func = gen_setting_names;
7867 rl_completion_append_character = '.';
7869 generator_func = gen_property_names;
7870 } else if (num >= 3) {
7871 if (num == 3 && should_complete_files (NULL, line))
7872 rl_attempted_completion_over = 0;
7873 else if (should_complete_vpn_uuids (NULL, line)) {
7874 rl_completion_display_matches_hook = uuid_display_hook;
7875 generator_func = gen_vpn_uuids;
7876 } else if ( should_complete_property_values (NULL, line, &multi)
7877 && (num == 3 || multi)) {
7878 generator_func = gen_property_values;
7879 } else if (should_complete_boolean (NULL, line) && num == 3)
7880 generator_func = gen_func_bool_values;
7882 } else if ( ( should_complete_cmd (line, end, "remove", &num, NULL)
7883 || should_complete_cmd (line, end, "describe", &num, NULL))
7885 if (level == 0 && (!dot || dot >= line + end)) {
7886 generator_func = gen_setting_names;
7887 rl_completion_append_character = '.';
7889 generator_func = gen_property_names;
7890 } else if (should_complete_cmd (line, end, "nmcli", &num, &word)) {
7892 generator_func = gen_cmd_nmcli;
7894 generator_func = get_gen_func_cmd_nmcli (word);
7895 } else if (should_complete_cmd (line, end, "print", &num, NULL) && num <= 2) {
7896 if (level == 0 && (!dot || dot >= line + end))
7897 generator_func = gen_cmd_print0;
7899 generator_func = gen_property_names;
7900 } else if (should_complete_cmd (line, end, "verify", &num, NULL) && num <= 2) {
7901 generator_func = gen_cmd_verify0;
7902 } else if (should_complete_cmd (line, end, "activate", &num, NULL) && num <= 2) {
7903 generator_func = gen_compat_devices;
7904 } else if (should_complete_cmd (line, end, "save", &num, NULL) && num <= 2) {
7905 generator_func = gen_cmd_save;
7906 } else if (should_complete_cmd (line, end, "help", &num, NULL) && num <= 2)
7907 generator_func = gen_nmcli_cmds_menu;
7910 /* Submenu - level 2 */
7912 generator_func = gen_nmcli_cmds_submenu;
7916 if ( should_complete_cmd (line, end, "add", &num, NULL)
7917 || should_complete_cmd (line, end, "set", &num, NULL)) {
7918 if (num <= 2 && should_complete_files (prompt_tmp, line))
7919 rl_attempted_completion_over = 0;
7920 else if (should_complete_vpn_uuids (prompt_tmp, line)) {
7921 rl_completion_display_matches_hook = uuid_display_hook;
7922 generator_func = gen_vpn_uuids;
7923 } else if ( should_complete_property_values (prompt_tmp, NULL, &multi)
7924 && (num <= 2 || multi)) {
7925 generator_func = gen_property_values;
7926 } else if (should_complete_boolean (prompt_tmp, NULL) && num <= 2)
7927 generator_func = gen_func_bool_values;
7929 if (should_complete_cmd (line, end, "print", &num, NULL) && num <= 2)
7930 generator_func = gen_cmd_print2;
7931 else if (should_complete_cmd (line, end, "help", &num, NULL) && num <= 2)
7932 generator_func = gen_nmcli_cmds_submenu;
7938 match_array = rl_completion_matches (text, generator_func);
7940 g_free (prompt_tmp);
7945 #define NMCLI_EDITOR_HISTORY ".nmcli-history"
7948 load_history_cmds (const char *uuid)
7957 filename = g_build_filename (g_get_home_dir (), NMCLI_EDITOR_HISTORY, NULL);
7958 kf = g_key_file_new ();
7959 if (!g_key_file_load_from_file (kf, filename, G_KEY_FILE_KEEP_COMMENTS, &err)) {
7960 if (g_error_matches (err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_PARSE))
7961 g_print ("Warning: %s parse error: %s\n", filename, err->message);
7962 g_key_file_free (kf);
7966 keys = g_key_file_get_keys (kf, uuid, NULL, NULL);
7967 for (i = 0; keys && keys[i]; i++) {
7968 line = g_key_file_get_string (kf, uuid, keys[i], NULL);
7974 g_key_file_free (kf);
7979 save_history_cmds (const char *uuid)
7981 HIST_ENTRY **hist = NULL;
7990 hist = history_list ();
7992 filename = g_build_filename (g_get_home_dir (), NMCLI_EDITOR_HISTORY, NULL);
7993 kf = g_key_file_new ();
7994 if (!g_key_file_load_from_file (kf, filename, G_KEY_FILE_KEEP_COMMENTS, &err)) {
7995 if ( !g_error_matches (err, G_FILE_ERROR, G_FILE_ERROR_NOENT)
7996 && !g_error_matches (err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND)) {
7997 g_print ("Warning: %s parse error: %s\n", filename, err->message);
7998 g_key_file_free (kf);
8000 g_clear_error (&err);
8003 g_clear_error (&err);
8006 /* Remove previous history group and save new history entries */
8007 g_key_file_remove_group (kf, uuid, NULL);
8008 for (i = 0; hist[i]; i++)
8010 key = g_strdup_printf ("%zd", i);
8011 g_key_file_set_string (kf, uuid, key, hist[i]->line);
8015 /* Write history to file */
8016 data = g_key_file_to_data (kf, &len, NULL);
8018 g_file_set_contents (filename, data, len, NULL);
8021 g_key_file_free (kf);
8026 /*----------------------------------------------------------------------------*/
8029 editor_show_connection (NMConnection *connection, NmCli *nmc)
8031 nmc->print_output = NMC_PRINT_PRETTY;
8032 nmc->multiline_output = TRUE;
8033 nmc->escape_values = 0;
8035 /* Remove any previous data */
8036 nmc_empty_output_fields (nmc);
8038 nmc_connection_profile_details (connection, nmc, nmc->editor_show_secrets);
8042 editor_show_setting (NMSetting *setting, NmCli *nmc)
8044 g_print (_("['%s' setting values]\n"),
8045 nm_setting_get_name (setting));
8047 nmc->print_output = NMC_PRINT_NORMAL;
8048 nmc->multiline_output = TRUE;
8049 nmc->escape_values = 0;
8051 /* Remove any previous data */
8052 nmc_empty_output_fields (nmc);
8054 setting_details (setting, nmc, NULL, nmc->editor_show_secrets);
8058 NMC_EDITOR_MAIN_CMD_UNKNOWN = 0,
8059 NMC_EDITOR_MAIN_CMD_GOTO,
8060 NMC_EDITOR_MAIN_CMD_REMOVE,
8061 NMC_EDITOR_MAIN_CMD_SET,
8062 NMC_EDITOR_MAIN_CMD_DESCRIBE,
8063 NMC_EDITOR_MAIN_CMD_PRINT,
8064 NMC_EDITOR_MAIN_CMD_VERIFY,
8065 NMC_EDITOR_MAIN_CMD_SAVE,
8066 NMC_EDITOR_MAIN_CMD_ACTIVATE,
8067 NMC_EDITOR_MAIN_CMD_BACK,
8068 NMC_EDITOR_MAIN_CMD_HELP,
8069 NMC_EDITOR_MAIN_CMD_NMCLI,
8070 NMC_EDITOR_MAIN_CMD_QUIT,
8073 static NmcEditorMainCmd
8074 parse_editor_main_cmd (const char *cmd, char **cmd_arg)
8076 NmcEditorMainCmd editor_cmd = NMC_EDITOR_MAIN_CMD_UNKNOWN;
8079 vec = nmc_strsplit_set (cmd, " \t", 2);
8080 if (g_strv_length (vec) < 1) {
8083 return NMC_EDITOR_MAIN_CMD_UNKNOWN;
8086 if (matches (vec[0], "goto") == 0)
8087 editor_cmd = NMC_EDITOR_MAIN_CMD_GOTO;
8088 else if (matches (vec[0], "remove") == 0)
8089 editor_cmd = NMC_EDITOR_MAIN_CMD_REMOVE;
8090 else if (matches (vec[0], "set") == 0)
8091 editor_cmd = NMC_EDITOR_MAIN_CMD_SET;
8092 else if (matches (vec[0], "describe") == 0)
8093 editor_cmd = NMC_EDITOR_MAIN_CMD_DESCRIBE;
8094 else if (matches (vec[0], "print") == 0)
8095 editor_cmd = NMC_EDITOR_MAIN_CMD_PRINT;
8096 else if (matches (vec[0], "verify") == 0)
8097 editor_cmd = NMC_EDITOR_MAIN_CMD_VERIFY;
8098 else if (matches (vec[0], "save") == 0)
8099 editor_cmd = NMC_EDITOR_MAIN_CMD_SAVE;
8100 else if (matches (vec[0], "activate") == 0)
8101 editor_cmd = NMC_EDITOR_MAIN_CMD_ACTIVATE;
8102 else if (matches (vec[0], "back") == 0)
8103 editor_cmd = NMC_EDITOR_MAIN_CMD_BACK;
8104 else if (matches (vec[0], "help") == 0 || strcmp (vec[0], "?") == 0)
8105 editor_cmd = NMC_EDITOR_MAIN_CMD_HELP;
8106 else if (matches (vec[0], "quit") == 0)
8107 editor_cmd = NMC_EDITOR_MAIN_CMD_QUIT;
8108 else if (matches (vec[0], "nmcli") == 0)
8109 editor_cmd = NMC_EDITOR_MAIN_CMD_NMCLI;
8111 /* set pointer to command argument */
8113 *cmd_arg = vec[1] ? g_strstrip (g_strdup (vec[1])) : NULL;
8120 editor_main_usage (void)
8122 g_print ("------------------------------------------------------------------------------\n");
8123 /* TRANSLATORS: do not translate command names and keywords before ::
8124 * However, you should translate terms enclosed in <>.
8126 g_print (_("---[ Main menu ]---\n"
8127 "goto [<setting> | <prop>] :: go to a setting or property\n"
8128 "remove <setting>[.<prop>] | <prop> :: remove setting or reset property value\n"
8129 "set [<setting>.<prop> <value>] :: set property value\n"
8130 "describe [<setting>.<prop>] :: describe property\n"
8131 "print [all | <setting>[.<prop>]] :: print the connection\n"
8132 "verify [all | fix] :: verify the connection\n"
8133 "save [persistent|temporary] :: save the connection\n"
8134 "activate [<ifname>] [/<ap>|<nsp>] :: activate the connection\n"
8135 "back :: go one level up (back)\n"
8136 "help/? [<command>] :: print this help\n"
8137 "nmcli <conf-option> <value> :: nmcli configuration\n"
8138 "quit :: exit nmcli\n"));
8139 g_print ("------------------------------------------------------------------------------\n");
8143 editor_main_help (const char *command)
8146 editor_main_usage ();
8148 /* detailed command descriptions */
8149 NmcEditorMainCmd cmd = parse_editor_main_cmd (command, NULL);
8152 case NMC_EDITOR_MAIN_CMD_GOTO:
8153 g_print (_("goto <setting>[.<prop>] | <prop> :: enter setting/property for editing\n\n"
8154 "This command enters into a setting or property for editing it.\n\n"
8155 "Examples: nmcli> goto connection\n"
8156 " nmcli connection> goto secondaries\n"
8157 " nmcli> goto ipv4.addresses\n"));
8159 case NMC_EDITOR_MAIN_CMD_REMOVE:
8160 g_print (_("remove <setting>[.<prop>] :: remove setting or reset property value\n\n"
8161 "This command removes an entire setting from the connection, or if a property\n"
8162 "is given, resets that property to the default value.\n\n"
8163 "Examples: nmcli> remove wifi-sec\n"
8164 " nmcli> remove eth.mtu\n"));
8166 case NMC_EDITOR_MAIN_CMD_SET:
8167 g_print (_("set [<setting>.<prop> <value>] :: set property value\n\n"
8168 "This command sets property value.\n\n"
8169 "Example: nmcli> set con.id My connection\n"));
8171 case NMC_EDITOR_MAIN_CMD_DESCRIBE:
8172 g_print (_("describe [<setting>.<prop>] :: describe property\n\n"
8173 "Shows property description. You can consult nm-settings(5) "
8174 "manual page to see all NM settings and properties.\n"));
8176 case NMC_EDITOR_MAIN_CMD_PRINT:
8177 g_print (_("print [all] :: print setting or connection values\n\n"
8178 "Shows current property or the whole connection.\n\n"
8179 "Example: nmcli ipv4> print all\n"));
8181 case NMC_EDITOR_MAIN_CMD_VERIFY:
8182 g_print (_("verify [all | fix] :: verify setting or connection validity\n\n"
8183 "Verifies whether the setting or connection is valid and can be saved later.\n"
8184 "It indicates invalid values on error. Some errors may be fixed automatically\n"
8185 "by 'fix' option.\n\n"
8186 "Examples: nmcli> verify\n"
8187 " nmcli> verify fix\n"
8188 " nmcli bond> verify\n"));
8190 case NMC_EDITOR_MAIN_CMD_SAVE:
8191 g_print (_("save [persistent|temporary] :: save the connection\n\n"
8192 "Sends the connection profile to NetworkManager that either will save it\n"
8193 "persistently, or will only keep it in memory. 'save' without an argument\n"
8194 "means 'save persistent'.\n"
8195 "Note that once you save the profile persistently those settings are saved\n"
8196 "across reboot or restart. Subsequent changes can also be temporary or\n"
8197 "persistent, but any temporary changes will not persist across reboot or\n"
8198 "restart. If you want to fully remove the persistent connection, the connection\n"
8199 "profile must be deleted.\n"));
8201 case NMC_EDITOR_MAIN_CMD_ACTIVATE:
8202 g_print (_("activate [<ifname>] [/<ap>|<nsp>] :: activate the connection\n\n"
8203 "Activates the connection.\n\n"
8204 "Available options:\n"
8205 "<ifname> - device the connection will be activated on\n"
8206 "/<ap>|<nsp> - AP (Wi-Fi) or NSP (WiMAX) (prepend with / when <ifname> is not specified)\n"));
8208 case NMC_EDITOR_MAIN_CMD_BACK:
8209 g_print (_("back :: go to upper menu level\n\n"));
8211 case NMC_EDITOR_MAIN_CMD_HELP:
8212 g_print (_("help/? [<command>] :: help for the nmcli commands\n\n"));
8214 case NMC_EDITOR_MAIN_CMD_NMCLI:
8215 g_print (_("nmcli [<conf-option> <value>] :: nmcli configuration\n\n"
8216 "Configures nmcli. The following options are available:\n"
8217 "status-line yes | no [default: no]\n"
8218 "save-confirmation yes | no [default: yes]\n"
8219 "show-secrets yes | no [default: no]\n"
8220 "prompt-color <color> | <0-8> [default: 0]\n"
8221 "%s" /* color table description */
8223 "Examples: nmcli> nmcli status-line yes\n"
8224 " nmcli> nmcli save-confirmation no\n"
8225 " nmcli> nmcli prompt-color 3\n"),
8227 " 1 = \33[30mblack\33[0m\n"
8228 " 2 = \33[31mred\33[0m\n"
8229 " 3 = \33[32mgreen\33[0m\n"
8230 " 4 = \33[33myellow\33[0m\n"
8231 " 5 = \33[34mblue\33[0m\n"
8232 " 6 = \33[35mmagenta\33[0m\n"
8233 " 7 = \33[36mcyan\33[0m\n"
8234 " 8 = \33[37mwhite\33[0m\n");
8236 case NMC_EDITOR_MAIN_CMD_QUIT:
8237 g_print (_("quit :: exit nmcli\n\n"
8238 "This command exits nmcli. When the connection being edited "
8239 "is not saved, the user is asked to confirm the action.\n"));
8242 g_print (_("Unknown command: '%s'\n"), command);
8249 NMC_EDITOR_SUB_CMD_UNKNOWN = 0,
8250 NMC_EDITOR_SUB_CMD_SET,
8251 NMC_EDITOR_SUB_CMD_ADD,
8252 NMC_EDITOR_SUB_CMD_CHANGE,
8253 NMC_EDITOR_SUB_CMD_REMOVE,
8254 NMC_EDITOR_SUB_CMD_DESCRIBE,
8255 NMC_EDITOR_SUB_CMD_PRINT,
8256 NMC_EDITOR_SUB_CMD_BACK,
8257 NMC_EDITOR_SUB_CMD_HELP,
8258 NMC_EDITOR_SUB_CMD_QUIT
8261 static NmcEditorSubCmd
8262 parse_editor_sub_cmd (const char *cmd, char **cmd_arg)
8264 NmcEditorSubCmd editor_cmd = NMC_EDITOR_SUB_CMD_UNKNOWN;
8267 vec = nmc_strsplit_set (cmd, " \t", 2);
8268 if (g_strv_length (vec) < 1) {
8271 return NMC_EDITOR_SUB_CMD_UNKNOWN;
8274 if (matches (vec[0], "set") == 0)
8275 editor_cmd = NMC_EDITOR_SUB_CMD_SET;
8276 else if (matches (vec[0], "add") == 0)
8277 editor_cmd = NMC_EDITOR_SUB_CMD_ADD;
8278 else if (matches (vec[0], "change") == 0)
8279 editor_cmd = NMC_EDITOR_SUB_CMD_CHANGE;
8280 else if (matches (vec[0], "remove") == 0)
8281 editor_cmd = NMC_EDITOR_SUB_CMD_REMOVE;
8282 else if (matches (vec[0], "describe") == 0)
8283 editor_cmd = NMC_EDITOR_SUB_CMD_DESCRIBE;
8284 else if (matches (vec[0], "print") == 0)
8285 editor_cmd = NMC_EDITOR_SUB_CMD_PRINT;
8286 else if (matches (vec[0], "back") == 0)
8287 editor_cmd = NMC_EDITOR_SUB_CMD_BACK;
8288 else if (matches (vec[0], "help") == 0 || strcmp (vec[0], "?") == 0)
8289 editor_cmd = NMC_EDITOR_SUB_CMD_HELP;
8290 else if (matches (vec[0], "quit") == 0)
8291 editor_cmd = NMC_EDITOR_SUB_CMD_QUIT;
8293 /* set pointer to command argument */
8295 *cmd_arg = g_strdup (vec[1]);
8302 editor_sub_help (void)
8304 g_print ("------------------------------------------------------------------------------\n");
8305 /* TRANSLATORS: do not translate command names and keywords before ::
8306 * However, you should translate terms enclosed in <>.
8308 g_print (_("---[ Property menu ]---\n"
8309 "set [<value>] :: set new value\n"
8310 "add [<value>] :: add new option to the property\n"
8311 "change :: change current value\n"
8312 "remove [<index> | <option>] :: delete the value\n"
8313 "describe :: describe property\n"
8314 "print [setting | connection] :: print property (setting/connection) value(s)\n"
8315 "back :: go to upper level\n"
8316 "help/? [<command>] :: print this help or command description\n"
8317 "quit :: exit nmcli\n"));
8318 g_print ("------------------------------------------------------------------------------\n");
8322 editor_sub_usage (const char *command)
8328 /* detailed command descriptions */
8329 NmcEditorSubCmd cmdsub = parse_editor_sub_cmd (command, NULL);
8332 case NMC_EDITOR_SUB_CMD_SET:
8333 g_print (_("set [<value>] :: set new value\n\n"
8334 "This command sets provided <value> to this property\n"));
8336 case NMC_EDITOR_SUB_CMD_ADD:
8337 g_print (_("add [<value>] :: append new value to the property\n\n"
8338 "This command adds provided <value> to this property, if "
8339 "the property is of a container type. For single-valued "
8340 "properties the property value is replaced (same as 'set').\n"));
8342 case NMC_EDITOR_SUB_CMD_CHANGE:
8343 g_print (_("change :: change current value\n\n"
8344 "Displays current value and allows editing it.\n"));
8346 case NMC_EDITOR_SUB_CMD_REMOVE:
8347 g_print (_("remove [<value>|<index>|<option name>] :: delete the value\n\n"
8348 "Removes the property value. For single-valued properties, this sets the\n"
8349 "property back to its default value. For container-type properties, this removes\n"
8350 "all the values of that property, or you can specify an argument to remove just\n"
8351 "a single item or option. The argument is either a value or index of the item to\n"
8352 "remove, or an option name (for properties with named options).\n\n"
8353 "Examples: nmcli ipv4.dns> remove 8.8.8.8\n"
8354 " nmcli ipv4.dns> remove 2\n"
8355 " nmcli bond.options> remove downdelay\n\n"));
8357 case NMC_EDITOR_SUB_CMD_DESCRIBE:
8358 g_print (_("describe :: describe property\n\n"
8359 "Shows property description. You can consult nm-settings(5) "
8360 "manual page to see all NM settings and properties.\n"));
8362 case NMC_EDITOR_SUB_CMD_PRINT:
8363 g_print (_("print [property|setting|connection] :: print property (setting, connection) value(s)\n\n"
8364 "Shows property value. Providing an argument you can also display "
8365 "values for the whole setting or connection.\n"));
8367 case NMC_EDITOR_SUB_CMD_BACK:
8368 g_print (_("back :: go to upper menu level\n\n"));
8370 case NMC_EDITOR_SUB_CMD_HELP:
8371 g_print (_("help/? [<command>] :: help for nmcli commands\n\n"));
8373 case NMC_EDITOR_SUB_CMD_QUIT:
8374 g_print (_("quit :: exit nmcli\n\n"
8375 "This command exits nmcli. When the connection being edited "
8376 "is not saved, the user is asked to confirm the action.\n"));
8379 g_print (_("Unknown command: '%s'\n"), command);
8385 /*----------------------------------------------------------------------------*/
8389 NMActiveConnection *ac;
8393 static gboolean nmc_editor_cb_called;
8394 static GError *nmc_editor_error;
8395 static MonitorACInfo *nmc_editor_monitor_ac;
8396 static GMutex nmc_editor_mutex;
8397 static GCond nmc_editor_cond;
8400 * Store 'error' to shared 'nmc_editor_error' and monitoring info to
8401 * 'nmc_editor_monitor_ac' and signal the condition so that
8402 * the 'editor-thread' thread could process that.
8405 set_info_and_signal_editor_thread (GError *error, MonitorACInfo *monitor_ac_info)
8407 g_mutex_lock (&nmc_editor_mutex);
8408 nmc_editor_cb_called = TRUE;
8409 nmc_editor_error = error ? g_error_copy (error) : NULL;
8410 nmc_editor_monitor_ac = monitor_ac_info;
8411 g_cond_signal (&nmc_editor_cond);
8412 g_mutex_unlock (&nmc_editor_mutex);
8416 add_connection_editor_cb (GObject *client,
8417 GAsyncResult *result,
8420 NMRemoteConnection *connection;
8421 GError *error = NULL;
8423 connection = nm_client_add_connection_finish (NM_CLIENT (client), result, &error);
8424 set_info_and_signal_editor_thread (error, NULL);
8426 g_clear_object (&connection);
8427 g_clear_error (&error);
8431 update_connection_editor_cb (GObject *connection,
8432 GAsyncResult *result,
8435 GError *error = NULL;
8437 nm_remote_connection_commit_changes_finish (NM_REMOTE_CONNECTION (connection),
8439 set_info_and_signal_editor_thread (error, NULL);
8440 g_clear_error (&error);
8444 progress_activation_editor_cb (gpointer user_data)
8446 MonitorACInfo *info = (MonitorACInfo *) user_data;
8447 NMDevice *device = info->device;
8448 NMActiveConnection *ac = info->ac;
8449 NMActiveConnectionState ac_state;
8450 NMDeviceState dev_state;
8455 ac_state = nm_active_connection_get_state (ac);
8456 dev_state = nm_device_get_state (device);
8458 nmc_terminal_show_progress (nmc_device_state_to_string (dev_state));
8460 if ( ac_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED
8461 || dev_state == NM_DEVICE_STATE_ACTIVATED) {
8462 nmc_terminal_erase_line ();
8463 g_print (_("Connection successfully activated (D-Bus active path: %s)\n"),
8464 nm_object_get_path (NM_OBJECT (ac)));
8465 goto finish; /* we are done */
8466 } else if ( ac_state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED
8467 || dev_state == NM_DEVICE_STATE_FAILED) {
8468 nmc_terminal_erase_line ();
8469 g_print (_("Error: Connection activation failed.\n"));
8470 goto finish; /* we are done */
8477 g_object_unref (device);
8479 g_object_unref (ac);
8484 activate_connection_editor_cb (GObject *client,
8485 GAsyncResult *result,
8488 ActivateConnectionInfo *info = (ActivateConnectionInfo *) user_data;
8489 NMDevice *device = info->device;
8490 const GPtrArray *ac_devs;
8491 MonitorACInfo *monitor_ac_info = NULL;
8492 NMActiveConnection *active;
8493 GError *error = NULL;
8495 active = nm_client_activate_connection_finish (NM_CLIENT (client), result, &error);
8499 ac_devs = nm_active_connection_get_devices (active);
8500 device = ac_devs->len > 0 ? g_ptr_array_index (ac_devs, 0) : NULL;
8503 monitor_ac_info = g_malloc0 (sizeof (AddConnectionInfo));
8504 monitor_ac_info->device = g_object_ref (device);
8505 monitor_ac_info->ac = active;
8506 monitor_ac_info->monitor_id = g_timeout_add (120, progress_activation_editor_cb, monitor_ac_info);
8508 g_object_unref (active);
8510 set_info_and_signal_editor_thread (error, monitor_ac_info);
8511 g_clear_error (&error);
8514 /*----------------------------------------------------------------------------*/
8517 print_property_description (NMSetting *setting, const char *prop_name)
8521 desc = nmc_setting_get_property_desc (setting, prop_name);
8522 g_print ("\n=== [%s] ===\n%s\n", prop_name, desc);
8527 print_setting_description (NMSetting *setting)
8529 /* Show description of all properties */
8533 all_props = nmc_setting_get_valid_properties (setting);
8534 g_print (("<<< %s >>>\n"), nm_setting_get_name (setting));
8535 for (i = 0; all_props && all_props[i]; i++)
8536 print_property_description (setting, all_props[i]);
8537 g_strfreev (all_props);
8541 connection_remove_setting (NMConnection *connection, NMSetting *setting)
8545 g_return_val_if_fail (setting, FALSE);
8547 mandatory = is_setting_mandatory (connection, setting);
8549 nm_connection_remove_setting (connection, G_OBJECT_TYPE (setting));
8552 g_print (_("Error: setting '%s' is mandatory and cannot be removed.\n"),
8553 nm_setting_get_name (setting));
8558 editor_show_status_line (NMConnection *connection, gboolean dirty, gboolean temp)
8560 NMSettingConnection *s_con;
8561 const char *con_type, *con_id, *con_uuid;
8563 s_con = nm_connection_get_setting_connection (connection);
8565 con_type = nm_setting_connection_get_connection_type (s_con);
8566 con_id = nm_connection_get_id (connection);
8567 con_uuid = nm_connection_get_uuid (connection);
8569 /* TRANSLATORS: status line in nmcli connection editor */
8570 g_print (_("[ Type: %s | Name: %s | UUID: %s | Dirty: %s | Temp: %s ]\n"),
8571 con_type, con_id, con_uuid,
8572 dirty ? _("yes") : _("no"),
8573 temp ? _("yes") : _("no"));
8577 refresh_remote_connection (GWeakRef *weak, NMRemoteConnection **remote)
8581 g_return_val_if_fail (remote != NULL, FALSE);
8583 previous = (*remote != NULL);
8585 g_object_unref (*remote);
8586 *remote = g_weak_ref_get (weak);
8588 return (previous && !*remote);
8592 is_connection_dirty (NMConnection *connection, NMRemoteConnection *remote)
8594 return !nm_connection_compare (connection,
8595 remote ? NM_CONNECTION (remote) : NULL,
8596 NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS |
8597 NM_SETTING_COMPARE_FLAG_IGNORE_TIMESTAMP);
8604 gboolean want_quit = FALSE;
8606 answer = nmc_readline (_("The connection is not saved. "
8607 "Do you really want to quit? %s"),
8608 prompt_yes_no (FALSE, NULL));
8609 answer = answer ? g_strstrip (answer) : NULL;
8610 if (answer && matches (answer, WORD_LOC_YES) == 0)
8618 * Submenu for detailed property editing
8619 * Return: TRUE - continue; FALSE - should quit
8622 property_edit_submenu (NmCli *nmc,
8623 NMConnection *connection,
8624 NMRemoteConnection **rem_con,
8625 GWeakRef *rem_con_weak,
8626 NMSetting *curr_setting,
8627 const char *prop_name)
8629 NmcEditorSubCmd cmdsub;
8630 gboolean cmd_property_loop = TRUE;
8631 gboolean should_quit = FALSE;
8632 char *prop_val_user;
8633 gboolean set_result;
8634 GError *tmp_err = NULL;
8637 GValue prop_g_value = G_VALUE_INIT;
8638 gboolean temp_changes;
8641 /* Set global variable for use in TAB completion */
8642 nmc_tab_completion.property = prop_name;
8644 prompt = nmc_colorize (nmc, nmc->editor_prompt_color, NMC_TERM_FORMAT_NORMAL,
8646 nm_setting_get_name (curr_setting), prop_name);
8648 while (cmd_property_loop) {
8649 char *cmd_property_user;
8650 char *cmd_property_arg;
8652 /* Get the remote connection again, it may have disapeared */
8653 removed = refresh_remote_connection (rem_con_weak, rem_con);
8655 g_print (_("The connection profile has been removed from another client. "
8656 "You may type 'save' in the main menu to restore it.\n"));
8658 /* Connection is dirty? (not saved or differs from the saved) */
8659 dirty = is_connection_dirty (connection, *rem_con);
8660 temp_changes = *rem_con ? nm_remote_connection_get_unsaved (*rem_con) : TRUE;
8661 if (nmc->editor_status_line)
8662 editor_show_status_line (connection, dirty, temp_changes);
8664 cmd_property_user = nmc_readline ("%s", prompt);
8665 if (!cmd_property_user || *cmd_property_user == '\0')
8667 cmdsub = parse_editor_sub_cmd (g_strstrip (cmd_property_user), &cmd_property_arg);
8670 case NMC_EDITOR_SUB_CMD_SET:
8671 case NMC_EDITOR_SUB_CMD_ADD:
8672 /* list, arrays,...: SET replaces the whole property value
8673 * ADD adds the new value(s)
8674 * single values: : both SET and ADD sets the new value
8676 if (!cmd_property_arg) {
8677 const char **avals = nmc_setting_get_property_allowed_values (curr_setting, prop_name);
8679 char *avals_str = nmc_util_strv_for_display (avals, FALSE);
8680 g_print (_("Allowed values for '%s' property: %s\n"),
8681 prop_name, avals_str);
8684 prop_val_user = nmc_readline (_("Enter '%s' value: "), prop_name);
8686 prop_val_user = g_strdup (cmd_property_arg);
8688 /* nmc_setting_set_property() only adds new value, thus we have to
8689 * remove the original value and save it for error cases.
8691 if (cmdsub == NMC_EDITOR_SUB_CMD_SET) {
8692 nmc_property_get_gvalue (curr_setting, prop_name, &prop_g_value);
8693 nmc_property_set_default_value (curr_setting, prop_name);
8696 set_result = nmc_setting_set_property (curr_setting, prop_name, prop_val_user, &tmp_err);
8697 g_free (prop_val_user);
8699 g_print (_("Error: failed to set '%s' property: %s\n"), prop_name, tmp_err->message);
8700 g_clear_error (&tmp_err);
8701 if (cmdsub == NMC_EDITOR_SUB_CMD_SET) {
8702 /* Block change signals and restore original value */
8703 g_signal_handlers_block_matched (curr_setting, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL);
8704 nmc_property_set_gvalue (curr_setting, prop_name, &prop_g_value);
8705 g_signal_handlers_unblock_matched (curr_setting, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL);
8708 if (G_IS_VALUE (&prop_g_value))
8709 g_value_unset (&prop_g_value);
8712 case NMC_EDITOR_SUB_CMD_CHANGE:
8713 rl_startup_hook = nmc_rl_set_deftext;
8714 nmc_rl_pre_input_deftext = nmc_setting_get_property_parsable (curr_setting, prop_name, NULL);
8715 prop_val_user = nmc_readline (_("Edit '%s' value: "), prop_name);
8717 nmc_property_get_gvalue (curr_setting, prop_name, &prop_g_value);
8718 nmc_property_set_default_value (curr_setting, prop_name);
8720 if (!nmc_setting_set_property (curr_setting, prop_name, prop_val_user, &tmp_err)) {
8721 g_print (_("Error: failed to set '%s' property: %s\n"), prop_name, tmp_err->message);
8722 g_clear_error (&tmp_err);
8723 g_signal_handlers_block_matched (curr_setting, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL);
8724 nmc_property_set_gvalue (curr_setting, prop_name, &prop_g_value);
8725 g_signal_handlers_unblock_matched (curr_setting, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL);
8727 g_free (prop_val_user);
8728 if (G_IS_VALUE (&prop_g_value))
8729 g_value_unset (&prop_g_value);
8732 case NMC_EDITOR_SUB_CMD_REMOVE:
8733 if (cmd_property_arg) {
8734 unsigned long val_int = G_MAXUINT32;
8735 char *option = NULL;
8737 if (!nmc_string_to_uint (cmd_property_arg, TRUE, 0, G_MAXUINT32, &val_int))
8738 option = g_strdup (cmd_property_arg);
8740 if (!nmc_setting_remove_property_option (curr_setting, prop_name,
8741 option ? g_strstrip (option) : NULL,
8744 g_print (_("Error: %s\n"), tmp_err->message);
8745 g_clear_error (&tmp_err);
8749 if (!nmc_setting_reset_property (curr_setting, prop_name, &tmp_err)) {
8750 g_print (_("Error: failed to remove value of '%s': %s\n"), prop_name,
8752 g_clear_error (&tmp_err);
8757 case NMC_EDITOR_SUB_CMD_DESCRIBE:
8758 /* Show property description */
8759 print_property_description (curr_setting, prop_name);
8762 case NMC_EDITOR_SUB_CMD_PRINT:
8763 /* Print current connection settings/properties */
8764 if (cmd_property_arg) {
8765 if (matches (cmd_property_arg, "setting") == 0)
8766 editor_show_setting (curr_setting, nmc);
8767 else if ( matches (cmd_property_arg, "connection") == 0
8768 || matches (cmd_property_arg, "all") == 0)
8769 editor_show_connection (connection, nmc);
8771 g_print (_("Unknown command argument: '%s'\n"), cmd_property_arg);
8773 char *prop_val = nmc_setting_get_property (curr_setting, prop_name, NULL);
8774 g_print ("%s: %s\n", prop_name, prop_val);
8779 case NMC_EDITOR_SUB_CMD_BACK:
8780 /* Set global variable for use in TAB completion */
8781 nmc_tab_completion.property = NULL;
8782 cmd_property_loop = FALSE;
8785 case NMC_EDITOR_SUB_CMD_HELP:
8786 editor_sub_usage (cmd_property_arg);
8789 case NMC_EDITOR_SUB_CMD_QUIT:
8790 if (is_connection_dirty (connection, *rem_con)) {
8791 if (confirm_quit ()) {
8792 cmd_property_loop = FALSE;
8793 should_quit = TRUE; /* we will quit nmcli */
8796 cmd_property_loop = FALSE;
8797 should_quit = TRUE; /* we will quit nmcli */
8801 case NMC_EDITOR_SUB_CMD_UNKNOWN:
8803 g_print (_("Unknown command: '%s'\n"), cmd_property_user);
8806 g_free (cmd_property_user);
8807 g_free (cmd_property_arg);
8811 return !should_quit;
8815 * Split 'str' in the following format: [[[setting.]property] [value]]
8816 * and return the components in 'setting', 'property' and 'value'
8817 * Use g_free() to deallocate the returned strings.
8820 split_editor_main_cmd_args (const char *str, char **setting, char **property, char **value)
8822 char **args, **items;
8827 args = nmc_strsplit_set (str, " \t", 2);
8829 items = nmc_strsplit_set (args[0], ".", 2);
8830 if (g_strv_length (items) == 2) {
8832 *setting = g_strdup (items[0]);
8834 *property = g_strdup (items[1]);
8837 *property = g_strdup (items[0]);
8841 if (value && args[1])
8842 *value = g_strstrip (g_strdup (args[1]));
8848 create_setting_by_name (const char *name, const NameItem *valid_settings_main, const NameItem *valid_settings_slave)
8850 const char *setting_name;
8851 NMSetting *setting = NULL;
8853 /* Get a valid setting name */
8854 setting_name = check_valid_name (name, valid_settings_main, valid_settings_slave, NULL);
8857 setting = nmc_setting_new_for_name (setting_name);
8859 return NULL; /* This should really not happen */
8860 nmc_setting_custom_init (setting);
8866 ask_check_setting (const char *arg,
8867 const NameItem *valid_settings_main,
8868 const NameItem *valid_settings_slave,
8869 const char *valid_settings_str)
8871 char *setting_name_user;
8872 const char *setting_name;
8876 g_print (_("Available settings: %s\n"), valid_settings_str);
8877 setting_name_user = nmc_readline (EDITOR_PROMPT_SETTING);
8879 setting_name_user = g_strdup (arg);
8881 if (setting_name_user)
8882 g_strstrip (setting_name_user);
8884 if (!(setting_name = check_valid_name (setting_name_user,
8885 valid_settings_main,
8886 valid_settings_slave,
8888 g_print (_("Error: invalid setting name; %s\n"), err->message);
8889 g_clear_error (&err);
8891 g_free (setting_name_user);
8892 return setting_name;
8896 ask_check_property (const char *arg,
8897 const char **valid_props,
8898 const char *valid_props_str)
8900 char *prop_name_user;
8901 const char *prop_name;
8902 GError *tmp_err = NULL;
8905 g_print (_("Available properties: %s\n"), valid_props_str);
8906 prop_name_user = nmc_readline (EDITOR_PROMPT_PROPERTY);
8908 g_strstrip (prop_name_user);
8910 prop_name_user = g_strdup (arg);
8912 if (!(prop_name = nmc_string_is_valid (prop_name_user, valid_props, &tmp_err))) {
8913 g_print (_("Error: property %s\n"), tmp_err->message);
8914 g_clear_error (&tmp_err);
8916 g_free (prop_name_user);
8920 /* Copy timestamp from src do dst */
8922 update_connection_timestamp (NMConnection *src, NMConnection *dst)
8924 NMSettingConnection *s_con_src, *s_con_dst;
8926 s_con_src = nm_connection_get_setting_connection (src);
8927 s_con_dst = nm_connection_get_setting_connection (dst);
8928 if (s_con_src && s_con_dst) {
8929 guint64 timestamp = nm_setting_connection_get_timestamp (s_con_src);
8930 g_object_set (s_con_dst, NM_SETTING_CONNECTION_TIMESTAMP, timestamp, NULL);
8935 confirm_connection_saving (NMConnection *local, NMConnection *remote)
8937 NMSettingConnection *s_con_loc, *s_con_rem;
8938 gboolean ac_local, ac_remote;
8939 gboolean confirmed = TRUE;
8941 s_con_loc = nm_connection_get_setting_connection (local);
8942 g_assert (s_con_loc);
8943 ac_local = nm_setting_connection_get_autoconnect (s_con_loc);
8946 s_con_rem = nm_connection_get_setting_connection (remote);
8947 g_assert (s_con_rem);
8948 ac_remote = nm_setting_connection_get_autoconnect (s_con_rem);
8952 if (ac_local && !ac_remote) {
8954 answer = nmc_readline (_("Saving the connection with 'autoconnect=yes'. "
8955 "That might result in an immediate activation of the connection.\n"
8956 "Do you still want to save? %s"), prompt_yes_no (TRUE, NULL));
8957 answer = answer ? g_strstrip (answer) : NULL;
8958 if (!answer || matches (answer, WORD_LOC_YES) == 0)
8970 NMSetting *curr_setting;
8972 char *valid_props_str;
8973 } NmcEditorMenuContext;
8976 menu_switch_to_level0 (NmCli *nmc,
8977 NmcEditorMenuContext *menu_ctx,
8979 NmcTermColor prompt_color)
8981 menu_ctx->level = 0;
8982 g_free (menu_ctx->main_prompt);
8983 menu_ctx->main_prompt = nmc_colorize (nmc, prompt_color, NMC_TERM_FORMAT_NORMAL, "%s", prompt);
8984 menu_ctx->curr_setting = NULL;
8985 g_strfreev (menu_ctx->valid_props);
8986 menu_ctx->valid_props = NULL;
8987 g_free (menu_ctx->valid_props_str);
8988 menu_ctx->valid_props_str = NULL;
8992 menu_switch_to_level1 (NmCli *nmc,
8993 NmcEditorMenuContext *menu_ctx,
8995 const char *setting_name,
8996 NmcTermColor prompt_color)
8998 menu_ctx->level = 1;
8999 g_free (menu_ctx->main_prompt);
9000 menu_ctx->main_prompt = nmc_colorize (nmc, prompt_color, NMC_TERM_FORMAT_NORMAL,
9001 "nmcli %s> ", setting_name);
9002 menu_ctx->curr_setting = setting;
9003 g_strfreev (menu_ctx->valid_props);
9004 menu_ctx->valid_props = nmc_setting_get_valid_properties (menu_ctx->curr_setting);
9005 g_free (menu_ctx->valid_props_str);
9006 menu_ctx->valid_props_str = g_strjoinv (", ", menu_ctx->valid_props);
9010 editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_type)
9012 NMSettingConnection *s_con;
9013 NMRemoteConnection *rem_con;
9014 NMRemoteConnection *con_tmp;
9015 GWeakRef weak = { { NULL } };
9017 NmcEditorMainCmd cmd;
9019 gboolean cmd_loop = TRUE;
9020 char *cmd_arg = NULL;
9021 char *cmd_arg_s, *cmd_arg_p, *cmd_arg_v;
9022 const char *BASE_PROMPT = "nmcli> ";
9023 const NameItem *valid_settings_main = NULL;
9024 const NameItem *valid_settings_slave = NULL;
9025 char *valid_settings_str = NULL;
9026 const char *s_type = NULL;
9028 AddConnectionInfo *info = NULL;
9030 gboolean temp_changes;
9031 GError *err1 = NULL;
9032 NmcEditorMenuContext menu_ctx;
9034 s_con = nm_connection_get_setting_connection (connection);
9036 s_type = nm_setting_connection_get_slave_type (s_con);
9037 slv_type = g_strdup_printf ("%s-slave", s_type ? s_type : "no");
9039 valid_settings_main = get_valid_settings_array (connection_type);
9040 valid_settings_slave = get_valid_settings_array (slv_type);
9043 valid_settings_str = get_valid_options_string (valid_settings_main, valid_settings_slave);
9044 g_print (_("You may edit the following settings: %s\n"), valid_settings_str);
9047 menu_ctx.main_prompt = nmc_colorize (nmc, nmc->editor_prompt_color, NMC_TERM_FORMAT_NORMAL,
9049 menu_ctx.curr_setting = NULL;
9050 menu_ctx.valid_props = NULL;
9051 menu_ctx.valid_props_str = NULL;
9053 /* Get remote connection */
9054 con_tmp = nm_client_get_connection_by_uuid (nmc->client,
9055 nm_connection_get_uuid (connection));
9056 g_weak_ref_init (&weak, con_tmp);
9057 rem_con = g_weak_ref_get (&weak);
9060 /* Connection is dirty? (not saved or differs from the saved) */
9061 dirty = is_connection_dirty (connection, rem_con);
9062 temp_changes = rem_con ? nm_remote_connection_get_unsaved (rem_con) : TRUE;
9063 if (nmc->editor_status_line)
9064 editor_show_status_line (connection, dirty, temp_changes);
9066 /* Read user input */
9067 cmd_user = nmc_readline ("%s", menu_ctx.main_prompt);
9069 /* Get the remote connection again, it may have disapeared */
9070 removed = refresh_remote_connection (&weak, &rem_con);
9072 g_print (_("The connection profile has been removed from another client. "
9073 "You may type 'save' to restore it.\n"));
9075 if (!cmd_user || *cmd_user == '\0')
9077 cmd = parse_editor_main_cmd (g_strstrip (cmd_user), &cmd_arg);
9082 split_editor_main_cmd_args (cmd_arg, &cmd_arg_s, &cmd_arg_p, &cmd_arg_v);
9084 case NMC_EDITOR_MAIN_CMD_SET:
9085 /* Set property value */
9087 if (menu_ctx.level == 1) {
9088 const char *prop_name;
9089 char *prop_val_user = NULL;
9091 GError *tmp_err = NULL;
9093 prop_name = ask_check_property (cmd_arg,
9094 (const char **) menu_ctx.valid_props,
9095 menu_ctx.valid_props_str);
9099 avals = nmc_setting_get_property_allowed_values (menu_ctx.curr_setting, prop_name);
9101 char *avals_str = nmc_util_strv_for_display (avals, FALSE);
9102 g_print (_("Allowed values for '%s' property: %s\n"),
9103 prop_name, avals_str);
9106 prop_val_user = nmc_readline (_("Enter '%s' value: "), prop_name);
9108 /* Set property value */
9109 if (!nmc_setting_set_property (menu_ctx.curr_setting, prop_name, prop_val_user, &tmp_err)) {
9110 g_print (_("Error: failed to set '%s' property: %s\n"), prop_name, tmp_err->message);
9111 g_clear_error (&tmp_err);
9114 g_print (_("Error: no setting selected; valid are [%s]\n"), valid_settings_str);
9115 g_print (_("use 'goto <setting>' first, or 'set <setting>.<property>'\n"));
9118 NMSetting *ss = NULL;
9119 gboolean created_ss = FALSE;
9121 GError *tmp_err = NULL;
9124 /* setting provided as "setting.property" */
9125 ss = is_setting_valid (connection, valid_settings_main, valid_settings_slave, cmd_arg_s);
9127 ss = create_setting_by_name (cmd_arg_s, valid_settings_main, valid_settings_slave);
9129 g_print (_("Error: invalid setting argument '%s'; valid are [%s]\n"),
9130 cmd_arg_s, valid_settings_str);
9136 if (menu_ctx.curr_setting)
9137 ss = menu_ctx.curr_setting;
9139 g_print (_("Error: missing setting for '%s' property\n"), cmd_arg_p);
9144 prop_name = is_property_valid (ss, cmd_arg_p, &tmp_err);
9146 g_print (_("Error: invalid property: %s\n"), tmp_err->message);
9147 g_clear_error (&tmp_err);
9149 g_object_unref (ss);
9157 const char **avals = nmc_setting_get_property_allowed_values (ss, prop_name);
9159 char *avals_str = nmc_util_strv_for_display (avals, FALSE);
9160 g_print (_("Allowed values for '%s' property: %s\n"),
9161 prop_name, avals_str);
9164 cmd_arg_v = nmc_readline (_("Enter '%s' value: "), prop_name);
9167 /* Set property value */
9168 if (!nmc_setting_set_property (ss, prop_name, cmd_arg_v, &tmp_err)) {
9169 g_print (_("Error: failed to set '%s' property: %s\n"),
9170 prop_name, tmp_err->message);
9171 g_clear_error (&tmp_err);
9175 nm_connection_add_setting (connection, ss);
9180 case NMC_EDITOR_MAIN_CMD_GOTO:
9181 /* cmd_arg_s != NULL means 'setting.property' argument */
9182 if (menu_ctx.level == 0 || cmd_arg_s) {
9183 /* in top level - no setting selected yet */
9184 const char *setting_name;
9186 const char *user_arg = cmd_arg_s ? cmd_arg_s : cmd_arg_p;
9188 setting_name = ask_check_setting (user_arg,
9189 valid_settings_main,
9190 valid_settings_slave,
9191 valid_settings_str);
9195 setting = nm_connection_get_setting_by_name (connection, setting_name);
9197 setting = nmc_setting_new_for_name (setting_name);
9199 g_print (_("Error: unknown setting '%s'\n"), setting_name);
9202 nmc_setting_custom_init (setting);
9203 nm_connection_add_setting (connection, setting);
9205 /* Set global variable for use in TAB completion */
9206 nmc_tab_completion.setting = setting;
9208 /* Switch to level 1 */
9209 menu_switch_to_level1 (nmc, &menu_ctx, setting, setting_name, nmc->editor_prompt_color);
9212 g_print (_("You may edit the following properties: %s\n"), menu_ctx.valid_props_str);
9216 if (menu_ctx.level == 1 || cmd_arg_s) {
9217 /* level 1 - setting selected */
9218 const char *prop_name;
9220 prop_name = ask_check_property (cmd_arg_p,
9221 (const char **) menu_ctx.valid_props,
9222 menu_ctx.valid_props_str);
9226 /* submenu - level 2 - editing properties */
9227 cmd_loop = property_edit_submenu (nmc,
9231 menu_ctx.curr_setting,
9236 case NMC_EDITOR_MAIN_CMD_REMOVE:
9237 /* Remove setting from connection, or delete value of a property */
9239 if (menu_ctx.level == 1) {
9240 GError *tmp_err = NULL;
9241 const char *prop_name;
9243 prop_name = ask_check_property (cmd_arg,
9244 (const char **) menu_ctx.valid_props,
9245 menu_ctx.valid_props_str);
9249 /* Delete property value */
9250 if (!nmc_setting_reset_property (menu_ctx.curr_setting, prop_name, &tmp_err)) {
9251 g_print (_("Error: failed to remove value of '%s': %s\n"), prop_name,
9253 g_clear_error (&tmp_err);
9256 g_print (_("Error: no argument given; valid are [%s]\n"), valid_settings_str);
9258 NMSetting *ss = NULL;
9262 /* cmd_arg_s != NULL means argument is "setting.property" */
9263 descr_all = !cmd_arg_s && !menu_ctx.curr_setting;
9264 user_s = descr_all ? cmd_arg_p : cmd_arg_s ? cmd_arg_s : NULL;
9266 ss = is_setting_valid (connection,
9267 valid_settings_main,
9268 valid_settings_slave,
9271 if (check_valid_name (user_s,
9272 valid_settings_main,
9273 valid_settings_slave,
9275 g_print (_("Setting '%s' is not present in the connection.\n"),
9278 g_print (_("Error: invalid setting argument '%s'; valid are [%s]\n"),
9279 user_s, valid_settings_str);
9283 ss = menu_ctx.curr_setting;
9286 /* Remove setting from the connection */
9287 connection_remove_setting (connection, ss);
9288 if (ss == menu_ctx.curr_setting) {
9289 /* If we removed the setting we are in, go up */
9290 menu_switch_to_level0 (nmc, &menu_ctx, BASE_PROMPT, nmc->editor_prompt_color);
9291 nmc_tab_completion.setting = NULL; /* for TAB completion */
9294 GError *tmp_err = NULL;
9295 char *prop_name = is_property_valid (ss, cmd_arg_p, &tmp_err);
9297 /* Delete property value */
9298 if (!nmc_setting_reset_property (ss, prop_name, &tmp_err)) {
9299 g_print (_("Error: failed to remove value of '%s': %s\n"), prop_name,
9301 g_clear_error (&tmp_err);
9304 /* If the string is not a property, try it as a setting */
9306 s_tmp = is_setting_valid (connection,
9307 valid_settings_main,
9308 valid_settings_slave,
9311 /* Remove setting from the connection */
9312 connection_remove_setting (connection, s_tmp);
9313 /* coverity[copy_paste_error] - suppress Coverity COPY_PASTE_ERROR defect */
9314 if (ss == menu_ctx.curr_setting) {
9315 /* If we removed the setting we are in, go up */
9316 menu_switch_to_level0 (nmc, &menu_ctx, BASE_PROMPT, nmc->editor_prompt_color);
9317 nmc_tab_completion.setting = NULL; /* for TAB completion */
9320 g_print (_("Error: %s properties, nor it is a setting name.\n"),
9322 g_clear_error (&tmp_err);
9329 case NMC_EDITOR_MAIN_CMD_DESCRIBE:
9330 /* Print property description */
9332 if (menu_ctx.level == 1) {
9333 const char *prop_name;
9335 prop_name = ask_check_property (cmd_arg,
9336 (const char **) menu_ctx.valid_props,
9337 menu_ctx.valid_props_str);
9341 /* Show property description */
9342 print_property_description (menu_ctx.curr_setting, prop_name);
9344 g_print (_("Error: no setting selected; valid are [%s]\n"), valid_settings_str);
9345 g_print (_("use 'goto <setting>' first, or 'describe <setting>.<property>'\n"));
9348 NMSetting *ss = NULL;
9349 gboolean unref_ss = FALSE;
9353 /* cmd_arg_s != NULL means argument is "setting.property" */
9354 descr_all = !cmd_arg_s && !menu_ctx.curr_setting;
9355 user_s = descr_all ? cmd_arg_p : cmd_arg_s ? cmd_arg_s : NULL;
9357 ss = is_setting_valid (connection,
9358 valid_settings_main,
9359 valid_settings_slave,
9362 ss = create_setting_by_name (user_s,
9363 valid_settings_main,
9364 valid_settings_slave);
9366 g_print (_("Error: invalid setting argument '%s'; valid are [%s]\n"),
9367 user_s, valid_settings_str);
9373 ss = menu_ctx.curr_setting;
9376 /* Show description for all properties */
9377 print_setting_description (ss);
9379 GError *tmp_err = NULL;
9380 char *prop_name = is_property_valid (ss, cmd_arg_p, &tmp_err);
9382 /* Show property description */
9383 print_property_description (ss, prop_name);
9385 /* If the string is not a property, try it as a setting */
9387 s_tmp = is_setting_valid (connection,
9388 valid_settings_main,
9389 valid_settings_slave,
9392 print_setting_description (s_tmp);
9394 g_print (_("Error: invalid property: %s, "
9395 "neither a valid setting name.\n"),
9397 g_clear_error (&tmp_err);
9402 g_object_unref (ss);
9406 case NMC_EDITOR_MAIN_CMD_PRINT:
9407 /* Print current connection settings/properties */
9409 if (strcmp (cmd_arg, "all") == 0)
9410 editor_show_connection (connection, nmc);
9412 NMSetting *ss = NULL;
9413 gboolean whole_setting;
9416 /* cmd_arg_s != NULL means argument is "setting.property" */
9417 whole_setting = !cmd_arg_s && !menu_ctx.curr_setting;
9418 user_s = whole_setting ? cmd_arg_p : cmd_arg_s ? cmd_arg_s : NULL;
9421 s_name = check_valid_name (user_s,
9422 valid_settings_main,
9423 valid_settings_slave,
9426 g_print (_("Error: unknown setting: '%s'\n"), user_s);
9429 ss = nm_connection_get_setting_by_name (connection, s_name);
9431 g_print (_("Error: '%s' setting not present in the connection\n"), s_name);
9435 ss = menu_ctx.curr_setting;
9437 if (whole_setting) {
9438 /* Print the whole setting */
9439 editor_show_setting (ss, nmc);
9442 char *prop_name = is_property_valid (ss, cmd_arg_p, &err);
9444 /* Print one property */
9445 char *prop_val = nmc_setting_get_property (ss, prop_name, NULL);
9446 g_print ("%s.%s: %s\n", nm_setting_get_name (ss),prop_name , prop_val);
9449 /* If the string is not a property, try it as a setting */
9451 s_tmp = is_setting_valid (connection,
9452 valid_settings_main,
9453 valid_settings_slave,
9456 /* Print the whole setting */
9457 editor_show_setting (s_tmp, nmc);
9459 g_print (_("Error: invalid property: %s%s\n"),
9461 cmd_arg_s ? "" : _(", neither a valid setting name"));
9462 g_clear_error (&err);
9468 if (menu_ctx.curr_setting)
9469 editor_show_setting (menu_ctx.curr_setting, nmc);
9471 editor_show_connection (connection, nmc);
9475 case NMC_EDITOR_MAIN_CMD_VERIFY:
9476 /* Verify current setting or the whole connection */
9477 if (cmd_arg && strcmp (cmd_arg, "all") && strcmp (cmd_arg, "fix")) {
9478 g_print (_("Invalid verify option: %s\n"), cmd_arg);
9482 if ( menu_ctx.curr_setting
9483 && (!cmd_arg || strcmp (cmd_arg, "all") != 0)) {
9484 GError *tmp_err = NULL;
9485 (void) nm_setting_verify (menu_ctx.curr_setting, NULL, &tmp_err);
9486 g_print (_("Verify setting '%s': %s\n"),
9487 nm_setting_get_name (menu_ctx.curr_setting),
9488 tmp_err ? tmp_err->message : "OK");
9489 g_clear_error (&tmp_err);
9491 GError *tmp_err = NULL;
9492 gboolean valid, modified;
9493 gboolean fixed = TRUE;
9495 valid = nm_connection_verify (connection, &tmp_err);
9496 if (!valid && (g_strcmp0 (cmd_arg, "fix") == 0)) {
9497 /* Try to fix normalizable errors */
9498 g_clear_error (&tmp_err);
9499 fixed = nm_connection_normalize (connection, NULL, &modified, &tmp_err);
9501 g_print (_("Verify connection: %s\n"),
9502 tmp_err ? tmp_err->message : "OK");
9504 g_print (_("The error cannot be fixed automatically.\n"));
9505 g_clear_error (&tmp_err);
9509 case NMC_EDITOR_MAIN_CMD_SAVE:
9510 /* Save the connection */
9511 if (nm_connection_verify (connection, &err1)) {
9512 gboolean persistent = TRUE;
9514 /* parse argument */
9516 if (matches (cmd_arg, "temporary") == 0)
9518 else if (matches (cmd_arg, "persistent") == 0)
9521 g_print (_("Error: invalid argument '%s'\n"), cmd_arg);
9526 /* Ask for save confirmation if the connection changes to autoconnect=yes */
9527 if (nmc->editor_save_confirmation)
9528 if (!confirm_connection_saving (connection, NM_CONNECTION (rem_con)))
9532 /* Tell the settings service to add the new connection */
9533 info = g_malloc0 (sizeof (AddConnectionInfo));
9535 info->con_name = g_strdup (nm_connection_get_id (connection));
9536 add_new_connection (persistent,
9539 add_connection_editor_cb,
9542 /* Save/update already saved (existing) connection */
9543 nm_connection_replace_settings_from_connection (NM_CONNECTION (rem_con),
9545 update_connection (persistent, rem_con, update_connection_editor_cb, NULL);
9548 g_mutex_lock (&nmc_editor_mutex);
9549 //FIXME: add also a timeout for cases the callback is not called
9550 while (!nmc_editor_cb_called)
9551 g_cond_wait (&nmc_editor_cond, &nmc_editor_mutex);
9553 if (nmc_editor_error) {
9554 g_print (_("Error: Failed to save '%s' (%s) connection: %s\n"),
9555 nm_connection_get_id (connection),
9556 nm_connection_get_uuid (connection),
9557 nmc_editor_error->message);
9558 g_error_free (nmc_editor_error);
9561 _("Connection '%s' (%s) successfully saved.\n") :
9562 _("Connection '%s' (%s) successfully updated.\n"),
9563 nm_connection_get_id (connection),
9564 nm_connection_get_uuid (connection));
9566 con_tmp = nm_client_get_connection_by_uuid (nmc->client,
9567 nm_connection_get_uuid (connection));
9568 g_weak_ref_set (&weak, con_tmp);
9569 refresh_remote_connection (&weak, &rem_con);
9571 /* Replace local connection with the remote one to be sure they are equal.
9572 * This mitigates problems with plugins not preserving some properties or
9573 * adding ipv{4,6} settings when not present.
9576 char *s_name = NULL;
9577 if (menu_ctx.curr_setting)
9578 s_name = g_strdup (nm_setting_get_name (menu_ctx.curr_setting));
9580 /* Update settings in the local connection */
9581 nm_connection_replace_settings_from_connection (connection,
9582 NM_CONNECTION (con_tmp));
9584 /* Also update setting for menu context and TAB-completion */
9585 menu_ctx.curr_setting = s_name ? nm_connection_get_setting_by_name (connection, s_name) : NULL;
9586 nmc_tab_completion.setting = menu_ctx.curr_setting;
9591 nmc_editor_cb_called = FALSE;
9592 nmc_editor_error = NULL;
9593 g_mutex_unlock (&nmc_editor_mutex);
9595 g_print (_("Error: connection verification failed: %s\n"),
9596 err1 ? err1->message : _("(unknown error)"));
9597 g_print (_("You may try running 'verify fix' to fix errors.\n"));
9600 g_clear_error (&err1);
9603 case NMC_EDITOR_MAIN_CMD_ACTIVATE:
9605 GError *tmp_err = NULL;
9606 const char *ifname = cmd_arg_p;
9607 const char *ap_nsp = cmd_arg_v;
9609 /* When only AP/NSP is specified it is prepended with '/' */
9611 if (ifname && ifname[0] == '/') {
9612 ap_nsp = ifname + 1;
9616 ap_nsp = ap_nsp && ap_nsp[0] == '/' ? ap_nsp + 1 : ap_nsp;
9618 if (is_connection_dirty (connection, rem_con)) {
9619 g_print (_("Error: connection is not saved. Type 'save' first.\n"));
9622 if (!nm_connection_verify (NM_CONNECTION (rem_con), &tmp_err)) {
9623 g_print (_("Error: connection is not valid: %s\n"), tmp_err->message);
9624 g_clear_error (&tmp_err);
9628 nmc->nowait_flag = FALSE;
9630 nmc->print_output = NMC_PRINT_PRETTY;
9631 if (!nmc_activate_connection (nmc, NM_CONNECTION (rem_con), ifname, ap_nsp, ap_nsp, NULL,
9632 activate_connection_editor_cb, &tmp_err)) {
9633 g_print (_("Error: Cannot activate connection: %s.\n"), tmp_err->message);
9634 g_clear_error (&tmp_err);
9638 g_mutex_lock (&nmc_editor_mutex);
9639 while (!nmc_editor_cb_called)
9640 g_cond_wait (&nmc_editor_cond, &nmc_editor_mutex);
9642 if (nmc_editor_error) {
9643 g_print (_("Error: Failed to activate '%s' (%s) connection: %s\n"),
9644 nm_connection_get_id (connection),
9645 nm_connection_get_uuid (connection),
9646 nmc_editor_error->message);
9647 g_error_free (nmc_editor_error);
9649 g_print (_("Monitoring connection activation (press any key to continue)\n"));
9650 nmc_get_user_input ("");
9653 if (nmc_editor_monitor_ac) {
9654 if (nmc_editor_monitor_ac->monitor_id)
9655 g_source_remove (nmc_editor_monitor_ac->monitor_id);
9656 g_free (nmc_editor_monitor_ac);
9658 nmc_editor_cb_called = FALSE;
9659 nmc_editor_error = NULL;
9660 nmc_editor_monitor_ac = NULL;
9661 g_mutex_unlock (&nmc_editor_mutex);
9663 /* Update timestamp in local connection */
9664 update_connection_timestamp (NM_CONNECTION (rem_con), connection);
9669 case NMC_EDITOR_MAIN_CMD_BACK:
9670 /* Go back (up) an the menu */
9671 if (menu_ctx.level == 1) {
9672 menu_switch_to_level0 (nmc, &menu_ctx, BASE_PROMPT, nmc->editor_prompt_color);
9673 nmc_tab_completion.setting = NULL; /* for TAB completion */
9677 case NMC_EDITOR_MAIN_CMD_HELP:
9678 /* Print command help */
9679 editor_main_help (cmd_arg);
9682 case NMC_EDITOR_MAIN_CMD_NMCLI:
9683 if (cmd_arg_p && matches (cmd_arg_p, "status-line") == 0) {
9684 GError *tmp_err = NULL;
9686 if (!nmc_string_to_bool (cmd_arg_v ? g_strstrip (cmd_arg_v) : "", &bb, &tmp_err)) {
9687 g_print (_("Error: status-line: %s\n"), tmp_err->message);
9688 g_clear_error (&tmp_err);
9690 nmc->editor_status_line = bb;
9691 } else if (cmd_arg_p && matches (cmd_arg_p, "save-confirmation") == 0) {
9692 GError *tmp_err = NULL;
9694 if (!nmc_string_to_bool (cmd_arg_v ? g_strstrip (cmd_arg_v) : "", &bb, &tmp_err)) {
9695 g_print (_("Error: save-confirmation: %s\n"), tmp_err->message);
9696 g_clear_error (&tmp_err);
9698 nmc->editor_save_confirmation = bb;
9699 } else if (cmd_arg_p && matches (cmd_arg_p, "show-secrets") == 0) {
9700 GError *tmp_err = NULL;
9702 if (!nmc_string_to_bool (cmd_arg_v ? g_strstrip (cmd_arg_v) : "", &bb, &tmp_err)) {
9703 g_print (_("Error: show-secrets: %s\n"), tmp_err->message);
9704 g_clear_error (&tmp_err);
9706 nmc->editor_show_secrets = bb;
9707 } else if (cmd_arg_p && matches (cmd_arg_p, "prompt-color") == 0) {
9708 GError *tmp_err = NULL;
9710 color = nmc_term_color_parse_string (cmd_arg_v ? g_strstrip (cmd_arg_v) : " ", &tmp_err);
9712 g_print (_("Error: bad color: %s\n"), tmp_err->message);
9713 g_clear_error (&tmp_err);
9715 nmc->editor_prompt_color = color;
9716 g_free (menu_ctx.main_prompt);
9717 if (menu_ctx.level == 0)
9718 menu_ctx.main_prompt = nmc_colorize (nmc, nmc->editor_prompt_color, NMC_TERM_FORMAT_NORMAL,
9721 menu_ctx.main_prompt = nmc_colorize (nmc, nmc->editor_prompt_color, NMC_TERM_FORMAT_NORMAL,
9723 nm_setting_get_name (menu_ctx.curr_setting));
9725 } else if (!cmd_arg_p) {
9726 g_print (_("Current nmcli configuration:\n"));
9727 g_print ("status-line: %s\n"
9728 "save-confirmation: %s\n"
9729 "show-secrets: %s\n"
9730 "prompt-color: %d\n",
9731 nmc->editor_status_line ? "yes" : "no",
9732 nmc->editor_save_confirmation ? "yes" : "no",
9733 nmc->editor_show_secrets ? "yes" : "no",
9734 nmc->editor_prompt_color);
9736 g_print (_("Invalid configuration option '%s'; allowed [%s]\n"),
9737 cmd_arg_v ? cmd_arg_v : "", "status-line, save-confirmation, show-secrets, prompt-color");
9741 case NMC_EDITOR_MAIN_CMD_QUIT:
9742 if (is_connection_dirty (connection, rem_con)) {
9743 if (confirm_quit ())
9744 cmd_loop = FALSE; /* quit command loop */
9746 cmd_loop = FALSE; /* quit command loop */
9749 case NMC_EDITOR_MAIN_CMD_UNKNOWN:
9751 g_print (_("Unknown command: '%s'\n"), cmd_user);
9761 g_free (valid_settings_str);
9762 g_free (menu_ctx.main_prompt);
9763 g_strfreev (menu_ctx.valid_props);
9764 g_free (menu_ctx.valid_props_str);
9766 g_object_unref (rem_con);
9767 g_weak_ref_clear (&weak);
9769 /* Save history file */
9770 save_history_cmds (nm_connection_get_uuid (connection));
9776 get_ethernet_device_name (NmCli *nmc)
9778 const GPtrArray *devices;
9781 devices = nm_client_get_devices (nmc->client);
9782 for (i = 0; i < devices->len; i++) {
9783 NMDevice *dev = g_ptr_array_index (devices, i);
9784 if (NM_IS_DEVICE_ETHERNET (dev))
9785 return nm_device_get_iface (dev);
9791 editor_init_new_connection (NmCli *nmc, NMConnection *connection)
9793 NMSetting *setting, *base_setting;
9794 NMSettingConnection *s_con;
9795 const char *con_type;
9796 const char *slave_type = NULL;
9798 s_con = nm_connection_get_setting_connection (connection);
9800 con_type = nm_setting_connection_get_connection_type (s_con);
9802 /* Initialize new connection according to its type using sensible defaults. */
9804 nmc_setting_connection_connect_handlers (s_con, connection);
9806 if (g_strcmp0 (con_type, "bond-slave") == 0)
9807 slave_type = NM_SETTING_BOND_SETTING_NAME;
9808 if (g_strcmp0 (con_type, "team-slave") == 0)
9809 slave_type = NM_SETTING_TEAM_SETTING_NAME;
9810 if (g_strcmp0 (con_type, "bridge-slave") == 0)
9811 slave_type = NM_SETTING_BRIDGE_SETTING_NAME;
9814 const char *dev_ifname = get_ethernet_device_name (nmc);
9816 /* For bond/team/bridge slaves add 'wired' setting */
9817 setting = nm_setting_wired_new ();
9818 nm_connection_add_setting (connection, setting);
9820 g_object_set (s_con,
9821 NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
9822 NM_SETTING_CONNECTION_MASTER, dev_ifname ? dev_ifname : "eth0",
9823 NM_SETTING_CONNECTION_SLAVE_TYPE, slave_type,
9826 /* Add a "base" setting to the connection by default */
9827 base_setting = nmc_setting_new_for_name (con_type);
9830 nm_connection_add_setting (connection, base_setting);
9832 /* Set a sensible bond/team/bridge interface name by default */
9833 if (g_strcmp0 (con_type, NM_SETTING_BOND_SETTING_NAME) == 0)
9834 g_object_set (s_con,
9835 NM_SETTING_CONNECTION_INTERFACE_NAME, "nm-bond",
9837 if (g_strcmp0 (con_type, NM_SETTING_TEAM_SETTING_NAME) == 0)
9838 g_object_set (s_con,
9839 NM_SETTING_CONNECTION_INTERFACE_NAME, "nm-team",
9841 if (g_strcmp0 (con_type, NM_SETTING_BRIDGE_SETTING_NAME) == 0)
9842 g_object_set (s_con,
9843 NM_SETTING_CONNECTION_INTERFACE_NAME, "nm-bridge",
9846 /* Set sensible initial VLAN values */
9847 if (g_strcmp0 (con_type, NM_SETTING_VLAN_SETTING_NAME) == 0) {
9848 const char *dev_ifname = get_ethernet_device_name (nmc);
9850 g_object_set (NM_SETTING_VLAN (base_setting),
9851 NM_SETTING_VLAN_PARENT, dev_ifname ? dev_ifname : "eth0",
9852 NM_SETTING_VLAN_ID, 1,
9854 g_object_set (s_con,
9855 NM_SETTING_CONNECTION_MASTER, dev_ifname ? dev_ifname : "eth0",
9856 NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_VLAN_SETTING_NAME,
9860 /* Initialize 'transport-mode' so that 'infiniband' is valid */
9861 if (g_strcmp0 (con_type, NM_SETTING_INFINIBAND_SETTING_NAME) == 0)
9862 g_object_set (NM_SETTING_INFINIBAND (base_setting),
9863 NM_SETTING_INFINIBAND_TRANSPORT_MODE, "datagram",
9866 /* Initialize 'number' so that 'cdma' is valid */
9867 if (g_strcmp0 (con_type, NM_SETTING_CDMA_SETTING_NAME) == 0)
9868 g_object_set (NM_SETTING_CDMA (base_setting),
9869 NM_SETTING_CDMA_NUMBER, "#777",
9872 /* Initialize 'number' so that 'gsm' is valid */
9873 if (g_strcmp0 (con_type, NM_SETTING_GSM_SETTING_NAME) == 0)
9874 g_object_set (NM_SETTING_GSM (base_setting),
9875 NM_SETTING_GSM_NUMBER, "*99#",
9879 if (g_strcmp0 (con_type, NM_SETTING_WIRELESS_SETTING_NAME) == 0) {
9880 /* For Wi-Fi set mode to "infrastructure". Even though mode == NULL
9881 * is regarded as "infrastructure", explicit value makes no doubts.
9883 g_object_set (NM_SETTING_WIRELESS (base_setting),
9884 NM_SETTING_WIRELESS_MODE, NM_SETTING_WIRELESS_MODE_INFRA,
9887 /* Do custom initialization for wifi setting */
9888 nmc_setting_custom_init (base_setting);
9892 if (g_strcmp0 (con_type, NM_SETTING_ADSL_SETTING_NAME) == 0) {
9893 /* Initialize a protocol */
9894 g_object_set (NM_SETTING_ADSL (base_setting),
9895 NM_SETTING_ADSL_PROTOCOL, NM_SETTING_ADSL_PROTOCOL_PPPOE,
9899 /* Always add IPv4 and IPv6 settings for non-slave connections */
9900 setting = nm_setting_ip4_config_new ();
9901 nmc_setting_custom_init (setting);
9902 nm_connection_add_setting (connection, setting);
9904 setting = nm_setting_ip6_config_new ();
9905 nmc_setting_custom_init (setting);
9906 nm_connection_add_setting (connection, setting);
9911 editor_init_existing_connection (NMConnection *connection)
9913 NMSettingIPConfig *s_ip4, *s_ip6;
9914 NMSettingWireless *s_wireless;
9915 NMSettingConnection *s_con;
9917 s_ip4 = nm_connection_get_setting_ip4_config (connection);
9918 s_ip6 = nm_connection_get_setting_ip6_config (connection);
9919 s_wireless = nm_connection_get_setting_wireless (connection);
9920 s_con = nm_connection_get_setting_connection (connection);
9923 nmc_setting_ip4_connect_handlers (s_ip4);
9925 nmc_setting_ip6_connect_handlers (s_ip6);
9927 nmc_setting_wireless_connect_handlers (s_wireless);
9929 nmc_setting_connection_connect_handlers (s_con, connection);
9932 static NMCResultCode
9933 do_connection_edit (NmCli *nmc, int argc, char **argv)
9935 NMConnection *connection = NULL;
9936 NMSettingConnection *s_con;
9937 const char *connection_type;
9939 char *default_name = NULL;
9940 const char *type = NULL;
9941 char *type_ask = NULL;
9942 const char *con_name = NULL;
9943 const char *con = NULL;
9944 const char *con_id = NULL;
9945 const char *con_uuid = NULL;
9946 const char *con_path = NULL;
9947 const char *selector = NULL;
9949 GError *error = NULL;
9950 GError *err1 = NULL;
9951 nmc_arg_t exp_args[] = { {"type", TRUE, &type, FALSE},
9952 {"con-name", TRUE, &con_name, FALSE},
9953 {"id", TRUE, &con_id, FALSE},
9954 {"uuid", TRUE, &con_uuid, FALSE},
9955 {"path", TRUE, &con_path, FALSE},
9958 nmc->return_value = NMC_RESULT_SUCCESS;
9963 if (!nmc_parse_args (exp_args, TRUE, &argc, &argv, &error)) {
9964 g_string_assign (nmc->return_text, error->message);
9965 nmc->return_value = error->code;
9966 g_clear_error (&error);
9971 /* Setup some readline completion stuff */
9972 /* Set a pointer to an alternative function to create matches */
9973 rl_attempted_completion_function = (rl_completion_func_t *) nmcli_editor_tab_completion;
9974 /* Use ' ' and '.' as word break characters */
9975 rl_completer_word_break_characters = ". ";
9978 if (con_id && !con_uuid && !con_path) {
9981 } else if (con_uuid && !con_id && !con_path) {
9984 } else if (con_path && !con_id && !con_uuid) {
9987 } else if (!con_path && !con_id && !con_uuid) {
9990 g_string_printf (nmc->return_text,
9991 _("Error: only one of 'id', uuid, or 'path' can be provided."));
9992 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
9998 /* Existing connection */
9999 NMConnection *found_con;
10001 found_con = nmc_find_connection (nmc->connections, selector, con, NULL);
10003 g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), con);
10004 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10008 /* Duplicate the connection and use that so that we need not
10009 * differentiate existing vs. new later
10011 connection = nm_simple_connection_new_clone (found_con);
10013 /* Merge secrets into the connection */
10014 update_secrets_in_connection (NM_REMOTE_CONNECTION (found_con), connection);
10016 s_con = nm_connection_get_setting_connection (connection);
10018 connection_type = nm_setting_connection_get_connection_type (s_con);
10021 g_print (_("Warning: editing existing connection '%s'; 'type' argument is ignored\n"),
10022 nm_connection_get_id (connection));
10024 g_print (_("Warning: editing existing connection '%s'; 'con-name' argument is ignored\n"),
10025 nm_connection_get_id (connection));
10027 /* Load previously saved history commands for the connection */
10028 load_history_cmds (nm_connection_get_uuid (connection));
10030 editor_init_existing_connection (connection);
10032 /* New connection */
10033 connection_type = check_valid_name (type, nmc_valid_connection_types, NULL, &err1);
10034 tmp_str = get_valid_options_string (nmc_valid_connection_types, NULL);
10036 while (!connection_type) {
10038 g_print (_("Valid connection types: %s\n"), tmp_str);
10040 g_print (_("Error: invalid connection type; %s\n"), err1->message);
10041 g_clear_error (&err1);
10043 type_ask = nmc_readline (EDITOR_PROMPT_CON_TYPE);
10044 type = type_ask = type_ask ? g_strstrip (type_ask) : NULL;
10045 connection_type = check_valid_name (type_ask, nmc_valid_connection_types, NULL, &err1);
10050 /* Create a new connection object */
10051 connection = nm_simple_connection_new ();
10053 /* Build up the 'connection' setting */
10054 s_con = (NMSettingConnection *) nm_setting_connection_new ();
10055 uuid = nm_utils_uuid_generate ();
10057 default_name = g_strdup (con_name);
10059 default_name = nmc_unique_connection_name (nmc->connections,
10060 get_name_alias (connection_type, nmc_valid_connection_types));
10062 g_object_set (s_con,
10063 NM_SETTING_CONNECTION_ID, default_name,
10064 NM_SETTING_CONNECTION_UUID, uuid,
10065 NM_SETTING_CONNECTION_TYPE, connection_type,
10068 g_free (default_name);
10069 nm_connection_add_setting (connection, NM_SETTING (s_con));
10071 /* Initialize the new connection so that it is valid from the start */
10072 editor_init_new_connection (nmc, connection);
10075 /* nmcli runs the editor */
10076 nmc->in_editor = TRUE;
10079 g_print (_("===| nmcli interactive connection editor |==="));
10082 g_print (_("Editing existing '%s' connection: '%s'"), connection_type, con);
10084 g_print (_("Adding a new '%s' connection"), connection_type);
10086 g_print (_("Type 'help' or '?' for available commands."));
10088 g_print (_("Type 'describe [<setting>.<prop>]' for detailed property description."));
10091 /* Set global variables for use in TAB completion */
10092 nmc_tab_completion.nmc = nmc;
10093 nmc_tab_completion.con_type = g_strdup (connection_type);
10094 nmc_tab_completion.connection = connection;
10096 /* Run menu loop */
10097 editor_menu_main (nmc, connection, connection_type);
10100 g_object_unref (connection);
10101 g_free (nmc_tab_completion.con_type);
10103 nmc->should_wait++;
10104 return nmc->return_value;
10107 g_assert (!connection);
10110 nmc->should_wait++;
10111 return nmc->return_value;
10116 modify_connection_cb (GObject *connection,
10117 GAsyncResult *result,
10118 gpointer user_data)
10120 NmCli *nmc = (NmCli *) user_data;
10121 GError *error = NULL;
10123 if (!nm_remote_connection_commit_changes_finish (NM_REMOTE_CONNECTION (connection),
10125 g_string_printf (nmc->return_text,
10126 _("Error: Failed to modify connection '%s': %s"),
10127 nm_connection_get_id (NM_CONNECTION (connection)),
10129 g_error_free (error);
10130 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10132 if (nmc->print_output == NMC_PRINT_PRETTY)
10133 g_print (_("Connection '%s' (%s) successfully modified.\n"),
10134 nm_connection_get_id (NM_CONNECTION (connection)),
10135 nm_connection_get_uuid (NM_CONNECTION (connection)));
10140 static NMCResultCode
10141 do_connection_modify (NmCli *nmc,
10142 gboolean temporary,
10146 NMConnection *connection = NULL;
10147 NMRemoteConnection *rc = NULL;
10149 const char *selector = NULL;
10150 GError *error = NULL;
10152 nmc->return_value = NMC_RESULT_SUCCESS;
10155 g_string_printf (nmc->return_text, _("Error: No arguments provided."));
10156 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10159 if ( strcmp (*argv, "id") == 0
10160 || strcmp (*argv, "uuid") == 0
10161 || strcmp (*argv, "path") == 0) {
10164 if (next_arg (&argc, &argv) != 0) {
10165 g_string_printf (nmc->return_text, _("Error: %s argument is missing."),
10167 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10174 g_string_printf (nmc->return_text, _("Error: connection ID is missing."));
10175 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10178 connection = nmc_find_connection (nmc->connections, selector, name, NULL);
10180 g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), name);
10181 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10184 rc = nm_client_get_connection_by_uuid (nmc->client,
10185 nm_connection_get_uuid (connection));
10187 g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), name);
10188 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10192 if (next_arg (&argc, &argv) != 0) {
10193 g_string_printf (nmc->return_text, _("Error: <setting>.<property> argument is missing."));
10194 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10198 if (!read_connection_properties (NM_CONNECTION (rc), argc, argv, &error)) {
10199 g_string_assign (nmc->return_text, error->message);
10200 nmc->return_value = error->code;
10201 g_clear_error (&error);
10205 update_connection (!temporary, rc, modify_connection_cb, nmc);
10207 nmc->should_wait++;
10209 return nmc->return_value;
10217 } CloneConnectionInfo;
10220 clone_connection_cb (GObject *client,
10221 GAsyncResult *result,
10222 gpointer user_data)
10224 CloneConnectionInfo *info = (CloneConnectionInfo *) user_data;
10225 NmCli *nmc = info->nmc;
10226 NMRemoteConnection *connection;
10227 GError *error = NULL;
10229 connection = nm_client_add_connection_finish (NM_CLIENT (client), result, &error);
10231 g_string_printf (nmc->return_text,
10232 _("Error: Failed to add '%s' connection: %s"),
10233 info->con_id, error->message);
10234 g_error_free (error);
10235 nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
10237 g_print (_("%s (%s) cloned as %s (%s).\n"),
10240 nm_connection_get_id (NM_CONNECTION (connection)),
10241 nm_connection_get_uuid (NM_CONNECTION (connection)));
10242 g_object_unref (connection);
10245 g_free (info->con_id);
10246 g_free (info->orig_id);
10247 g_free (info->orig_uuid);
10248 g_slice_free (CloneConnectionInfo, info);
10252 static NMCResultCode
10253 do_connection_clone (NmCli *nmc, gboolean temporary, int argc, char **argv)
10255 NMConnection *connection = NULL;
10256 NMConnection *new_connection = NULL;
10257 NMSettingConnection *s_con;
10258 CloneConnectionInfo *info;
10260 const char *new_name;
10261 char *name_ask = NULL;
10262 char *new_name_ask = NULL;
10263 const char *selector = NULL;
10268 name = name_ask = nmc_readline (PROMPT_CONNECTION);
10269 new_name = new_name_ask = nmc_readline (_("New connection name: "));
10271 g_string_printf (nmc->return_text, _("Error: No arguments provided."));
10272 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10276 if ( strcmp (*argv, "id") == 0
10277 || strcmp (*argv, "uuid") == 0
10278 || strcmp (*argv, "path") == 0) {
10281 if (next_arg (&argc, &argv) != 0) {
10282 g_string_printf (nmc->return_text, _("Error: %s argument is missing."),
10284 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10289 if (next_arg (&argc, &argv) != 0) {
10290 g_string_printf (nmc->return_text, _("Error: <new name> argument is missing."));
10291 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10295 if (next_arg (&argc, &argv) == 0) {
10296 g_string_printf (nmc->return_text, _("Error: unexpected extra argument '%s'."), *argv);
10297 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10303 g_string_printf (nmc->return_text, _("Error: connection ID is missing."));
10304 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10308 g_string_printf (nmc->return_text, _("Error: <new name> argument is missing."));
10309 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10313 connection = nmc_find_connection (nmc->connections, selector, name, NULL);
10315 g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), name);
10316 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10320 /* Copy the connection */
10321 new_connection = nm_simple_connection_new_clone (connection);
10323 s_con = nm_connection_get_setting_connection (new_connection);
10325 uuid = nm_utils_uuid_generate ();
10326 g_object_set (s_con,
10327 NM_SETTING_CONNECTION_ID, new_name,
10328 NM_SETTING_CONNECTION_UUID, uuid,
10332 /* Merge secrets into the new connection */
10333 update_secrets_in_connection (NM_REMOTE_CONNECTION (connection), new_connection);
10335 info = g_slice_new0 (CloneConnectionInfo);
10337 info->orig_id = g_strdup (nm_connection_get_id (connection));
10338 info->orig_uuid = g_strdup (nm_connection_get_uuid (connection));
10339 info->con_id = g_strdup (nm_connection_get_id (new_connection));
10341 /* Add the new cloned connection to NetworkManager */
10342 add_new_connection (!temporary,
10345 clone_connection_cb,
10348 nmc->should_wait = TRUE;
10350 if (new_connection)
10351 g_object_unref (new_connection);
10353 g_free (new_name_ask);
10355 return nmc->return_value;
10359 delete_cb (GObject *con, GAsyncResult *result, gpointer user_data)
10361 ConnectionCbInfo *info = (ConnectionCbInfo *) user_data;
10362 GError *error = NULL;
10364 if (!nm_remote_connection_delete_finish (NM_REMOTE_CONNECTION (con), result, &error)) {
10365 g_string_printf (info->nmc->return_text, _("Error: not all connections deleted."));
10366 g_printerr (_("Error: Connection deletion failed: %s"),
10368 g_error_free (error);
10369 info->nmc->return_value = NMC_RESULT_ERROR_CON_DEL;
10370 connection_cb_info_finish (info, con);
10372 if (info->nmc->nowait_flag)
10373 connection_cb_info_finish (info, con);
10377 static NMCResultCode
10378 do_connection_delete (NmCli *nmc, int argc, char **argv)
10380 NMConnection *connection;
10381 ConnectionCbInfo *info = NULL;
10382 GSList *queue = NULL, *iter;
10383 char **arg_arr = NULL;
10384 char **arg_ptr = argv;
10385 int arg_num = argc;
10386 GString *invalid_cons = NULL;
10389 if (nmc->timeout == -1)
10394 char *line = nmc_readline (PROMPT_CONNECTIONS);
10395 nmc_string_to_arg_array (line, NULL, TRUE, &arg_arr, &arg_num);
10399 if (arg_num == 0) {
10400 g_string_printf (nmc->return_text, _("Error: No connection specified."));
10401 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10406 while (arg_num > 0) {
10407 const char *selector = NULL;
10409 if ( strcmp (*arg_ptr, "id") == 0
10410 || strcmp (*arg_ptr, "uuid") == 0
10411 || strcmp (*arg_ptr, "path") == 0) {
10412 selector = *arg_ptr;
10413 if (next_arg (&arg_num, &arg_ptr) != 0) {
10414 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector);
10415 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10420 connection = nmc_find_connection (nmc->connections, selector, *arg_ptr, &pos);
10422 /* Check if the connection is unique. */
10423 /* Calling delete for the same connection repeatedly would result in
10424 * NM responding for the last D-Bus call only and we would stall. */
10425 if (!g_slist_find (queue, connection))
10426 queue = g_slist_prepend (queue, g_object_ref (connection));
10428 g_printerr (_("Error: unknown connection '%s'\n"), *arg_ptr);
10429 g_string_printf (nmc->return_text, _("Error: not all active connections found."));
10430 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10432 invalid_cons = g_string_new (NULL);
10433 g_string_append_printf (invalid_cons, "'%s', ", *arg_ptr);
10436 /* Take next argument (if there's no other connection of the same name) */
10438 next_arg (&arg_num, &arg_ptr);
10442 g_string_printf (nmc->return_text, _("Error: no connection provided."));
10443 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10446 queue = g_slist_reverse (queue);
10448 info = g_slice_new0 (ConnectionCbInfo);
10450 info->queue = queue;
10451 info->timeout_id = g_timeout_add_seconds (nmc->timeout, connection_op_timeout_cb, info);
10453 nmc->nowait_flag = (nmc->timeout == 0);
10454 nmc->should_wait++;
10456 g_signal_connect (nmc->client, NM_CLIENT_CONNECTION_REMOVED,
10457 G_CALLBACK (connection_removed_cb), info);
10459 /* Now delete the connections */
10460 for (iter = queue; iter; iter = g_slist_next (iter))
10461 nm_remote_connection_delete_async (NM_REMOTE_CONNECTION (iter->data),
10462 NULL, delete_cb, info);
10465 if (invalid_cons) {
10466 g_string_truncate (invalid_cons, invalid_cons->len-2); /* truncate trailing ", " */
10467 g_string_printf (nmc->return_text, _("Error: cannot delete unknown connection(s): %s."),
10468 invalid_cons->str);
10469 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10470 g_string_free (invalid_cons, TRUE);
10472 g_strfreev (arg_arr);
10473 return nmc->return_value;
10477 connection_changed (NMConnection *connection, NmCli *nmc)
10479 g_print (_("%s: connection profile changed\n"), nm_connection_get_id (connection));
10483 connection_watch (NmCli *nmc, NMConnection *connection)
10485 nmc->should_wait++;
10486 g_signal_connect (connection, NM_CONNECTION_CHANGED, G_CALLBACK (connection_changed), nmc);
10490 connection_unwatch (NmCli *nmc, NMConnection *connection)
10492 if (g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_changed), nmc))
10493 nmc->should_wait--;
10495 /* Terminate if all the watched connections disappeared. */
10496 if (!nmc->should_wait)
10501 connection_added (NMClient *client, NMRemoteConnection *con, NmCli *nmc)
10503 NMConnection *connection = NM_CONNECTION (con);
10505 g_print (_("%s: connection profile created\n"), nm_connection_get_id (connection));
10506 connection_watch (nmc, connection);
10510 connection_removed (NMClient *client, NMRemoteConnection *con, NmCli *nmc)
10512 NMConnection *connection = NM_CONNECTION (con);
10514 g_print (_("%s: connection profile removed\n"), nm_connection_get_id (connection));
10515 connection_unwatch (nmc, connection);
10518 static NMCResultCode
10519 do_connection_monitor (NmCli *nmc, int argc, char **argv)
10522 /* No connections specified. Monitor all. */
10525 nmc->connections = nm_client_get_connections (nmc->client);
10526 for (i = 0; i < nmc->connections->len; i++)
10527 connection_watch (nmc, g_ptr_array_index (nmc->connections, i));
10529 /* We'll watch the connection additions too, never exit. */
10530 nmc->should_wait++;
10531 g_signal_connect (nmc->client, NM_CLIENT_CONNECTION_ADDED, G_CALLBACK (connection_added), nmc);
10533 /* Look up the specified connections and watch them. */
10534 NMConnection *connection;
10535 char **arg_ptr = argv;
10536 int arg_num = argc;
10540 const char *selector = NULL;
10542 if ( strcmp (*arg_ptr, "id") == 0
10543 || strcmp (*arg_ptr, "uuid") == 0
10544 || strcmp (*arg_ptr, "path") == 0) {
10545 selector = *arg_ptr;
10546 if (next_arg (&arg_num, &arg_ptr) != 0) {
10547 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector);
10548 return NMC_RESULT_ERROR_USER_INPUT;
10552 connection = nmc_find_connection (nmc->connections, selector, *arg_ptr, &pos);
10554 connection_watch (nmc, connection);
10556 g_printerr (_("Error: unknown connection '%s'\n"), *arg_ptr);
10557 g_string_printf (nmc->return_text, _("Error: not all connections found."));
10558 return NMC_RESULT_ERROR_NOT_FOUND;
10561 /* Take next argument (if there's no other connection of the same name) */
10563 next_arg (&arg_num, &arg_ptr);
10564 } while (arg_num > 0);
10567 g_signal_connect (nmc->client, NM_CLIENT_CONNECTION_REMOVED, G_CALLBACK (connection_removed), nmc);
10569 return NMC_RESULT_SUCCESS;
10572 static NMCResultCode
10573 do_connection_reload (NmCli *nmc, int argc, char **argv)
10575 GError *error = NULL;
10577 nmc->return_value = NMC_RESULT_SUCCESS;
10579 if (!nm_client_reload_connections (nmc->client, NULL, &error)) {
10580 g_string_printf (nmc->return_text, _("Error: failed to reload connections: %s."),
10582 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10583 g_clear_error (&error);
10586 return nmc->return_value;
10589 static NMCResultCode
10590 do_connection_load (NmCli *nmc, int argc, char **argv)
10592 GError *error = NULL;
10593 char **filenames, **failures = NULL;
10596 nmc->return_value = NMC_RESULT_SUCCESS;
10599 g_string_printf (nmc->return_text, _("Error: No connection specified."));
10600 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10601 return nmc->return_value;
10604 filenames = g_new (char *, argc + 1);
10605 for (i = 0; i < argc; i++)
10606 filenames[i] = argv[i];
10607 filenames[i] = NULL;
10609 nm_client_load_connections (nmc->client, filenames, &failures, NULL, &error);
10610 g_free (filenames);
10612 g_string_printf (nmc->return_text, _("Error: failed to load connection: %s."),
10614 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10615 g_error_free (error);
10619 for (i = 0; failures[i]; i++)
10620 g_printerr (_("Could not load file '%s'\n"), failures[i]);
10621 g_strfreev (failures);
10624 return nmc->return_value;
10627 // FIXME: change the text when non-VPN connection types are supported
10628 #define PROMPT_IMPORT_TYPE PROMPT_VPN_TYPE
10629 #define PROMPT_IMPORT_FILE _("File to import: ")
10631 static NMCResultCode
10632 do_connection_import (NmCli *nmc, gboolean temporary, int argc, char **argv)
10634 GError *error = NULL;
10635 const char *type = NULL, *filename = NULL;
10636 char *type_ask = NULL, *filename_ask = NULL;
10637 AddConnectionInfo *info;
10638 NMConnection *connection = NULL;
10639 NMVpnEditorPlugin *plugin;
10643 type_ask = nmc_readline (PROMPT_IMPORT_TYPE);
10644 filename_ask = nmc_readline (PROMPT_IMPORT_FILE);
10645 type = type_ask = type_ask ? g_strstrip (type_ask) : NULL;
10646 filename = filename_ask = filename_ask ? g_strstrip (filename_ask) : NULL;
10648 g_string_printf (nmc->return_text, _("Error: No arguments provided."));
10649 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10655 if (strcmp (*argv, "type") == 0) {
10656 if (next_arg (&argc, &argv) != 0) {
10657 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
10658 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10664 g_printerr (_("Warning: 'type' already specified, ignoring extra one.\n"));
10666 } else if (strcmp (*argv, "file") == 0) {
10667 if (next_arg (&argc, &argv) != 0) {
10668 g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1));
10669 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10675 g_printerr (_("Warning: 'file' already specified, ignoring extra one.\n"));
10677 g_string_printf (nmc->return_text, _("Unknown parameter: %s"), *argv);
10678 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10687 g_string_printf (nmc->return_text, _("Error: 'type' argument is required."));
10688 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10692 g_string_printf (nmc->return_text, _("Error: 'file' argument is required."));
10693 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10697 /* Import VPN configuration */
10698 plugin = nm_vpn_get_plugin_by_service (type, &error);
10700 g_string_printf (nmc->return_text, _("Error: failed to load VPN plugin: %s."),
10702 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10706 connection = nm_vpn_editor_plugin_import (plugin, filename, &error);
10708 g_string_printf (nmc->return_text, _("Error: failed to import '%s': %s."),
10709 filename, error->message);
10710 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10714 info = g_malloc0 (sizeof (AddConnectionInfo));
10716 info->con_name = g_strdup (nm_connection_get_id (connection));
10718 /* Add the new imported connection to NetworkManager */
10719 add_new_connection (!temporary,
10725 nmc->should_wait = TRUE;
10728 g_object_unref (connection);
10729 g_clear_error (&error);
10731 g_free (filename_ask);
10732 return nmc->return_value;
10735 static NMCResultCode
10736 do_connection_export (NmCli *nmc, int argc, char **argv)
10738 NMConnection *connection = NULL;
10740 const char *out_name = NULL;
10741 char *name_ask = NULL;
10742 char *out_name_ask = NULL;
10743 const char *path = NULL;
10744 const char *selector = NULL;
10745 const char *type = NULL;
10746 NMVpnEditorPlugin *plugin;
10747 GError *error = NULL;
10748 char tmpfile[] = "/tmp/nmcli-export-temp-XXXXXX";
10752 name_ask = nmc_readline (PROMPT_VPN_CONNECTION);
10753 name = name_ask = name_ask ? g_strstrip (name_ask) : NULL;
10754 out_name = out_name_ask = nmc_readline (_("Output file name: "));
10756 g_string_printf (nmc->return_text, _("Error: No arguments provided."));
10757 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10761 if ( strcmp (*argv, "id") == 0
10762 || strcmp (*argv, "uuid") == 0
10763 || strcmp (*argv, "path") == 0) {
10766 if (next_arg (&argc, &argv) != 0) {
10767 g_string_printf (nmc->return_text, _("Error: %s argument is missing."),
10769 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10774 if (next_arg (&argc, &argv) == 0)
10777 if (next_arg (&argc, &argv) == 0) {
10778 g_string_printf (nmc->return_text, _("Error: unknown extra argument: '%s'."), *argv);
10779 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10785 g_string_printf (nmc->return_text, _("Error: connection ID is missing."));
10786 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10789 connection = nmc_find_connection (nmc->connections, selector, name, NULL);
10791 g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), name);
10792 nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
10796 type = nm_connection_get_connection_type (connection);
10797 if (g_strcmp0 (type, NM_SETTING_VPN_SETTING_NAME) != 0) {
10798 g_string_printf (nmc->return_text, _("Error: the connection is not VPN."));
10799 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
10802 type = nm_setting_vpn_get_service_type (nm_connection_get_setting_vpn (connection));
10804 /* Export VPN configuration */
10805 plugin = nm_vpn_get_plugin_by_service (type, &error);
10807 g_string_printf (nmc->return_text, _("Error: failed to load VPN plugin: %s."),
10809 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10817 fd = g_mkstemp (tmpfile);
10819 g_string_printf (nmc->return_text, _("Error: failed to create temporary file %s."), tmpfile);
10820 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10827 if (!nm_vpn_editor_plugin_export (plugin, path, connection, &error)) {
10828 g_string_printf (nmc->return_text, _("Error: failed to export '%s': %s."),
10829 nm_connection_get_id (connection), error->message);
10830 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10834 /* No output file -> copy data to stdout */
10836 char *contents = NULL;
10838 if (!g_file_get_contents (path, &contents, &len, &error)) {
10839 g_string_printf (nmc->return_text, _("Error: failed to read temporary file '%s': %s."),
10840 path, error->message);
10841 nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
10844 g_print ("%s", contents);
10849 if (!out_name && path)
10851 g_clear_error (&error);
10853 g_free (out_name_ask);
10854 return nmc->return_value;
10862 } NmcEditorThreadData;
10864 static GThread *editor_thread;
10865 static NmcEditorThreadData editor_thread_data;
10868 * We need to run do_connection_edit() in a thread so that
10869 * glib main loop is not blocked and could receive and process D-Bus
10873 connection_editor_thread_func (gpointer data)
10875 NmcEditorThreadData *td = (NmcEditorThreadData *) data;
10877 /* run editor for editing/adding connections */
10878 td->nmc->return_value = do_connection_edit (td->nmc, td->argc, td->argv);
10880 /* quit glib main loop now that we are done with this thread */
10887 gen_func_connection_names (const char *text, int state)
10890 const char **connections;
10893 if (nm_cli.connections->len == 0)
10896 connections = g_new (const char *, nm_cli.connections->len + 1);
10897 for (i = 0; i < nm_cli.connections->len; i++) {
10898 NMConnection *con = NM_CONNECTION (nm_cli.connections->pdata[i]);
10899 const char *id = nm_connection_get_id (con);
10900 connections[i] = id;
10902 connections[i] = NULL;
10904 ret = nmc_rl_gen_func_basic (text, state, connections);
10906 g_free (connections);
10911 gen_func_active_connection_names (const char *text, int state)
10914 const GPtrArray *acs;
10915 const char **connections;
10918 if (!nm_cli.client)
10921 acs = nm_client_get_active_connections (nm_cli.client);
10922 if (!acs || acs->len == 0)
10925 connections = g_new (const char *, acs->len + 1);
10926 for (i = 0; i < acs->len; i++)
10927 connections[i] = nm_active_connection_get_id (acs->pdata[i]);
10928 connections[i] = NULL;
10930 ret = nmc_rl_gen_func_basic (text, state, connections);
10932 g_free (connections);
10937 nmcli_con_tab_completion (const char *text, int start, int end)
10939 char **match_array = NULL;
10940 rl_compentry_func_t *generator_func = NULL;
10942 /* Disable readline's default filename completion */
10943 rl_attempted_completion_over = 1;
10945 if (g_strcmp0 (rl_prompt, PROMPT_CONNECTION) == 0) {
10946 /* Disable appending space after completion */
10947 rl_completion_append_character = '\0';
10949 if (!is_single_word (rl_line_buffer))
10952 generator_func = gen_func_connection_names;
10953 } else if (g_strcmp0 (rl_prompt, PROMPT_CONNECTIONS) == 0) {
10954 generator_func = gen_func_connection_names;
10955 } else if (g_strcmp0 (rl_prompt, PROMPT_ACTIVE_CONNECTIONS) == 0) {
10956 generator_func = gen_func_active_connection_names;
10957 } else if (g_strcmp0 (rl_prompt, PROMPT_IMPORT_TYPE) == 0) {
10958 generator_func = gen_func_vpn_types;
10959 } else if (g_strcmp0 (rl_prompt, PROMPT_IMPORT_FILE) == 0) {
10960 rl_attempted_completion_over = 0;
10961 rl_complete_with_tilde_expansion = 1;
10962 } else if (g_strcmp0 (rl_prompt, PROMPT_VPN_CONNECTION) == 0) {
10963 generator_func = gen_vpn_ids;
10966 if (generator_func)
10967 match_array = rl_completion_matches (text, generator_func);
10969 return match_array;
10973 parse_preferred_connection_order (const char *order, GError **error)
10975 char **strv, **iter;
10979 gboolean inverse, unique;
10982 strv = nmc_strsplit_set (order, ":", -1);
10983 if (!strv || !*strv) {
10984 g_set_error (error, NMCLI_ERROR, 0,
10985 _("incorrect string '%s' of '--order' option"), order);
10990 order_arr = g_array_sized_new (FALSE, FALSE, sizeof (NmcSortOrder), 4);
10991 for (iter = strv; iter && *iter; iter++) {
10996 if (str[0] == '+' || str[0] == '-')
10999 if (matches (str, "active") == 0)
11000 val = inverse ? NMC_SORT_ACTIVE_INV : NMC_SORT_ACTIVE;
11001 else if (matches (str, "name") == 0)
11002 val = inverse ? NMC_SORT_NAME_INV : NMC_SORT_NAME;
11003 else if (matches (str, "type") == 0)
11004 val = inverse ? NMC_SORT_TYPE_INV : NMC_SORT_TYPE;
11005 else if (matches (str, "path") == 0)
11006 val = inverse ? NMC_SORT_PATH_INV : NMC_SORT_PATH;
11008 g_array_unref (order_arr);
11010 g_set_error (error, NMCLI_ERROR, 0,
11011 _("incorrect item '%s' in '--order' option"), *iter);
11014 /* Check for duplicates and ignore them. */
11016 for (i = 0; i < order_arr->len; i++) {
11017 if (abs (g_array_index (order_arr, NmcSortOrder, i)) - abs (val) == 0) {
11023 /* Value is ok and unique, add it to the array */
11025 g_array_append_val (order_arr, val);
11032 /* Entry point function for connections-related commands: 'nmcli connection' */
11034 do_connections (NmCli *nmc, int argc, char **argv)
11036 GError *error = NULL;
11038 /* Register polkit agent */
11039 nmc_start_polkit_agent_start_try (nmc);
11041 /* Set completion function for 'nmcli con' */
11042 rl_attempted_completion_function = (rl_completion_func_t *) nmcli_con_tab_completion;
11044 /* Exit early on help */
11045 if (nmc_arg_is_help (*argv)) {
11047 return nmc->return_value;
11049 if (argc != 0 && nmc_arg_is_help (*(argv+1))) {
11050 if (usage_connection_second_level (*argv))
11051 return nmc->return_value;
11054 /* Get NMClient object early */
11055 nmc->get_client (nmc);
11057 /* Check whether NetworkManager is running */
11058 if (!nm_client_get_nm_running (nmc->client)) {
11059 g_string_printf (nmc->return_text, _("Error: NetworkManager is not running."));
11060 nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING;
11061 return nmc->return_value;
11063 /* Compare NM and nmcli versions */
11064 if (!nmc_versions_match (nmc))
11065 return nmc->return_value;
11067 /* Get the connection list */
11068 nmc->connections = nm_client_get_connections (nmc->client);
11070 /* Now parse the command line and perform the required operation */
11072 if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error))
11074 nmc->return_value = do_connections_show (nmc, FALSE, FALSE, NULL, argc, argv);
11076 if (matches (*argv, "show") == 0) {
11077 gboolean active = FALSE;
11078 gboolean show_secrets = FALSE;
11079 GArray *order = NULL;
11082 next_arg (&argc, &argv);
11083 /* check connection show options [--active] [--show-secrets] */
11084 for (i = 0; i < 3; i++) {
11085 if (!active && nmc_arg_is_option (*argv, "active")) {
11087 next_arg (&argc, &argv);
11089 /* --show-secrets is deprecated in favour of global --show-secrets */
11090 /* Keep it here for backwards compatibility */
11091 if (!show_secrets && nmc_arg_is_option (*argv, "show-secrets")) {
11092 show_secrets = TRUE;
11093 next_arg (&argc, &argv);
11095 if (!order && nmc_arg_is_option (*argv, "order")) {
11096 if (next_arg (&argc, &argv) != 0) {
11097 g_set_error_literal (&error, NMCLI_ERROR, 0,
11098 _("'--order' argument is missing"));
11101 order = parse_preferred_connection_order (*argv, &error);
11104 next_arg (&argc, &argv);
11107 show_secrets = nmc->show_secrets || show_secrets;
11108 nmc->return_value = do_connections_show (nmc, active, show_secrets, order, argc, argv);
11110 g_array_unref (order);
11111 } else if (matches(*argv, "up") == 0) {
11112 nmc->return_value = do_connection_up (nmc, argc-1, argv+1);
11113 } else if (matches(*argv, "down") == 0) {
11114 nmc->return_value = do_connection_down (nmc, argc-1, argv+1);
11115 } else if (matches(*argv, "add") == 0) {
11116 nmc->return_value = do_connection_add (nmc, argc-1, argv+1);
11117 } else if (matches(*argv, "edit") == 0) {
11118 nmc->should_wait++;
11119 editor_thread_data.nmc = nmc;
11120 editor_thread_data.argc = argc - 1;
11121 editor_thread_data.argv = argv + 1;
11122 editor_thread = g_thread_new ("editor-thread", connection_editor_thread_func, &editor_thread_data);
11123 g_thread_unref (editor_thread);
11124 } else if (matches(*argv, "delete") == 0) {
11125 nmc->return_value = do_connection_delete (nmc, argc-1, argv+1);
11126 } else if (matches(*argv, "reload") == 0) {
11127 nmc->return_value = do_connection_reload (nmc, argc-1, argv+1);
11128 } else if (matches(*argv, "load") == 0) {
11129 nmc->return_value = do_connection_load (nmc, argc-1, argv+1);
11130 } else if (matches (*argv, "modify") == 0) {
11131 gboolean temporary = FALSE;
11133 next_arg (&argc, &argv);
11134 if (nmc_arg_is_option (*argv, "temporary")) {
11136 next_arg (&argc, &argv);
11138 nmc->return_value = do_connection_modify (nmc, temporary, argc, argv);
11139 } else if (matches (*argv, "clone") == 0) {
11140 gboolean temporary = FALSE;
11142 next_arg (&argc, &argv);
11143 if (nmc_arg_is_option (*argv, "temporary")) {
11145 next_arg (&argc, &argv);
11147 nmc->return_value = do_connection_clone (nmc, temporary, argc, argv);
11148 } else if (matches(*argv, "import") == 0) {
11149 gboolean temporary = FALSE;
11151 next_arg (&argc, &argv);
11152 if (nmc_arg_is_option (*argv, "temporary")) {
11154 next_arg (&argc, &argv);
11156 nmc->return_value = do_connection_import (nmc, temporary, argc, argv);
11157 } else if (matches(*argv, "export") == 0) {
11158 nmc->return_value = do_connection_export (nmc, argc-1, argv+1);
11159 } else if (matches(*argv, "monitor") == 0) {
11160 nmc->return_value = do_connection_monitor (nmc, argc-1, argv+1);
11163 g_string_printf (nmc->return_text, _("Error: '%s' is not valid 'connection' command."), *argv);
11164 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
11168 return nmc->return_value;
11171 g_string_printf (nmc->return_text, _("Error: %s."), error->message);
11172 nmc->return_value = NMC_RESULT_ERROR_USER_INPUT;
11173 g_error_free (error);
11174 return nmc->return_value;
11178 monitor_connections (NmCli *nmc)
11180 do_connection_monitor (nmc, 0, NULL);